@typeship-ax/cli 0.10.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -4
- package/README.md +4 -4
- package/api.json +4829 -679
- package/api.md +387 -91
- package/dist/cli-agent.d.ts +5 -5
- package/dist/cli-agent.d.ts.map +1 -1
- package/dist/cli-agent.js +14 -10
- package/dist/cli.js +151 -75
- package/dist/core/http.d.ts +3 -2
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/http.js +18 -6
- package/dist/errors.d.ts +17 -10
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +24 -15
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/ops.d.ts +4 -0
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js +45 -35
- package/dist/polling-login.d.ts.map +1 -1
- package/dist/polling-login.js +11 -1
- package/dist/resources/account.d.ts +4 -4
- package/dist/resources/account.d.ts.map +1 -1
- package/dist/resources/account.js +10 -5
- package/dist/resources/api-keys.d.ts +40 -14
- package/dist/resources/api-keys.d.ts.map +1 -1
- package/dist/resources/api-keys.js +45 -13
- package/dist/resources/definition-revisions.d.ts +35 -18
- package/dist/resources/definition-revisions.d.ts.map +1 -1
- package/dist/resources/definition-revisions.js +43 -15
- package/dist/resources/definitions.d.ts +21 -6
- package/dist/resources/definitions.d.ts.map +1 -1
- package/dist/resources/definitions.js +16 -3
- package/dist/resources/generate.d.ts +44 -15
- package/dist/resources/generate.d.ts.map +1 -1
- package/dist/resources/generate.js +55 -13
- package/dist/resources/generations.d.ts +14 -6
- package/dist/resources/generations.d.ts.map +1 -1
- package/dist/resources/generations.js +26 -5
- package/dist/resources/projects.d.ts +106 -53
- package/dist/resources/projects.d.ts.map +1 -1
- package/dist/resources/projects.js +78 -31
- package/dist/resources/targets.d.ts +240 -40
- package/dist/resources/targets.d.ts.map +1 -1
- package/dist/resources/targets.js +307 -21
- package/dist/schemas.d.ts +1 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +171 -111
- package/dist/types.d.ts +1969 -154
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +87 -3
- package/package.json +3 -3
- package/src/cli-agent.ts +17 -13
- package/src/cli.ts +138 -70
- package/src/core/http.ts +17 -6
- package/src/errors.ts +25 -15
- package/src/index.ts +9 -5
- package/src/ops.ts +49 -35
- package/src/polling-login.ts +10 -1
- package/src/resources/account.ts +11 -4
- package/src/resources/api-keys.ts +84 -13
- package/src/resources/definition-revisions.ts +74 -16
- package/src/resources/definitions.ts +28 -4
- package/src/resources/generate.ts +78 -13
- package/src/resources/generations.ts +31 -4
- package/src/resources/projects.ts +141 -39
- package/src/resources/targets.ts +561 -27
- package/src/schemas.ts +172 -112
- package/src/types.ts +2105 -154
package/dist/cli-agent.d.ts
CHANGED
|
@@ -94,8 +94,8 @@ export declare function upsertAgentBlock(file: string, marker: string, body: str
|
|
|
94
94
|
file: string;
|
|
95
95
|
updated: boolean;
|
|
96
96
|
};
|
|
97
|
-
/** AGENTS.md
|
|
98
|
-
export declare function agentInstructionsFile(cwd: string
|
|
97
|
+
/** AGENTS.md for every harness; preserve an existing CLAUDE.md when AGENTS.md is absent. */
|
|
98
|
+
export declare function agentInstructionsFile(cwd: string): string;
|
|
99
99
|
export interface CommandFlagSummary {
|
|
100
100
|
flag: string;
|
|
101
101
|
/** string | number | boolean | array | object | json | file */
|
|
@@ -164,8 +164,8 @@ export declare function installSkills(repo: string | null, options?: {
|
|
|
164
164
|
}): SkillsInstallResult;
|
|
165
165
|
/**
|
|
166
166
|
* A response that carries files: an array property whose items have string
|
|
167
|
-
* `path` and `content
|
|
168
|
-
*
|
|
167
|
+
* `path` and `content`, as an API that returns generated or exported files
|
|
168
|
+
* does. `--out <dir>` writes them.
|
|
169
169
|
*/
|
|
170
170
|
export declare function bundleProperty(outputSchema: Record<string, unknown> | undefined): string | null;
|
|
171
171
|
/** A non-page response whose result is a collection wrapped in one array
|
|
@@ -182,7 +182,7 @@ export declare function writeBundle(dir: string, files: {
|
|
|
182
182
|
};
|
|
183
183
|
/**
|
|
184
184
|
* A response that carries a claim: an object property `claim` with a string
|
|
185
|
-
* `url
|
|
185
|
+
* `url`, as an anonymous run that can later be saved has. The CLI tells the person
|
|
186
186
|
* where to claim and leaves a breadcrumb in the working directory so a
|
|
187
187
|
* later session (or `doctor`) can list what is still unclaimed.
|
|
188
188
|
*/
|
package/dist/cli-agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-agent.d.ts","sourceRoot":"","sources":["../src/cli-agent.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AACH,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,cAAc,GACd,YAAY,GACZ,WAAW,GACX,iBAAiB,GACjB,cAAc,GACd,cAAc,GACd,cAAc,GACd,eAAe,GACf,mBAAmB,GACnB,cAAc,GACd,uBAAuB,GACvB,eAAe,GACf,iBAAiB,GACjB,cAAc,GACd,kBAAkB,GAClB,aAAa,CAAC;AAElB,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,qGAAqG;IACrG,MAAM,EAAE,OAAO,GAAG,iBAAiB,CAAC;IACpC,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,mFAAmF;IACnF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,QAAQ,CAQvD;AAED,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,CAAC,GAAG,CAAC,CAYlD;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACvE,aAAa,
|
|
1
|
+
{"version":3,"file":"cli-agent.d.ts","sourceRoot":"","sources":["../src/cli-agent.ts"],"names":[],"mappings":"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"}
|
package/dist/cli-agent.js
CHANGED
|
@@ -43,7 +43,12 @@ export function classifyApiError(error, context) {
|
|
|
43
43
|
// (no such account)" said one thing three times.
|
|
44
44
|
const base = e.message ? e.message : String(error);
|
|
45
45
|
if (e.violations !== undefined) {
|
|
46
|
-
|
|
46
|
+
const nextStep = e.direction === "response"
|
|
47
|
+
? "The API response does not match its schema. Check detail.violations and the API specification."
|
|
48
|
+
: e.target === "parameters"
|
|
49
|
+
? "Correct the parameter values named in detail.violations to match their constraints, then run the command again."
|
|
50
|
+
: "Correct the body fields named in detail.violations to match their constraints, then run the command again.";
|
|
51
|
+
return { code: "VALIDATION_FAILED", message: base, detail: { ...(e.name ? { error: e.name } : {}), violations: e.violations }, nextSteps: [nextStep] };
|
|
47
52
|
}
|
|
48
53
|
if (e.name === "TransportError" || (typeof e.status !== "number" && /fetch|ECONN|ENOTFOUND|timed out|TLS|abort/i.test(base))) {
|
|
49
54
|
return {
|
|
@@ -280,7 +285,6 @@ export const MCP_CLIENTS = [
|
|
|
280
285
|
file: (cwd) => join(cwd, ".cursor", "mcp.json"),
|
|
281
286
|
detect: () => existsSync(join(home(), ".cursor")),
|
|
282
287
|
write: (existing, name, entry) => jsonMerge(existing, ["mcpServers"], name, standardEntry(entry)),
|
|
283
|
-
incompatible: "Cursor does not yet speak MCP 2026-07-28, which is the only version this server serves; the entry is written but Cursor will not connect until it does.",
|
|
284
288
|
},
|
|
285
289
|
];
|
|
286
290
|
export function findMcpClient(id) {
|
|
@@ -332,15 +336,15 @@ export function upsertAgentBlock(file, marker, body) {
|
|
|
332
336
|
function escapeRegExp(text) {
|
|
333
337
|
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
334
338
|
}
|
|
335
|
-
/** AGENTS.md
|
|
336
|
-
export function agentInstructionsFile(cwd
|
|
339
|
+
/** AGENTS.md for every harness; preserve an existing CLAUDE.md when AGENTS.md is absent. */
|
|
340
|
+
export function agentInstructionsFile(cwd) {
|
|
337
341
|
const agents = join(cwd, "AGENTS.md");
|
|
338
342
|
const claude = join(cwd, "CLAUDE.md");
|
|
339
343
|
if (existsSync(agents))
|
|
340
344
|
return agents;
|
|
341
345
|
if (existsSync(claude))
|
|
342
346
|
return claude;
|
|
343
|
-
return
|
|
347
|
+
return agents;
|
|
344
348
|
}
|
|
345
349
|
/** One line per command, pipe-delimited: the compact index that goes into AGENTS.md. */
|
|
346
350
|
/** string, number, usd|eur, string[], usd|eur[], object, json — the type as a reader expects it. */
|
|
@@ -365,7 +369,7 @@ export function agentBlock(ctx, commands) {
|
|
|
365
369
|
return [
|
|
366
370
|
"## " + ctx.bin + " CLI (" + ctx.apiTitle + ")",
|
|
367
371
|
"",
|
|
368
|
-
"
|
|
372
|
+
"API commands write JSON on stdout; discovery commands take --json. Errors are JSON on stderr ({status, issues[{code,message}], next_steps}), exit 0/1/2. Non-interactive under an agent: no prompts, no browsers.",
|
|
369
373
|
"",
|
|
370
374
|
...((ctx.omittedOperations?.length ?? 0) > 0 ? ["- Plan limit: generated " + (ctx.generatedOperationCount ?? commands.length) + " of " + ((ctx.generatedOperationCount ?? commands.length) + ctx.omittedOperations.length) + " operations. Omitted: " + ctx.omittedOperations.map((op) => "`" + op.tool + "` (" + op.method + " " + op.path + ")").join(", ") + ". These return `PLAN_LIMIT`; upgrade and regenerate before use."] : []),
|
|
371
375
|
"- Auth: " + auth + " in the environment, or `" + ctx.bin + " login`. Never write a key into a file in this repo.",
|
|
@@ -442,8 +446,8 @@ export function installSkills(repo, options = {}) {
|
|
|
442
446
|
// ---- file bundles ------------------------------------------------------------------
|
|
443
447
|
/**
|
|
444
448
|
* A response that carries files: an array property whose items have string
|
|
445
|
-
* `path` and `content
|
|
446
|
-
*
|
|
449
|
+
* `path` and `content`, as an API that returns generated or exported files
|
|
450
|
+
* does. `--out <dir>` writes them.
|
|
447
451
|
*/
|
|
448
452
|
export function bundleProperty(outputSchema) {
|
|
449
453
|
const props = outputSchema?.properties;
|
|
@@ -462,7 +466,7 @@ export function bundleProperty(outputSchema) {
|
|
|
462
466
|
export function collectionProperty(outputSchema) {
|
|
463
467
|
const props = outputSchema?.properties;
|
|
464
468
|
// Response metadata is not part of the collection's domain shape. APIs
|
|
465
|
-
//
|
|
469
|
+
// may put request_id beside the array on every JSON response.
|
|
466
470
|
const names = props ? Object.keys(props).filter((name) => name !== "request_id" && name !== "requestId") : [];
|
|
467
471
|
if (!props || names.length !== 1)
|
|
468
472
|
return null;
|
|
@@ -486,7 +490,7 @@ export function writeBundle(dir, files) {
|
|
|
486
490
|
// ---- claims ----------------------------------------------------------------------------
|
|
487
491
|
/**
|
|
488
492
|
* A response that carries a claim: an object property `claim` with a string
|
|
489
|
-
* `url
|
|
493
|
+
* `url`, as an anonymous run that can later be saved has. The CLI tells the person
|
|
490
494
|
* where to claim and leaves a breadcrumb in the working directory so a
|
|
491
495
|
* later session (or `doctor`) can list what is still unclaimed.
|
|
492
496
|
*/
|
package/dist/cli.js
CHANGED
|
@@ -20,6 +20,8 @@ import { homedir, hostname } from "node:os";
|
|
|
20
20
|
import { basename, join } from "node:path";
|
|
21
21
|
import { fileURLToPath } from "node:url";
|
|
22
22
|
import { TypeshipClient, formatDebugEvent } from "./index.js";
|
|
23
|
+
import { validateAgainstSchema, ValidationError } from "./core/http.js";
|
|
24
|
+
import { SCHEMAS, DEFS } from "./schemas.js";
|
|
23
25
|
import { GLOBALS, OMITTED_OPS, OPS, buildArgs, findOp, missingRequired } from "./ops.js";
|
|
24
26
|
import { MCP_CLIENTS, agentGuide, agentBlock, agentInstructionsFile, agentMode, bundleProperty, claimProperty, classifyApiError, collectionProperty, detectHarness, envelope, exitCodeFor, findMcpClient, installSkills, mcpConfigured, pendingClaims, recordClaim, summarizeDoctor, upsertAgentBlock, writeBundle, writeMcpConfig, } from "./cli-agent.js";
|
|
25
27
|
import { relativeDate } from "./dates.js";
|
|
@@ -33,7 +35,7 @@ const BASIC = null;
|
|
|
33
35
|
const EXCLUDED_OPS = 0;
|
|
34
36
|
/** Generated CLI operations that are intentionally unavailable to MCP. */
|
|
35
37
|
const MCP_EXCLUDED_OPS = 0;
|
|
36
|
-
const VERSION = "0.
|
|
38
|
+
const VERSION = "0.20.0";
|
|
37
39
|
const API_VERSION = "1.0.0";
|
|
38
40
|
const SPEC_FORMAT = "openapi";
|
|
39
41
|
const IDENTITY_POLICY = {};
|
|
@@ -43,7 +45,7 @@ const ENVIRONMENTS = {};
|
|
|
43
45
|
const HAS_MCP = false;
|
|
44
46
|
const PKG_NAME = "@typeship-ax/cli";
|
|
45
47
|
const UPDATE_NOTICE = false;
|
|
46
|
-
const API_DESCRIPTION = "Resolve an OpenAPI or GraphQL Definition, diagnose it, and keep every\nselected
|
|
48
|
+
const API_DESCRIPTION = "Resolve an OpenAPI or GraphQL Definition, diagnose it, and keep every\nselected CLI, MCP, and SDK Target current.\n\nEvery operation but one requires a bearer credential: an organization\nAPI key from the console, or an OAuth access token carrying the operation's\nread, generate, or write capability and the organization selected during\nconsent. OAuth grants cannot switch organizations after consent. A browser\nsession is not a credential for this API. The exception is POST /generate,\nwhich works anonymously with the free plan's limits.\n\nExamples use Parcel, a fictional delivery service. Replace its domains,\nrepository names, and resource identifiers with your own. The hosted\npetstore Definition is a runnable sample.\n";
|
|
47
49
|
const DOCS_URL_DEFAULT = "https://typeship.dev";
|
|
48
50
|
const DOCS_INDEX_URL_DEFAULT = null;
|
|
49
51
|
const RELAY = null;
|
|
@@ -136,7 +138,8 @@ function parseArgv(argv) {
|
|
|
136
138
|
}
|
|
137
139
|
else if (isBooleanFlag(arg.slice(2), positionals)) {
|
|
138
140
|
const next = argv[i + 1];
|
|
139
|
-
|
|
141
|
+
const nullable = positionals.length >= 2 && findOp(positionals[0], positionals[1])?.params.some((p) => p.flag === arg.slice(2) && p.nullable);
|
|
142
|
+
if (next === "true" || next === "false" || (next === "null" && nullable)) {
|
|
140
143
|
setFlag(arg.slice(2), next);
|
|
141
144
|
i++;
|
|
142
145
|
}
|
|
@@ -215,33 +218,32 @@ function out(value) {
|
|
|
215
218
|
/** --fields a,b.c: the dotted paths to keep in API results (null = everything). Set in main(). */
|
|
216
219
|
let FIELDS = null;
|
|
217
220
|
/** Keep only FIELDS of a result: arrays item by item, objects by dotted path; scalars untouched. */
|
|
218
|
-
function project(value) {
|
|
219
|
-
if (
|
|
221
|
+
function project(value, paths = FIELDS) {
|
|
222
|
+
if (paths === null)
|
|
220
223
|
return value;
|
|
221
224
|
if (Array.isArray(value))
|
|
222
|
-
return value.map(project);
|
|
225
|
+
return value.map((item) => project(item, paths));
|
|
223
226
|
if (value === null || typeof value !== "object")
|
|
224
227
|
return value;
|
|
228
|
+
const groups = new Map();
|
|
229
|
+
for (const [key, ...rest] of paths) {
|
|
230
|
+
if (key === undefined)
|
|
231
|
+
continue;
|
|
232
|
+
const group = groups.get(key);
|
|
233
|
+
if (group)
|
|
234
|
+
group.push(rest);
|
|
235
|
+
else
|
|
236
|
+
groups.set(key, [rest]);
|
|
237
|
+
}
|
|
225
238
|
const out = {};
|
|
226
|
-
for (const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (cursor === null || typeof cursor !== "object" || Array.isArray(cursor)) {
|
|
230
|
-
cursor = undefined;
|
|
231
|
-
break;
|
|
232
|
-
}
|
|
233
|
-
cursor = cursor[key];
|
|
234
|
-
}
|
|
235
|
-
if (cursor === undefined)
|
|
239
|
+
for (const [key, rests] of groups) {
|
|
240
|
+
const child = value[key];
|
|
241
|
+
if (child === undefined)
|
|
236
242
|
continue;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
target[key] = {};
|
|
242
|
-
target = target[key];
|
|
243
|
-
}
|
|
244
|
-
target[path[path.length - 1]] = cursor;
|
|
243
|
+
if (rests.some((rest) => rest.length === 0))
|
|
244
|
+
out[key] = child;
|
|
245
|
+
else if (child !== null && typeof child === "object")
|
|
246
|
+
out[key] = project(child, rests);
|
|
245
247
|
}
|
|
246
248
|
return out;
|
|
247
249
|
}
|
|
@@ -292,12 +294,24 @@ function humanError(body, code) {
|
|
|
292
294
|
lines.push(" " + paintErr("dim", (v.path ? v.path + ": " : "") + (v.message ?? "")));
|
|
293
295
|
}
|
|
294
296
|
else if (detail?.body !== undefined) {
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
297
|
+
// Keep every API error and its field visible, even when the headline
|
|
298
|
+
// already used the first message. Unstructured bodies retain a summary.
|
|
299
|
+
const apiErrors = typeof detail.body === "object" && detail.body !== null
|
|
300
|
+
? detail.body.errors : undefined;
|
|
301
|
+
const entries = Array.isArray(apiErrors) ? apiErrors.filter((entry) => entry !== null && typeof entry === "object" && typeof entry.message === "string") : [];
|
|
302
|
+
if (entries.length > 0) {
|
|
303
|
+
for (const entry of entries) {
|
|
304
|
+
const field = typeof entry.field === "string" ? entry.field : "";
|
|
305
|
+
const location = typeof entry.in === "string" ? entry.in + " " : "";
|
|
306
|
+
lines.push(" " + paintErr("dim", (field ? location + field + ": " : "") + entry.message));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
const compact = typeof detail.body === "string" ? detail.body : JSON.stringify(detail.body);
|
|
311
|
+
const firstWords = (issue?.message ?? "").slice(0, 40);
|
|
312
|
+
if (compact && compact !== "{}" && !(firstWords && compact.includes(firstWords)))
|
|
313
|
+
lines.push(" " + paintErr("dim", "API said: " + (compact.length > 300 ? compact.slice(0, 297) + "…" : compact)));
|
|
314
|
+
}
|
|
301
315
|
const requestId = detail.request_id ?? detail.body?.request_id ?? detail.body?.requestId;
|
|
302
316
|
if (typeof requestId === "string")
|
|
303
317
|
lines.push(" " + paintErr("dim", "request id: " + requestId));
|
|
@@ -668,7 +682,6 @@ async function cmdLogin(parsed) {
|
|
|
668
682
|
...AUTH_SCALARS.map((a) => " " + BIN + " login --" + a.flag + " <value>"),
|
|
669
683
|
...(CLI_AUTH_URL ? [" " + BIN + " login approve in the browser: a key is minted for you (add --no-browser to print the link instead of opening it)"] : []),
|
|
670
684
|
" " + BIN + " login --with-token read the credential from stdin (CI)",
|
|
671
|
-
" " + BIN + " login --console-check <file> test browser login for your Typeship Console without replacing saved logins",
|
|
672
685
|
...(HAS_OAUTH_LOGIN ? [" " + BIN + " login --client-id <id> OAuth " + OAUTH_LOGIN_METHOD + " login" + (OAUTH_CLIENT_ID ? " (a default id is built in)" : "")] : []),
|
|
673
686
|
...(HAS_OAUTH_LOGIN ? [" " + BIN + " login --no-browser print the approval URL instead of opening a browser", " " + BIN + " login --device use device authorization when your provider supports it"] : []),
|
|
674
687
|
...(BASIC ? [" " + BIN + " login --username <u> --password <p>"] : []),
|
|
@@ -963,12 +976,11 @@ async function cmdMcp(parsed) {
|
|
|
963
976
|
" " + BIN + " mcp install --codex Codex CLI (~/.codex/config.toml)",
|
|
964
977
|
" " + BIN + " mcp install --vscode VS Code (./.vscode/mcp.json)",
|
|
965
978
|
" " + BIN + " mcp install --windsurf | --gemini | --opencode | --zed | --claude-desktop",
|
|
966
|
-
" " + BIN + " mcp install --cursor Cursor (./.cursor/mcp.json
|
|
979
|
+
" " + BIN + " mcp install --cursor Cursor (./.cursor/mcp.json)",
|
|
967
980
|
" " + BIN + " mcp --url <https://...> use a remote MCP endpoint instead of the local server",
|
|
968
981
|
" " + BIN + " mcp install --claude --read-only register a read-only server (writes are not callable)",
|
|
969
982
|
"",
|
|
970
983
|
(MCP_URL ? "Default entry: the hosted endpoint " + MCP_URL + " with the auth env var as a reference (never a literal key)." : "Default entry: this package's local stdio server, which reads credentials saved by '" + BIN + " login' or the CLI's auth env vars."),
|
|
971
|
-
"--all skips Cursor until it speaks MCP 2026-07-28.",
|
|
972
984
|
];
|
|
973
985
|
process.stdout.write(lines.join("\n") + "\n");
|
|
974
986
|
await flushExit(0);
|
|
@@ -1066,7 +1078,7 @@ function commandSummaries() {
|
|
|
1066
1078
|
paginated: op.paginated,
|
|
1067
1079
|
destructive: op.safety === "destructive",
|
|
1068
1080
|
auth: op.auth,
|
|
1069
|
-
flags: op.params.filter((p) => p.kind !== "path").map((p) => ({ flag: p.flag, type: p.type, ...(p.items ? { items: p.items } : {}), ...(p.enum ? { enum: p.enum } : {}), required: p.required, ...(p.description ? { description: p.description.split("\n")[0] } : {}) })),
|
|
1081
|
+
flags: op.params.filter((p) => p.kind !== "path").map((p) => ({ flag: p.flag, type: p.type, ...(p.nullable ? { nullable: true } : {}), ...(p.items ? { items: p.items } : {}), ...(p.enum ? { enum: p.enum } : {}), required: p.required, ...(p.description ? { description: p.description.split("\n")[0] } : {}) })),
|
|
1070
1082
|
}));
|
|
1071
1083
|
}
|
|
1072
1084
|
/**
|
|
@@ -1215,7 +1227,7 @@ async function cmdDoctor(parsed) {
|
|
|
1215
1227
|
}
|
|
1216
1228
|
const checks = [];
|
|
1217
1229
|
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
1218
|
-
checks.push({ name: "node", ok: nodeMajor >=
|
|
1230
|
+
checks.push({ name: "node", ok: nodeMajor >= 20, detail: process.version, ...(nodeMajor >= 20 ? {} : { fix: "Install Node 20 or newer." }) });
|
|
1219
1231
|
checks.push({ name: "cli", ok: true, detail: BIN + " " + VERSION + " (" + PKG_NAME + ")" });
|
|
1220
1232
|
let source = null;
|
|
1221
1233
|
let storageProblem;
|
|
@@ -1276,7 +1288,7 @@ async function cmdDoctor(parsed) {
|
|
|
1276
1288
|
* init: the one command that connects a machine (or a repo) to this API.
|
|
1277
1289
|
* Stores a credential when given one, installs the skills repository, writes
|
|
1278
1290
|
* MCP config for every agent client found, and upserts a marked block into
|
|
1279
|
-
* AGENTS.md (CLAUDE.md
|
|
1291
|
+
* AGENTS.md (or an existing CLAUDE.md when AGENTS.md is absent). Idempotent; --all takes the
|
|
1280
1292
|
* defaults without asking; every part reports and none of them fails init.
|
|
1281
1293
|
*/
|
|
1282
1294
|
async function cmdInit(parsed) {
|
|
@@ -1288,7 +1300,7 @@ async function cmdInit(parsed) {
|
|
|
1288
1300
|
" --all do everything without prompting (implied under an agent)",
|
|
1289
1301
|
" --no-skills | --no-mcp | --no-agents-md skip a part",
|
|
1290
1302
|
"",
|
|
1291
|
-
"Writes: " + credsPath() + " (credential), the skills directory of each agent on this machine" + (SKILLS_REPO ? " (npx skills add " + SKILLS_REPO + ")" : " (no skills repository configured)") + ", MCP config for detected clients, and a '" + BIN + "' block in ./AGENTS.md.",
|
|
1303
|
+
"Writes: " + credsPath() + " (credential), the skills directory of each agent on this machine" + (SKILLS_REPO ? " (npx skills add " + SKILLS_REPO + ")" : " (no skills repository configured)") + ", MCP config for detected clients, and a '" + BIN + "' block in ./AGENTS.md (or an existing ./CLAUDE.md when AGENTS.md is absent).",
|
|
1292
1304
|
].join("\n") + "\n");
|
|
1293
1305
|
await flushExit(0);
|
|
1294
1306
|
}
|
|
@@ -1361,7 +1373,7 @@ async function cmdInit(parsed) {
|
|
|
1361
1373
|
report.agents_md = { status: "skipped" };
|
|
1362
1374
|
}
|
|
1363
1375
|
else {
|
|
1364
|
-
const file = agentInstructionsFile(cwd
|
|
1376
|
+
const file = agentInstructionsFile(cwd);
|
|
1365
1377
|
const result = upsertAgentBlock(file, BIN + " agent-contract", agentBlock(agentContext(), commandSummaries()));
|
|
1366
1378
|
report.agents_md = { status: result.updated ? "updated" : "written", file: result.file };
|
|
1367
1379
|
}
|
|
@@ -1928,6 +1940,8 @@ function helpSentence(description) {
|
|
|
1928
1940
|
}
|
|
1929
1941
|
/** Type column text for a param: string, number, string[], a|b|c, enum, object, json, path. */
|
|
1930
1942
|
function typeLabel(p) {
|
|
1943
|
+
if (p.nullable)
|
|
1944
|
+
return typeLabel({ ...p, nullable: false }) + "|null";
|
|
1931
1945
|
if (p.type === "file")
|
|
1932
1946
|
return "path (uploaded)";
|
|
1933
1947
|
if (p.format && p.type === "string")
|
|
@@ -2008,7 +2022,7 @@ function printRoot(stream = process.stdout) {
|
|
|
2008
2022
|
}
|
|
2009
2023
|
const width = termWidth();
|
|
2010
2024
|
const lines = [];
|
|
2011
|
-
lines.push(paintOut("bold", BIN) + ": " + "typeship API" + " (v" + "1.0.0" + "), package " + "0.
|
|
2025
|
+
lines.push(paintOut("bold", BIN) + ": " + "typeship API" + " (v" + "1.0.0" + "), package " + "0.20.0");
|
|
2012
2026
|
lines.push("");
|
|
2013
2027
|
lines.push(paintOut("bold", "Usage:") + " " + BIN + " <resource> <command> [args] [--flags]");
|
|
2014
2028
|
lines.push("");
|
|
@@ -2033,7 +2047,7 @@ function printRoot(stream = process.stdout) {
|
|
|
2033
2047
|
lines.push(...labeled("Upgrade: ", "https://typeship.dev/pricing, then regenerate without the operation cap", width, 14));
|
|
2034
2048
|
}
|
|
2035
2049
|
lines.push("");
|
|
2036
|
-
const flagsText = "-v/--version, -h/--help, --debug, --non-interactive, --color on|off|auto, --base-url <url>, --profile <name>, --credentials @<file>|-, --data '<json>', --fields <a,b.c>, --all (paginated lists), --validate (schema-check bodies)" +
|
|
2050
|
+
const flagsText = "-v/--version, -h/--help, --debug, --non-interactive, --color on|off|auto, --base-url <url>, --profile <name>, --credentials @<file>|-, --data '<json>', --fields <a,b.c>, --all (paginated lists), --validate (schema-check parameters and JSON bodies)" +
|
|
2037
2051
|
(AUTH_SCALARS.length > 0 ? ", " + AUTH_SCALARS.map((a) => "--" + a.flag + " <value>").join(", ") : "");
|
|
2038
2052
|
lines.push(...labeled(paintOut("bold", "Global flags:") + " ", flagsText, width, 14).map((l, i) => (i === 0 ? l : l)));
|
|
2039
2053
|
lines.push(...labeled("Credential env vars: ", [
|
|
@@ -2094,17 +2108,18 @@ function commandExtras(op) {
|
|
|
2094
2108
|
extras.push(["--all", "stream every item from every page (NDJSON)"]);
|
|
2095
2109
|
const collectionField = collectionProperty(op.outputSchema);
|
|
2096
2110
|
extras.push(["--fields <a,b.c>", "keep only these fields of the result" + (op.paginated ? " (per item)" : collectionField ? " (per item in " + collectionField + ")" : "")]);
|
|
2097
|
-
if (bundleProperty(op.outputSchema) !== null)
|
|
2111
|
+
if ((op.fileBundleProperty ?? bundleProperty(op.outputSchema)) !== null)
|
|
2098
2112
|
extras.push(["--out <dir>", "write the response's files ({path, content}) into a directory"]);
|
|
2099
2113
|
if (op.safety === "destructive")
|
|
2100
2114
|
extras.push(["--force, -y", "destructive: required without a terminal, skips the prompt with one"]);
|
|
2101
2115
|
return extras;
|
|
2102
2116
|
}
|
|
2103
|
-
/** One runnable example
|
|
2117
|
+
/** One runnable example from required inputs and authored optional values. */
|
|
2104
2118
|
function exampleLine(op) {
|
|
2105
2119
|
const parts = [BIN, op.command[0], op.command[1]];
|
|
2106
2120
|
for (const p of op.params) {
|
|
2107
|
-
|
|
2121
|
+
const hasExample = p.type !== "file" && Object.hasOwn(op.exampleArguments, p.name);
|
|
2122
|
+
if (!p.required && !hasExample)
|
|
2108
2123
|
continue;
|
|
2109
2124
|
const generated = p.type === "file" ? undefined : op.exampleArguments[p.name];
|
|
2110
2125
|
const values = p.type === "array" ? p.items?.enum : p.enum;
|
|
@@ -2115,7 +2130,7 @@ function exampleLine(op) {
|
|
|
2115
2130
|
: p.type === "object" ? {}
|
|
2116
2131
|
: p.type === "array" ? [p.items?.type === "number" ? 1 : "value"]
|
|
2117
2132
|
: "value";
|
|
2118
|
-
const value = generated
|
|
2133
|
+
const value = hasExample ? generated : fallback;
|
|
2119
2134
|
const sample = typeof value === "string" ? shellQuote(value)
|
|
2120
2135
|
: typeof value === "object" ? shellQuote(JSON.stringify(value))
|
|
2121
2136
|
: String(value);
|
|
@@ -2124,10 +2139,11 @@ function exampleLine(op) {
|
|
|
2124
2139
|
else
|
|
2125
2140
|
parts.push("--" + p.flag, sample);
|
|
2126
2141
|
}
|
|
2127
|
-
const
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2142
|
+
const showBody = op.bodyStyle === "data" && (Object.hasOwn(op.exampleArguments, "body")
|
|
2143
|
+
|| (op.inputSchema.required ?? []).includes("body"));
|
|
2144
|
+
if (showBody) {
|
|
2145
|
+
const body = Object.hasOwn(op.exampleArguments, "body") ? op.exampleArguments.body : {};
|
|
2146
|
+
parts.push(op.bodyKind === "binary" ? "--file ./file" : "--data " + shellQuote(JSON.stringify(body)));
|
|
2131
2147
|
}
|
|
2132
2148
|
if (op.safety === "destructive")
|
|
2133
2149
|
parts.push("--force");
|
|
@@ -2158,6 +2174,9 @@ function printOp(op) {
|
|
|
2158
2174
|
if (op.paginated)
|
|
2159
2175
|
lines.push(" " + usageLine(op) + " --all | jq -r '.id'");
|
|
2160
2176
|
const arrayFlag = rows.find((p) => p.type === "array");
|
|
2177
|
+
if (rows.some((p) => p.nullable)) {
|
|
2178
|
+
lines.push("", "Nullable body flags accept null for JSON null. Use --data to send the literal string null.");
|
|
2179
|
+
}
|
|
2161
2180
|
if (arrayFlag) {
|
|
2162
2181
|
const sample = arrayFlag.items?.enum ? arrayFlag.items.enum.slice(0, 2).join(",") : arrayFlag.items?.type === "number" ? "1,2" : "a,b";
|
|
2163
2182
|
lines.push("", "Array flags take a comma list (--" + arrayFlag.flag + " " + sample + "), the flag repeated, or a JSON array.");
|
|
@@ -2246,6 +2265,8 @@ function coerceScalar(flag, type, enumValues, text) {
|
|
|
2246
2265
|
* Objects must be JSON. Enum values are checked locally.
|
|
2247
2266
|
*/
|
|
2248
2267
|
function coerce(spec, raw, repeated) {
|
|
2268
|
+
if (spec.nullable && raw === "null" && repeated === undefined)
|
|
2269
|
+
return null;
|
|
2249
2270
|
if (spec.type === "file") {
|
|
2250
2271
|
if (raw === true)
|
|
2251
2272
|
fail(2, "--" + spec.flag + " expects a file path");
|
|
@@ -2310,8 +2331,66 @@ function coerce(spec, raw, repeated) {
|
|
|
2310
2331
|
}
|
|
2311
2332
|
return coerceScalar(spec.flag, spec.type === "json" ? "json" : spec.type === "number" ? "number" : "string", spec.enum, text);
|
|
2312
2333
|
}
|
|
2334
|
+
/** Use validation schemas, not the shortened agent-facing schemas: every union
|
|
2335
|
+
* branch and referenced definition must remain valid input. */
|
|
2336
|
+
function validateParameters(op, values, flags) {
|
|
2337
|
+
if (flags.get("validate") !== true)
|
|
2338
|
+
return;
|
|
2339
|
+
const schemas = SCHEMAS[op.resource + "." + op.method]?.params;
|
|
2340
|
+
if (!schemas)
|
|
2341
|
+
return;
|
|
2342
|
+
const violations = [];
|
|
2343
|
+
for (const parameter of op.params) {
|
|
2344
|
+
if (parameter.kind === "body")
|
|
2345
|
+
continue;
|
|
2346
|
+
let value = values[parameter.name];
|
|
2347
|
+
if (value === undefined && parameter.global) {
|
|
2348
|
+
const global = GLOBALS.find((entry) => entry.name === parameter.name);
|
|
2349
|
+
const raw = global ? process.env["TYPESHIP_" + global.envSuffix] : undefined;
|
|
2350
|
+
if (raw !== undefined)
|
|
2351
|
+
value = coerce(parameter, raw);
|
|
2352
|
+
}
|
|
2353
|
+
if (value === undefined)
|
|
2354
|
+
continue;
|
|
2355
|
+
if (parameter.kind === "path")
|
|
2356
|
+
value = coerce(parameter, String(value));
|
|
2357
|
+
const key = parameter.kind + "." + parameter.name;
|
|
2358
|
+
validateAgainstSchema(value, schemas[key], key, violations, DEFS);
|
|
2359
|
+
}
|
|
2360
|
+
if (violations.length)
|
|
2361
|
+
failApi(new ValidationError("request", violations, "parameters"), false);
|
|
2362
|
+
}
|
|
2313
2363
|
/** Whether the last client built carried any credential; failApi tells NO_AUTH from AUTH_INVALID with it. */
|
|
2314
2364
|
let LAST_CLIENT_HAD_CREDENTIAL = false;
|
|
2365
|
+
/** Check the same complete alternatives the request runtime can select, after
|
|
2366
|
+
* per-scheme flag, environment, profile, and OAuth resolution. */
|
|
2367
|
+
function requireOperationCredentials(op, options) {
|
|
2368
|
+
if (op.auth !== "required")
|
|
2369
|
+
return;
|
|
2370
|
+
const supplied = (value) => typeof value === "function" || (typeof value === "string" && value.length > 0)
|
|
2371
|
+
|| (value !== null && typeof value === "object" && "username" in value && "password" in value && typeof value.username === "string" && value.username.length > 0 && typeof value.password === "string" && value.password.length > 0);
|
|
2372
|
+
const named = Object.fromEntries(Object.entries(options.credentials ?? {}).filter(([, value]) => supplied(value)));
|
|
2373
|
+
const available = namedCredentialAvailability(NAMED_SCHEMES, new Set(Object.keys(options).filter((key) => supplied(options[key]))), named);
|
|
2374
|
+
if (op.credentialOptions?.some((alternative) => alternative.length > 0 && alternative.every((option) => available.has(option))))
|
|
2375
|
+
return;
|
|
2376
|
+
const alternatives = (op.credentialOptions ?? []).filter((alternative) => alternative.length > 0 && alternative.every((option) => option.startsWith("credentials.")));
|
|
2377
|
+
const missing = alternatives.map((alternative) => alternative.filter((option) => !available.has(option)).map((option) => option.slice("credentials.".length)));
|
|
2378
|
+
const names = new Set(missing.flat());
|
|
2379
|
+
const relevant = AUTH_SCALARS.filter((scalar) => [...names].some((name) => NAMED_SCHEMES[name]?.options.includes(scalar.option)));
|
|
2380
|
+
const needsBasic = [...names].some((name) => NAMED_SCHEMES[name]?.options.includes("basicAuth"));
|
|
2381
|
+
failWith({
|
|
2382
|
+
status: "action_required",
|
|
2383
|
+
code: "NO_AUTH",
|
|
2384
|
+
message: op.command.join(" ") + " needs one complete credential alternative (" + wireOf(op) + "). " + (missing.length
|
|
2385
|
+
? "Missing security schemes: " + missing.map((names) => names.join(" + ")).join(" OR ") + "."
|
|
2386
|
+
: "The declared security requirements have no supported, compatible alternative in this CLI."),
|
|
2387
|
+
nextSteps: alternatives.length ? [
|
|
2388
|
+
...relevant.map((a) => "Set " + a.env + " in the environment, pass --" + a.flag + " <value>, or run '" + BIN + " login'."),
|
|
2389
|
+
...(needsBasic && BASIC ? ["Set " + BASIC.envUser + " and " + BASIC.envPass + ", or pass --username and --password."] : []),
|
|
2390
|
+
"Supply all schemes in one alternative through " + "TYPESHIP_CREDENTIALS" + " or --credentials @<JSON-file>: " + alternatives.map((alternative) => alternative.map((option) => option.slice("credentials.".length)).join(" + ")).join(" OR ") + ".",
|
|
2391
|
+
] : ["Check the operation's security schemes in the API definition and regenerate with a supported, compatible alternative."],
|
|
2392
|
+
});
|
|
2393
|
+
}
|
|
2315
2394
|
function validateCredentialsInput(flags) {
|
|
2316
2395
|
const input = flags.get("credentials");
|
|
2317
2396
|
if (BASIC && flags.has("username") !== flags.has("password"))
|
|
@@ -2444,18 +2523,17 @@ async function makeClient(flags, op, candidate, forIdentity = false) {
|
|
|
2444
2523
|
options[g.option] = value;
|
|
2445
2524
|
}
|
|
2446
2525
|
LAST_CLIENT_HAD_CREDENTIAL = Object.keys(options.credentials ?? {}).length > 0 || AUTH_SCALARS.some((a) => options[a.option] !== undefined) || options.basicAuth !== undefined || options.bearerToken !== undefined;
|
|
2447
|
-
|
|
2448
|
-
//
|
|
2449
|
-
//
|
|
2450
|
-
// "non-interactive", so usage by surface does not count CI as agents.
|
|
2451
|
-
// The API can read it back; it carries no secrets.
|
|
2526
|
+
requireOperationCredentials(op, options);
|
|
2527
|
+
// Identify the package and version. Optional harness and caller details
|
|
2528
|
+
// let the API distinguish agent traffic from other non-interactive use.
|
|
2452
2529
|
const harness = detectHarness();
|
|
2453
2530
|
const explicitAgent = flags.get("mode") === "agent" || process.env["TYPESHIP_MODE"] === "agent";
|
|
2454
2531
|
const behaving = agentMode({ flagMode: flags.get("mode"), envMode: process.env["TYPESHIP_MODE"], stdoutIsTTY: process.stdout.isTTY === true, stdinIsTTY: process.stdin.isTTY === true });
|
|
2455
|
-
const caller = harness || explicitAgent ? "
|
|
2532
|
+
const caller = harness || explicitAgent ? "agent" : behaving ? "non-interactive" : null;
|
|
2533
|
+
const details = [harness ? "harness=" + harness : null, caller].filter(Boolean).join("; ");
|
|
2456
2534
|
options.defaultHeaders = {
|
|
2457
2535
|
...options.defaultHeaders,
|
|
2458
|
-
"User-Agent": PKG_NAME + "-cli/" + VERSION +
|
|
2536
|
+
"User-Agent": PKG_NAME + "-cli/" + VERSION + (details ? " (" + details + ")" : ""),
|
|
2459
2537
|
};
|
|
2460
2538
|
if (forIdentity) {
|
|
2461
2539
|
options.fetch = identityFetch(baseUrl);
|
|
@@ -2528,8 +2606,8 @@ async function main() {
|
|
|
2528
2606
|
fail(2, "--format json is the only format; output is always JSON.");
|
|
2529
2607
|
}
|
|
2530
2608
|
if (parsed.flags.has("version") || parsed.positionals[0] === "version") {
|
|
2531
|
-
// "
|
|
2532
|
-
//
|
|
2609
|
+
// "acme 1.0.0 (acme 1.0.0)" would say the name twice; when the API's
|
|
2610
|
+
// title is the bin, name the API version as such.
|
|
2533
2611
|
const apiLabel = API_TITLE.toLowerCase().replace(/[^a-z0-9]/g, "") === BIN.toLowerCase().replace(/[^a-z0-9]/g, "") ? "API " + API_VERSION : API_TITLE + " " + API_VERSION;
|
|
2534
2612
|
process.stdout.write(BIN + " " + VERSION + " (" + apiLabel + ", generated by typeship)\n");
|
|
2535
2613
|
await flushExit(0);
|
|
@@ -2697,27 +2775,14 @@ async function main() {
|
|
|
2697
2775
|
fail(2, "--fields expects at least one field path");
|
|
2698
2776
|
}
|
|
2699
2777
|
// --out <dir> materializes a file-shaped response (see cli-agent.ts bundleProperty).
|
|
2700
|
-
const bundleField = bundleProperty(op.outputSchema);
|
|
2778
|
+
const bundleField = op.fileBundleProperty ?? bundleProperty(op.outputSchema);
|
|
2701
2779
|
const collectionField = collectionProperty(op.outputSchema);
|
|
2702
2780
|
const outDir = typeof parsed.flags.get("out") === "string" ? parsed.flags.get("out") : undefined;
|
|
2703
2781
|
if (outDir !== undefined && bundleField === null) {
|
|
2704
2782
|
fail(2, "--out applies to commands whose response carries files ({path, content}); " + op.command.join(" ") + " does not.");
|
|
2705
2783
|
}
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
if (op.auth === "required" && (AUTH_SCALARS.length > 0 || BASIC || HAS_OAUTH_LOGIN) && credentialSource(parsed.flags) === null) {
|
|
2709
|
-
failWith({
|
|
2710
|
-
status: "action_required",
|
|
2711
|
-
code: "NO_AUTH",
|
|
2712
|
-
message: op.command.join(" ") + " needs a credential (" + wireOf(op) + " is authenticated) and none was found.",
|
|
2713
|
-
nextSteps: [
|
|
2714
|
-
...AUTH_SCALARS.map((a) => "Set " + a.env + " in the environment, pass --" + a.flag + " <value>, or run '" + BIN + " login'."),
|
|
2715
|
-
...(BASIC ? ["Set " + BASIC.envUser + " and " + BASIC.envPass + ", or pass --username and --password."] : []),
|
|
2716
|
-
...(AUTH_SCALARS.length === 0 && !BASIC ? ["Run '" + BIN + " login'."] : []),
|
|
2717
|
-
"'" + BIN + " auth check' shows what the CLI would send.",
|
|
2718
|
-
],
|
|
2719
|
-
});
|
|
2720
|
-
}
|
|
2784
|
+
validateParameters(op, values, parsed.flags);
|
|
2785
|
+
const client = await makeClient(parsed.flags, op);
|
|
2721
2786
|
// Destructive commands need --force. A person gets asked; an agent gets
|
|
2722
2787
|
// an action_required envelope with the exact command to run, so nothing
|
|
2723
2788
|
// is deleted on a guess.
|
|
@@ -2736,7 +2801,6 @@ async function main() {
|
|
|
2736
2801
|
if (answer !== "y" && answer !== "yes")
|
|
2737
2802
|
failWith({ status: "action_required", code: "CONFIRMATION_REQUIRED", message: "Cancelled.", nextSteps: ["Run again with --force to skip the prompt: " + rerun] });
|
|
2738
2803
|
}
|
|
2739
|
-
const client = await makeClient(parsed.flags, op);
|
|
2740
2804
|
const selectValue = typeof parsed.flags.get("select") === "string" ? parsed.flags.get("select") : undefined;
|
|
2741
2805
|
const args = buildArgs(op, values, dataBody, selectValue);
|
|
2742
2806
|
const target = client[op.resource];
|
|
@@ -2752,7 +2816,19 @@ async function main() {
|
|
|
2752
2816
|
failApi(e, LAST_CLIENT_HAD_CREDENTIAL);
|
|
2753
2817
|
}
|
|
2754
2818
|
}
|
|
2755
|
-
|
|
2819
|
+
let result = await callResult;
|
|
2820
|
+
if (result.ok && op.httpMethod === "POST" && op.path === "/projects/{project_id}/generations") {
|
|
2821
|
+
const batch = result.data;
|
|
2822
|
+
const generations = client.generations;
|
|
2823
|
+
const completed = [];
|
|
2824
|
+
for (const generation of batch.data) {
|
|
2825
|
+
const waited = await generations.wait(generation.id);
|
|
2826
|
+
if (!waited.ok)
|
|
2827
|
+
failApi(waited.error, LAST_CLIENT_HAD_CREDENTIAL);
|
|
2828
|
+
completed.push(waited.data);
|
|
2829
|
+
}
|
|
2830
|
+
result = { ...result, data: { ...batch, data: completed } };
|
|
2831
|
+
}
|
|
2756
2832
|
if (result.ok) {
|
|
2757
2833
|
if (op.sse) {
|
|
2758
2834
|
// Server-sent events as NDJSON, one line per event, until the stream ends.
|
package/dist/core/http.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Runtime core. Generated by typeship — https://typeship.dev
|
|
3
|
-
* Zero dependencies: built on the platform fetch API (Node
|
|
3
|
+
* Zero dependencies: built on the platform fetch API (Node 20+, browsers, edge).
|
|
4
4
|
*/
|
|
5
5
|
export interface RequestOptions {
|
|
6
6
|
/** Abort the request (composed with the per-attempt timeout). */
|
|
@@ -81,8 +81,9 @@ export interface Violation {
|
|
|
81
81
|
* ApiResult, like every other failure. */
|
|
82
82
|
export declare class ValidationError extends Error {
|
|
83
83
|
readonly direction: "request" | "response";
|
|
84
|
+
readonly target: "body" | "parameters";
|
|
84
85
|
readonly violations: Violation[];
|
|
85
|
-
constructor(direction: "request" | "response", violations: Violation[]);
|
|
86
|
+
constructor(direction: "request" | "response", violations: Violation[], target?: "body" | "parameters");
|
|
86
87
|
}
|
|
87
88
|
/**
|
|
88
89
|
* Validates against the depth-capped JSON Schema subset emitted in
|