@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.
Files changed (3) hide show
  1. package/README.md +33 -3
  2. package/dist/index.js +217 -137
  3. 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>`, `--profile/-p <name>`,
96
- `--timeout <ms>`, `--limit <n>`, `--quiet/-q`, `--no-color`, `--verbose`, `--request-id <id>`,
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
- var CLI_VERSION = "0.1.2";
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
- writeFileSync(path, JSON.stringify(config, null, 2), { mode: 384 });
112
- chmodSync(path, 384);
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({ jsonrpc: "2.0", id, method, params }),
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(7 /* ToolExecutionFailed */, response.error.message, "tools/list");
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(7 /* ToolExecutionFailed */, response.error.message, name);
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: "get_replenishment_offers", description: "Live and historical Subscribe and Save replenishment offer status" },
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
- "get_replenishment_offers",
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
- let chain = Promise.resolve();
3628
+ const inflight = /* @__PURE__ */ new Set();
3593
3629
  const dispatch = (line) => {
3594
- chain = chain.then(async () => {
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
- chain.then(() => {
3664
+ Promise.allSettled([...inflight]).then(() => {
3625
3665
  ctx.err("bridge stopped (stdin closed)");
3626
3666
  resolve(0 /* Success */);
3627
- }).catch(reject);
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 { Entry } from "@napi-rs/keyring";
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").option("--group-by <field>", "advisory grain hint (documented; not wired server-side)").action(
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), groupBy: asString(options.groupBy) }
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(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcentral/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "license": "MIT",
5
5
  "description": "agentcentral CLI — local MCP bridge and generic tool runner for Amazon seller data",
6
6
  "keywords": [