@agentcentral/cli 0.1.3 → 0.1.5
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 +29 -3
- package/dist/index.js +186 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,6 +76,28 @@ agentcentral tools call update_keyword_bids @bids.json \
|
|
|
76
76
|
--yes --idempotency-key "$(uuidgen)"
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
#### Auto-pagination (`--all` / `--paginate`)
|
|
80
|
+
|
|
81
|
+
Some live-API read tools return their results across multiple pages. Pass `--all` (alias `--paginate`)
|
|
82
|
+
to `tools call` and the CLI follows the server's continuation block, fetching every page and emitting
|
|
83
|
+
the concatenated rows:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
agentcentral tools call get_sponsored_ads_entities_live '{"ad_product":"SP"}' --all
|
|
87
|
+
agentcentral tools call get_audience_segments '{}' --all --format ndjson
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Notes:
|
|
91
|
+
|
|
92
|
+
- `--all` is reads only. Combining it with a write tool or with `--yes` is a usage error (exit 2).
|
|
93
|
+
- For `json`, `csv`, `table`, and `markdown`, the rows from every page are accumulated and rendered
|
|
94
|
+
once. For `ndjson`, each page's rows stream to stdout as they arrive (incremental); with `--output`,
|
|
95
|
+
ndjson buffers and writes the file once instead.
|
|
96
|
+
- Auto-pagination stops at a 100-page cap and warns on stderr if the cap is hit, so narrow the query
|
|
97
|
+
to fetch the rest.
|
|
98
|
+
- A tool whose response carries no pagination block is a single call plus a one-line stderr note, not
|
|
99
|
+
an error. This is also how `--all` degrades gracefully against an older server.
|
|
100
|
+
|
|
79
101
|
### Convenience read commands
|
|
80
102
|
|
|
81
103
|
Thin wrappers over the same hosted read tools. Each maps friendly flags onto the exact parameters its
|
|
@@ -122,9 +144,13 @@ Every command accepts: `--format/-f json|ndjson|table|csv|markdown`, `--output/-
|
|
|
122
144
|
`--profile/-p <name>`, `--timeout <ms>`, `--limit <n>`, `--quiet/-q`, `--no-color`, `--verbose`,
|
|
123
145
|
`--request-id <id>`, `--yes/-y`, `--idempotency-key <key>`, `--api-key-stdin`.
|
|
124
146
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
is
|
|
147
|
+
`tools call` also accepts `--all` / `--paginate` (auto-pagination, reads only; see above).
|
|
148
|
+
|
|
149
|
+
Truncation is reported authoritatively from the server, not guessed. When the result envelope says it
|
|
150
|
+
hit the row limit (`more_rows_possible` / `limit_reached`), the CLI warns on stderr with the row count
|
|
151
|
+
(and the exact `returned N of M matching rows` phrasing when the tool supplies a total). When more
|
|
152
|
+
pages are available, it instead notes that you can pass `--all` to fetch them. These messages go to
|
|
153
|
+
stderr only (honoring `--quiet`) and never touch stdout.
|
|
128
154
|
|
|
129
155
|
## Exit codes
|
|
130
156
|
|
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";
|
|
@@ -31,6 +34,7 @@ var DEFAULT_RETRY = {
|
|
|
31
34
|
honorRetryAfter: true
|
|
32
35
|
};
|
|
33
36
|
var API_KEY_PREFIX = "ac_live_";
|
|
37
|
+
var MAX_AUTO_PAGES = 100;
|
|
34
38
|
|
|
35
39
|
// src/config.ts
|
|
36
40
|
import {
|
|
@@ -3287,6 +3291,83 @@ function formatOutput(value, format) {
|
|
|
3287
3291
|
}
|
|
3288
3292
|
}
|
|
3289
3293
|
|
|
3294
|
+
// src/pagination.ts
|
|
3295
|
+
function readNumber(source, key) {
|
|
3296
|
+
const value = source[key];
|
|
3297
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3298
|
+
}
|
|
3299
|
+
function readBoolean(source, key) {
|
|
3300
|
+
const value = source[key];
|
|
3301
|
+
return typeof value === "boolean" ? value : void 0;
|
|
3302
|
+
}
|
|
3303
|
+
function readPagination(structured) {
|
|
3304
|
+
const raw = structured.pagination;
|
|
3305
|
+
if (!raw || typeof raw !== "object") {
|
|
3306
|
+
return void 0;
|
|
3307
|
+
}
|
|
3308
|
+
const block = raw;
|
|
3309
|
+
const mode = block.mode;
|
|
3310
|
+
const continueParam = block.continue_param;
|
|
3311
|
+
if (mode !== "token" && mode !== "offset" || typeof continueParam !== "string") {
|
|
3312
|
+
return void 0;
|
|
3313
|
+
}
|
|
3314
|
+
const nextToken = typeof block.next_token === "string" || block.next_token === null ? block.next_token : void 0;
|
|
3315
|
+
const nextOffset = readNumber(block, "next_offset");
|
|
3316
|
+
const derivedHasMore = mode === "token" ? nextToken != null : nextOffset != null;
|
|
3317
|
+
const hasMore = readBoolean(block, "has_more") ?? derivedHasMore;
|
|
3318
|
+
return {
|
|
3319
|
+
mode,
|
|
3320
|
+
...nextToken !== void 0 ? { next_token: nextToken } : {},
|
|
3321
|
+
...nextOffset !== void 0 ? { next_offset: nextOffset } : {},
|
|
3322
|
+
continue_param: continueParam,
|
|
3323
|
+
has_more: hasMore
|
|
3324
|
+
};
|
|
3325
|
+
}
|
|
3326
|
+
function readResultMeta(result) {
|
|
3327
|
+
const structured = result.structuredContent;
|
|
3328
|
+
if (!structured || typeof structured !== "object") {
|
|
3329
|
+
return {};
|
|
3330
|
+
}
|
|
3331
|
+
return {
|
|
3332
|
+
rowCount: readNumber(structured, "row_count"),
|
|
3333
|
+
requestedLimit: readNumber(structured, "requested_limit"),
|
|
3334
|
+
limitReached: readBoolean(structured, "limit_reached"),
|
|
3335
|
+
moreRowsPossible: readBoolean(structured, "more_rows_possible"),
|
|
3336
|
+
totalMatching: readNumber(structured, "total_matching"),
|
|
3337
|
+
pagination: readPagination(structured)
|
|
3338
|
+
};
|
|
3339
|
+
}
|
|
3340
|
+
function warnFromResultMeta(ctx, meta, allRequested) {
|
|
3341
|
+
if (meta.pagination?.has_more && !allRequested) {
|
|
3342
|
+
ctx.err("note: more pages available; pass --all to fetch them.");
|
|
3343
|
+
return;
|
|
3344
|
+
}
|
|
3345
|
+
if (meta.moreRowsPossible || meta.limitReached) {
|
|
3346
|
+
const rows = meta.rowCount;
|
|
3347
|
+
if (typeof meta.totalMatching === "number" && typeof rows === "number") {
|
|
3348
|
+
ctx.err(
|
|
3349
|
+
`warning: returned ${rows} of ${meta.totalMatching} matching rows at the limit. Raise --limit or narrow the query (deep paging is not available for this tool).`
|
|
3350
|
+
);
|
|
3351
|
+
return;
|
|
3352
|
+
}
|
|
3353
|
+
const count = typeof rows === "number" ? `${rows} rows` : "rows";
|
|
3354
|
+
ctx.err(
|
|
3355
|
+
`warning: returned ${count} at the limit. Raise --limit or narrow the query (deep paging is not available for this tool).`
|
|
3356
|
+
);
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
function readRows(result) {
|
|
3360
|
+
const structured = result.structuredContent;
|
|
3361
|
+
if (!structured || typeof structured !== "object") {
|
|
3362
|
+
return [];
|
|
3363
|
+
}
|
|
3364
|
+
const rows = structured.rows;
|
|
3365
|
+
return Array.isArray(rows) ? rows : [];
|
|
3366
|
+
}
|
|
3367
|
+
function nextContinueValue(pagination) {
|
|
3368
|
+
return pagination.mode === "token" ? pagination.next_token : pagination.next_offset;
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3290
3371
|
// src/commands/tools.ts
|
|
3291
3372
|
function parseJsonArgs(text, origin) {
|
|
3292
3373
|
try {
|
|
@@ -3446,14 +3527,6 @@ function selectOutputValue(result) {
|
|
|
3446
3527
|
return text;
|
|
3447
3528
|
}
|
|
3448
3529
|
}
|
|
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
|
-
}
|
|
3457
3530
|
function echoWriteSummary(ctx, result) {
|
|
3458
3531
|
const env = writeEnvelopeFrom(result);
|
|
3459
3532
|
if (!env) {
|
|
@@ -3470,11 +3543,28 @@ function echoWriteSummary(ctx, result) {
|
|
|
3470
3543
|
ctx.err("note: a prior submission with this idempotency key stands; no new write was made.");
|
|
3471
3544
|
}
|
|
3472
3545
|
}
|
|
3546
|
+
async function emitRendered(ctx, rendered) {
|
|
3547
|
+
if (ctx.flags.output) {
|
|
3548
|
+
await writeFile(ctx.flags.output, rendered, { encoding: "utf8", mode: 384 });
|
|
3549
|
+
ctx.err(`wrote result to ${ctx.flags.output}`);
|
|
3550
|
+
} else {
|
|
3551
|
+
ctx.out(rendered);
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3473
3554
|
async function toolsCall(ctx, name, inputSpec) {
|
|
3555
|
+
if (ctx.flags.all && ctx.flags.yes) {
|
|
3556
|
+
throw new CliError(
|
|
3557
|
+
2 /* UsageError */,
|
|
3558
|
+
"--all is for reads only; it cannot be combined with --yes."
|
|
3559
|
+
);
|
|
3560
|
+
}
|
|
3474
3561
|
const input = await resolveInput(inputSpec, readStdinOnce);
|
|
3475
3562
|
const client = await ctx.getClient();
|
|
3476
3563
|
const tools = await client.listTools({ timeoutMs: ctx.flags.timeoutMs });
|
|
3477
3564
|
const def = requireTool(tools, name);
|
|
3565
|
+
if (ctx.flags.all) {
|
|
3566
|
+
return toolsCallAll(ctx, client, def, name, input);
|
|
3567
|
+
}
|
|
3478
3568
|
const args = buildCallArgs(ctx, def, input);
|
|
3479
3569
|
const response = await client.rpc(
|
|
3480
3570
|
"tools/call",
|
|
@@ -3490,17 +3580,89 @@ async function toolsCall(ctx, name, inputSpec) {
|
|
|
3490
3580
|
return classified.code;
|
|
3491
3581
|
}
|
|
3492
3582
|
echoWriteSummary(ctx, result);
|
|
3583
|
+
const meta = readResultMeta(result);
|
|
3584
|
+
warnFromResultMeta(ctx, meta, false);
|
|
3493
3585
|
const value = selectOutputValue(result);
|
|
3494
|
-
warnIfLimitTruncated(ctx, value);
|
|
3495
3586
|
const rendered = formatOutput(value, ctx.flags.format);
|
|
3496
|
-
|
|
3497
|
-
await writeFile(ctx.flags.output, rendered, { encoding: "utf8", mode: 384 });
|
|
3498
|
-
ctx.err(`wrote result to ${ctx.flags.output}`);
|
|
3499
|
-
} else {
|
|
3500
|
-
ctx.out(rendered);
|
|
3501
|
-
}
|
|
3587
|
+
await emitRendered(ctx, rendered);
|
|
3502
3588
|
return classified.code;
|
|
3503
3589
|
}
|
|
3590
|
+
async function toolsCallAll(ctx, client, def, name, input) {
|
|
3591
|
+
if (toolClassification(def).isWrite || ctx.flags.yes) {
|
|
3592
|
+
throw new CliError(
|
|
3593
|
+
2 /* UsageError */,
|
|
3594
|
+
"--all is for reads only; it cannot be combined with a write tool or --yes."
|
|
3595
|
+
);
|
|
3596
|
+
}
|
|
3597
|
+
const streamNdjson = ctx.flags.format === "ndjson" && !ctx.flags.output;
|
|
3598
|
+
const accumulated = [];
|
|
3599
|
+
let args = buildCallArgs(ctx, def, input);
|
|
3600
|
+
let pages = 0;
|
|
3601
|
+
let exitCode = 0 /* Success */;
|
|
3602
|
+
const collect = (rows) => {
|
|
3603
|
+
if (streamNdjson) {
|
|
3604
|
+
if (rows.length > 0) {
|
|
3605
|
+
ctx.out(formatOutput(rows, "ndjson"));
|
|
3606
|
+
}
|
|
3607
|
+
} else {
|
|
3608
|
+
accumulated.push(...rows);
|
|
3609
|
+
}
|
|
3610
|
+
};
|
|
3611
|
+
for (; ; ) {
|
|
3612
|
+
if (pages >= MAX_AUTO_PAGES) {
|
|
3613
|
+
ctx.err(
|
|
3614
|
+
`warning: pagination stopped at the ${MAX_AUTO_PAGES}-page cap; more pages may remain. Narrow the query to fetch the rest.`
|
|
3615
|
+
);
|
|
3616
|
+
break;
|
|
3617
|
+
}
|
|
3618
|
+
const response = await client.rpc(
|
|
3619
|
+
"tools/call",
|
|
3620
|
+
{ name, arguments: args },
|
|
3621
|
+
{ timeoutMs: ctx.flags.timeoutMs }
|
|
3622
|
+
);
|
|
3623
|
+
pages += 1;
|
|
3624
|
+
const classified = classifyResponse(response);
|
|
3625
|
+
const result = classified.result;
|
|
3626
|
+
if (!result) {
|
|
3627
|
+
if (classified.detail) {
|
|
3628
|
+
ctx.err(classified.detail);
|
|
3629
|
+
}
|
|
3630
|
+
exitCode = classified.code;
|
|
3631
|
+
break;
|
|
3632
|
+
}
|
|
3633
|
+
if (classified.code !== 0 /* Success */) {
|
|
3634
|
+
if (classified.detail) {
|
|
3635
|
+
ctx.err(classified.detail);
|
|
3636
|
+
}
|
|
3637
|
+
collect(readRows(result));
|
|
3638
|
+
ctx.err(`warning: pagination stopped after ${pages} page(s) on a page error.`);
|
|
3639
|
+
exitCode = classified.code;
|
|
3640
|
+
break;
|
|
3641
|
+
}
|
|
3642
|
+
collect(readRows(result));
|
|
3643
|
+
const meta = readResultMeta(result);
|
|
3644
|
+
if (!meta.pagination) {
|
|
3645
|
+
if (pages === 1) {
|
|
3646
|
+
ctx.err("note: this tool returned no pagination block; fetched a single page.");
|
|
3647
|
+
}
|
|
3648
|
+
warnFromResultMeta(ctx, meta, true);
|
|
3649
|
+
break;
|
|
3650
|
+
}
|
|
3651
|
+
if (!meta.pagination.has_more) {
|
|
3652
|
+
break;
|
|
3653
|
+
}
|
|
3654
|
+
const next = nextContinueValue(meta.pagination);
|
|
3655
|
+
if (next == null) {
|
|
3656
|
+
break;
|
|
3657
|
+
}
|
|
3658
|
+
args = { ...args, [meta.pagination.continue_param]: next };
|
|
3659
|
+
}
|
|
3660
|
+
if (!streamNdjson) {
|
|
3661
|
+
const rendered = formatOutput(accumulated, ctx.flags.format);
|
|
3662
|
+
await emitRendered(ctx, rendered);
|
|
3663
|
+
}
|
|
3664
|
+
return exitCode;
|
|
3665
|
+
}
|
|
3504
3666
|
async function readStdinOnce() {
|
|
3505
3667
|
const chunks = [];
|
|
3506
3668
|
for await (const chunk of process.stdin) {
|
|
@@ -4067,7 +4229,7 @@ async function profilesUse(ctx, name) {
|
|
|
4067
4229
|
}
|
|
4068
4230
|
|
|
4069
4231
|
// src/keychain.ts
|
|
4070
|
-
import { createRequire } from "module";
|
|
4232
|
+
import { createRequire as createRequire2 } from "module";
|
|
4071
4233
|
var SERVICE = "agentcentral-cli";
|
|
4072
4234
|
var DEFAULT_ACCOUNT = "default";
|
|
4073
4235
|
var DISABLE_ENV = "AGENT_CENTRAL_DISABLE_KEYCHAIN";
|
|
@@ -4076,7 +4238,7 @@ var loaderOverride = void 0;
|
|
|
4076
4238
|
function disabled() {
|
|
4077
4239
|
return process.env[DISABLE_ENV] === "1";
|
|
4078
4240
|
}
|
|
4079
|
-
var requireNative =
|
|
4241
|
+
var requireNative = createRequire2(import.meta.url);
|
|
4080
4242
|
function loadEntryClass() {
|
|
4081
4243
|
if (entryClassCache !== void 0) {
|
|
4082
4244
|
return entryClassCache;
|
|
@@ -4272,14 +4434,6 @@ function selectOutputValue2(result) {
|
|
|
4272
4434
|
return text;
|
|
4273
4435
|
}
|
|
4274
4436
|
}
|
|
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
|
-
}
|
|
4283
4437
|
async function emit(ctx, name, args) {
|
|
4284
4438
|
const client = await ctx.getClient();
|
|
4285
4439
|
const response = await client.rpc(
|
|
@@ -4295,8 +4449,9 @@ async function emit(ctx, name, args) {
|
|
|
4295
4449
|
}
|
|
4296
4450
|
return classified.code;
|
|
4297
4451
|
}
|
|
4452
|
+
const meta = readResultMeta(result);
|
|
4453
|
+
warnFromResultMeta(ctx, meta, ctx.flags.all === true);
|
|
4298
4454
|
const value = selectOutputValue2(result);
|
|
4299
|
-
warnIfLimitTruncated2(ctx, value);
|
|
4300
4455
|
const rendered = formatOutput(value, ctx.flags.format);
|
|
4301
4456
|
if (ctx.flags.output) {
|
|
4302
4457
|
await writeFile2(ctx.flags.output, rendered, { encoding: "utf8", mode: 384 });
|
|
@@ -4439,7 +4594,9 @@ function parseGlobalFlags(options) {
|
|
|
4439
4594
|
verbose: options.verbose === true,
|
|
4440
4595
|
requestId: asString(options.requestId),
|
|
4441
4596
|
yes: options.yes === true,
|
|
4442
|
-
idempotencyKey: asString(options.idempotencyKey)
|
|
4597
|
+
idempotencyKey: asString(options.idempotencyKey),
|
|
4598
|
+
// --all and its --paginate alias both land here.
|
|
4599
|
+
all: options.all === true || options.paginate === true
|
|
4443
4600
|
};
|
|
4444
4601
|
}
|
|
4445
4602
|
async function readKeyFromStdin() {
|
|
@@ -4534,7 +4691,7 @@ async function run(argv) {
|
|
|
4534
4691
|
withGlobalOptions(cli.command("tools schema <tool>", "print a tool's JSON Schema")).action(
|
|
4535
4692
|
(tool, options) => dispatch((ctx) => toolsSchema(ctx, tool), options)
|
|
4536
4693
|
);
|
|
4537
|
-
withGlobalOptions(cli.command("tools call <tool> [input]", "invoke a tool")).option("-i, --input <spec>", "tool input as JSON, @file, a file path, or - for stdin").action(
|
|
4694
|
+
withGlobalOptions(cli.command("tools call <tool> [input]", "invoke a tool")).option("-i, --input <spec>", "tool input as JSON, @file, a file path, or - for stdin").option("--all, --paginate", "follow the server's pagination block and fetch every page (reads only)").action(
|
|
4538
4695
|
(tool, input, options) => dispatch((ctx) => toolsCall(ctx, tool, asString(options.input) ?? input), options)
|
|
4539
4696
|
);
|
|
4540
4697
|
withGlobalOptions(cli.command("mcp serve", "run the transparent stdio MCP bridge")).option("--domain <domain>", "advisory domain scope (repeatable / comma-separated)").option("--read-only", "advisory: hide write tools from tools/list").action(
|