|
1 | 1 | import type { paths } from "../generated/api"; |
2 | | -import { createApiClient } from "./client"; |
3 | | -import { CLIError } from "../util/cli-error"; |
4 | | -import { getCliPrefix } from "../util/package-manager"; |
5 | 2 |
|
6 | 3 | // --- Types extracted from the generated schema --- |
7 | 4 |
|
@@ -48,160 +45,3 @@ export function isSingleContentRule( |
48 | 45 | ): rule is SingleContentRule { |
49 | 46 | return !isFileSetRule(rule); |
50 | 47 | } |
51 | | - |
52 | | -// --- Helpers --- |
53 | | - |
54 | | -/** Extract error details from an untyped error response body */ |
55 | | -function parseErrorBody(rawError: unknown): Record<string, unknown> { |
56 | | - if (rawError && typeof rawError === "object") { |
57 | | - return rawError as Record<string, unknown>; |
58 | | - } |
59 | | - return {}; |
60 | | -} |
61 | | - |
62 | | -/** |
63 | | - * The server returns the same 404 `organization_not_found` whether the org |
64 | | - * isn't yours or its GitHub App installation doesn't cover this repository (it |
65 | | - * deliberately doesn't distinguish, to avoid leaking org existence), so the |
66 | | - * message names both causes — coverage first, since a resolved org subject |
67 | | - * makes membership the less likely one. |
68 | | - */ |
69 | | -function orgNotFoundMessage(): string { |
70 | | - return [ |
71 | | - "Taskless could not act on this repository for your organization.", |
72 | | - "", |
73 | | - "Most often the organization's Taskless GitHub App installation does not cover this repository. It can also mean your login no longer has access to the organization.", |
74 | | - "", |
75 | | - "- Confirm the Taskless app is installed on this repository's owner and includes this repository.", |
76 | | - `- If access recently changed, re-authenticate with \`${getCliPrefix()} auth login\`.`, |
77 | | - ].join("\n"); |
78 | | -} |
79 | | - |
80 | | -// --- API functions --- |
81 | | - |
82 | | -/** Submit a new rule generation request */ |
83 | | -export async function submitRule( |
84 | | - token: string, |
85 | | - request: { |
86 | | - /** Org subject: Taskless UUID (preferred) or numeric GitHub org id. */ |
87 | | - orgId: string | number; |
88 | | - repositoryUrl: string; |
89 | | - prompt: string; |
90 | | - successCases?: string[]; |
91 | | - failureCases?: string[]; |
92 | | - } |
93 | | -) { |
94 | | - const client = createApiClient(token); |
95 | | - const { data, error, response } = await client.POST("/cli/api/request", { |
96 | | - body: request, |
97 | | - }); |
98 | | - |
99 | | - if (!data) { |
100 | | - const errorData = parseErrorBody(error); |
101 | | - if (response.status === 400 && errorData.error === "validation_error") { |
102 | | - const details = (errorData.details as string[]) ?? []; |
103 | | - throw new Error(`Validation error: ${details.join(", ")}`); |
104 | | - } |
105 | | - if ( |
106 | | - response.status === 403 && |
107 | | - errorData.error === "repository_not_accessible" |
108 | | - ) { |
109 | | - throw new Error( |
110 | | - [ |
111 | | - "Repository is not accessible to this organization.", |
112 | | - "", |
113 | | - "- Verify that your local `origin` remote points to the intended GitHub repository.", |
114 | | - "- Confirm that your GitHub user/organization has access to that repository.", |
115 | | - `- If you recently changed access or remotes, try re-authenticating with \`${getCliPrefix()} auth login\`.`, |
116 | | - ].join("\n") |
117 | | - ); |
118 | | - } |
119 | | - if ( |
120 | | - response.status === 404 && |
121 | | - errorData.error === "organization_not_found" |
122 | | - ) { |
123 | | - throw new Error(orgNotFoundMessage()); |
124 | | - } |
125 | | - throw new Error( |
126 | | - `Request submission failed (HTTP ${String(response.status)})` |
127 | | - ); |
128 | | - } |
129 | | - |
130 | | - return data; |
131 | | -} |
132 | | - |
133 | | -/** Poll for rule generation status */ |
134 | | -export async function pollRuleStatus(token: string, requestId: string) { |
135 | | - const client = createApiClient(token); |
136 | | - const { data, error, response } = await client.GET( |
137 | | - "/cli/api/request/{requestId}", |
138 | | - { |
139 | | - params: { path: { requestId } }, |
140 | | - } |
141 | | - ); |
142 | | - |
143 | | - if (!data) { |
144 | | - const errorData = parseErrorBody(error); |
145 | | - if (response.status === 403 && errorData.error === "access_denied") { |
146 | | - throw new Error("Access denied to this request."); |
147 | | - } |
148 | | - if (response.status === 404 && errorData.error === "request_not_found") { |
149 | | - throw new Error("Request not found. It may have expired."); |
150 | | - } |
151 | | - throw new Error(`Status polling failed (HTTP ${String(response.status)})`); |
152 | | - } |
153 | | - |
154 | | - return data; |
155 | | -} |
156 | | - |
157 | | -/** Submit an improve/iterate request for an existing rule */ |
158 | | -export async function iterateRule( |
159 | | - token: string, |
160 | | - requestId: string, |
161 | | - request: { |
162 | | - /** Org subject: Taskless UUID (preferred) or numeric GitHub org id. */ |
163 | | - orgId: string | number; |
164 | | - guidance: string; |
165 | | - references?: Array<{ filename: string; content: string }>; |
166 | | - } |
167 | | -) { |
168 | | - const client = createApiClient(token); |
169 | | - const { data, error, response } = await client.POST( |
170 | | - "/cli/api/request/{requestId}/iterate", |
171 | | - { |
172 | | - params: { path: { requestId } }, |
173 | | - body: request, |
174 | | - } |
175 | | - ); |
176 | | - |
177 | | - if (!data) { |
178 | | - const errorData = parseErrorBody(error); |
179 | | - if (response.status === 400 && errorData.error === "validation_error") { |
180 | | - const details = (errorData.details as string[]) ?? []; |
181 | | - throw new Error(`Validation error: ${details.join(", ")}`); |
182 | | - } |
183 | | - if (response.status === 403 && errorData.error === "access_denied") { |
184 | | - throw new Error("Access denied to this request."); |
185 | | - } |
186 | | - if (response.status === 404 && errorData.error === "request_not_found") { |
187 | | - // The caller supplied this ticket id (from `rule create --json`, or from |
188 | | - // the user), so "no such id" is a state they can act on: re-check the id. |
189 | | - // The code travels on the error so `improveCommand` reports |
190 | | - // RULE_NOT_FOUND rather than folding it into NETWORK_ERROR, which would |
191 | | - // tell an agent to retry an id that will never resolve. |
192 | | - throw new CLIError( |
193 | | - "Rule not found. It may have expired.", |
194 | | - "RULE_NOT_FOUND" |
195 | | - ); |
196 | | - } |
197 | | - if ( |
198 | | - response.status === 404 && |
199 | | - errorData.error === "organization_not_found" |
200 | | - ) { |
201 | | - throw new Error(orgNotFoundMessage()); |
202 | | - } |
203 | | - throw new Error(`Iterate request failed (HTTP ${String(response.status)})`); |
204 | | - } |
205 | | - |
206 | | - return data; |
207 | | -} |
0 commit comments