@agentcentral/cli 0.1.2 → 0.1.4
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 +217 -137
- 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
|
@@ -17,7 +17,10 @@ var CliError = class extends Error {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
// src/constants.ts
|
|
20
|
-
|
|
20
|
+
import { createRequire } from "module";
|
|
21
|
+
var nodeRequire = createRequire(import.meta.url);
|
|
22
|
+
var pkg = nodeRequire("../package.json");
|
|
23
|
+
var CLI_VERSION = pkg.version;
|
|
21
24
|
var DEFAULT_MCP_ENDPOINT = "https://mcp.agentcentral.to/mcp";
|
|
22
25
|
var ENV_API_KEY = "AGENT_CENTRAL_API_KEY";
|
|
23
26
|
var ENV_API_KEY_LEGACY = "AGENTCENTRAL_API_KEY";
|
|
@@ -34,13 +37,14 @@ var API_KEY_PREFIX = "ac_live_";
|
|
|
34
37
|
|
|
35
38
|
// src/config.ts
|
|
36
39
|
import {
|
|
37
|
-
chmodSync,
|
|
38
40
|
closeSync,
|
|
39
41
|
existsSync,
|
|
40
42
|
fstatSync,
|
|
41
43
|
mkdirSync,
|
|
42
44
|
openSync,
|
|
43
45
|
readFileSync,
|
|
46
|
+
renameSync,
|
|
47
|
+
unlinkSync,
|
|
44
48
|
writeFileSync
|
|
45
49
|
} from "fs";
|
|
46
50
|
import { homedir } from "os";
|
|
@@ -107,9 +111,23 @@ function loadConfig(env = process.env) {
|
|
|
107
111
|
function saveConfig(config, env = process.env) {
|
|
108
112
|
const path = configPath(env);
|
|
109
113
|
const dir = dirname(path);
|
|
114
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
110
115
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
const data = JSON.stringify(config, null, 2);
|
|
117
|
+
try {
|
|
118
|
+
writeFileSync(tmp, data, { mode: 384, flag: "wx" });
|
|
119
|
+
renameSync(tmp, path);
|
|
120
|
+
} catch (error) {
|
|
121
|
+
try {
|
|
122
|
+
unlinkSync(tmp);
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
throw new CliError(
|
|
126
|
+
1 /* GeneralFailure */,
|
|
127
|
+
`failed to write config ${path}`,
|
|
128
|
+
error instanceof Error ? error.message : String(error)
|
|
129
|
+
);
|
|
130
|
+
}
|
|
113
131
|
}
|
|
114
132
|
function upsertProfile(config, profile) {
|
|
115
133
|
const exists = config.profiles.some((p) => p.name === profile.name);
|
|
@@ -243,7 +261,128 @@ async function resolveAuth(input) {
|
|
|
243
261
|
};
|
|
244
262
|
}
|
|
245
263
|
|
|
264
|
+
// src/exit-codes.ts
|
|
265
|
+
var RPC_INVALID_PARAMS = -32602;
|
|
266
|
+
var DETAIL_MAX_LENGTH = 180;
|
|
267
|
+
var PERMISSION_PATTERN = /no_amazon|not connected|expired|suspended|revoked|past_due|trial expired|permission|not authorized|scope/i;
|
|
268
|
+
var VALIDATION_PATTERN = /invalid|validation|must be|required/i;
|
|
269
|
+
function writeEnvelopeFrom(result) {
|
|
270
|
+
const structured = result.structuredContent;
|
|
271
|
+
if (!structured) {
|
|
272
|
+
return void 0;
|
|
273
|
+
}
|
|
274
|
+
const hasWriteShape = "mode" in structured || "status" in structured || "requested_count" in structured;
|
|
275
|
+
if (!hasWriteShape) {
|
|
276
|
+
return void 0;
|
|
277
|
+
}
|
|
278
|
+
return structured;
|
|
279
|
+
}
|
|
280
|
+
function toolClassification(def) {
|
|
281
|
+
const hint = def.annotations;
|
|
282
|
+
const annotationWrite = hint?.destructiveHint === true || hint?.readOnlyHint === false;
|
|
283
|
+
const properties = def.inputSchema.properties;
|
|
284
|
+
const props = properties && typeof properties === "object" ? properties : {};
|
|
285
|
+
const schemaWrite = "confirm" in props && "idempotency_key" in props;
|
|
286
|
+
const isWrite = annotationWrite || schemaWrite;
|
|
287
|
+
return {
|
|
288
|
+
isWrite,
|
|
289
|
+
supportsPreview: isWrite
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function classifyToolText(text) {
|
|
293
|
+
if (PERMISSION_PATTERN.test(text)) {
|
|
294
|
+
return 4 /* PermissionDenied */;
|
|
295
|
+
}
|
|
296
|
+
if (VALIDATION_PATTERN.test(text)) {
|
|
297
|
+
return 5 /* ValidationFailed */;
|
|
298
|
+
}
|
|
299
|
+
return void 0;
|
|
300
|
+
}
|
|
301
|
+
function joinContentText(result) {
|
|
302
|
+
return result.content.map((block) => typeof block.text === "string" ? block.text : "").filter((text) => text.length > 0).join(" ").trim();
|
|
303
|
+
}
|
|
304
|
+
function truncateDetail(text) {
|
|
305
|
+
if (text.length <= DETAIL_MAX_LENGTH) {
|
|
306
|
+
return text;
|
|
307
|
+
}
|
|
308
|
+
return `${text.slice(0, DETAIL_MAX_LENGTH)}\u2026`;
|
|
309
|
+
}
|
|
310
|
+
function classifyWriteStatus(status) {
|
|
311
|
+
switch (status) {
|
|
312
|
+
case "validation_error":
|
|
313
|
+
return 5 /* ValidationFailed */;
|
|
314
|
+
case "partial":
|
|
315
|
+
return 9 /* PartialWrite */;
|
|
316
|
+
case "blocked":
|
|
317
|
+
case "failed":
|
|
318
|
+
return 7 /* ToolExecutionFailed */;
|
|
319
|
+
case "duplicate_blocked":
|
|
320
|
+
case "accepted":
|
|
321
|
+
case "preview":
|
|
322
|
+
default:
|
|
323
|
+
return 0 /* Success */;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function summarizeWriteEnvelope(env) {
|
|
327
|
+
const parts = [];
|
|
328
|
+
if (typeof env.requested_count === "number") {
|
|
329
|
+
parts.push(`requested=${env.requested_count}`);
|
|
330
|
+
}
|
|
331
|
+
if (typeof env.accepted_count === "number") {
|
|
332
|
+
parts.push(`accepted=${env.accepted_count}`);
|
|
333
|
+
}
|
|
334
|
+
if (typeof env.failed_count === "number") {
|
|
335
|
+
parts.push(`failed=${env.failed_count}`);
|
|
336
|
+
}
|
|
337
|
+
if (typeof env.previewed_count === "number") {
|
|
338
|
+
parts.push(`previewed=${env.previewed_count}`);
|
|
339
|
+
}
|
|
340
|
+
const status = env.status ?? "unknown";
|
|
341
|
+
return parts.length > 0 ? `${status} (${parts.join(", ")})` : status;
|
|
342
|
+
}
|
|
343
|
+
function classifyResponse(response) {
|
|
344
|
+
if (response.error) {
|
|
345
|
+
const message = response.error.message;
|
|
346
|
+
const code = response.error.code === RPC_INVALID_PARAMS ? 5 /* ValidationFailed */ : classifyToolText(message) ?? 7 /* ToolExecutionFailed */;
|
|
347
|
+
return {
|
|
348
|
+
code,
|
|
349
|
+
status: "rpc_error",
|
|
350
|
+
detail: message
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
const result = response.result;
|
|
354
|
+
const env = writeEnvelopeFrom(result);
|
|
355
|
+
if (env?.status) {
|
|
356
|
+
return {
|
|
357
|
+
code: classifyWriteStatus(env.status),
|
|
358
|
+
status: env.status,
|
|
359
|
+
detail: summarizeWriteEnvelope(env),
|
|
360
|
+
result
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
if (result.isError) {
|
|
364
|
+
const text = joinContentText(result);
|
|
365
|
+
const code = classifyToolText(text) ?? 7 /* ToolExecutionFailed */;
|
|
366
|
+
return {
|
|
367
|
+
code,
|
|
368
|
+
status: "tool_error",
|
|
369
|
+
detail: truncateDetail(text),
|
|
370
|
+
result
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
code: 0 /* Success */,
|
|
375
|
+
status: "ok",
|
|
376
|
+
detail: truncateDetail(joinContentText(result)),
|
|
377
|
+
result
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
246
381
|
// src/transport.ts
|
|
382
|
+
var RPC_INVALID_PARAMS2 = -32602;
|
|
383
|
+
function classifyRpcError(error) {
|
|
384
|
+
return error.code === RPC_INVALID_PARAMS2 ? 5 /* ValidationFailed */ : classifyToolText(error.message) ?? 7 /* ToolExecutionFailed */;
|
|
385
|
+
}
|
|
247
386
|
function parseSseJsonRpc(text) {
|
|
248
387
|
const dataLines = text.trim().split(/\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim());
|
|
249
388
|
return JSON.parse(dataLines.length ? dataLines.join("\n") : text);
|
|
@@ -293,13 +432,14 @@ function createClient(auth, opts = {}) {
|
|
|
293
432
|
counter += 1;
|
|
294
433
|
return counter;
|
|
295
434
|
};
|
|
296
|
-
async function attempt(method, params, id, timeoutMs) {
|
|
435
|
+
async function attempt(method, params, id, timeoutMs, notification) {
|
|
436
|
+
const requestBody = notification ? { jsonrpc: "2.0", method, params } : { jsonrpc: "2.0", id, method, params };
|
|
297
437
|
let res;
|
|
298
438
|
try {
|
|
299
439
|
res = await fetch(auth.endpoint, {
|
|
300
440
|
method: "POST",
|
|
301
441
|
headers,
|
|
302
|
-
body: JSON.stringify(
|
|
442
|
+
body: JSON.stringify(requestBody),
|
|
303
443
|
signal: AbortSignal.timeout(timeoutMs)
|
|
304
444
|
});
|
|
305
445
|
} catch (error) {
|
|
@@ -336,16 +476,20 @@ function createClient(auth, opts = {}) {
|
|
|
336
476
|
method
|
|
337
477
|
);
|
|
338
478
|
}
|
|
479
|
+
if (notification) {
|
|
480
|
+
return { jsonrpc: "2.0" };
|
|
481
|
+
}
|
|
339
482
|
return parseSseJsonRpc(text);
|
|
340
483
|
}
|
|
341
484
|
async function rpc(method, params, rpcOpts) {
|
|
342
485
|
const timeoutMs = rpcOpts?.timeoutMs ?? defaultTimeoutMs;
|
|
486
|
+
const notification = rpcOpts?.notification === true;
|
|
343
487
|
const id = rpcOpts?.id ?? nextId();
|
|
344
488
|
const maxAttempts = Math.max(1, retry.maxAttempts);
|
|
345
489
|
let lastReason = "transport failure";
|
|
346
490
|
for (let n = 1; n <= maxAttempts; n += 1) {
|
|
347
491
|
try {
|
|
348
|
-
return await attempt(method, params ?? {}, id, timeoutMs);
|
|
492
|
+
return await attempt(method, params ?? {}, id, timeoutMs, notification);
|
|
349
493
|
} catch (error) {
|
|
350
494
|
if (error instanceof CliError) {
|
|
351
495
|
throw error;
|
|
@@ -370,7 +514,7 @@ function createClient(auth, opts = {}) {
|
|
|
370
514
|
async function listTools(listOpts) {
|
|
371
515
|
const response = await rpc("tools/list", {}, { timeoutMs: listOpts?.timeoutMs });
|
|
372
516
|
if (response.error) {
|
|
373
|
-
throw new CliError(
|
|
517
|
+
throw new CliError(classifyRpcError(response.error), response.error.message, "tools/list");
|
|
374
518
|
}
|
|
375
519
|
const result = response.result ?? {};
|
|
376
520
|
return result.tools ?? [];
|
|
@@ -382,7 +526,7 @@ function createClient(auth, opts = {}) {
|
|
|
382
526
|
{ timeoutMs: callOpts?.timeoutMs }
|
|
383
527
|
);
|
|
384
528
|
if (response.error) {
|
|
385
|
-
throw new CliError(
|
|
529
|
+
throw new CliError(classifyRpcError(response.error), response.error.message, name);
|
|
386
530
|
}
|
|
387
531
|
return response.result ?? {};
|
|
388
532
|
}
|
|
@@ -1473,7 +1617,7 @@ var TOOL_DOMAINS = [
|
|
|
1473
1617
|
{ name: "get_buybox_status", description: "Live Buy Box status plus daily win-rate history" },
|
|
1474
1618
|
{ name: "get_competitive_pricing", description: "Live price points plus historical pricing snapshots" },
|
|
1475
1619
|
{ name: "get_offer_listings", description: "Live seller offers for an ASIN" },
|
|
1476
|
-
{ name: "
|
|
1620
|
+
{ name: "get_subscribe_and_save", description: "Subscribe & Save offer status, history, and Amazon-provided metrics" },
|
|
1477
1621
|
{ name: "get_fba_eligibility", description: "Check FBA eligibility per ASIN" },
|
|
1478
1622
|
{ name: "get_listing_restrictions", description: "Listing restrictions and approvals" }
|
|
1479
1623
|
]
|
|
@@ -2032,7 +2176,7 @@ var TOOL_MARKETPLACE_SUPPORT_DECLARED_TOOL_NAMES = [
|
|
|
2032
2176
|
"get_buybox_status",
|
|
2033
2177
|
"get_competitive_pricing",
|
|
2034
2178
|
"get_offer_listings",
|
|
2035
|
-
"
|
|
2179
|
+
"get_subscribe_and_save",
|
|
2036
2180
|
"get_fba_eligibility",
|
|
2037
2181
|
"get_listing_restrictions",
|
|
2038
2182
|
"get_financial_events",
|
|
@@ -3146,123 +3290,6 @@ function formatOutput(value, format) {
|
|
|
3146
3290
|
}
|
|
3147
3291
|
}
|
|
3148
3292
|
|
|
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
3293
|
// src/commands/tools.ts
|
|
3267
3294
|
function parseJsonArgs(text, origin) {
|
|
3268
3295
|
try {
|
|
@@ -3422,6 +3449,14 @@ function selectOutputValue(result) {
|
|
|
3422
3449
|
return text;
|
|
3423
3450
|
}
|
|
3424
3451
|
}
|
|
3452
|
+
function warnIfLimitTruncated(ctx, value) {
|
|
3453
|
+
const limit = ctx.flags.limit;
|
|
3454
|
+
if (limit != null && Array.isArray(value) && value.length === limit) {
|
|
3455
|
+
ctx.err(
|
|
3456
|
+
`warning: result returned exactly --limit (${limit}) rows; output may be truncated. Raise --limit or narrow the query.`
|
|
3457
|
+
);
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3425
3460
|
function echoWriteSummary(ctx, result) {
|
|
3426
3461
|
const env = writeEnvelopeFrom(result);
|
|
3427
3462
|
if (!env) {
|
|
@@ -3459,6 +3494,7 @@ async function toolsCall(ctx, name, inputSpec) {
|
|
|
3459
3494
|
}
|
|
3460
3495
|
echoWriteSummary(ctx, result);
|
|
3461
3496
|
const value = selectOutputValue(result);
|
|
3497
|
+
warnIfLimitTruncated(ctx, value);
|
|
3462
3498
|
const rendered = formatOutput(value, ctx.flags.format);
|
|
3463
3499
|
if (ctx.flags.output) {
|
|
3464
3500
|
await writeFile(ctx.flags.output, rendered, { encoding: "utf8", mode: 384 });
|
|
@@ -3516,7 +3552,7 @@ async function handleMessage(line, client, opts) {
|
|
|
3516
3552
|
const method = typeof message.method === "string" ? message.method : "";
|
|
3517
3553
|
if (message.id === void 0) {
|
|
3518
3554
|
try {
|
|
3519
|
-
await client.rpc(method, message.params);
|
|
3555
|
+
await client.rpc(method, message.params, { notification: true });
|
|
3520
3556
|
} catch {
|
|
3521
3557
|
}
|
|
3522
3558
|
return null;
|
|
@@ -3589,9 +3625,9 @@ function pump(ctx, client, opts, input = process.stdin) {
|
|
|
3589
3625
|
return new Promise((resolve, reject) => {
|
|
3590
3626
|
input.setEncoding("utf8");
|
|
3591
3627
|
let buffer = "";
|
|
3592
|
-
|
|
3628
|
+
const inflight = /* @__PURE__ */ new Set();
|
|
3593
3629
|
const dispatch = (line) => {
|
|
3594
|
-
|
|
3630
|
+
const task = (async () => {
|
|
3595
3631
|
try {
|
|
3596
3632
|
const out = await handleMessage(line, client, opts);
|
|
3597
3633
|
if (out !== null) {
|
|
@@ -3600,6 +3636,10 @@ function pump(ctx, client, opts, input = process.stdin) {
|
|
|
3600
3636
|
} catch (error) {
|
|
3601
3637
|
ctx.err(`bridge error: ${error instanceof Error ? error.message : String(error)}`);
|
|
3602
3638
|
}
|
|
3639
|
+
})();
|
|
3640
|
+
inflight.add(task);
|
|
3641
|
+
void task.finally(() => {
|
|
3642
|
+
inflight.delete(task);
|
|
3603
3643
|
});
|
|
3604
3644
|
};
|
|
3605
3645
|
input.on("data", (data) => {
|
|
@@ -3621,10 +3661,10 @@ function pump(ctx, client, opts, input = process.stdin) {
|
|
|
3621
3661
|
if (tail.trim() !== "") {
|
|
3622
3662
|
dispatch(tail);
|
|
3623
3663
|
}
|
|
3624
|
-
|
|
3664
|
+
Promise.allSettled([...inflight]).then(() => {
|
|
3625
3665
|
ctx.err("bridge stopped (stdin closed)");
|
|
3626
3666
|
resolve(0 /* Success */);
|
|
3627
|
-
}
|
|
3667
|
+
}, reject);
|
|
3628
3668
|
});
|
|
3629
3669
|
});
|
|
3630
3670
|
}
|
|
@@ -4030,17 +4070,37 @@ async function profilesUse(ctx, name) {
|
|
|
4030
4070
|
}
|
|
4031
4071
|
|
|
4032
4072
|
// src/keychain.ts
|
|
4033
|
-
import {
|
|
4073
|
+
import { createRequire as createRequire2 } from "module";
|
|
4034
4074
|
var SERVICE = "agentcentral-cli";
|
|
4035
4075
|
var DEFAULT_ACCOUNT = "default";
|
|
4036
4076
|
var DISABLE_ENV = "AGENT_CENTRAL_DISABLE_KEYCHAIN";
|
|
4077
|
+
var entryClassCache = void 0;
|
|
4078
|
+
var loaderOverride = void 0;
|
|
4037
4079
|
function disabled() {
|
|
4038
4080
|
return process.env[DISABLE_ENV] === "1";
|
|
4039
4081
|
}
|
|
4082
|
+
var requireNative = createRequire2(import.meta.url);
|
|
4083
|
+
function loadEntryClass() {
|
|
4084
|
+
if (entryClassCache !== void 0) {
|
|
4085
|
+
return entryClassCache;
|
|
4086
|
+
}
|
|
4087
|
+
try {
|
|
4088
|
+
const loaded = loaderOverride ? loaderOverride() : requireNative("@napi-rs/keyring").Entry;
|
|
4089
|
+
entryClassCache = loaded;
|
|
4090
|
+
return loaded;
|
|
4091
|
+
} catch {
|
|
4092
|
+
entryClassCache = null;
|
|
4093
|
+
return null;
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4040
4096
|
function keychainAvailable() {
|
|
4041
4097
|
if (disabled()) {
|
|
4042
4098
|
return false;
|
|
4043
4099
|
}
|
|
4100
|
+
const Entry = loadEntryClass();
|
|
4101
|
+
if (!Entry) {
|
|
4102
|
+
return false;
|
|
4103
|
+
}
|
|
4044
4104
|
try {
|
|
4045
4105
|
const entry = new Entry(SERVICE, DEFAULT_ACCOUNT);
|
|
4046
4106
|
try {
|
|
@@ -4056,6 +4116,10 @@ function keychainGet(account) {
|
|
|
4056
4116
|
if (disabled()) {
|
|
4057
4117
|
return null;
|
|
4058
4118
|
}
|
|
4119
|
+
const Entry = loadEntryClass();
|
|
4120
|
+
if (!Entry) {
|
|
4121
|
+
return null;
|
|
4122
|
+
}
|
|
4059
4123
|
try {
|
|
4060
4124
|
const entry = new Entry(SERVICE, account ?? DEFAULT_ACCOUNT);
|
|
4061
4125
|
const secret = entry.getPassword();
|
|
@@ -4069,6 +4133,10 @@ function keychainSet(account, secret) {
|
|
|
4069
4133
|
if (disabled()) {
|
|
4070
4134
|
return false;
|
|
4071
4135
|
}
|
|
4136
|
+
const Entry = loadEntryClass();
|
|
4137
|
+
if (!Entry) {
|
|
4138
|
+
return false;
|
|
4139
|
+
}
|
|
4072
4140
|
try {
|
|
4073
4141
|
const entry = new Entry(SERVICE, account ?? DEFAULT_ACCOUNT);
|
|
4074
4142
|
entry.setPassword(secret);
|
|
@@ -4081,6 +4149,10 @@ function keychainDelete(account) {
|
|
|
4081
4149
|
if (disabled()) {
|
|
4082
4150
|
return false;
|
|
4083
4151
|
}
|
|
4152
|
+
const Entry = loadEntryClass();
|
|
4153
|
+
if (!Entry) {
|
|
4154
|
+
return false;
|
|
4155
|
+
}
|
|
4084
4156
|
try {
|
|
4085
4157
|
const entry = new Entry(SERVICE, account ?? DEFAULT_ACCOUNT);
|
|
4086
4158
|
return entry.deletePassword();
|
|
@@ -4169,7 +4241,6 @@ function extraFlags(ctx) {
|
|
|
4169
4241
|
const flags = ctx.flags;
|
|
4170
4242
|
return {
|
|
4171
4243
|
since: flags.since,
|
|
4172
|
-
groupBy: flags.groupBy,
|
|
4173
4244
|
asin: flags.asin,
|
|
4174
4245
|
fulfillmentChannel: flags.fulfillmentChannel
|
|
4175
4246
|
};
|
|
@@ -4204,6 +4275,14 @@ function selectOutputValue2(result) {
|
|
|
4204
4275
|
return text;
|
|
4205
4276
|
}
|
|
4206
4277
|
}
|
|
4278
|
+
function warnIfLimitTruncated2(ctx, value) {
|
|
4279
|
+
const limit = ctx.flags.limit;
|
|
4280
|
+
if (limit != null && Array.isArray(value) && value.length === limit) {
|
|
4281
|
+
ctx.err(
|
|
4282
|
+
`warning: result returned exactly --limit (${limit}) rows; output may be truncated. Raise --limit or narrow the query.`
|
|
4283
|
+
);
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4207
4286
|
async function emit(ctx, name, args) {
|
|
4208
4287
|
const client = await ctx.getClient();
|
|
4209
4288
|
const response = await client.rpc(
|
|
@@ -4220,6 +4299,7 @@ async function emit(ctx, name, args) {
|
|
|
4220
4299
|
return classified.code;
|
|
4221
4300
|
}
|
|
4222
4301
|
const value = selectOutputValue2(result);
|
|
4302
|
+
warnIfLimitTruncated2(ctx, value);
|
|
4223
4303
|
const rendered = formatOutput(value, ctx.flags.format);
|
|
4224
4304
|
if (ctx.flags.output) {
|
|
4225
4305
|
await writeFile2(ctx.flags.output, rendered, { encoding: "utf8", mode: 384 });
|
|
@@ -4499,9 +4579,9 @@ async function run(argv) {
|
|
|
4499
4579
|
withGlobalOptions(cli.command("inventory health", "summarize inventory health")).action(
|
|
4500
4580
|
(options) => dispatch((ctx) => inventoryHealth(ctx), options)
|
|
4501
4581
|
);
|
|
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").
|
|
4582
|
+
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
4583
|
(options) => dispatch((ctx) => adsPerformance(ctx), options, {
|
|
4504
|
-
extra: { since: asString(options.since)
|
|
4584
|
+
extra: { since: asString(options.since) }
|
|
4505
4585
|
})
|
|
4506
4586
|
);
|
|
4507
4587
|
withGlobalOptions(cli.command("catalog quality", "summarize listing quality")).option("--asin <asin>", "scope to a single ASIN").action(
|