@typeship-ax/cli 0.22.0 → 0.24.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 +13 -9
- package/README.md +14 -27
- package/api.json +8898 -9026
- package/api.md +370 -328
- package/dist/arguments.d.ts +54 -0
- package/dist/arguments.d.ts.map +1 -0
- package/dist/arguments.js +265 -0
- package/dist/cli-agent.d.ts +73 -9
- package/dist/cli-agent.d.ts.map +1 -1
- package/dist/cli-agent.js +331 -44
- package/dist/cli.js +802 -291
- package/dist/core/http.d.ts +162 -19
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/http.js +381 -48
- package/dist/core/pagination.d.ts +42 -6
- package/dist/core/pagination.d.ts.map +1 -1
- package/dist/core/pagination.js +111 -17
- package/dist/credential-storage.d.ts +10 -3
- package/dist/credential-storage.d.ts.map +1 -1
- package/dist/credential-storage.js +15 -6
- package/dist/dates.d.ts +1 -1
- package/dist/dates.js +1 -1
- package/dist/errors.d.ts +20 -84
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -108
- package/dist/fields.d.ts +36 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +187 -0
- package/dist/index.d.ts +28 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -25
- package/dist/named-credentials.d.ts +19 -0
- package/dist/named-credentials.d.ts.map +1 -1
- package/dist/named-credentials.js +81 -1
- package/dist/oauth-login.d.ts +8 -2
- package/dist/oauth-login.d.ts.map +1 -1
- package/dist/oauth-login.js +31 -19
- package/dist/oauth-request.d.ts +7 -1
- package/dist/oauth-request.d.ts.map +1 -1
- package/dist/oauth-request.js +26 -4
- package/dist/oauth-session.d.ts +13 -1
- package/dist/oauth-session.d.ts.map +1 -1
- package/dist/oauth-session.js +34 -18
- package/dist/ops.d.ts +58 -5
- package/dist/ops.d.ts.map +1 -1
- package/dist/ops.js +110 -41
- package/dist/polling-login.d.ts +8 -2
- package/dist/polling-login.d.ts.map +1 -1
- package/dist/polling-login.js +25 -11
- package/dist/resources/api-keys.d.ts +10 -7
- package/dist/resources/api-keys.d.ts.map +1 -1
- package/dist/resources/api-keys.js +10 -31
- package/dist/resources/deliveries.d.ts +89 -5
- package/dist/resources/deliveries.d.ts.map +1 -1
- package/dist/resources/deliveries.js +96 -19
- package/dist/resources/drafts.d.ts +16 -16
- package/dist/resources/drafts.d.ts.map +1 -1
- package/dist/resources/drafts.js +12 -65
- package/dist/resources/files.d.ts +4 -4
- package/dist/resources/files.d.ts.map +1 -1
- package/dist/resources/files.js +3 -12
- package/dist/resources/generations.d.ts +16 -16
- package/dist/resources/generations.d.ts.map +1 -1
- package/dist/resources/generations.js +23 -47
- package/dist/resources/organization.d.ts +4 -4
- package/dist/resources/organization.d.ts.map +1 -1
- package/dist/resources/organization.js +3 -10
- package/dist/resources/{generate.d.ts → packages.d.ts} +16 -16
- package/dist/resources/packages.d.ts.map +1 -0
- package/dist/resources/{generate.js → packages.js} +13 -29
- package/dist/resources/projects.d.ts +50 -50
- package/dist/resources/projects.d.ts.map +1 -1
- package/dist/resources/projects.js +60 -116
- package/dist/resources/releases.d.ts +22 -17
- package/dist/resources/releases.d.ts.map +1 -1
- package/dist/resources/releases.js +19 -40
- package/dist/resources/spec-revisions.d.ts +16 -7
- package/dist/resources/spec-revisions.d.ts.map +1 -1
- package/dist/resources/spec-revisions.js +7 -29
- package/dist/resources/specs.d.ts +7 -7
- package/dist/resources/specs.d.ts.map +1 -1
- package/dist/resources/specs.js +6 -34
- package/dist/resources/targets.d.ts +49 -49
- package/dist/resources/targets.d.ts.map +1 -1
- package/dist/resources/targets.js +59 -115
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +83 -81
- package/dist/search.d.ts +54 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +421 -0
- package/dist/table.d.ts +28 -0
- package/dist/table.d.ts.map +1 -0
- package/dist/table.js +167 -0
- package/dist/type-docs.d.ts +61 -0
- package/dist/type-docs.d.ts.map +1 -0
- package/dist/type-docs.js +174 -0
- package/dist/types.d.ts +499 -339
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +18 -18
- package/package.json +5 -2
- package/src/arguments.ts +254 -0
- package/src/cli-agent.ts +351 -46
- package/src/cli.ts +753 -266
- package/src/core/http.ts +457 -58
- package/src/core/pagination.ts +129 -18
- package/src/credential-storage.ts +16 -6
- package/src/dates.ts +1 -1
- package/src/errors.ts +46 -115
- package/src/fields.ts +167 -0
- package/src/index.ts +45 -28
- package/src/named-credentials.ts +66 -1
- package/src/oauth-login.ts +36 -21
- package/src/oauth-request.ts +32 -6
- package/src/oauth-session.ts +37 -19
- package/src/ops.ts +146 -45
- package/src/polling-login.ts +24 -11
- package/src/resources/api-keys.ts +34 -48
- package/src/resources/deliveries.ts +213 -32
- package/src/resources/drafts.ts +62 -109
- package/src/resources/files.ts +19 -20
- package/src/resources/generations.ts +61 -79
- package/src/resources/organization.ts +11 -16
- package/src/resources/{generate.ts → packages.ts} +43 -51
- package/src/resources/projects.ts +145 -200
- package/src/resources/releases.ts +50 -67
- package/src/resources/spec-revisions.ts +40 -49
- package/src/resources/specs.ts +39 -59
- package/src/resources/targets.ts +144 -194
- package/src/schemas.ts +83 -81
- package/src/search.ts +434 -0
- package/src/table.ts +167 -0
- package/src/type-docs.ts +205 -0
- package/src/types.ts +538 -357
- package/dist/console-login-check.d.ts +0 -21
- package/dist/console-login-check.d.ts.map +0 -1
- package/dist/console-login-check.js +0 -107
- package/dist/console-login-contract.d.ts +0 -45
- package/dist/console-login-contract.d.ts.map +0 -1
- package/dist/console-login-contract.js +0 -40
- package/dist/resources/generate.d.ts.map +0 -1
- package/dist/resources/publications.d.ts +0 -47
- package/dist/resources/publications.d.ts.map +0 -1
- package/dist/resources/publications.js +0 -70
- package/src/console-login-check.ts +0 -88
- package/src/console-login-contract.ts +0 -65
- package/src/resources/publications.ts +0 -140
package/dist/table.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `--format table` for the generated CLI: an API result as text for a
|
|
3
|
+
* person at a terminal. Generated by Typeship — https://typeship.dev
|
|
4
|
+
*
|
|
5
|
+
* JSON stays the default and the automation format; this view is opt-in and
|
|
6
|
+
* lossy by design. Lists become columns chosen by name (identifiers, then
|
|
7
|
+
* names, then status-like fields) while they fit the terminal width; a single
|
|
8
|
+
* object becomes one "field value" line per field in the same order. Cells longer than
|
|
9
|
+
* MAX_CELL characters end in "…", and the footer names every column or field
|
|
10
|
+
* that was left out. Pure: no I/O, so tests render without a terminal.
|
|
11
|
+
*/
|
|
12
|
+
/** The longest cell before it is cut with "…". */
|
|
13
|
+
export const MAX_CELL = 40;
|
|
14
|
+
/** A single object shows at most this many (flattened) fields. */
|
|
15
|
+
export const MAX_FIELDS = 60;
|
|
16
|
+
const ID_KEYS = new Set(["id", "sid", "uuid", "key", "slug", "code", "symbol", "number", "handle"]);
|
|
17
|
+
const NAME_KEYS = new Set(["name", "display_name", "displayname", "friendly_name", "friendlyname", "title", "label", "email", "username", "login", "to", "from"]);
|
|
18
|
+
const STATUS_KEYS = new Set(["status", "state"]);
|
|
19
|
+
const DESCRIPTOR_KEYS = new Set(["type", "kind", "direction", "enabled", "active", "amount", "currency", "price", "total"]);
|
|
20
|
+
/**
|
|
21
|
+
* Lower is shown first: identifiers, names, status, descriptors, everything
|
|
22
|
+
* else, dates, then references and URLs. `primaryId` is the row's own
|
|
23
|
+
* identifier when it is spelled like a reference (shipment_id with no id).
|
|
24
|
+
*/
|
|
25
|
+
function rank(key, primaryId) {
|
|
26
|
+
const k = key.toLowerCase();
|
|
27
|
+
if (ID_KEYS.has(k) || key === primaryId)
|
|
28
|
+
return 0;
|
|
29
|
+
if (NAME_KEYS.has(k))
|
|
30
|
+
return 1;
|
|
31
|
+
if (STATUS_KEYS.has(k))
|
|
32
|
+
return 2;
|
|
33
|
+
if (DESCRIPTOR_KEYS.has(k))
|
|
34
|
+
return 3;
|
|
35
|
+
if (/(^|_)(created|updated)(_at|_on)?$|^date_|^(created|updated)|At$/.test(key))
|
|
36
|
+
return 5;
|
|
37
|
+
if (/(_id|_sid|Id|_ids|_uri|_url|Url|Uri)$/.test(key) || k === "uri" || k === "url")
|
|
38
|
+
return 6;
|
|
39
|
+
return 4;
|
|
40
|
+
}
|
|
41
|
+
/** Keys in display order. The resource's own *_id (shipment_id for shipments) counts as the identifier when no id-like key exists. */
|
|
42
|
+
function byRank(keys, resource) {
|
|
43
|
+
const bare = (text) => text.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
44
|
+
const own = resource ? bare(resource).replace(/ies$/, "y").replace(/(ses|xes|s)$/, (m) => m === "s" ? "" : m.slice(0, -2)) + "id" : undefined;
|
|
45
|
+
const primaryId = keys.some((key) => ID_KEYS.has(key.toLowerCase())) ? undefined
|
|
46
|
+
: keys.find((key) => bare(key) === own) ?? keys.find((key) => /(_id|Id)$/.test(key));
|
|
47
|
+
return keys.map((key, index) => ({ key, index, rank: rank(key, primaryId) })).sort((a, b) => a.rank - b.rank || a.index - b.index).map((c) => c.key);
|
|
48
|
+
}
|
|
49
|
+
function isPlainObject(value) {
|
|
50
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
51
|
+
}
|
|
52
|
+
function scalar(value) {
|
|
53
|
+
if (value === null || value === undefined)
|
|
54
|
+
return "";
|
|
55
|
+
if (typeof value === "string")
|
|
56
|
+
return value.replace(/\s+/g, " ").trim();
|
|
57
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint")
|
|
58
|
+
return String(value);
|
|
59
|
+
if (Array.isArray(value)) {
|
|
60
|
+
if (value.length === 0)
|
|
61
|
+
return "[]";
|
|
62
|
+
return value.every((v) => v === null || typeof v !== "object") ? value.map(scalar).join(", ") : "[" + value.length + " items]";
|
|
63
|
+
}
|
|
64
|
+
if (isPlainObject(value)) {
|
|
65
|
+
const entries = Object.entries(value);
|
|
66
|
+
if (entries.length === 0)
|
|
67
|
+
return "{}";
|
|
68
|
+
return entries.every(([, v]) => v === null || typeof v !== "object")
|
|
69
|
+
? entries.map(([k, v]) => k + "=" + scalar(v)).join(" ")
|
|
70
|
+
: "{" + entries.length + " fields}";
|
|
71
|
+
}
|
|
72
|
+
return String(value);
|
|
73
|
+
}
|
|
74
|
+
function cut(text, max) {
|
|
75
|
+
return text.length > max ? text.slice(0, Math.max(1, max - 1)) + "…" : text;
|
|
76
|
+
}
|
|
77
|
+
function pad(text, width) {
|
|
78
|
+
return text + " ".repeat(Math.max(0, width - text.length));
|
|
79
|
+
}
|
|
80
|
+
/** The rows and footer lines of a list-shaped result, or null for a single object. */
|
|
81
|
+
function listOf(value, collectionField) {
|
|
82
|
+
if (Array.isArray(value))
|
|
83
|
+
return { rows: value, footer: [] };
|
|
84
|
+
if (!isPlainObject(value))
|
|
85
|
+
return null;
|
|
86
|
+
// The CLI's page envelope: {items, hasMore, nextPage?, nextCommand?, request_id?}.
|
|
87
|
+
if (Array.isArray(value.items) && typeof value.hasMore === "boolean") {
|
|
88
|
+
return { rows: value.items, footer: typeof value.nextCommand === "string" ? ["More results: " + value.nextCommand] : [] };
|
|
89
|
+
}
|
|
90
|
+
if (collectionField && Array.isArray(value[collectionField])) {
|
|
91
|
+
const rest = Object.entries(value).filter(([k, v]) => k !== collectionField && v !== null && typeof v !== "object");
|
|
92
|
+
return { rows: value[collectionField], footer: rest.length ? [rest.map(([k, v]) => k + ": " + scalar(v)).join(" ")] : [] };
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
function renderRows(rows, footer, options) {
|
|
97
|
+
const heading = options.heading ?? ((text) => text);
|
|
98
|
+
if (rows.length === 0)
|
|
99
|
+
return ["No items.", ...footer].join("\n") + "\n";
|
|
100
|
+
if (!rows.every(isPlainObject)) {
|
|
101
|
+
return [...rows.map((row) => cut(scalar(row), Math.max(MAX_CELL, options.width ?? 120))), "", rows.length + (rows.length === 1 ? " item" : " items"), ...footer].join("\n") + "\n";
|
|
102
|
+
}
|
|
103
|
+
const keys = [];
|
|
104
|
+
for (const row of rows)
|
|
105
|
+
for (const key of Object.keys(row))
|
|
106
|
+
if (!keys.includes(key))
|
|
107
|
+
keys.push(key);
|
|
108
|
+
const ordered = byRank(keys, options.resource);
|
|
109
|
+
const width = Math.max(40, options.width ?? 120);
|
|
110
|
+
const shown = [];
|
|
111
|
+
let used = 0;
|
|
112
|
+
for (const key of ordered) {
|
|
113
|
+
const cells = rows.map((row) => cut(scalar(row[key]), MAX_CELL));
|
|
114
|
+
// A column empty in every row says nothing.
|
|
115
|
+
if (cells.every((cell) => cell === ""))
|
|
116
|
+
continue;
|
|
117
|
+
const columnWidth = Math.max(Math.min(key.length, MAX_CELL), ...cells.map((cell) => cell.length));
|
|
118
|
+
if (shown.length > 0 && used + 2 + columnWidth > width)
|
|
119
|
+
continue;
|
|
120
|
+
shown.push({ key, width: columnWidth });
|
|
121
|
+
used += (shown.length > 1 ? 2 : 0) + columnWidth;
|
|
122
|
+
}
|
|
123
|
+
const line = (cells) => cells.map((cell, i) => i === cells.length - 1 ? cell : pad(cell, shown[i].width)).join(" ").trimEnd();
|
|
124
|
+
const lines = [
|
|
125
|
+
heading(line(shown.map((c) => cut(c.key.toUpperCase(), c.width)))),
|
|
126
|
+
...rows.map((row) => line(shown.map((c) => cut(scalar(row[c.key]), MAX_CELL)))),
|
|
127
|
+
"",
|
|
128
|
+
];
|
|
129
|
+
const hidden = ordered.filter((key) => !shown.some((c) => c.key === key));
|
|
130
|
+
lines.push(rows.length + (rows.length === 1 ? " item" : " items") + (hidden.length ? "; not shown: " + hidden.join(", ") : ""));
|
|
131
|
+
return [...lines, ...footer].join("\n") + "\n";
|
|
132
|
+
}
|
|
133
|
+
/** One object as "field value" lines, identifiers and status first; nested objects flatten to dotted paths. */
|
|
134
|
+
function renderObject(value, options) {
|
|
135
|
+
const fields = [];
|
|
136
|
+
const walk = (object, prefix, depth) => {
|
|
137
|
+
const keys = depth === 0 ? byRank(Object.keys(object), options.resource) : Object.keys(object);
|
|
138
|
+
for (const key of keys) {
|
|
139
|
+
const v = object[key];
|
|
140
|
+
const path = prefix + key;
|
|
141
|
+
if (isPlainObject(v) && depth < 2 && Object.keys(v).length > 0)
|
|
142
|
+
walk(v, path + ".", depth + 1);
|
|
143
|
+
else
|
|
144
|
+
fields.push([path, scalar(v)]);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
walk(value, "", 0);
|
|
148
|
+
if (fields.length === 0)
|
|
149
|
+
return "No fields.\n";
|
|
150
|
+
const heading = options.heading ?? ((text) => text);
|
|
151
|
+
const shown = fields.slice(0, MAX_FIELDS);
|
|
152
|
+
const keyWidth = Math.min(MAX_CELL, Math.max(...shown.map(([key]) => key.length)));
|
|
153
|
+
const valueWidth = Math.max(MAX_CELL, (options.width ?? 120) - keyWidth - 2);
|
|
154
|
+
const lines = shown.map(([key, text]) => (heading(pad(cut(key, keyWidth), keyWidth)) + " " + cut(text, valueWidth)).trimEnd());
|
|
155
|
+
if (fields.length > shown.length)
|
|
156
|
+
lines.push("", (fields.length - shown.length) + " more fields not shown; use --format json or --fields.");
|
|
157
|
+
return lines.join("\n") + "\n";
|
|
158
|
+
}
|
|
159
|
+
/** The text --format table prints for a result that would otherwise print as JSON. */
|
|
160
|
+
export function renderTable(value, options = {}) {
|
|
161
|
+
const list = listOf(value, options.collectionField);
|
|
162
|
+
if (list)
|
|
163
|
+
return renderRows(list.rows, list.footer, options);
|
|
164
|
+
if (isPlainObject(value))
|
|
165
|
+
return renderObject(value, options);
|
|
166
|
+
return scalar(value) + "\n";
|
|
167
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named input types for the docs surfaces (MCP read_docs, the CLI's docs
|
|
3
|
+
* command): a type's reference by name, and an argument path such as
|
|
4
|
+
* "filter.team.key" resolved to its type. Each type is described once with
|
|
5
|
+
* its fields typed by name, so a lookup is bounded and every type it
|
|
6
|
+
* mentions can be looked up the same way.
|
|
7
|
+
*/
|
|
8
|
+
export interface InputTypeField {
|
|
9
|
+
/** As an agent writes it: a type name, string[], "a"|"b", object. */
|
|
10
|
+
type: string;
|
|
11
|
+
required?: true;
|
|
12
|
+
description?: string;
|
|
13
|
+
default?: unknown;
|
|
14
|
+
deprecated?: true;
|
|
15
|
+
}
|
|
16
|
+
export interface InputTypeDoc {
|
|
17
|
+
description?: string;
|
|
18
|
+
/** An input object's fields. */
|
|
19
|
+
fields?: Record<string, InputTypeField>;
|
|
20
|
+
/** An enum's values. */
|
|
21
|
+
values?: unknown[];
|
|
22
|
+
/** A union's member types. */
|
|
23
|
+
variants?: string[];
|
|
24
|
+
}
|
|
25
|
+
export interface InputTypes {
|
|
26
|
+
types: Record<string, InputTypeDoc>;
|
|
27
|
+
/** Per tool, each argument whose type is (or contains) a named type. */
|
|
28
|
+
args: Record<string, Record<string, string>>;
|
|
29
|
+
}
|
|
30
|
+
/** The table's type names an expression mentions, in order. */
|
|
31
|
+
export declare function namedTypesIn(table: InputTypes | undefined, expression: string | undefined): string[];
|
|
32
|
+
/** A type by exact name, else by a case-insensitive match. */
|
|
33
|
+
export declare function findInputType(table: InputTypes | undefined, name: string): string | undefined;
|
|
34
|
+
/** How to read another type, in the caller's own syntax. */
|
|
35
|
+
export type TypeLookupHint = (name: string) => string;
|
|
36
|
+
/** A named type's reference: description, then its fields, values or
|
|
37
|
+
* members, then how to read the named types it mentions. */
|
|
38
|
+
export declare function inputTypeText(table: InputTypes, name: string, lookup: TypeLookupHint): string;
|
|
39
|
+
export type PathLookup = {
|
|
40
|
+
ok: true;
|
|
41
|
+
text: string;
|
|
42
|
+
} | {
|
|
43
|
+
ok: false;
|
|
44
|
+
message: string;
|
|
45
|
+
available: string[];
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* What one argument path means: the field's type, whether it is required,
|
|
49
|
+
* its description, and, when its type is named, that type's reference in
|
|
50
|
+
* full (a comparator's operators, an enum's values). `root` is an
|
|
51
|
+
* operation (its tool name, inline input schema, and how the caller names
|
|
52
|
+
* it) or a named type.
|
|
53
|
+
*/
|
|
54
|
+
export declare function argumentPathText(table: InputTypes | undefined, root: {
|
|
55
|
+
tool: string;
|
|
56
|
+
inputSchema: Record<string, unknown>;
|
|
57
|
+
label?: string;
|
|
58
|
+
} | {
|
|
59
|
+
type: string;
|
|
60
|
+
}, path: string, lookup: TypeLookupHint): PathLookup;
|
|
61
|
+
//# sourceMappingURL=type-docs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-docs.d.ts","sourceRoot":"","sources":["../src/type-docs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACxC,wBAAwB;IACxB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACpC,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC9C;AAUD,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAGpG;AASD,8DAA8D;AAC9D,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK7F;AAYD,4DAA4D;AAC5D,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAEtD;4DAC4D;AAC5D,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAyB7F;AA6BD,MAAM,MAAM,UAAU,GAClB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAExD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,UAAU,GAAG,SAAS,EAC7B,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAC/F,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,cAAc,GACrB,UAAU,CAyDZ"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named input types for the docs surfaces (MCP read_docs, the CLI's docs
|
|
3
|
+
* command): a type's reference by name, and an argument path such as
|
|
4
|
+
* "filter.team.key" resolved to its type. Each type is described once with
|
|
5
|
+
* its fields typed by name, so a lookup is bounded and every type it
|
|
6
|
+
* mentions can be looked up the same way.
|
|
7
|
+
*/
|
|
8
|
+
/** Enum values a type page lists before a count. */
|
|
9
|
+
const TYPE_ENUM_VALUES = 200;
|
|
10
|
+
/** One line of prose: links reduced to their text. */
|
|
11
|
+
function prose(text) {
|
|
12
|
+
return (text ?? "").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\s+/g, " ").trim();
|
|
13
|
+
}
|
|
14
|
+
/** The table's type names an expression mentions, in order. */
|
|
15
|
+
export function namedTypesIn(table, expression) {
|
|
16
|
+
if (!table || !expression)
|
|
17
|
+
return [];
|
|
18
|
+
return [...new Set(expression.split(/[|()[\]\s]+/).filter((part) => Object.hasOwn(table.types, part)))];
|
|
19
|
+
}
|
|
20
|
+
/** The one input object an expression names (IssueFilter, IssueFilter[]),
|
|
21
|
+
* whose fields a path continues into. */
|
|
22
|
+
function objectTypeOf(table, expression) {
|
|
23
|
+
const objects = namedTypesIn(table, expression).filter((name) => table.types[name].fields);
|
|
24
|
+
return objects.length === 1 ? objects[0] : undefined;
|
|
25
|
+
}
|
|
26
|
+
/** A type by exact name, else by a case-insensitive match. */
|
|
27
|
+
export function findInputType(table, name) {
|
|
28
|
+
if (!table)
|
|
29
|
+
return undefined;
|
|
30
|
+
if (Object.hasOwn(table.types, name))
|
|
31
|
+
return name;
|
|
32
|
+
const lower = name.toLowerCase();
|
|
33
|
+
return Object.keys(table.types).find((candidate) => candidate.toLowerCase() === lower);
|
|
34
|
+
}
|
|
35
|
+
function fieldLine(name, field) {
|
|
36
|
+
const extras = [
|
|
37
|
+
...(field.required ? ["required"] : []),
|
|
38
|
+
...(field.default !== undefined ? ["default " + JSON.stringify(field.default)] : []),
|
|
39
|
+
...(field.deprecated ? ["deprecated"] : []),
|
|
40
|
+
];
|
|
41
|
+
const description = prose(field.description);
|
|
42
|
+
return " " + name + " (" + field.type + (extras.length ? ", " + extras.join(", ") : "") + ")" + (description ? ": " + description : "");
|
|
43
|
+
}
|
|
44
|
+
/** A named type's reference: description, then its fields, values or
|
|
45
|
+
* members, then how to read the named types it mentions. */
|
|
46
|
+
export function inputTypeText(table, name, lookup) {
|
|
47
|
+
const doc = table.types[name];
|
|
48
|
+
const kind = doc.fields ? "input object" : doc.values ? "enum" : "union";
|
|
49
|
+
const lines = [name + " (" + kind + ")"];
|
|
50
|
+
if (doc.description)
|
|
51
|
+
lines.push(prose(doc.description));
|
|
52
|
+
const mentioned = [];
|
|
53
|
+
if (doc.fields) {
|
|
54
|
+
const entries = Object.entries(doc.fields);
|
|
55
|
+
lines.push("", entries.length === 0 ? "No fields." : "Fields (" + entries.length + "):");
|
|
56
|
+
for (const [field, spec] of entries) {
|
|
57
|
+
lines.push(fieldLine(field, spec));
|
|
58
|
+
mentioned.push(...namedTypesIn(table, spec.type));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (doc.values) {
|
|
62
|
+
const values = doc.values.slice(0, TYPE_ENUM_VALUES).map((value) => JSON.stringify(value));
|
|
63
|
+
lines.push("", "Values: " + values.join(", ") + (doc.values.length > TYPE_ENUM_VALUES ? ", … " + (doc.values.length - TYPE_ENUM_VALUES) + " more" : ""));
|
|
64
|
+
}
|
|
65
|
+
else if (doc.variants) {
|
|
66
|
+
lines.push("", "One of: " + doc.variants.join(" | "));
|
|
67
|
+
for (const variant of doc.variants)
|
|
68
|
+
mentioned.push(...namedTypesIn(table, variant));
|
|
69
|
+
}
|
|
70
|
+
const others = [...new Set(mentioned)].filter((other) => other !== name);
|
|
71
|
+
if (others.length > 0) {
|
|
72
|
+
lines.push("", "Types used above (" + others.length + "): " + others.join(", ") + ". Read one with " + lookup("<type>") + ".");
|
|
73
|
+
}
|
|
74
|
+
return lines.join("\n");
|
|
75
|
+
}
|
|
76
|
+
function inlineObject(schema) {
|
|
77
|
+
if (!schema || typeof schema !== "object")
|
|
78
|
+
return undefined;
|
|
79
|
+
if (schema.properties && typeof schema.properties === "object")
|
|
80
|
+
return schema;
|
|
81
|
+
if (schema.items)
|
|
82
|
+
return inlineObject(schema.items);
|
|
83
|
+
const variants = schema.anyOf ?? schema.oneOf;
|
|
84
|
+
if (Array.isArray(variants)) {
|
|
85
|
+
const objects = variants.map(inlineObject).filter((variant) => variant !== undefined);
|
|
86
|
+
if (objects.length === 1)
|
|
87
|
+
return objects[0];
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
function inlineType(schema) {
|
|
92
|
+
if (Array.isArray(schema.enum))
|
|
93
|
+
return schema.enum.filter((value) => value !== null).map((value) => JSON.stringify(value)).join("|");
|
|
94
|
+
const variants = schema.anyOf ?? schema.oneOf;
|
|
95
|
+
if (Array.isArray(variants))
|
|
96
|
+
return [...new Set(variants.map(inlineType))].filter((t) => t !== "null").join("|") || "null";
|
|
97
|
+
const types = (Array.isArray(schema.type) ? schema.type : typeof schema.type === "string" ? [schema.type] : []).filter((t) => t !== "null");
|
|
98
|
+
if (types.includes("array")) {
|
|
99
|
+
const inner = schema.items ? inlineType(schema.items) : "any";
|
|
100
|
+
return (/[| ]/.test(inner) ? "(" + inner + ")" : inner) + "[]";
|
|
101
|
+
}
|
|
102
|
+
return types.join("|") || (schema.properties ? "object" : "any");
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* What one argument path means: the field's type, whether it is required,
|
|
106
|
+
* its description, and, when its type is named, that type's reference in
|
|
107
|
+
* full (a comparator's operators, an enum's values). `root` is an
|
|
108
|
+
* operation (its tool name, inline input schema, and how the caller names
|
|
109
|
+
* it) or a named type.
|
|
110
|
+
*/
|
|
111
|
+
export function argumentPathText(table, root, path, lookup) {
|
|
112
|
+
const segments = path.split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
|
|
113
|
+
if (segments.length === 0)
|
|
114
|
+
return { ok: false, message: "The path is empty.", available: [] };
|
|
115
|
+
// Walk the named-type table where the path has a type name, else the
|
|
116
|
+
// operation's inline schema.
|
|
117
|
+
let typeName = "type" in root ? root.type : undefined;
|
|
118
|
+
let inline = "type" in root ? undefined : root.inputSchema;
|
|
119
|
+
let expression;
|
|
120
|
+
let field;
|
|
121
|
+
const label = "type" in root ? root.type : root.label ?? root.tool;
|
|
122
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
123
|
+
const segment = segments[index];
|
|
124
|
+
const at = index === 0 ? label : label + " " + segments.slice(0, index).join(".");
|
|
125
|
+
if (typeName && table) {
|
|
126
|
+
const fields = table.types[typeName].fields ?? {};
|
|
127
|
+
if (!Object.hasOwn(fields, segment))
|
|
128
|
+
return { ok: false, message: at + " (" + typeName + ") has no field \"" + segment + "\".", available: Object.keys(fields) };
|
|
129
|
+
field = fields[segment];
|
|
130
|
+
expression = field.type;
|
|
131
|
+
inline = undefined;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const object = inlineObject(inline);
|
|
135
|
+
const properties = object?.properties ?? {};
|
|
136
|
+
if (!object || !Object.hasOwn(properties, segment)) {
|
|
137
|
+
return { ok: false, message: object ? at + " has no field \"" + segment + "\"." : at + " is not an object; the path cannot continue past it.", available: Object.keys(properties) };
|
|
138
|
+
}
|
|
139
|
+
const child = properties[segment];
|
|
140
|
+
const topLevel = index === 0 && !("type" in root) ? table?.args[root.tool]?.[segment] : undefined;
|
|
141
|
+
expression = topLevel ?? inlineType(child);
|
|
142
|
+
field = {
|
|
143
|
+
type: expression,
|
|
144
|
+
...(Array.isArray(object.required) && object.required.includes(segment) ? { required: true } : {}),
|
|
145
|
+
...(typeof child.description === "string" ? { description: child.description } : {}),
|
|
146
|
+
};
|
|
147
|
+
inline = child;
|
|
148
|
+
}
|
|
149
|
+
typeName = table ? objectTypeOf(table, expression) : undefined;
|
|
150
|
+
if (!typeName && index < segments.length - 1 && !inlineObject(inline)) {
|
|
151
|
+
return { ok: false, message: label + " " + segments.slice(0, index + 1).join(".") + " is " + expression + ", not an object; the path cannot continue past it.", available: [] };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const lines = [label + " " + segments.join(".") + ": " + field.type + (field.required ? " (required)" : "")];
|
|
155
|
+
const description = prose(field.description);
|
|
156
|
+
if (description)
|
|
157
|
+
lines.push(description);
|
|
158
|
+
const named = namedTypesIn(table, expression);
|
|
159
|
+
if (named.length > 0 && table) {
|
|
160
|
+
for (const name of named)
|
|
161
|
+
lines.push("", inputTypeText(table, name, lookup));
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
const object = inlineObject(inline);
|
|
165
|
+
if (object) {
|
|
166
|
+
const required = new Set(object.required ?? []);
|
|
167
|
+
lines.push("", "Fields:");
|
|
168
|
+
for (const [name, child] of Object.entries(object.properties ?? {})) {
|
|
169
|
+
lines.push(fieldLine(name, { type: inlineType(child), ...(required.has(name) ? { required: true } : {}), ...(child.description ? { description: child.description } : {}) }));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return { ok: true, text: lines.join("\n") };
|
|
174
|
+
}
|