@odla-ai/cli 0.25.6 → 0.25.8
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 +13 -1
- package/dist/bin.cjs +144 -30
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-MWLZO2EZ.js → chunk-PM5FBL3S.js} +145 -31
- package/dist/chunk-PM5FBL3S.js.map +1 -0
- package/dist/index.cjs +144 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/odla/SKILL.md +4 -0
- package/dist/chunk-MWLZO2EZ.js.map +0 -1
package/README.md
CHANGED
|
@@ -72,7 +72,8 @@ snapshot instead of scraping Studio or composing collector routes themselves:
|
|
|
72
72
|
npx odla-ai o11y status --app <appId> --env prod --minutes 60 --json
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
Schema
|
|
75
|
+
Schema v4 keeps application RED, reconciled live-sync load/freshness and
|
|
76
|
+
subscription recompute/fanout/payload/commit-to-send performance, the exact
|
|
76
77
|
latest protected commit-to-visible canary, collector ingest/scheduler trust,
|
|
77
78
|
and Cloudflare-owned Worker runtime evidence separate. It derives one
|
|
78
79
|
`healthy`, `degraded`, or `unhealthy` verdict with stable reason codes while
|
|
@@ -192,6 +193,8 @@ npx odla-ai code connect --env prod --once # bounded enrollment + heartbeat proo
|
|
|
192
193
|
npx odla-ai provision --dry-run
|
|
193
194
|
npx odla-ai provision --email owner@example.com --write-dev-vars --push-secrets
|
|
194
195
|
npx odla-ai smoke --env dev
|
|
196
|
+
npx odla-ai agent jobs --env dev --state dead_letter --json
|
|
197
|
+
npx odla-ai agent retry <job-id> --env dev --json
|
|
195
198
|
npx odla-ai secrets push --env dev
|
|
196
199
|
npx odla-ai secrets set clerk_webhook_secret --env dev --stdin
|
|
197
200
|
npx odla-ai secrets set-clerk-key --env dev --from-env CLERK_SECRET_KEY
|
|
@@ -213,6 +216,15 @@ npx odla-ai skill install
|
|
|
213
216
|
npx odla-ai version
|
|
214
217
|
```
|
|
215
218
|
|
|
219
|
+
`agent jobs` is the remote-operator view of commit-trigger delivery. It reads
|
|
220
|
+
content-free lifecycle metadata—job id, trigger, entity reference, state,
|
|
221
|
+
attempts, timing, and bounded error/status codes—using an audience-bound
|
|
222
|
+
developer token for an owner of the selected environment. `agent retry` accepts one exact
|
|
223
|
+
dead-lettered id and resets its attempt lease; it does not replay successful or
|
|
224
|
+
currently running work. Successful receipts are retained for seven days (and
|
|
225
|
+
bounded to the newest 1,000 per tenant); unresolved dead letters remain until
|
|
226
|
+
an operator requeues them.
|
|
227
|
+
|
|
216
228
|
`code connect` is the first-party personal Code terminal runtime. Run the exact
|
|
217
229
|
command copied from **Studio → Code → Terminal** while your shell is in the
|
|
218
230
|
local Git checkout. `--platform`, `--app-id`, and `--env` make the requested
|
package/dist/bin.cjs
CHANGED
|
@@ -1020,11 +1020,6 @@ async function adminCommand(parsed) {
|
|
|
1020
1020
|
});
|
|
1021
1021
|
}
|
|
1022
1022
|
|
|
1023
|
-
// src/app-export.ts
|
|
1024
|
-
var import_node_fs5 = require("fs");
|
|
1025
|
-
var import_node_stream = require("stream");
|
|
1026
|
-
var import_promises = require("stream/promises");
|
|
1027
|
-
|
|
1028
1023
|
// src/config.ts
|
|
1029
1024
|
var import_node_fs4 = require("fs");
|
|
1030
1025
|
var import_node_path4 = require("path");
|
|
@@ -1333,7 +1328,90 @@ function bothTenants(cfg) {
|
|
|
1333
1328
|
return { sandbox: (0, import_apps.tenantIdFor)(cfg.app.id, "dev"), live: (0, import_apps.tenantIdFor)(cfg.app.id, "prod") };
|
|
1334
1329
|
}
|
|
1335
1330
|
|
|
1331
|
+
// src/agent-command.ts
|
|
1332
|
+
async function agentCommand(parsed, deps = {}) {
|
|
1333
|
+
const action = parsed.positionals[1];
|
|
1334
|
+
if (action !== "jobs" && action !== "retry") {
|
|
1335
|
+
throw new Error(`unknown agent action "${action ?? ""}". Try "odla-ai agent jobs --json".`);
|
|
1336
|
+
}
|
|
1337
|
+
assertArgs(parsed, ["config", "env", "state", "limit", "json", "token", "email"], action === "jobs" ? 2 : 3);
|
|
1338
|
+
if (action === "retry" && (parsed.options.state !== void 0 || parsed.options.limit !== void 0)) {
|
|
1339
|
+
throw new Error('--state and --limit are supported only by "agent jobs"');
|
|
1340
|
+
}
|
|
1341
|
+
const cfg = await loadProjectConfig(stringOpt(parsed.options.config) ?? "odla.config.mjs");
|
|
1342
|
+
const { env, tenant } = resolveTenant(cfg, stringOpt(parsed.options.env));
|
|
1343
|
+
const doFetch = deps.fetch ?? fetch;
|
|
1344
|
+
const out = deps.stdout ?? console;
|
|
1345
|
+
const credential2 = await getDeveloperToken(
|
|
1346
|
+
cfg,
|
|
1347
|
+
{
|
|
1348
|
+
configPath: cfg.configPath,
|
|
1349
|
+
token: stringOpt(parsed.options.token),
|
|
1350
|
+
email: stringOpt(parsed.options.email),
|
|
1351
|
+
open: false
|
|
1352
|
+
},
|
|
1353
|
+
doFetch,
|
|
1354
|
+
out
|
|
1355
|
+
);
|
|
1356
|
+
const base = `${cfg.dbEndpoint}/app/${encodeURIComponent(tenant)}/admin/agent-jobs`;
|
|
1357
|
+
const headers = { authorization: `Bearer ${credential2}` };
|
|
1358
|
+
if (action === "retry") {
|
|
1359
|
+
const id = parsed.positionals[2];
|
|
1360
|
+
const res2 = await doFetch(`${base}/${encodeURIComponent(id)}/retry`, { method: "POST", headers });
|
|
1361
|
+
const body2 = await readJson(res2);
|
|
1362
|
+
if (!res2.ok) throw new Error(`agent retry failed (${res2.status}): ${errorMessage(body2)}`);
|
|
1363
|
+
const result2 = { v: 1, appId: cfg.app.id, env, tenant, ...body2 };
|
|
1364
|
+
if (parsed.options.json === true) out.log(JSON.stringify(result2, null, 2));
|
|
1365
|
+
else out.log(`${tenant}: requeued ${id}`);
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
const state2 = stringOpt(parsed.options.state);
|
|
1369
|
+
const allowed = ["pending", "running", "succeeded", "dead_letter"];
|
|
1370
|
+
if (state2 && !allowed.includes(state2)) {
|
|
1371
|
+
throw new Error(`--state must be one of ${allowed.join(", ")}`);
|
|
1372
|
+
}
|
|
1373
|
+
const url = new URL(base);
|
|
1374
|
+
if (state2) url.searchParams.set("state", state2);
|
|
1375
|
+
const limit = numberOpt(parsed.options.limit, "--limit");
|
|
1376
|
+
if (limit) url.searchParams.set("limit", String(limit));
|
|
1377
|
+
const res = await doFetch(url, { headers });
|
|
1378
|
+
const body = await readJson(res);
|
|
1379
|
+
if (!res.ok) throw new Error(`agent jobs failed (${res.status}): ${errorMessage(body)}`);
|
|
1380
|
+
const result = { v: 1, appId: cfg.app.id, env, tenant, jobs: body.jobs ?? [], summary: body.summary };
|
|
1381
|
+
if (parsed.options.json === true) {
|
|
1382
|
+
out.log(JSON.stringify(result, null, 2));
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
const summary = body.summary;
|
|
1386
|
+
out.log(
|
|
1387
|
+
`${tenant}: ${summary?.pending ?? 0} pending, ${summary?.running ?? 0} running, ${summary?.deadLetter ?? 0} dead-letter, ${summary?.succeeded ?? 0} succeeded`
|
|
1388
|
+
);
|
|
1389
|
+
for (const job of body.jobs ?? []) {
|
|
1390
|
+
out.log(
|
|
1391
|
+
[job.state, job.id, job.triggerId, `${job.entityNs}/${job.entityId}`, `attempts=${job.attempts}`, job.lastErrorCode ?? ""].filter(Boolean).join(" ")
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
async function readJson(res) {
|
|
1396
|
+
try {
|
|
1397
|
+
return await res.json();
|
|
1398
|
+
} catch {
|
|
1399
|
+
return {};
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
function errorMessage(body) {
|
|
1403
|
+
const error = body.error;
|
|
1404
|
+
if (typeof error === "string") return error;
|
|
1405
|
+
if (error && typeof error === "object" && typeof error.message === "string") {
|
|
1406
|
+
return error.message;
|
|
1407
|
+
}
|
|
1408
|
+
return "request failed";
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1336
1411
|
// src/app-export.ts
|
|
1412
|
+
var import_node_fs5 = require("fs");
|
|
1413
|
+
var import_node_stream = require("stream");
|
|
1414
|
+
var import_promises = require("stream/promises");
|
|
1337
1415
|
async function appExport(options) {
|
|
1338
1416
|
const cfg = await loadProjectConfig(options.configPath);
|
|
1339
1417
|
const out = options.stdout ?? console;
|
|
@@ -2311,6 +2389,7 @@ var CAPABILITIES = {
|
|
|
2311
2389
|
"apply Google Calendar booking config, then drive state-bound consent, status, discovery, and disconnect flows (bookings run live through the platform proxy)",
|
|
2312
2390
|
"validate integration contracts offline and smoke-test a provisioned db environment plus anonymous capability routes",
|
|
2313
2391
|
"read one versioned o11y status envelope spanning application RED, current live-sync freshness/load, the protected commit-to-visible canary, collector ingest/scheduler trust, provider-owned runtime metrics, and a bounded machine verdict",
|
|
2392
|
+
"inspect durable agent wakeups as a versioned JSON envelope and explicitly requeue one dead-lettered job with a scoped environment credential",
|
|
2314
2393
|
"run app-attributed hosted security discovery and independent validation without provider keys",
|
|
2315
2394
|
"connect/revoke source-read-only GitHub sources and drive commit-pinned hosted security jobs without PATs or provider keys",
|
|
2316
2395
|
"let admins manage system AI policy and credentials, inspect attributed usage, and read immutable admin changes through separate short-lived capability-only approvals"
|
|
@@ -3946,8 +4025,8 @@ async function materializeGitTree(source, commitSha, options = {}) {
|
|
|
3946
4025
|
const maxFiles = options.maxFiles ?? 2e4;
|
|
3947
4026
|
const maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
3948
4027
|
const inventory = (await gitOutput(sourceDir, ["ls-tree", "-rz", commitSha], 16 * 1024 * 1024)).toString("utf8").split("\0").filter(Boolean);
|
|
3949
|
-
const entries = inventory.flatMap((
|
|
3950
|
-
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(
|
|
4028
|
+
const entries = inventory.flatMap((record7) => {
|
|
4029
|
+
const match = /^(100644|100755) blob ([0-9a-f]{40,64})\t([\s\S]+)$/.exec(record7);
|
|
3951
4030
|
return match && allowedWorkspacePath(match[3]) ? [{ mode: match[1], hash: match[2], path: match[3] }] : [];
|
|
3952
4031
|
});
|
|
3953
4032
|
if (entries.length > maxFiles) throw new Error(`workspace exceeds ${maxFiles} files`);
|
|
@@ -4195,8 +4274,8 @@ function normalize(value) {
|
|
|
4195
4274
|
if (Array.isArray(value)) return value.map(normalize);
|
|
4196
4275
|
if (value instanceof Uint8Array) return { $bytes: [...value] };
|
|
4197
4276
|
if (typeof value === "object") {
|
|
4198
|
-
const
|
|
4199
|
-
return Object.fromEntries(Object.keys(
|
|
4277
|
+
const record7 = value;
|
|
4278
|
+
return Object.fromEntries(Object.keys(record7).filter((key) => record7[key] !== void 0).sort().map((key) => [key, normalize(record7[key])]));
|
|
4200
4279
|
}
|
|
4201
4280
|
throw new CamelError("state_conflict", "Canonical JSON rejects unsupported values.");
|
|
4202
4281
|
}
|
|
@@ -6490,6 +6569,8 @@ Usage:
|
|
|
6490
6569
|
odla-ai discuss resolve <topic> [--reopen] [--mutation-id <id>]
|
|
6491
6570
|
odla-ai discuss who --q <text> [--app <id>] [--kinds user,pm:task] [--json]
|
|
6492
6571
|
odla-ai discuss watch [<topic>] [--by <authorId>] [--self <authorId>] [--interval <s>] [--timeout <s>] [--json]
|
|
6572
|
+
odla-ai agent jobs [--env dev] [--state pending|running|succeeded|dead_letter] [--limit 50] [--email <email>] [--json]
|
|
6573
|
+
odla-ai agent retry <job-id> [--env dev] [--email <email>] [--json]
|
|
6493
6574
|
odla-ai o11y status [--app <id>] [--env prod] [--minutes 60] [--json]
|
|
6494
6575
|
odla-ai whoami [--json]
|
|
6495
6576
|
odla-ai runbook ask "<question>" [--app <id>] [--all] [--json]
|
|
@@ -6537,6 +6618,8 @@ Usage:
|
|
|
6537
6618
|
odla-ai version
|
|
6538
6619
|
|
|
6539
6620
|
Commands:
|
|
6621
|
+
agent Inspect durable agent wakeups and explicitly requeue a
|
|
6622
|
+
dead-lettered job; JSON output is stable for remote operators.
|
|
6540
6623
|
runbook odla's operational procedures, stored in the database and read at
|
|
6541
6624
|
the moment they are followed. "ask" gives a written, cited answer;
|
|
6542
6625
|
"search" the passages behind it; "get" the whole document.
|
|
@@ -7650,8 +7733,8 @@ async function pmAdd(ctx, entity, parsed) {
|
|
|
7650
7733
|
emit2(ctx, res, () => ctx.out.log(`created ${entity} ${res.id}`));
|
|
7651
7734
|
}
|
|
7652
7735
|
async function pmGet(ctx, entity, id) {
|
|
7653
|
-
const { record:
|
|
7654
|
-
emit2(ctx,
|
|
7736
|
+
const { record: record7 } = await pmRequest(ctx, "GET", `/${entity}/${encodeURIComponent(id)}`);
|
|
7737
|
+
emit2(ctx, record7, () => printRecord(ctx, entity, record7));
|
|
7655
7738
|
}
|
|
7656
7739
|
async function pmSet(ctx, entity, id, parsed) {
|
|
7657
7740
|
const patch2 = collectEntityFields(entity, parsed, true);
|
|
@@ -7696,9 +7779,9 @@ async function pmHandoff(ctx, parsed) {
|
|
|
7696
7779
|
]);
|
|
7697
7780
|
const handoff = {
|
|
7698
7781
|
appId,
|
|
7699
|
-
unmetGoals: goals.filter((
|
|
7700
|
-
activeTasks: tasks.filter((
|
|
7701
|
-
openBugs: bugs.filter((
|
|
7782
|
+
unmetGoals: goals.filter((record7) => record7.status !== "met"),
|
|
7783
|
+
activeTasks: tasks.filter((record7) => record7.column !== "done"),
|
|
7784
|
+
openBugs: bugs.filter((record7) => record7.status !== "fixed" && record7.status !== "wontfix")
|
|
7702
7785
|
};
|
|
7703
7786
|
const result = {
|
|
7704
7787
|
...handoff,
|
|
@@ -7717,10 +7800,10 @@ async function pmHandoff(ctx, parsed) {
|
|
|
7717
7800
|
]) {
|
|
7718
7801
|
ctx.out.log(`${label}:`);
|
|
7719
7802
|
if (!records.length) ctx.out.log("- (none)");
|
|
7720
|
-
else for (const
|
|
7803
|
+
else for (const record7 of records) printRecord(
|
|
7721
7804
|
ctx,
|
|
7722
7805
|
label === "unmet goals" ? "goal" : label === "active tasks" ? "task" : "bug",
|
|
7723
|
-
|
|
7806
|
+
record7
|
|
7724
7807
|
);
|
|
7725
7808
|
}
|
|
7726
7809
|
});
|
|
@@ -7861,6 +7944,21 @@ function statusVerdict(reads) {
|
|
|
7861
7944
|
severity: "degraded"
|
|
7862
7945
|
});
|
|
7863
7946
|
}
|
|
7947
|
+
const performance = record5(reads.liveSync.body.performance) ? reads.liveSync.body.performance : null;
|
|
7948
|
+
if (performance?.status === "unavailable") {
|
|
7949
|
+
reasons.push({
|
|
7950
|
+
source: "liveSync",
|
|
7951
|
+
code: "subscription_metrics_unavailable",
|
|
7952
|
+
severity: "degraded"
|
|
7953
|
+
});
|
|
7954
|
+
}
|
|
7955
|
+
if (numeric2(performance?.telemetryDropped) > 0) {
|
|
7956
|
+
reasons.push({
|
|
7957
|
+
source: "liveSync",
|
|
7958
|
+
code: "subscription_telemetry_dropped",
|
|
7959
|
+
severity: "degraded"
|
|
7960
|
+
});
|
|
7961
|
+
}
|
|
7864
7962
|
const canaryStatus = String(reads.canary.body.status ?? "");
|
|
7865
7963
|
if (canaryStatus === "failing") {
|
|
7866
7964
|
reasons.push({
|
|
@@ -7908,6 +8006,12 @@ function statusVerdict(reads) {
|
|
|
7908
8006
|
reasons
|
|
7909
8007
|
};
|
|
7910
8008
|
}
|
|
8009
|
+
function record5(value) {
|
|
8010
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8011
|
+
}
|
|
8012
|
+
function numeric2(value) {
|
|
8013
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
8014
|
+
}
|
|
7911
8015
|
function collectorReason(read3, fallback) {
|
|
7912
8016
|
const reasons = read3.body.reasons;
|
|
7913
8017
|
if (!Array.isArray(reasons)) return fallback;
|
|
@@ -7965,7 +8069,7 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
7965
8069
|
const [application, liveSync, canary, collector, provider] = await Promise.all([
|
|
7966
8070
|
read2(`${base}/metrics/red?${query}`, headers, doFetch),
|
|
7967
8071
|
read2(
|
|
7968
|
-
`${base}/live-sync/health?${
|
|
8072
|
+
`${base}/live-sync/health?${query}`,
|
|
7969
8073
|
headers,
|
|
7970
8074
|
doFetch
|
|
7971
8075
|
),
|
|
@@ -7985,7 +8089,7 @@ async function o11yCommand(parsed, deps = {}) {
|
|
|
7985
8089
|
provider
|
|
7986
8090
|
});
|
|
7987
8091
|
const status = {
|
|
7988
|
-
schemaVersion:
|
|
8092
|
+
schemaVersion: 4,
|
|
7989
8093
|
scope: { appId, env, minutes },
|
|
7990
8094
|
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7991
8095
|
verdict,
|
|
@@ -8025,42 +8129,47 @@ function printStatus2(status, out) {
|
|
|
8025
8129
|
out.log(
|
|
8026
8130
|
`o11y status ${status.scope.appId}/${status.scope.env} (${status.scope.minutes}m)`
|
|
8027
8131
|
);
|
|
8028
|
-
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(
|
|
8132
|
+
const routes = Array.isArray(status.application.body.routes) ? status.application.body.routes.filter(record6) : [];
|
|
8029
8133
|
const requests = routes.reduce(
|
|
8030
|
-
(total, row) => total +
|
|
8134
|
+
(total, row) => total + numeric3(row.requests),
|
|
8031
8135
|
0
|
|
8032
8136
|
);
|
|
8033
8137
|
const errors = routes.reduce(
|
|
8034
|
-
(total, row) => total +
|
|
8138
|
+
(total, row) => total + numeric3(row.errors),
|
|
8035
8139
|
0
|
|
8036
8140
|
);
|
|
8037
8141
|
out.log(
|
|
8038
8142
|
`application ${status.application.httpStatus} ${requests} requests ${errors} errors`
|
|
8039
8143
|
);
|
|
8040
8144
|
out.log(
|
|
8041
|
-
|
|
8145
|
+
liveSyncLine(status.liveSync)
|
|
8042
8146
|
);
|
|
8043
|
-
const canaryDurations =
|
|
8147
|
+
const canaryDurations = record6(status.canary.body.durationsMs) ? status.canary.body.durationsMs : {};
|
|
8044
8148
|
out.log(
|
|
8045
8149
|
`canary ${status.canary.httpStatus} ${String(status.canary.body.status ?? status.canary.body.error ?? "unavailable")} ${optionalNumeric(canaryDurations.publishToVisibleMs)} publish-to-visible`
|
|
8046
8150
|
);
|
|
8047
|
-
const collectorIngest =
|
|
8048
|
-
const collectorStorage =
|
|
8151
|
+
const collectorIngest = record6(status.collector.body.ingest) ? status.collector.body.ingest : {};
|
|
8152
|
+
const collectorStorage = record6(collectorIngest.storage) ? collectorIngest.storage : {};
|
|
8049
8153
|
out.log(
|
|
8050
|
-
`collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${
|
|
8154
|
+
`collector ${status.collector.httpStatus} ${String(status.collector.body.status ?? status.collector.body.error ?? "unavailable")} ${numeric3(collectorStorage.affectedPoints)} affected points`
|
|
8051
8155
|
);
|
|
8052
|
-
const providerMetrics =
|
|
8156
|
+
const providerMetrics = record6(status.provider.body.metrics) ? status.provider.body.metrics : {};
|
|
8053
8157
|
out.log(
|
|
8054
|
-
`cloudflare ${status.provider.httpStatus} ${String(status.provider.body.status ?? status.provider.body.error ?? "unavailable")} ${
|
|
8158
|
+
`cloudflare ${status.provider.httpStatus} ${String(status.provider.body.status ?? status.provider.body.error ?? "unavailable")} ${numeric3(providerMetrics.requests)} invocations ${numeric3(providerMetrics.errors)} runtime errors`
|
|
8055
8159
|
);
|
|
8056
8160
|
out.log(
|
|
8057
8161
|
`verdict ${status.verdict.status} ${status.verdict.reasons.map((reason) => `${reason.source}:${reason.code}`).join(", ") || "all required signals healthy"}`
|
|
8058
8162
|
);
|
|
8059
8163
|
}
|
|
8060
|
-
function
|
|
8164
|
+
function liveSyncLine(read3) {
|
|
8165
|
+
const performance = record6(read3.body.performance) ? read3.body.performance : {};
|
|
8166
|
+
const commitToSend = record6(performance.commitToSend) ? performance.commitToSend : {};
|
|
8167
|
+
return `live-sync ${read3.httpStatus} ${String(read3.body.status ?? read3.body.error ?? "unavailable")} ${numeric3(read3.body.activeConnections)} active ${optionalNumeric(commitToSend.p95)} commit-to-send p95 ${numeric3(performance.sendFailures)} send failures`;
|
|
8168
|
+
}
|
|
8169
|
+
function record6(value) {
|
|
8061
8170
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8062
8171
|
}
|
|
8063
|
-
function
|
|
8172
|
+
function numeric3(value) {
|
|
8064
8173
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
8065
8174
|
}
|
|
8066
8175
|
function optionalNumeric(value) {
|
|
@@ -8493,6 +8602,7 @@ var PM_ENTITIES = Object.fromEntries(
|
|
|
8493
8602
|
["goal", "conformance", "task", "kanban", "decision", "bug"].map((entity) => [entity, PM_ACTIONS])
|
|
8494
8603
|
);
|
|
8495
8604
|
var COMMAND_SURFACE = {
|
|
8605
|
+
agent: { jobs: {}, retry: {} },
|
|
8496
8606
|
admin: {
|
|
8497
8607
|
ai: {
|
|
8498
8608
|
show: {},
|
|
@@ -10308,6 +10418,10 @@ async function runCli(argv = process.argv.slice(2), dependencies = {}) {
|
|
|
10308
10418
|
await adminCommand(parsed);
|
|
10309
10419
|
return;
|
|
10310
10420
|
}
|
|
10421
|
+
if (command === "agent") {
|
|
10422
|
+
await agentCommand(parsed, runtime);
|
|
10423
|
+
return;
|
|
10424
|
+
}
|
|
10311
10425
|
if (command === "app") {
|
|
10312
10426
|
await appCommand(parsed, runtime);
|
|
10313
10427
|
return;
|