@agentuity/cli 1.0.34 → 1.0.36
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/bin/cli.ts +43 -11
- package/dist/ai-help.js +60 -0
- package/dist/ai-help.js.map +1 -1
- package/dist/cache/agent-intro.d.ts +10 -0
- package/dist/cache/agent-intro.d.ts.map +1 -1
- package/dist/cache/agent-intro.js +50 -0
- package/dist/cache/agent-intro.js.map +1 -1
- package/dist/cache/index.d.ts +1 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +1 -1
- package/dist/cache/index.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +50 -7
- package/dist/cli.js.map +1 -1
- package/dist/cmd/ai/intro.d.ts.map +1 -1
- package/dist/cmd/ai/intro.js +26 -6
- package/dist/cmd/ai/intro.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +4 -0
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/create-namespace.js +3 -3
- package/dist/cmd/cloud/keyvalue/create-namespace.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/delete-namespace.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/delete-namespace.js +7 -5
- package/dist/cmd/cloud/keyvalue/delete-namespace.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/delete.js +9 -3
- package/dist/cmd/cloud/keyvalue/delete.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/get.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/get.js +6 -3
- package/dist/cmd/cloud/keyvalue/get.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/keys.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/keys.js +9 -3
- package/dist/cmd/cloud/keyvalue/keys.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/list-namespaces.js +3 -3
- package/dist/cmd/cloud/keyvalue/list-namespaces.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/repl.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/repl.js +3 -1
- package/dist/cmd/cloud/keyvalue/repl.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/search.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/search.js +6 -3
- package/dist/cmd/cloud/keyvalue/search.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/set.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/set.js +7 -5
- package/dist/cmd/cloud/keyvalue/set.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/stats.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/stats.js +6 -3
- package/dist/cmd/cloud/keyvalue/stats.js.map +1 -1
- package/dist/cmd/cloud/region/index.d.ts.map +1 -1
- package/dist/cmd/cloud/region/index.js +3 -1
- package/dist/cmd/cloud/region/index.js.map +1 -1
- package/dist/cmd/cloud/region/list.d.ts +2 -0
- package/dist/cmd/cloud/region/list.d.ts.map +1 -0
- package/dist/cmd/cloud/region/list.js +55 -0
- package/dist/cmd/cloud/region/list.js.map +1 -0
- package/dist/cmd/cloud/task/create.d.ts.map +1 -1
- package/dist/cmd/cloud/task/create.js +19 -0
- package/dist/cmd/cloud/task/create.js.map +1 -1
- package/dist/cmd/project/domain/check.d.ts.map +1 -1
- package/dist/cmd/project/domain/check.js +1 -5
- package/dist/cmd/project/domain/check.js.map +1 -1
- package/dist/domain.d.ts +9 -8
- package/dist/domain.d.ts.map +1 -1
- package/dist/domain.js +19 -62
- package/dist/domain.js.map +1 -1
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +45 -1
- package/dist/output.js.map +1 -1
- package/dist/schema-generator.d.ts +9 -1
- package/dist/schema-generator.d.ts.map +1 -1
- package/dist/schema-generator.js +57 -24
- package/dist/schema-generator.js.map +1 -1
- package/dist/schema-parser.d.ts +2 -2
- package/dist/schema-parser.d.ts.map +1 -1
- package/dist/schema-parser.js +44 -3
- package/dist/schema-parser.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +6 -6
- package/src/ai-help.ts +61 -0
- package/src/cache/agent-intro.ts +54 -0
- package/src/cache/index.ts +6 -1
- package/src/cli.ts +84 -11
- package/src/cmd/ai/intro.ts +26 -6
- package/src/cmd/cloud/deploy.ts +5 -0
- package/src/cmd/cloud/keyvalue/create-namespace.ts +3 -3
- package/src/cmd/cloud/keyvalue/delete-namespace.ts +7 -5
- package/src/cmd/cloud/keyvalue/delete.ts +9 -3
- package/src/cmd/cloud/keyvalue/get.ts +6 -3
- package/src/cmd/cloud/keyvalue/keys.ts +9 -3
- package/src/cmd/cloud/keyvalue/list-namespaces.ts +3 -3
- package/src/cmd/cloud/keyvalue/repl.ts +3 -1
- package/src/cmd/cloud/keyvalue/search.ts +6 -3
- package/src/cmd/cloud/keyvalue/set.ts +7 -5
- package/src/cmd/cloud/keyvalue/stats.ts +6 -3
- package/src/cmd/cloud/region/index.ts +3 -1
- package/src/cmd/cloud/region/list.ts +62 -0
- package/src/cmd/cloud/task/create.ts +22 -0
- package/src/cmd/project/domain/check.ts +0 -4
- package/src/domain.ts +28 -72
- package/src/output.ts +46 -1
- package/src/schema-generator.ts +62 -27
- package/src/schema-parser.ts +57 -3
- package/src/types.ts +3 -0
package/src/domain.ts
CHANGED
|
@@ -14,14 +14,8 @@ interface DNSSuccess extends BaseDNSResult {
|
|
|
14
14
|
success: true;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
interface DNSPending extends BaseDNSResult {
|
|
18
|
-
success: true;
|
|
19
|
-
pending: true;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
17
|
interface DNSMissing extends BaseDNSResult {
|
|
23
18
|
success: false;
|
|
24
|
-
pending: false;
|
|
25
19
|
}
|
|
26
20
|
|
|
27
21
|
interface DNSError extends BaseDNSResult {
|
|
@@ -34,27 +28,23 @@ interface DNSMisconfigured extends BaseDNSResult {
|
|
|
34
28
|
misconfigured: string;
|
|
35
29
|
}
|
|
36
30
|
|
|
37
|
-
export type DNSResult = DNSSuccess |
|
|
38
|
-
export type DNSFailed =
|
|
31
|
+
export type DNSResult = DNSSuccess | DNSMissing | DNSError | DNSMisconfigured;
|
|
32
|
+
export type DNSFailed = DNSMissing | DNSError | DNSMisconfigured;
|
|
39
33
|
|
|
40
34
|
export function isMisconfigured(x: DNSResult): x is DNSMisconfigured {
|
|
41
35
|
return 'misconfigured' in x && !!x.misconfigured;
|
|
42
36
|
}
|
|
43
37
|
|
|
44
38
|
export function isMissing(x: DNSResult): x is DNSMissing {
|
|
45
|
-
return
|
|
39
|
+
return x.success === false && !('error' in x) && !('misconfigured' in x);
|
|
46
40
|
}
|
|
47
41
|
|
|
48
42
|
export function isError(x: DNSResult): x is DNSError {
|
|
49
43
|
return 'error' in x && !!x.error;
|
|
50
44
|
}
|
|
51
45
|
|
|
52
|
-
export function isPending(x: DNSResult): x is DNSPending {
|
|
53
|
-
return 'pending' in x && x.pending === true && x.success === true;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
46
|
export function isSuccess(x: DNSResult): x is DNSSuccess {
|
|
57
|
-
return x.success === true
|
|
47
|
+
return x.success === true;
|
|
58
48
|
}
|
|
59
49
|
|
|
60
50
|
const timeoutMs = 5000;
|
|
@@ -95,30 +85,6 @@ async function fetchDNSRecord(name: string, type: string): Promise<string | null
|
|
|
95
85
|
return records[0] ?? null;
|
|
96
86
|
}
|
|
97
87
|
|
|
98
|
-
/**
|
|
99
|
-
* Check if a domain has a valid TLS certificate by making a HEAD request.
|
|
100
|
-
* This also triggers Let's Encrypt certificate provisioning on first access.
|
|
101
|
-
* Returns true if the TLS certificate is valid (any HTTP status code received).
|
|
102
|
-
* Returns false if the certificate is not yet provisioned (timeout or TLS error).
|
|
103
|
-
*/
|
|
104
|
-
async function checkTLSCertificate(domain: string): Promise<boolean> {
|
|
105
|
-
try {
|
|
106
|
-
await fetch(`https://${domain}`, {
|
|
107
|
-
method: 'HEAD',
|
|
108
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
109
|
-
redirect: 'manual',
|
|
110
|
-
// @ts-expect-error - cache is supported by Bun's fetch at runtime but missing from type definitions
|
|
111
|
-
cache: 'no-store',
|
|
112
|
-
});
|
|
113
|
-
// Any HTTP response means TLS handshake succeeded and certificate is valid
|
|
114
|
-
return true;
|
|
115
|
-
} catch {
|
|
116
|
-
// Timeout, TLS certificate error, connection refused, etc.
|
|
117
|
-
// All indicate the certificate is not yet provisioned
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
88
|
const LOCAL_DNS = 'agentuity.io';
|
|
123
89
|
const PRODUCTION_DNS = 'agentuity.run';
|
|
124
90
|
|
|
@@ -197,25 +163,14 @@ export async function checkCustomDomainForDNS(
|
|
|
197
163
|
|
|
198
164
|
if (result) {
|
|
199
165
|
if (result === proxy) {
|
|
200
|
-
// DNS is
|
|
201
|
-
const tlsValid = await checkTLSCertificate(domain);
|
|
202
|
-
if (tlsValid) {
|
|
203
|
-
return {
|
|
204
|
-
domain,
|
|
205
|
-
target: proxy,
|
|
206
|
-
aRecordTarget,
|
|
207
|
-
recordType: 'CNAME',
|
|
208
|
-
success: true,
|
|
209
|
-
} as DNSSuccess;
|
|
210
|
-
}
|
|
166
|
+
// DNS is correctly configured
|
|
211
167
|
return {
|
|
212
168
|
domain,
|
|
213
169
|
target: proxy,
|
|
214
170
|
aRecordTarget,
|
|
215
171
|
recordType: 'CNAME',
|
|
216
172
|
success: true,
|
|
217
|
-
|
|
218
|
-
} as DNSPending;
|
|
173
|
+
} as DNSSuccess;
|
|
219
174
|
}
|
|
220
175
|
return {
|
|
221
176
|
domain,
|
|
@@ -278,25 +233,14 @@ export async function checkCustomDomainForDNS(
|
|
|
278
233
|
if (domainARecords.length > 0) {
|
|
279
234
|
const matching = domainARecords.some((a) => ionIPs.includes(a));
|
|
280
235
|
if (matching) {
|
|
281
|
-
// DNS is
|
|
282
|
-
const tlsValid = await checkTLSCertificate(domain);
|
|
283
|
-
if (tlsValid) {
|
|
284
|
-
return {
|
|
285
|
-
domain,
|
|
286
|
-
target: proxy,
|
|
287
|
-
aRecordTarget,
|
|
288
|
-
recordType: 'A',
|
|
289
|
-
success: true,
|
|
290
|
-
} as DNSSuccess;
|
|
291
|
-
}
|
|
236
|
+
// DNS is correctly configured
|
|
292
237
|
return {
|
|
293
238
|
domain,
|
|
294
239
|
target: proxy,
|
|
295
240
|
aRecordTarget,
|
|
296
241
|
recordType: 'A',
|
|
297
242
|
success: true,
|
|
298
|
-
|
|
299
|
-
} as DNSPending;
|
|
243
|
+
} as DNSSuccess;
|
|
300
244
|
}
|
|
301
245
|
return {
|
|
302
246
|
domain,
|
|
@@ -318,7 +262,6 @@ export async function checkCustomDomainForDNS(
|
|
|
318
262
|
target: proxy,
|
|
319
263
|
aRecordTarget,
|
|
320
264
|
recordType: 'CNAME',
|
|
321
|
-
pending: false,
|
|
322
265
|
} as DNSMissing;
|
|
323
266
|
})
|
|
324
267
|
);
|
|
@@ -371,13 +314,6 @@ export async function promptForDNS(
|
|
|
371
314
|
aRecordTarget: r.aRecordTarget,
|
|
372
315
|
status: tui.colorWarning(`${tui.ICONS.error} ${r.misconfigured}`),
|
|
373
316
|
});
|
|
374
|
-
} else if (isPending(r)) {
|
|
375
|
-
records.push({
|
|
376
|
-
domain: r.domain,
|
|
377
|
-
cnameTarget: r.target,
|
|
378
|
-
aRecordTarget: r.aRecordTarget,
|
|
379
|
-
status: tui.colorWarning('⌛️ Pending'),
|
|
380
|
-
});
|
|
381
317
|
} else if (isMissing(r)) {
|
|
382
318
|
records.push({
|
|
383
319
|
domain: r.domain,
|
|
@@ -420,3 +356,23 @@ export async function promptForDNS(
|
|
|
420
356
|
break;
|
|
421
357
|
}
|
|
422
358
|
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Trigger TLS certificate provisioning for custom domains by making HTTPS requests.
|
|
362
|
+
* This causes the Ion proxy to initiate Let's Encrypt certificate issuance.
|
|
363
|
+
* Fire-and-forget — failures are silently ignored since certs will be provisioned
|
|
364
|
+
* on first real request if this doesn't succeed.
|
|
365
|
+
*/
|
|
366
|
+
export async function triggerTLSProvisioning(domains: string[]): Promise<void> {
|
|
367
|
+
await Promise.allSettled(
|
|
368
|
+
domains.map((domain) =>
|
|
369
|
+
fetch(`https://${domain}`, {
|
|
370
|
+
method: 'HEAD',
|
|
371
|
+
signal: AbortSignal.timeout(10000),
|
|
372
|
+
redirect: 'manual',
|
|
373
|
+
}).catch(() => {
|
|
374
|
+
// Silently ignore — cert will be provisioned on first real request
|
|
375
|
+
})
|
|
376
|
+
)
|
|
377
|
+
);
|
|
378
|
+
}
|
package/src/output.ts
CHANGED
|
@@ -63,11 +63,56 @@ export function shouldDisableColors(options: GlobalOptions): boolean {
|
|
|
63
63
|
return options.json === true || options.quiet === true || !isTTYLike();
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Filter data to only include specified fields.
|
|
68
|
+
* Supports dot notation for nested fields (e.g., "id,name,properties.title").
|
|
69
|
+
* For arrays, applies the filter to each element.
|
|
70
|
+
*/
|
|
71
|
+
function filterFields(data: unknown, fields: string[]): unknown {
|
|
72
|
+
if (Array.isArray(data)) {
|
|
73
|
+
return data.map((item) => filterFields(item, fields));
|
|
74
|
+
}
|
|
75
|
+
if (data && typeof data === 'object') {
|
|
76
|
+
const result: Record<string, unknown> = {};
|
|
77
|
+
for (const field of fields) {
|
|
78
|
+
const parts = field.split('.');
|
|
79
|
+
let current: unknown = data;
|
|
80
|
+
let target: Record<string, unknown> = result;
|
|
81
|
+
for (let i = 0; i < parts.length; i++) {
|
|
82
|
+
const part = parts[i] as string;
|
|
83
|
+
if (
|
|
84
|
+
current &&
|
|
85
|
+
typeof current === 'object' &&
|
|
86
|
+
part in (current as Record<string, unknown>)
|
|
87
|
+
) {
|
|
88
|
+
if (i === parts.length - 1) {
|
|
89
|
+
target[part] = (current as Record<string, unknown>)[part];
|
|
90
|
+
} else {
|
|
91
|
+
target[part] = target[part] || {};
|
|
92
|
+
target = target[part] as Record<string, unknown>;
|
|
93
|
+
current = (current as Record<string, unknown>)[part];
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
break; // path segment not found — stop traversal for this field
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
return data;
|
|
103
|
+
}
|
|
104
|
+
|
|
66
105
|
/**
|
|
67
106
|
* Output JSON to stdout (for agent consumption)
|
|
68
107
|
*/
|
|
69
108
|
export function outputJSON(data: unknown): void {
|
|
70
|
-
|
|
109
|
+
let output = data;
|
|
110
|
+
const options = getOutputOptions();
|
|
111
|
+
if (options?.fields) {
|
|
112
|
+
const fields = options.fields.split(',').map((f) => f.trim());
|
|
113
|
+
output = filterFields(output, fields);
|
|
114
|
+
}
|
|
115
|
+
console.log(JSON.stringify(output, null, 2));
|
|
71
116
|
}
|
|
72
117
|
|
|
73
118
|
/**
|
package/src/schema-generator.ts
CHANGED
|
@@ -75,10 +75,41 @@ export interface CLISchema {
|
|
|
75
75
|
commands: SchemaCommand[];
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Apply args, options, and response from a CommandSchemas to a SchemaCommand.
|
|
80
|
+
* Shared by both extractCommandSchema and extractSubcommandSchema.
|
|
81
|
+
*/
|
|
82
|
+
function applySchemaFields(schema: SchemaCommand, schemas: CommandSchemas): void {
|
|
83
|
+
if (schemas.args) {
|
|
84
|
+
const parsedArgs = parseArgsSchema(schemas.args);
|
|
85
|
+
schema.arguments = parsedArgs.metadata.map((arg) => ({
|
|
86
|
+
name: arg.name,
|
|
87
|
+
type: arg.variadic ? 'array' : 'string',
|
|
88
|
+
required: !arg.optional,
|
|
89
|
+
variadic: arg.variadic,
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (schemas.options) {
|
|
94
|
+
const parsedOptions = parseOptionsSchema(schemas.options);
|
|
95
|
+
schema.options = parsedOptions.map((opt) => ({
|
|
96
|
+
name: opt.name,
|
|
97
|
+
type: opt.type,
|
|
98
|
+
required: !opt.hasDefault,
|
|
99
|
+
default: opt.defaultValue,
|
|
100
|
+
description: opt.description,
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (schemas.response) {
|
|
105
|
+
schema.response = z.toJSONSchema(schemas.response);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
78
109
|
/**
|
|
79
110
|
* Extract schema information from a CommandDefinition
|
|
80
111
|
*/
|
|
81
|
-
function extractCommandSchema(def: CommandDefinition): SchemaCommand {
|
|
112
|
+
export function extractCommandSchema(def: CommandDefinition): SchemaCommand {
|
|
82
113
|
const schema: SchemaCommand = {
|
|
83
114
|
name: def.name,
|
|
84
115
|
description: def.description,
|
|
@@ -151,6 +182,14 @@ function extractCommandSchema(def: CommandDefinition): SchemaCommand {
|
|
|
151
182
|
};
|
|
152
183
|
}
|
|
153
184
|
|
|
185
|
+
// Extract args and options from schema if available
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
187
|
+
if ((def as any).schema) {
|
|
188
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
189
|
+
const schemas = (def as any).schema as CommandSchemas;
|
|
190
|
+
applySchemaFields(schema, schemas);
|
|
191
|
+
}
|
|
192
|
+
|
|
154
193
|
// Extract subcommands recursively
|
|
155
194
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
195
|
if ((def as any).subcommands) {
|
|
@@ -166,7 +205,7 @@ function extractCommandSchema(def: CommandDefinition): SchemaCommand {
|
|
|
166
205
|
/**
|
|
167
206
|
* Extract schema information from a SubcommandDefinition
|
|
168
207
|
*/
|
|
169
|
-
function extractSubcommandSchema(def: SubcommandDefinition): SchemaCommand {
|
|
208
|
+
export function extractSubcommandSchema(def: SubcommandDefinition): SchemaCommand {
|
|
170
209
|
const schema: SchemaCommand = {
|
|
171
210
|
name: def.name,
|
|
172
211
|
description: def.description,
|
|
@@ -249,31 +288,7 @@ function extractSubcommandSchema(def: SubcommandDefinition): SchemaCommand {
|
|
|
249
288
|
// Extract args and options from schema if available
|
|
250
289
|
if (d.schema) {
|
|
251
290
|
const schemas = d.schema as CommandSchemas;
|
|
252
|
-
|
|
253
|
-
if (schemas.args) {
|
|
254
|
-
const parsedArgs = parseArgsSchema(schemas.args);
|
|
255
|
-
schema.arguments = parsedArgs.metadata.map((arg) => ({
|
|
256
|
-
name: arg.name,
|
|
257
|
-
type: arg.variadic ? 'array' : 'string',
|
|
258
|
-
required: !arg.optional,
|
|
259
|
-
variadic: arg.variadic,
|
|
260
|
-
}));
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (schemas.options) {
|
|
264
|
-
const parsedOptions = parseOptionsSchema(schemas.options);
|
|
265
|
-
schema.options = parsedOptions.map((opt) => ({
|
|
266
|
-
name: opt.name,
|
|
267
|
-
type: opt.type,
|
|
268
|
-
required: !opt.hasDefault,
|
|
269
|
-
default: opt.defaultValue,
|
|
270
|
-
description: opt.description,
|
|
271
|
-
}));
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (schemas.response) {
|
|
275
|
-
schema.response = z.toJSONSchema(schemas.response);
|
|
276
|
-
}
|
|
291
|
+
applySchemaFields(schema, schemas);
|
|
277
292
|
}
|
|
278
293
|
|
|
279
294
|
// Extract nested subcommands recursively
|
|
@@ -410,6 +425,26 @@ export function generateCLISchema(
|
|
|
410
425
|
default: false,
|
|
411
426
|
description: 'Validate arguments and options without executing',
|
|
412
427
|
},
|
|
428
|
+
{
|
|
429
|
+
name: 'input',
|
|
430
|
+
type: 'string',
|
|
431
|
+
required: false,
|
|
432
|
+
description: 'Pass arguments and options as a JSON object (for agents)',
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
name: 'describe',
|
|
436
|
+
type: 'boolean',
|
|
437
|
+
required: false,
|
|
438
|
+
default: false,
|
|
439
|
+
description: 'Output command schema as JSON for agent introspection',
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
name: 'fields',
|
|
443
|
+
type: 'string',
|
|
444
|
+
required: false,
|
|
445
|
+
description:
|
|
446
|
+
'Filter JSON output to specified fields (comma-separated, dot notation for nested)',
|
|
447
|
+
},
|
|
413
448
|
],
|
|
414
449
|
commands: commands.map(extractCommandSchema),
|
|
415
450
|
};
|
package/src/schema-parser.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type { ZodType } from 'zod';
|
|
2
2
|
import type { CommandSchemas } from './types';
|
|
3
|
+
import { StructuredError } from '@agentuity/core';
|
|
4
|
+
|
|
5
|
+
const InputJSONParseError = StructuredError('InputJSONParseError')<{
|
|
6
|
+
flag: string;
|
|
7
|
+
errorType: string;
|
|
8
|
+
}>();
|
|
9
|
+
const InputJSONTypeError = StructuredError('InputJSONTypeError')<{
|
|
10
|
+
flag: string;
|
|
11
|
+
expected: string;
|
|
12
|
+
actual: string;
|
|
13
|
+
}>();
|
|
3
14
|
|
|
4
15
|
export interface ParsedArgs {
|
|
5
16
|
names: string[];
|
|
@@ -394,7 +405,8 @@ export function buildValidationInput(
|
|
|
394
405
|
schemas: CommandSchemas,
|
|
395
406
|
rawArgs: unknown[],
|
|
396
407
|
rawOptions: Record<string, unknown>,
|
|
397
|
-
_options?: { usesStdin?: boolean }
|
|
408
|
+
_options?: { usesStdin?: boolean },
|
|
409
|
+
inputJson?: string
|
|
398
410
|
): { args: Record<string, unknown>; options: Record<string, unknown> } {
|
|
399
411
|
const result = { args: {} as Record<string, unknown>, options: {} as Record<string, unknown> };
|
|
400
412
|
|
|
@@ -442,6 +454,47 @@ export function buildValidationInput(
|
|
|
442
454
|
}
|
|
443
455
|
}
|
|
444
456
|
|
|
457
|
+
// Merge --input JSON values: CLI flags take precedence over --input values
|
|
458
|
+
if (inputJson !== undefined) {
|
|
459
|
+
let parsed: Record<string, unknown>;
|
|
460
|
+
try {
|
|
461
|
+
parsed = JSON.parse(inputJson) as Record<string, unknown>;
|
|
462
|
+
} catch (e) {
|
|
463
|
+
throw new InputJSONParseError({
|
|
464
|
+
message: `Invalid JSON in --input flag: ${e instanceof Error ? e.message : String(e)}`,
|
|
465
|
+
flag: '--input',
|
|
466
|
+
errorType: 'json_parse',
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
471
|
+
throw new InputJSONTypeError({
|
|
472
|
+
message: `Invalid JSON in --input flag: expected a JSON object, got ${parsed === null ? 'null' : Array.isArray(parsed) ? 'array' : typeof parsed}`,
|
|
473
|
+
flag: '--input',
|
|
474
|
+
expected: 'object',
|
|
475
|
+
actual: parsed === null ? 'null' : Array.isArray(parsed) ? 'array' : typeof parsed,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (schemas.args) {
|
|
480
|
+
const argsMeta = parseArgsSchema(schemas.args);
|
|
481
|
+
for (const name of argsMeta.names) {
|
|
482
|
+
if (result.args[name] === undefined && parsed[name] !== undefined) {
|
|
483
|
+
result.args[name] = parsed[name];
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (schemas.options) {
|
|
489
|
+
const optsMeta = parseOptionsSchema(schemas.options);
|
|
490
|
+
for (const opt of optsMeta) {
|
|
491
|
+
if (result.options[opt.name] === undefined && parsed[opt.name] !== undefined) {
|
|
492
|
+
result.options[opt.name] = parsed[opt.name];
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
445
498
|
return result;
|
|
446
499
|
}
|
|
447
500
|
|
|
@@ -453,9 +506,10 @@ export async function buildValidationInputAsync(
|
|
|
453
506
|
schemas: CommandSchemas,
|
|
454
507
|
rawArgs: unknown[],
|
|
455
508
|
rawOptions: Record<string, unknown>,
|
|
456
|
-
options?: { usesStdin?: boolean }
|
|
509
|
+
options?: { usesStdin?: boolean },
|
|
510
|
+
inputJson?: string
|
|
457
511
|
): Promise<{ args: Record<string, unknown>; options: Record<string, unknown> }> {
|
|
458
|
-
const result = buildValidationInput(schemas, rawArgs, rawOptions, options);
|
|
512
|
+
const result = buildValidationInput(schemas, rawArgs, rawOptions, options, inputJson);
|
|
459
513
|
|
|
460
514
|
// Check for stdin confirmation if:
|
|
461
515
|
// 1. Command has a confirm option in schema
|
package/src/types.ts
CHANGED