@agentcentral/cli 0.1.2 → 0.1.3
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/README.md +33 -3
- package/dist/index.js +213 -136
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,6 +53,7 @@ The config lives at `${XDG_CONFIG_HOME:-~/.config}/agentcentral/config.json`, wr
|
|
|
53
53
|
```bash
|
|
54
54
|
agentcentral tools list # key-scoped tools (json)
|
|
55
55
|
agentcentral tools list --format table # aligned table
|
|
56
|
+
agentcentral tools list --domain inventory # filter by server domain
|
|
56
57
|
agentcentral tools describe get_orders # full descriptor
|
|
57
58
|
agentcentral tools schema get_orders # raw JSON Schema
|
|
58
59
|
|
|
@@ -62,6 +63,12 @@ agentcentral tools call get_orders @args.json
|
|
|
62
63
|
echo '{"limit":5}' | agentcentral tools call get_orders -
|
|
63
64
|
```
|
|
64
65
|
|
|
66
|
+
A new hosted tool is callable (`tools list`, `describe`, `schema`, `call`) with no CLI release. The one
|
|
67
|
+
caveat is domain filtering: `tools list --domain` and `mcp serve --domain` fall back to the bundled
|
|
68
|
+
`TOOL_DOMAINS` registry from `@agent-central/shared` when the server omits `_meta` domain hints, so a
|
|
69
|
+
brand-new tool will not appear under a domain filter until that registry is updated (or the server emits
|
|
70
|
+
the tool's domain via `_meta`).
|
|
71
|
+
|
|
65
72
|
Write tools default to a non-mutating **preview**. To submit, pass `--yes` and an `--idempotency-key`:
|
|
66
73
|
|
|
67
74
|
```bash
|
|
@@ -69,6 +76,25 @@ agentcentral tools call update_keyword_bids @bids.json \
|
|
|
69
76
|
--yes --idempotency-key "$(uuidgen)"
|
|
70
77
|
```
|
|
71
78
|
|
|
79
|
+
### Convenience read commands
|
|
80
|
+
|
|
81
|
+
Thin wrappers over the same hosted read tools. Each maps friendly flags onto the exact parameters its
|
|
82
|
+
backing tool declares. They return facts only and add no recommendations or agentcentral-authored next
|
|
83
|
+
actions.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
agentcentral inventory health # -> get_inventory_health (--limit)
|
|
87
|
+
agentcentral inventory risk --asin B00... # -> get_inventory_risk_triage (--asin, --limit)
|
|
88
|
+
agentcentral ads performance --since 30d # -> get_campaign_performance (--since, --limit)
|
|
89
|
+
agentcentral catalog quality --asin B00... # -> get_listing_quality (--asin, --limit)
|
|
90
|
+
agentcentral actions history --since 7d # -> get_action_history (--since, --limit; needs agent_actions:read scope)
|
|
91
|
+
agentcentral today --fulfillment-channel AFN # -> get_todays_sales (current-day only; --fulfillment-channel AFN|MFN, --limit)
|
|
92
|
+
agentcentral orders --since 30d --asin B00... # -> get_orders (--since, --asin, --fulfillment-channel AFN|MFN, --limit)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`--since` accepts a relative lookback (`30d`) or an absolute `YYYY-MM-DD` date. `today` is current-day
|
|
96
|
+
only and takes no `--since`.
|
|
97
|
+
|
|
72
98
|
### Run the stdio MCP bridge
|
|
73
99
|
|
|
74
100
|
`mcp serve` relays newline-delimited JSON-RPC between a local MCP client and the hosted server. stdout
|
|
@@ -92,9 +118,13 @@ agentcentral doctor --format markdown
|
|
|
92
118
|
|
|
93
119
|
## Global flags
|
|
94
120
|
|
|
95
|
-
Every command accepts: `--format/-f json|ndjson|table`, `--output/-o <path>`,
|
|
96
|
-
`--timeout <ms>`, `--limit <n>`, `--quiet/-q`, `--no-color`, `--verbose`,
|
|
97
|
-
`--yes/-y`, `--idempotency-key <key>`, `--api-key-stdin`.
|
|
121
|
+
Every command accepts: `--format/-f json|ndjson|table|csv|markdown`, `--output/-o <path>`,
|
|
122
|
+
`--profile/-p <name>`, `--timeout <ms>`, `--limit <n>`, `--quiet/-q`, `--no-color`, `--verbose`,
|
|
123
|
+
`--request-id <id>`, `--yes/-y`, `--idempotency-key <key>`, `--api-key-stdin`.
|
|
124
|
+
|
|
125
|
+
When `--limit <n>` is set and the result comes back with exactly `n` rows, the CLI emits a best-effort
|
|
126
|
+
truncation WARNING to stderr. The hosted server exposes no truncation flag or total-row count, so this
|
|
127
|
+
is a heuristic, it only fires when `--limit` is set, and it never touches stdout.
|
|
98
128
|
|
|
99
129
|
## Exit codes
|
|
100
130
|
|
package/dist/index.js
CHANGED
|
@@ -34,13 +34,14 @@ var API_KEY_PREFIX = "ac_live_";
|
|
|
34
34
|
|
|
35
35
|
// src/config.ts
|
|
36
36
|
import {
|
|
37
|
-
chmodSync,
|
|
38
37
|
closeSync,
|
|
39
38
|
existsSync,
|
|
40
39
|
fstatSync,
|
|
41
40
|
mkdirSync,
|
|
42
41
|
openSync,
|
|
43
42
|
readFileSync,
|
|
43
|
+
renameSync,
|
|
44
|
+
unlinkSync,
|
|
44
45
|
writeFileSync
|
|
45
46
|
} from "fs";
|
|
46
47
|
import { homedir } from "os";
|
|
@@ -107,9 +108,23 @@ function loadConfig(env = process.env) {
|
|
|
107
108
|
function saveConfig(config, env = process.env) {
|
|
108
109
|
const path = configPath(env);
|
|
109
110
|
const dir = dirname(path);
|
|
111
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
110
112
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
111
|
-
|
|
112
|
-
|
|
113
|
+
const data = JSON.stringify(config, null, 2);
|
|
114
|
+
try {
|
|
115
|
+
writeFileSync(tmp, data, { mode: 384, flag: "wx" });
|
|
116
|
+
renameSync(tmp, path);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
try {
|
|
119
|
+
unlinkSync(tmp);
|
|
120
|
+
} catch {
|
|
121
|
+
}
|
|
122
|
+
throw new CliError(
|
|
123
|
+
1 /* GeneralFailure */,
|
|
124
|
+
`failed to write config ${path}`,
|
|
125
|
+
error instanceof Error ? error.message : String(error)
|
|
126
|
+
);
|
|
127
|
+
}
|
|
113
128
|
}
|
|
114
129
|
function upsertProfile(config, profile) {
|
|
115
130
|
const exists = config.profiles.some((p) => p.name === profile.name);
|
|
@@ -243,7 +258,128 @@ async function resolveAuth(input) {
|
|
|
243
258
|
};
|
|
244
259
|
}
|
|
245
260
|
|
|
261
|
+
// src/exit-codes.ts
|
|
262
|
+
var RPC_INVALID_PARAMS = -32602;
|
|
263
|
+
var DETAIL_MAX_LENGTH = 180;
|
|
264
|
+
var PERMISSION_PATTERN = /no_amazon|not connected|expired|suspended|revoked|past_due|trial expired|permission|not authorized|scope/i;
|
|
265
|
+
var VALIDATION_PATTERN = /invalid|validation|must be|required/i;
|
|
266
|
+
function writeEnvelopeFrom(result) {
|
|
267
|
+
const structured = result.structuredContent;
|
|
268
|
+
if (!structured) {
|
|
269
|
+
return void 0;
|
|
270
|
+
}
|
|
271
|
+
const hasWriteShape = "mode" in structured || "status" in structured || "requested_count" in structured;
|
|
272
|
+
if (!hasWriteShape) {
|
|
273
|
+
return void 0;
|
|
274
|
+
}
|
|
275
|
+
return structured;
|
|
276
|
+
}
|
|
277
|
+
function toolClassification(def) {
|
|
278
|
+
const hint = def.annotations;
|
|
279
|
+
const annotationWrite = hint?.destructiveHint === true || hint?.readOnlyHint === false;
|
|
280
|
+
const properties = def.inputSchema.properties;
|
|
281
|
+
const props = properties && typeof properties === "object" ? properties : {};
|
|
282
|
+
const schemaWrite = "confirm" in props && "idempotency_key" in props;
|
|
283
|
+
const isWrite = annotationWrite || schemaWrite;
|
|
284
|
+
return {
|
|
285
|
+
isWrite,
|
|
286
|
+
supportsPreview: isWrite
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function classifyToolText(text) {
|
|
290
|
+
if (PERMISSION_PATTERN.test(text)) {
|
|
291
|
+
return 4 /* PermissionDenied */;
|
|
292
|
+
}
|
|
293
|
+
if (VALIDATION_PATTERN.test(text)) {
|
|
294
|
+
return 5 /* ValidationFailed */;
|
|
295
|
+
}
|
|
296
|
+
return void 0;
|
|
297
|
+
}
|
|
298
|
+
function joinContentText(result) {
|
|
299
|
+
return result.content.map((block) => typeof block.text === "string" ? block.text : "").filter((text) => text.length > 0).join(" ").trim();
|
|
300
|
+
}
|
|
301
|
+
function truncateDetail(text) {
|
|
302
|
+
if (text.length <= DETAIL_MAX_LENGTH) {
|
|
303
|
+
return text;
|
|
304
|
+
}
|
|
305
|
+
return `${text.slice(0, DETAIL_MAX_LENGTH)}\u2026`;
|
|
306
|
+
}
|
|
307
|
+
function classifyWriteStatus(status) {
|
|
308
|
+
switch (status) {
|
|
309
|
+
case "validation_error":
|
|
310
|
+
return 5 /* ValidationFailed */;
|
|
311
|
+
case "partial":
|
|
312
|
+
return 9 /* PartialWrite */;
|
|
313
|
+
case "blocked":
|
|
314
|
+
case "failed":
|
|
315
|
+
return 7 /* ToolExecutionFailed */;
|
|
316
|
+
case "duplicate_blocked":
|
|
317
|
+
case "accepted":
|
|
318
|
+
case "preview":
|
|
319
|
+
default:
|
|
320
|
+
return 0 /* Success */;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function summarizeWriteEnvelope(env) {
|
|
324
|
+
const parts = [];
|
|
325
|
+
if (typeof env.requested_count === "number") {
|
|
326
|
+
parts.push(`requested=${env.requested_count}`);
|
|
327
|
+
}
|
|
328
|
+
if (typeof env.accepted_count === "number") {
|
|
329
|
+
parts.push(`accepted=${env.accepted_count}`);
|
|
330
|
+
}
|
|
331
|
+
if (typeof env.failed_count === "number") {
|
|
332
|
+
parts.push(`failed=${env.failed_count}`);
|
|
333
|
+
}
|
|
334
|
+
if (typeof env.previewed_count === "number") {
|
|
335
|
+
parts.push(`previewed=${env.previewed_count}`);
|
|
336
|
+
}
|
|
337
|
+
const status = env.status ?? "unknown";
|
|
338
|
+
return parts.length > 0 ? `${status} (${parts.join(", ")})` : status;
|
|
339
|
+
}
|
|
340
|
+
function classifyResponse(response) {
|
|
341
|
+
if (response.error) {
|
|
342
|
+
const message = response.error.message;
|
|
343
|
+
const code = response.error.code === RPC_INVALID_PARAMS ? 5 /* ValidationFailed */ : classifyToolText(message) ?? 7 /* ToolExecutionFailed */;
|
|
344
|
+
return {
|
|
345
|
+
code,
|
|
346
|
+
status: "rpc_error",
|
|
347
|
+
detail: message
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
const result = response.result;
|
|
351
|
+
const env = writeEnvelopeFrom(result);
|
|
352
|
+
if (env?.status) {
|
|
353
|
+
return {
|
|
354
|
+
code: classifyWriteStatus(env.status),
|
|
355
|
+
status: env.status,
|
|
356
|
+
detail: summarizeWriteEnvelope(env),
|
|
357
|
+
result
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
if (result.isError) {
|
|
361
|
+
const text = joinContentText(result);
|
|
362
|
+
const code = classifyToolText(text) ?? 7 /* ToolExecutionFailed */;
|
|
363
|
+
return {
|
|
364
|
+
code,
|
|
365
|
+
status: "tool_error",
|
|
366
|
+
detail: truncateDetail(text),
|
|
367
|
+
result
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
return {
|
|
371
|
+
code: 0 /* Success */,
|
|
372
|
+
status: "ok",
|
|
373
|
+
detail: truncateDetail(joinContentText(result)),
|
|
374
|
+
result
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
246
378
|
// src/transport.ts
|
|
379
|
+
var RPC_INVALID_PARAMS2 = -32602;
|
|
380
|
+
function classifyRpcError(error) {
|
|
381
|
+
return error.code === RPC_INVALID_PARAMS2 ? 5 /* ValidationFailed */ : classifyToolText(error.message) ?? 7 /* ToolExecutionFailed */;
|
|
382
|
+
}
|
|
247
383
|
function parseSseJsonRpc(text) {
|
|
248
384
|
const dataLines = text.trim().split(/\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim());
|
|
249
385
|
return JSON.parse(dataLines.length ? dataLines.join("\n") : text);
|
|
@@ -293,13 +429,14 @@ function createClient(auth, opts = {}) {
|
|
|
293
429
|
counter += 1;
|
|
294
430
|
return counter;
|
|
295
431
|
};
|
|
296
|
-
async function attempt(method, params, id, timeoutMs) {
|
|
432
|
+
async function attempt(method, params, id, timeoutMs, notification) {
|
|
433
|
+
const requestBody = notification ? { jsonrpc: "2.0", method, params } : { jsonrpc: "2.0", id, method, params };
|
|
297
434
|
let res;
|
|
298
435
|
try {
|
|
299
436
|
res = await fetch(auth.endpoint, {
|
|
300
437
|
method: "POST",
|
|
301
438
|
headers,
|
|
302
|
-
body: JSON.stringify(
|
|
439
|
+
body: JSON.stringify(requestBody),
|
|
303
440
|
signal: AbortSignal.timeout(timeoutMs)
|
|
304
441
|
});
|
|
305
442
|
} catch (error) {
|
|
@@ -336,16 +473,20 @@ function createClient(auth, opts = {}) {
|
|
|
336
473
|
method
|
|
337
474
|
);
|
|
338
475
|
}
|
|
476
|
+
if (notification) {
|
|
477
|
+
return { jsonrpc: "2.0" };
|
|
478
|
+
}
|
|
339
479
|
return parseSseJsonRpc(text);
|
|
340
480
|
}
|
|
341
481
|
async function rpc(method, params, rpcOpts) {
|
|
342
482
|
const timeoutMs = rpcOpts?.timeoutMs ?? defaultTimeoutMs;
|
|
483
|
+
const notification = rpcOpts?.notification === true;
|
|
343
484
|
const id = rpcOpts?.id ?? nextId();
|
|
344
485
|
const maxAttempts = Math.max(1, retry.maxAttempts);
|
|
345
486
|
let lastReason = "transport failure";
|
|
346
487
|
for (let n = 1; n <= maxAttempts; n += 1) {
|
|
347
488
|
try {
|
|
348
|
-
return await attempt(method, params ?? {}, id, timeoutMs);
|
|
489
|
+
return await attempt(method, params ?? {}, id, timeoutMs, notification);
|
|
349
490
|
} catch (error) {
|
|
350
491
|
if (error instanceof CliError) {
|
|
351
492
|
throw error;
|
|
@@ -370,7 +511,7 @@ function createClient(auth, opts = {}) {
|
|
|
370
511
|
async function listTools(listOpts) {
|
|
371
512
|
const response = await rpc("tools/list", {}, { timeoutMs: listOpts?.timeoutMs });
|
|
372
513
|
if (response.error) {
|
|
373
|
-
throw new CliError(
|
|
514
|
+
throw new CliError(classifyRpcError(response.error), response.error.message, "tools/list");
|
|
374
515
|
}
|
|
375
516
|
const result = response.result ?? {};
|
|
376
517
|
return result.tools ?? [];
|
|
@@ -382,7 +523,7 @@ function createClient(auth, opts = {}) {
|
|
|
382
523
|
{ timeoutMs: callOpts?.timeoutMs }
|
|
383
524
|
);
|
|
384
525
|
if (response.error) {
|
|
385
|
-
throw new CliError(
|
|
526
|
+
throw new CliError(classifyRpcError(response.error), response.error.message, name);
|
|
386
527
|
}
|
|
387
528
|
return response.result ?? {};
|
|
388
529
|
}
|
|
@@ -1473,7 +1614,7 @@ var TOOL_DOMAINS = [
|
|
|
1473
1614
|
{ name: "get_buybox_status", description: "Live Buy Box status plus daily win-rate history" },
|
|
1474
1615
|
{ name: "get_competitive_pricing", description: "Live price points plus historical pricing snapshots" },
|
|
1475
1616
|
{ name: "get_offer_listings", description: "Live seller offers for an ASIN" },
|
|
1476
|
-
{ name: "
|
|
1617
|
+
{ name: "get_subscribe_and_save", description: "Subscribe & Save offer status, history, and Amazon-provided metrics" },
|
|
1477
1618
|
{ name: "get_fba_eligibility", description: "Check FBA eligibility per ASIN" },
|
|
1478
1619
|
{ name: "get_listing_restrictions", description: "Listing restrictions and approvals" }
|
|
1479
1620
|
]
|
|
@@ -2032,7 +2173,7 @@ var TOOL_MARKETPLACE_SUPPORT_DECLARED_TOOL_NAMES = [
|
|
|
2032
2173
|
"get_buybox_status",
|
|
2033
2174
|
"get_competitive_pricing",
|
|
2034
2175
|
"get_offer_listings",
|
|
2035
|
-
"
|
|
2176
|
+
"get_subscribe_and_save",
|
|
2036
2177
|
"get_fba_eligibility",
|
|
2037
2178
|
"get_listing_restrictions",
|
|
2038
2179
|
"get_financial_events",
|
|
@@ -3146,123 +3287,6 @@ function formatOutput(value, format) {
|
|
|
3146
3287
|
}
|
|
3147
3288
|
}
|
|
3148
3289
|
|
|
3149
|
-
// src/exit-codes.ts
|
|
3150
|
-
var RPC_INVALID_PARAMS = -32602;
|
|
3151
|
-
var DETAIL_MAX_LENGTH = 180;
|
|
3152
|
-
var PERMISSION_PATTERN = /no_amazon|not connected|expired|suspended|revoked|past_due|trial expired|permission|not authorized|scope/i;
|
|
3153
|
-
var VALIDATION_PATTERN = /invalid|validation|must be|required/i;
|
|
3154
|
-
function writeEnvelopeFrom(result) {
|
|
3155
|
-
const structured = result.structuredContent;
|
|
3156
|
-
if (!structured) {
|
|
3157
|
-
return void 0;
|
|
3158
|
-
}
|
|
3159
|
-
const hasWriteShape = "mode" in structured || "status" in structured || "requested_count" in structured;
|
|
3160
|
-
if (!hasWriteShape) {
|
|
3161
|
-
return void 0;
|
|
3162
|
-
}
|
|
3163
|
-
return structured;
|
|
3164
|
-
}
|
|
3165
|
-
function toolClassification(def) {
|
|
3166
|
-
const hint = def.annotations;
|
|
3167
|
-
const annotationWrite = hint?.destructiveHint === true || hint?.readOnlyHint === false;
|
|
3168
|
-
const properties = def.inputSchema.properties;
|
|
3169
|
-
const props = properties && typeof properties === "object" ? properties : {};
|
|
3170
|
-
const schemaWrite = "confirm" in props && "idempotency_key" in props;
|
|
3171
|
-
const isWrite = annotationWrite || schemaWrite;
|
|
3172
|
-
return {
|
|
3173
|
-
isWrite,
|
|
3174
|
-
supportsPreview: isWrite
|
|
3175
|
-
};
|
|
3176
|
-
}
|
|
3177
|
-
function classifyToolText(text) {
|
|
3178
|
-
if (PERMISSION_PATTERN.test(text)) {
|
|
3179
|
-
return 4 /* PermissionDenied */;
|
|
3180
|
-
}
|
|
3181
|
-
if (VALIDATION_PATTERN.test(text)) {
|
|
3182
|
-
return 5 /* ValidationFailed */;
|
|
3183
|
-
}
|
|
3184
|
-
return void 0;
|
|
3185
|
-
}
|
|
3186
|
-
function joinContentText(result) {
|
|
3187
|
-
return result.content.map((block) => typeof block.text === "string" ? block.text : "").filter((text) => text.length > 0).join(" ").trim();
|
|
3188
|
-
}
|
|
3189
|
-
function truncateDetail(text) {
|
|
3190
|
-
if (text.length <= DETAIL_MAX_LENGTH) {
|
|
3191
|
-
return text;
|
|
3192
|
-
}
|
|
3193
|
-
return `${text.slice(0, DETAIL_MAX_LENGTH)}\u2026`;
|
|
3194
|
-
}
|
|
3195
|
-
function classifyWriteStatus(status) {
|
|
3196
|
-
switch (status) {
|
|
3197
|
-
case "validation_error":
|
|
3198
|
-
return 5 /* ValidationFailed */;
|
|
3199
|
-
case "partial":
|
|
3200
|
-
return 9 /* PartialWrite */;
|
|
3201
|
-
case "blocked":
|
|
3202
|
-
case "failed":
|
|
3203
|
-
return 7 /* ToolExecutionFailed */;
|
|
3204
|
-
case "duplicate_blocked":
|
|
3205
|
-
case "accepted":
|
|
3206
|
-
case "preview":
|
|
3207
|
-
default:
|
|
3208
|
-
return 0 /* Success */;
|
|
3209
|
-
}
|
|
3210
|
-
}
|
|
3211
|
-
function summarizeWriteEnvelope(env) {
|
|
3212
|
-
const parts = [];
|
|
3213
|
-
if (typeof env.requested_count === "number") {
|
|
3214
|
-
parts.push(`requested=${env.requested_count}`);
|
|
3215
|
-
}
|
|
3216
|
-
if (typeof env.accepted_count === "number") {
|
|
3217
|
-
parts.push(`accepted=${env.accepted_count}`);
|
|
3218
|
-
}
|
|
3219
|
-
if (typeof env.failed_count === "number") {
|
|
3220
|
-
parts.push(`failed=${env.failed_count}`);
|
|
3221
|
-
}
|
|
3222
|
-
if (typeof env.previewed_count === "number") {
|
|
3223
|
-
parts.push(`previewed=${env.previewed_count}`);
|
|
3224
|
-
}
|
|
3225
|
-
const status = env.status ?? "unknown";
|
|
3226
|
-
return parts.length > 0 ? `${status} (${parts.join(", ")})` : status;
|
|
3227
|
-
}
|
|
3228
|
-
function classifyResponse(response) {
|
|
3229
|
-
if (response.error) {
|
|
3230
|
-
const message = response.error.message;
|
|
3231
|
-
const code = response.error.code === RPC_INVALID_PARAMS ? 5 /* ValidationFailed */ : classifyToolText(message) ?? 7 /* ToolExecutionFailed */;
|
|
3232
|
-
return {
|
|
3233
|
-
code,
|
|
3234
|
-
status: "rpc_error",
|
|
3235
|
-
detail: message
|
|
3236
|
-
};
|
|
3237
|
-
}
|
|
3238
|
-
const result = response.result;
|
|
3239
|
-
const env = writeEnvelopeFrom(result);
|
|
3240
|
-
if (env?.status) {
|
|
3241
|
-
return {
|
|
3242
|
-
code: classifyWriteStatus(env.status),
|
|
3243
|
-
status: env.status,
|
|
3244
|
-
detail: summarizeWriteEnvelope(env),
|
|
3245
|
-
result
|
|
3246
|
-
};
|
|
3247
|
-
}
|
|
3248
|
-
if (result.isError) {
|
|
3249
|
-
const text = joinContentText(result);
|
|
3250
|
-
const code = classifyToolText(text) ?? 7 /* ToolExecutionFailed */;
|
|
3251
|
-
return {
|
|
3252
|
-
code,
|
|
3253
|
-
status: "tool_error",
|
|
3254
|
-
detail: truncateDetail(text),
|
|
3255
|
-
result
|
|
3256
|
-
};
|
|
3257
|
-
}
|
|
3258
|
-
return {
|
|
3259
|
-
code: 0 /* Success */,
|
|
3260
|
-
status: "ok",
|
|
3261
|
-
detail: truncateDetail(joinContentText(result)),
|
|
3262
|
-
result
|
|
3263
|
-
};
|
|
3264
|
-
}
|
|
3265
|
-
|
|
3266
3290
|
// src/commands/tools.ts
|
|
3267
3291
|
function parseJsonArgs(text, origin) {
|
|
3268
3292
|
try {
|
|
@@ -3422,6 +3446,14 @@ function selectOutputValue(result) {
|
|
|
3422
3446
|
return text;
|
|
3423
3447
|
}
|
|
3424
3448
|
}
|
|
3449
|
+
function warnIfLimitTruncated(ctx, value) {
|
|
3450
|
+
const limit = ctx.flags.limit;
|
|
3451
|
+
if (limit != null && Array.isArray(value) && value.length === limit) {
|
|
3452
|
+
ctx.err(
|
|
3453
|
+
`warning: result returned exactly --limit (${limit}) rows; output may be truncated. Raise --limit or narrow the query.`
|
|
3454
|
+
);
|
|
3455
|
+
}
|
|
3456
|
+
}
|
|
3425
3457
|
function echoWriteSummary(ctx, result) {
|
|
3426
3458
|
const env = writeEnvelopeFrom(result);
|
|
3427
3459
|
if (!env) {
|
|
@@ -3459,6 +3491,7 @@ async function toolsCall(ctx, name, inputSpec) {
|
|
|
3459
3491
|
}
|
|
3460
3492
|
echoWriteSummary(ctx, result);
|
|
3461
3493
|
const value = selectOutputValue(result);
|
|
3494
|
+
warnIfLimitTruncated(ctx, value);
|
|
3462
3495
|
const rendered = formatOutput(value, ctx.flags.format);
|
|
3463
3496
|
if (ctx.flags.output) {
|
|
3464
3497
|
await writeFile(ctx.flags.output, rendered, { encoding: "utf8", mode: 384 });
|
|
@@ -3516,7 +3549,7 @@ async function handleMessage(line, client, opts) {
|
|
|
3516
3549
|
const method = typeof message.method === "string" ? message.method : "";
|
|
3517
3550
|
if (message.id === void 0) {
|
|
3518
3551
|
try {
|
|
3519
|
-
await client.rpc(method, message.params);
|
|
3552
|
+
await client.rpc(method, message.params, { notification: true });
|
|
3520
3553
|
} catch {
|
|
3521
3554
|
}
|
|
3522
3555
|
return null;
|
|
@@ -3589,9 +3622,9 @@ function pump(ctx, client, opts, input = process.stdin) {
|
|
|
3589
3622
|
return new Promise((resolve, reject) => {
|
|
3590
3623
|
input.setEncoding("utf8");
|
|
3591
3624
|
let buffer = "";
|
|
3592
|
-
|
|
3625
|
+
const inflight = /* @__PURE__ */ new Set();
|
|
3593
3626
|
const dispatch = (line) => {
|
|
3594
|
-
|
|
3627
|
+
const task = (async () => {
|
|
3595
3628
|
try {
|
|
3596
3629
|
const out = await handleMessage(line, client, opts);
|
|
3597
3630
|
if (out !== null) {
|
|
@@ -3600,6 +3633,10 @@ function pump(ctx, client, opts, input = process.stdin) {
|
|
|
3600
3633
|
} catch (error) {
|
|
3601
3634
|
ctx.err(`bridge error: ${error instanceof Error ? error.message : String(error)}`);
|
|
3602
3635
|
}
|
|
3636
|
+
})();
|
|
3637
|
+
inflight.add(task);
|
|
3638
|
+
void task.finally(() => {
|
|
3639
|
+
inflight.delete(task);
|
|
3603
3640
|
});
|
|
3604
3641
|
};
|
|
3605
3642
|
input.on("data", (data) => {
|
|
@@ -3621,10 +3658,10 @@ function pump(ctx, client, opts, input = process.stdin) {
|
|
|
3621
3658
|
if (tail.trim() !== "") {
|
|
3622
3659
|
dispatch(tail);
|
|
3623
3660
|
}
|
|
3624
|
-
|
|
3661
|
+
Promise.allSettled([...inflight]).then(() => {
|
|
3625
3662
|
ctx.err("bridge stopped (stdin closed)");
|
|
3626
3663
|
resolve(0 /* Success */);
|
|
3627
|
-
}
|
|
3664
|
+
}, reject);
|
|
3628
3665
|
});
|
|
3629
3666
|
});
|
|
3630
3667
|
}
|
|
@@ -4030,17 +4067,37 @@ async function profilesUse(ctx, name) {
|
|
|
4030
4067
|
}
|
|
4031
4068
|
|
|
4032
4069
|
// src/keychain.ts
|
|
4033
|
-
import {
|
|
4070
|
+
import { createRequire } from "module";
|
|
4034
4071
|
var SERVICE = "agentcentral-cli";
|
|
4035
4072
|
var DEFAULT_ACCOUNT = "default";
|
|
4036
4073
|
var DISABLE_ENV = "AGENT_CENTRAL_DISABLE_KEYCHAIN";
|
|
4074
|
+
var entryClassCache = void 0;
|
|
4075
|
+
var loaderOverride = void 0;
|
|
4037
4076
|
function disabled() {
|
|
4038
4077
|
return process.env[DISABLE_ENV] === "1";
|
|
4039
4078
|
}
|
|
4079
|
+
var requireNative = createRequire(import.meta.url);
|
|
4080
|
+
function loadEntryClass() {
|
|
4081
|
+
if (entryClassCache !== void 0) {
|
|
4082
|
+
return entryClassCache;
|
|
4083
|
+
}
|
|
4084
|
+
try {
|
|
4085
|
+
const loaded = loaderOverride ? loaderOverride() : requireNative("@napi-rs/keyring").Entry;
|
|
4086
|
+
entryClassCache = loaded;
|
|
4087
|
+
return loaded;
|
|
4088
|
+
} catch {
|
|
4089
|
+
entryClassCache = null;
|
|
4090
|
+
return null;
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4040
4093
|
function keychainAvailable() {
|
|
4041
4094
|
if (disabled()) {
|
|
4042
4095
|
return false;
|
|
4043
4096
|
}
|
|
4097
|
+
const Entry = loadEntryClass();
|
|
4098
|
+
if (!Entry) {
|
|
4099
|
+
return false;
|
|
4100
|
+
}
|
|
4044
4101
|
try {
|
|
4045
4102
|
const entry = new Entry(SERVICE, DEFAULT_ACCOUNT);
|
|
4046
4103
|
try {
|
|
@@ -4056,6 +4113,10 @@ function keychainGet(account) {
|
|
|
4056
4113
|
if (disabled()) {
|
|
4057
4114
|
return null;
|
|
4058
4115
|
}
|
|
4116
|
+
const Entry = loadEntryClass();
|
|
4117
|
+
if (!Entry) {
|
|
4118
|
+
return null;
|
|
4119
|
+
}
|
|
4059
4120
|
try {
|
|
4060
4121
|
const entry = new Entry(SERVICE, account ?? DEFAULT_ACCOUNT);
|
|
4061
4122
|
const secret = entry.getPassword();
|
|
@@ -4069,6 +4130,10 @@ function keychainSet(account, secret) {
|
|
|
4069
4130
|
if (disabled()) {
|
|
4070
4131
|
return false;
|
|
4071
4132
|
}
|
|
4133
|
+
const Entry = loadEntryClass();
|
|
4134
|
+
if (!Entry) {
|
|
4135
|
+
return false;
|
|
4136
|
+
}
|
|
4072
4137
|
try {
|
|
4073
4138
|
const entry = new Entry(SERVICE, account ?? DEFAULT_ACCOUNT);
|
|
4074
4139
|
entry.setPassword(secret);
|
|
@@ -4081,6 +4146,10 @@ function keychainDelete(account) {
|
|
|
4081
4146
|
if (disabled()) {
|
|
4082
4147
|
return false;
|
|
4083
4148
|
}
|
|
4149
|
+
const Entry = loadEntryClass();
|
|
4150
|
+
if (!Entry) {
|
|
4151
|
+
return false;
|
|
4152
|
+
}
|
|
4084
4153
|
try {
|
|
4085
4154
|
const entry = new Entry(SERVICE, account ?? DEFAULT_ACCOUNT);
|
|
4086
4155
|
return entry.deletePassword();
|
|
@@ -4169,7 +4238,6 @@ function extraFlags(ctx) {
|
|
|
4169
4238
|
const flags = ctx.flags;
|
|
4170
4239
|
return {
|
|
4171
4240
|
since: flags.since,
|
|
4172
|
-
groupBy: flags.groupBy,
|
|
4173
4241
|
asin: flags.asin,
|
|
4174
4242
|
fulfillmentChannel: flags.fulfillmentChannel
|
|
4175
4243
|
};
|
|
@@ -4204,6 +4272,14 @@ function selectOutputValue2(result) {
|
|
|
4204
4272
|
return text;
|
|
4205
4273
|
}
|
|
4206
4274
|
}
|
|
4275
|
+
function warnIfLimitTruncated2(ctx, value) {
|
|
4276
|
+
const limit = ctx.flags.limit;
|
|
4277
|
+
if (limit != null && Array.isArray(value) && value.length === limit) {
|
|
4278
|
+
ctx.err(
|
|
4279
|
+
`warning: result returned exactly --limit (${limit}) rows; output may be truncated. Raise --limit or narrow the query.`
|
|
4280
|
+
);
|
|
4281
|
+
}
|
|
4282
|
+
}
|
|
4207
4283
|
async function emit(ctx, name, args) {
|
|
4208
4284
|
const client = await ctx.getClient();
|
|
4209
4285
|
const response = await client.rpc(
|
|
@@ -4220,6 +4296,7 @@ async function emit(ctx, name, args) {
|
|
|
4220
4296
|
return classified.code;
|
|
4221
4297
|
}
|
|
4222
4298
|
const value = selectOutputValue2(result);
|
|
4299
|
+
warnIfLimitTruncated2(ctx, value);
|
|
4223
4300
|
const rendered = formatOutput(value, ctx.flags.format);
|
|
4224
4301
|
if (ctx.flags.output) {
|
|
4225
4302
|
await writeFile2(ctx.flags.output, rendered, { encoding: "utf8", mode: 384 });
|
|
@@ -4499,9 +4576,9 @@ async function run(argv) {
|
|
|
4499
4576
|
withGlobalOptions(cli.command("inventory health", "summarize inventory health")).action(
|
|
4500
4577
|
(options) => dispatch((ctx) => inventoryHealth(ctx), options)
|
|
4501
4578
|
);
|
|
4502
|
-
withGlobalOptions(cli.command("ads performance", "summarize campaign performance")).option("--since <dur>", "lookback window: a relative duration like 30d or a YYYY-MM-DD date").
|
|
4579
|
+
withGlobalOptions(cli.command("ads performance", "summarize campaign performance")).option("--since <dur>", "lookback window: a relative duration like 30d or a YYYY-MM-DD date").action(
|
|
4503
4580
|
(options) => dispatch((ctx) => adsPerformance(ctx), options, {
|
|
4504
|
-
extra: { since: asString(options.since)
|
|
4581
|
+
extra: { since: asString(options.since) }
|
|
4505
4582
|
})
|
|
4506
4583
|
);
|
|
4507
4584
|
withGlobalOptions(cli.command("catalog quality", "summarize listing quality")).option("--asin <asin>", "scope to a single ASIN").action(
|