@runtypelabs/cli 2.44.1 → 2.46.0
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 +45 -4
- package/dist/index.js +758 -66
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -644,6 +644,7 @@ Manage agents.
|
|
|
644
644
|
| `agents execute <id> -m <message>` | Execute an agent with a message |
|
|
645
645
|
| `agents task <agent> -g <goal>` | Run a multi-session task (see Marathon section) |
|
|
646
646
|
| `agents ensure <file>` | Converge a definition from a file |
|
|
647
|
+
| `agents pull <name>` | Print the definition file `ensure` reads |
|
|
647
648
|
| `agents delete <id>` | Delete an agent |
|
|
648
649
|
|
|
649
650
|
`agents execute` and `dispatch --agent` take `--alias <name>` or `--version-id <id>`
|
|
@@ -677,7 +678,48 @@ runtype agents aliases activate agent_abc live --version-id agentver_9 --revisio
|
|
|
677
678
|
|
|
678
679
|
`agents ensure <file> --deploy <alias>` saves and activates in one step;
|
|
679
680
|
`--release publish|none` is the older spelling of the same thing, and supplying both
|
|
680
|
-
is refused. A non-live deploy leaves live unchanged.
|
|
681
|
+
is refused. A non-live deploy leaves live unchanged. `--bindings-file <file>` (a JSON
|
|
682
|
+
object of `{ SECRET_NAME: value }`) replaces the secret bindings of the `--deploy`
|
|
683
|
+
alias; `{}` clears them. Keep that file out of version control.
|
|
684
|
+
|
|
685
|
+
### Config as code: `ensure` and `pull`
|
|
686
|
+
|
|
687
|
+
Each entity the TypeScript SDK converges has an `ensure` and a `pull` subcommand.
|
|
688
|
+
`pull` prints the definition file that `ensure` reads (stdout, or `-o <file>`), so
|
|
689
|
+
`runtype flows pull "Triage" > flows/triage.json` absorbs a dashboard edit into the repo.
|
|
690
|
+
|
|
691
|
+
| Command | Definition file |
|
|
692
|
+
| --------------------------------------------- | ------------------------------ |
|
|
693
|
+
| `agents ensure` / `agents pull` | `defineAgent` input + `skills` |
|
|
694
|
+
| `agents skills ensure` / `agents skills pull` | `defineSkill` input |
|
|
695
|
+
| `flows ensure` / `flows pull` | `defineFlow` input |
|
|
696
|
+
| `tools ensure` / `tools pull` | `defineTool` input |
|
|
697
|
+
| `products ensure` / `products pull` | `defineProduct` input |
|
|
698
|
+
| `surfaces ensure` / `surfaces pull --product` | `defineSurface` input |
|
|
699
|
+
| `products ensure-fpo` / `products pull-fpo` | Full Product Object |
|
|
700
|
+
|
|
701
|
+
`agents skills` manages Runtype Agent Skills, the context bundles deployed agents
|
|
702
|
+
load. `runtype skills` installs skills into local coding agents; it is unrelated.
|
|
703
|
+
|
|
704
|
+
Every `ensure` takes `--dry-run`, `--expect-no-changes` (exit 1 on drift, for CI),
|
|
705
|
+
`--overwrite-managed` (converge over a dashboard edit) and
|
|
706
|
+
`--expected-remote-hash <hash>`. A dry run prints the remote hash; pass it to the
|
|
707
|
+
apply so the write is refused if someone changed the entity in between:
|
|
708
|
+
|
|
709
|
+
```bash
|
|
710
|
+
runtype flows ensure flows/triage.json --dry-run
|
|
711
|
+
runtype flows ensure flows/triage.json --expected-remote-hash <remoteHash>
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
Agents, skills and flows also take `--release none|publish` (agents: `--deploy`) and
|
|
715
|
+
`--label` / `--notes`, which stamp provenance such as a git SHA on the appended
|
|
716
|
+
version. `products ensure-fpo` also takes `--prune`, which removes product-scoped
|
|
717
|
+
capabilities and surfaces the FPO no longer declares, and `--hash-probe`, which
|
|
718
|
+
skips the upload when the live product already matches (useful for a pulled FPO).
|
|
719
|
+
|
|
720
|
+
`client-tokens ensure <file>` converges a client token by name (`--dry-run`,
|
|
721
|
+
`--expect-no-changes`, `--json`). It has no `pull`, and it never deletes or
|
|
722
|
+
regenerates a token.
|
|
681
723
|
|
|
682
724
|
##### Cleaning up a pull request's preview
|
|
683
725
|
|
|
@@ -701,9 +743,8 @@ jobs:
|
|
|
701
743
|
RUNTYPE_API_KEY: ${{ secrets.RUNTYPE_API_KEY }}
|
|
702
744
|
```
|
|
703
745
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
frees the quota immediately.
|
|
746
|
+
There is no limit on how many preview aliases an agent or organization holds.
|
|
747
|
+
Archiving on pull-request close, as above, and the 14-day expiry retire them.
|
|
707
748
|
|
|
708
749
|
#### Promoting between organizations
|
|
709
750
|
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { Box, Text, render, useApp, useInput, useStdout } from "ink";
|
|
|
11
11
|
import open from "open";
|
|
12
12
|
import crypto$1 from "crypto";
|
|
13
13
|
import express from "express";
|
|
14
|
-
import {
|
|
14
|
+
import { ClientTokensNamespace, DEFAULT_STALL_STOP_AFTER, ENSURE_RELEASE_DEPLOY_CONFLICT_MESSAGE, FlowsNamespace, LEDGER_ARTIFACT_LINE_PREFIX, ProductsNamespace, RuntypeApiError, RuntypeClient, SkillsNamespace, SurfacesNamespace, ToolsNamespace, activateAgentPromotion, buildLedgerOffloadReference, compileWorkflowConfig, createClient, defaultWorkflow, defineFlow, defineFpo, defineProduct, defineSkill, defineSurface, defineTool, deployWorkflow, ensureDefaultWorkflowHooks, evaluateGeneratedRuntimeToolProposal, gameWorkflow, getDefaultPlanPath, isBuiltinToolExecuteResponse, isWorkflowHookRef, parseFinalBuffer, parseLedgerArtifactRelativePath, parseOffloadedOutputId, parseSSEChunk, prepareAgentPromotion, processStream, registerWorkflowHook, validateAgentPromotion } from "@runtypelabs/sdk";
|
|
15
15
|
import Conf from "conf";
|
|
16
16
|
import os, { tmpdir } from "os";
|
|
17
17
|
import path$1, { delimiter, dirname as dirname$1, join as join$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
|
|
@@ -3318,6 +3318,114 @@ function handleIntersectionResults(result, left, right) {
|
|
|
3318
3318
|
result.value = merged.data;
|
|
3319
3319
|
return result;
|
|
3320
3320
|
}
|
|
3321
|
+
const $ZodTuple = /*@__PURE__*/ $constructor("$ZodTuple", (inst, def) => {
|
|
3322
|
+
$ZodType.init(inst, def);
|
|
3323
|
+
const items = def.items;
|
|
3324
|
+
const memo = globalConfig.memoizer;
|
|
3325
|
+
memo?.attach(inst);
|
|
3326
|
+
inst._zod.parse = (payload, ctx) => {
|
|
3327
|
+
const input = payload.value;
|
|
3328
|
+
if (!Array.isArray(input)) {
|
|
3329
|
+
payload.issues.push({
|
|
3330
|
+
input,
|
|
3331
|
+
inst,
|
|
3332
|
+
expected: "tuple",
|
|
3333
|
+
code: "invalid_type"
|
|
3334
|
+
});
|
|
3335
|
+
return payload;
|
|
3336
|
+
}
|
|
3337
|
+
payload.value = memo ? memo.alloc(inst, payload, [], ctx) : [];
|
|
3338
|
+
const proms = [];
|
|
3339
|
+
const optinStart = getTupleOptStart(items, "optin");
|
|
3340
|
+
const optoutStart = getTupleOptStart(items, "optout");
|
|
3341
|
+
if (!def.rest) {
|
|
3342
|
+
if (input.length < optinStart) {
|
|
3343
|
+
payload.issues.push({
|
|
3344
|
+
code: "too_small",
|
|
3345
|
+
minimum: optinStart,
|
|
3346
|
+
inclusive: true,
|
|
3347
|
+
input,
|
|
3348
|
+
inst,
|
|
3349
|
+
origin: "array"
|
|
3350
|
+
});
|
|
3351
|
+
return payload;
|
|
3352
|
+
}
|
|
3353
|
+
if (input.length > items.length) payload.issues.push({
|
|
3354
|
+
code: "too_big",
|
|
3355
|
+
maximum: items.length,
|
|
3356
|
+
inclusive: true,
|
|
3357
|
+
input,
|
|
3358
|
+
inst,
|
|
3359
|
+
origin: "array"
|
|
3360
|
+
});
|
|
3361
|
+
}
|
|
3362
|
+
const itemResults = new Array(items.length);
|
|
3363
|
+
const abortEarly = def.rest ? ctx?.abortEarly : void 0;
|
|
3364
|
+
let itemAborted = false;
|
|
3365
|
+
for (let i = 0; i < items.length; i++) {
|
|
3366
|
+
const r = items[i]._zod.run({
|
|
3367
|
+
value: input[i],
|
|
3368
|
+
issues: []
|
|
3369
|
+
}, ctx);
|
|
3370
|
+
if (r instanceof Promise) proms.push(r.then((rr) => {
|
|
3371
|
+
itemResults[i] = rr;
|
|
3372
|
+
}));
|
|
3373
|
+
else {
|
|
3374
|
+
itemResults[i] = r;
|
|
3375
|
+
if (abortEarly && !itemAborted && r.issues.length) itemAborted = aborted(r);
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
if (def.rest && !itemAborted) {
|
|
3379
|
+
let i = items.length - 1;
|
|
3380
|
+
const rest = input.slice(items.length);
|
|
3381
|
+
let seen = payload.issues.length;
|
|
3382
|
+
for (const el of rest) {
|
|
3383
|
+
if (abortEarly && payload.issues.length !== seen) {
|
|
3384
|
+
if (aborted(payload, seen)) break;
|
|
3385
|
+
seen = payload.issues.length;
|
|
3386
|
+
}
|
|
3387
|
+
i++;
|
|
3388
|
+
const result = def.rest._zod.run({
|
|
3389
|
+
value: el,
|
|
3390
|
+
issues: []
|
|
3391
|
+
}, ctx);
|
|
3392
|
+
if (result instanceof Promise) proms.push(result.then((r) => handleTupleResult(r, payload, i)));
|
|
3393
|
+
else handleTupleResult(result, payload, i);
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
if (proms.length) return Promise.all(proms).then(() => handleTupleResults(itemResults, payload, items, input, optoutStart));
|
|
3397
|
+
return handleTupleResults(itemResults, payload, items, input, optoutStart);
|
|
3398
|
+
};
|
|
3399
|
+
});
|
|
3400
|
+
function getTupleOptStart(items, key) {
|
|
3401
|
+
for (let i = items.length - 1; i >= 0; i--) if (!(key === "optin" ? items[i]._zod.optin !== void 0 : items[i]._zod.optout === "optional")) return i + 1;
|
|
3402
|
+
return 0;
|
|
3403
|
+
}
|
|
3404
|
+
function handleTupleResult(result, final, index) {
|
|
3405
|
+
if (result.issues.length) final.issues.push(...prefixIssues(index, result.issues));
|
|
3406
|
+
final.value[index] = result.value;
|
|
3407
|
+
}
|
|
3408
|
+
function handleTupleResults(itemResults, final, items, input, optoutStart) {
|
|
3409
|
+
for (let i = 0; i < items.length; i++) {
|
|
3410
|
+
const r = itemResults[i];
|
|
3411
|
+
const isPresent = i < input.length;
|
|
3412
|
+
if (!isPresent && i >= optoutStart && items[i]._zod.optin === "optional") {
|
|
3413
|
+
final.value.length = i;
|
|
3414
|
+
break;
|
|
3415
|
+
}
|
|
3416
|
+
if (r.issues.length) {
|
|
3417
|
+
if (!isPresent && i >= optoutStart) {
|
|
3418
|
+
final.value.length = i;
|
|
3419
|
+
break;
|
|
3420
|
+
}
|
|
3421
|
+
final.issues.push(...prefixIssues(i, r.issues));
|
|
3422
|
+
}
|
|
3423
|
+
final.value[i] = r.value;
|
|
3424
|
+
}
|
|
3425
|
+
for (let i = final.value.length - 1; i >= input.length; i--) if (items[i]._zod.optout === "optional" && final.value[i] === void 0) final.value.length = i;
|
|
3426
|
+
else break;
|
|
3427
|
+
return final;
|
|
3428
|
+
}
|
|
3321
3429
|
const $ZodRecord = /*@__PURE__*/ $constructor("$ZodRecord", (inst, def) => {
|
|
3322
3430
|
$ZodType.init(inst, def);
|
|
3323
3431
|
const memo = globalConfig.memoizer;
|
|
@@ -5394,6 +5502,58 @@ const intersectionProcessor = (schema, ctx, json, params) => {
|
|
|
5394
5502
|
json.allOf = allOf;
|
|
5395
5503
|
ctx.intersections.push(allOf);
|
|
5396
5504
|
};
|
|
5505
|
+
const tupleProcessor = (schema, ctx, _json, params) => {
|
|
5506
|
+
const json = _json;
|
|
5507
|
+
const def = schema._zod.def;
|
|
5508
|
+
json.type = "array";
|
|
5509
|
+
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
5510
|
+
const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
5511
|
+
const prefixItems = def.items.map((x, i) => processSchema(x, ctx, {
|
|
5512
|
+
...params,
|
|
5513
|
+
path: [
|
|
5514
|
+
...params.path,
|
|
5515
|
+
prefixPath,
|
|
5516
|
+
i
|
|
5517
|
+
]
|
|
5518
|
+
}));
|
|
5519
|
+
const rest = def.rest ? processSchema(def.rest, ctx, {
|
|
5520
|
+
...params,
|
|
5521
|
+
path: [
|
|
5522
|
+
...params.path,
|
|
5523
|
+
restPath,
|
|
5524
|
+
...ctx.target === "openapi-3.0" ? [def.items.length] : []
|
|
5525
|
+
]
|
|
5526
|
+
}) : null;
|
|
5527
|
+
let minItems = def.items.length;
|
|
5528
|
+
while (minItems > 0) {
|
|
5529
|
+
const item = def.items[minItems - 1];
|
|
5530
|
+
if (!(ctx.io === "input" ? inputOptin(item) !== void 0 : item._zod.optout === "optional")) break;
|
|
5531
|
+
minItems--;
|
|
5532
|
+
}
|
|
5533
|
+
const maxItems = def.items.length;
|
|
5534
|
+
const isClosed = !def.rest;
|
|
5535
|
+
if (ctx.target === "draft-2020-12") {
|
|
5536
|
+
json.prefixItems = prefixItems;
|
|
5537
|
+
if (isClosed) json.items = false;
|
|
5538
|
+
else if (rest) json.items = rest;
|
|
5539
|
+
if (minItems > 0) json.minItems = minItems;
|
|
5540
|
+
if (isClosed) json.maxItems = maxItems;
|
|
5541
|
+
} else if (ctx.target === "openapi-3.0") {
|
|
5542
|
+
json.items = { anyOf: prefixItems };
|
|
5543
|
+
if (rest) json.items.anyOf.push(rest);
|
|
5544
|
+
if (minItems > 0) json.minItems = minItems;
|
|
5545
|
+
if (isClosed) json.maxItems = maxItems;
|
|
5546
|
+
} else {
|
|
5547
|
+
json.items = prefixItems;
|
|
5548
|
+
if (isClosed) json.additionalItems = false;
|
|
5549
|
+
else if (rest) json.additionalItems = rest;
|
|
5550
|
+
if (minItems > 0) json.minItems = minItems;
|
|
5551
|
+
if (isClosed) json.maxItems = maxItems;
|
|
5552
|
+
}
|
|
5553
|
+
const { minimum, maximum } = aggregateChecks(schema);
|
|
5554
|
+
if (typeof minimum === "number") json.minItems = minimum;
|
|
5555
|
+
if (typeof maximum === "number") json.maxItems = maximum;
|
|
5556
|
+
};
|
|
5397
5557
|
/** JSON object keys are always strings, so a numeric record key schema is re-expressed over the
|
|
5398
5558
|
* numeric-string form the record parser matches. Deferred to `finalize`, after the flatten: a key
|
|
5399
5559
|
* behind a wrapper only carries its own `type` before then, and a union key only has its branches.
|
|
@@ -6315,6 +6475,39 @@ function intersection(left, right) {
|
|
|
6315
6475
|
right
|
|
6316
6476
|
});
|
|
6317
6477
|
}
|
|
6478
|
+
const ZodTuple = /*@__PURE__*/ $constructor("ZodTuple", (inst, def) => {
|
|
6479
|
+
_ensureDefaultMemoizer();
|
|
6480
|
+
$ZodTuple.init(inst, def);
|
|
6481
|
+
ZodType.init(inst, def);
|
|
6482
|
+
inst._zod.processJSONSchema = (ctx, json, params) => tupleProcessor(inst, ctx, json, params);
|
|
6483
|
+
}, {
|
|
6484
|
+
rest(rest) {
|
|
6485
|
+
return this.clone({
|
|
6486
|
+
...this._zod.def,
|
|
6487
|
+
rest
|
|
6488
|
+
});
|
|
6489
|
+
},
|
|
6490
|
+
partial() {
|
|
6491
|
+
const def = this._zod.def;
|
|
6492
|
+
if (def.checks?.length) throw new Error(".partial() cannot be used on tuple schemas containing refinements");
|
|
6493
|
+
return this.clone({
|
|
6494
|
+
...def,
|
|
6495
|
+
items: def.items.map((item) => new ZodOptional({
|
|
6496
|
+
type: "optional",
|
|
6497
|
+
innerType: item
|
|
6498
|
+
}))
|
|
6499
|
+
});
|
|
6500
|
+
}
|
|
6501
|
+
});
|
|
6502
|
+
function tuple(items, _paramsOrRest, _params) {
|
|
6503
|
+
const hasRest = _paramsOrRest instanceof $ZodType;
|
|
6504
|
+
return new ZodTuple({
|
|
6505
|
+
type: "tuple",
|
|
6506
|
+
items,
|
|
6507
|
+
rest: hasRest ? _paramsOrRest : null,
|
|
6508
|
+
...normalizeParams(hasRest ? _params : _paramsOrRest)
|
|
6509
|
+
});
|
|
6510
|
+
}
|
|
6318
6511
|
const ZodRecord = /*@__PURE__*/ $constructor("ZodRecord", (inst, def) => {
|
|
6319
6512
|
_ensureDefaultMemoizer();
|
|
6320
6513
|
$ZodRecord.init(inst, def);
|
|
@@ -8041,6 +8234,19 @@ const RUN_COMPLETENESS_VALUES = [
|
|
|
8041
8234
|
/** Which lane produced the header: a Runtype-hosted run, or one exported into ingest. */
|
|
8042
8235
|
const RUN_SOURCES = ["hosted", "ingested"];
|
|
8043
8236
|
const RUN_KINDS = ["agent", "flow"];
|
|
8237
|
+
const runViaSchema = object({
|
|
8238
|
+
kind: _enum([
|
|
8239
|
+
"schedule",
|
|
8240
|
+
"surface",
|
|
8241
|
+
"api",
|
|
8242
|
+
"mcp",
|
|
8243
|
+
"cli",
|
|
8244
|
+
"test",
|
|
8245
|
+
"eval"
|
|
8246
|
+
]).describe("One word for the way the run started."),
|
|
8247
|
+
id: string().nullable().describe("The thing that started it: the schedule id (schedule), product surface id (surface), API key id (api), the acting user id (cli) or eval suite id (eval). Null for mcp and test, and for an api run whose header predates key stamping."),
|
|
8248
|
+
label: string().nullable().describe("Its display name when the API could resolve one: schedule name, surface name, API key name as it was at dispatch, MCP client name, the acting user's first name, or eval suite name. Null when unresolved, for example a deleted schedule.")
|
|
8249
|
+
});
|
|
8044
8250
|
/** The sentence every overlay field repeats, so absence is never read as `false`. */
|
|
8045
8251
|
const ABSENT = "Absent means the overlay did not answer, which is not false and not null.";
|
|
8046
8252
|
const str = (d) => string().nullable().describe(d);
|
|
@@ -8085,6 +8291,7 @@ object({
|
|
|
8085
8291
|
inlineDefinition: boolean().nullable().describe("True when the dispatch carried a complete inline agent definition (name and model) beside or instead of an agentId, so the run executed that inline config and no stored version; such a row stamps no agentVersionId. False when the run executed the saved definition by reference. NULL when the header states nothing: a flow run, a lane that resolves no agent definition, or a row that predates the field."),
|
|
8086
8292
|
requestedFlowId: string().max(128).nullish().describe(`The flow.id an inline /v1/dispatch named when the caller's organization does not own a flow with that id. Such a run executes as the "virtual" flow, so flowId reads "virtual" and this field carries the caller's id (control characters stripped, cut to 128 characters) so the caller can still recognise its runs. It is a label only: it grants nothing, is never used as a sender or resume identity, and is not a list filter; filter with flowId=virtual and match on this field. Absent when the run executed under the id it named.`),
|
|
8087
8293
|
test: boolean().nullable().meta({ deprecated: true }).describe("DEPRECATED: a derived alias of origin, kept for one release — read origin instead. True when the run came from the dashboard, i.e. origin === \"dashboard\"; NULL exactly when origin is NULL. It is never derived from storeResults, which defaults to false across the whole flow lane and which an API caller sets for its own privacy reasons: reading it as \"test\" labelled production runs as tests. A run whose header predates origin therefore reads NULL rather than claiming either answer."),
|
|
8294
|
+
via: runViaSchema.nullish().describe("How the run started, for a Via column. Forward-only for schedule, api key, cli, mcp and eval detail: a header written before those stamps reads null, or falls back to the coarse origin/surface facts when they alone answer it. A label is a snapshot or a lookup and never authority; mcp, cli and sdk are self-declared by the client."),
|
|
8088
8295
|
stopReason: string().nullish().describe("Stop reason from the terminal frame."),
|
|
8089
8296
|
iterations: number$1().nullish().describe("Loop iterations from the terminal frame."),
|
|
8090
8297
|
inputPreview: string().nullish().describe(`The run's first user message, secret-scrubbed and cut to 160 characters, so a list can say what a run was asked to do without reading content. Absent means no input text is recorded for this run, which is not an empty message: the span store holds no run-level input at all, so a run with no control-plane row (a flow leg, a storeResults:false run) has none.`),
|
|
@@ -8135,6 +8342,8 @@ function runListCompactOmitSentence() {
|
|
|
8135
8342
|
const RUN_LIST_VIEWS = ["compact", "full"];
|
|
8136
8343
|
/** Whether the control-plane overlay runs for this page. */
|
|
8137
8344
|
const RUN_LIST_OVERLAY_MODES = ["live", "none"];
|
|
8345
|
+
/** The most runs `includeCount` counts: a bounded probe, never a window-wide COUNT(*). */
|
|
8346
|
+
const RUN_LIST_COUNT_CAP = 1e3;
|
|
8138
8347
|
/**
|
|
8139
8348
|
* The three statuses a header COLUMN can answer. `queued`, `awaiting` and
|
|
8140
8349
|
* `cancelled` are overlay-only, and filtering after the overlay would drop rows
|
|
@@ -8167,7 +8376,8 @@ object({
|
|
|
8167
8376
|
view: _enum(RUN_LIST_VIEWS).optional().describe(runListCompactOmitSentence()),
|
|
8168
8377
|
overlay: _enum(RUN_LIST_OVERLAY_MODES).optional().describe("live adds control-plane fields; defaults to live for a session, none for an API key."),
|
|
8169
8378
|
limit: string().optional().describe(`Page size, at most 100.`),
|
|
8170
|
-
cursor: string().optional().describe("Keyset cursor from a previous page.")
|
|
8379
|
+
cursor: string().optional().describe("Keyset cursor from a previous page."),
|
|
8380
|
+
includeCount: string().optional().describe(`"true" adds pagination.totalCount and pagination.totalCountCapped: the number of runs matching this request's filters within its window, ignoring the cursor and limit, counted up to ${RUN_LIST_COUNT_CAP}. Past that the count stops at ${RUN_LIST_COUNT_CAP} and totalCountCapped is true, so read it as "more than ${RUN_LIST_COUNT_CAP}". Omitted, or any other value, the response carries neither field.`)
|
|
8171
8381
|
});
|
|
8172
8382
|
object({
|
|
8173
8383
|
kind: _enum([
|
|
@@ -12478,7 +12688,48 @@ const GEN_AI_ATTRIBUTES = {
|
|
|
12478
12688
|
toolCallId: "gen_ai.tool.call.id"
|
|
12479
12689
|
};
|
|
12480
12690
|
//#endregion
|
|
12481
|
-
//#region ../shared/dist/
|
|
12691
|
+
//#region ../shared/dist/context-manifest-wire.mjs
|
|
12692
|
+
const entryTupleSchema = union([tuple([
|
|
12693
|
+
string(),
|
|
12694
|
+
number$1().int(),
|
|
12695
|
+
number$1().int(),
|
|
12696
|
+
number$1().int(),
|
|
12697
|
+
number$1().int(),
|
|
12698
|
+
number$1().int(),
|
|
12699
|
+
number$1().int()
|
|
12700
|
+
]), tuple([
|
|
12701
|
+
string(),
|
|
12702
|
+
number$1().int(),
|
|
12703
|
+
number$1().int(),
|
|
12704
|
+
number$1().int(),
|
|
12705
|
+
number$1().int(),
|
|
12706
|
+
number$1().int(),
|
|
12707
|
+
number$1().int(),
|
|
12708
|
+
string(),
|
|
12709
|
+
string()
|
|
12710
|
+
])]);
|
|
12711
|
+
object({
|
|
12712
|
+
v: literal(1),
|
|
12713
|
+
s: number$1().int().nonnegative(),
|
|
12714
|
+
e: array(entryTupleSchema),
|
|
12715
|
+
b: array(number$1().int().nonnegative()),
|
|
12716
|
+
x: array(tuple([entryTupleSchema, number$1().int()]))
|
|
12717
|
+
});
|
|
12718
|
+
object({
|
|
12719
|
+
v: literal(1),
|
|
12720
|
+
b: array(tuple([
|
|
12721
|
+
string(),
|
|
12722
|
+
string(),
|
|
12723
|
+
union([literal(0), literal(1)])
|
|
12724
|
+
]))
|
|
12725
|
+
});
|
|
12726
|
+
object({
|
|
12727
|
+
hash: string(),
|
|
12728
|
+
content: string(),
|
|
12729
|
+
truncated: boolean()
|
|
12730
|
+
});
|
|
12731
|
+
//#endregion
|
|
12732
|
+
//#region ../shared/dist/chunk-_ZhmfUpr.mjs
|
|
12482
12733
|
/**
|
|
12483
12734
|
* GenAI semconv content attributes, set by a customer's own instrumentation when it opts into content export.
|
|
12484
12735
|
* Two first-party producers write them: @runtypelabs/runtime's OTLP tracer for exported agents (content on by default), and @runtypelabs/flue-otel (opt-in).
|
|
@@ -12498,7 +12749,9 @@ const GEN_AI_CONTENT_ATTRIBUTES = {
|
|
|
12498
12749
|
/** `execute_tool` span: the arguments the tool was called with. */
|
|
12499
12750
|
toolCallArguments: GEN_AI_TOOL_CALL_ARGUMENTS,
|
|
12500
12751
|
/** `execute_tool` span: the result the tool returned. */
|
|
12501
|
-
toolCallResult: GEN_AI_TOOL_CALL_RESULT
|
|
12752
|
+
toolCallResult: GEN_AI_TOOL_CALL_RESULT,
|
|
12753
|
+
/** `chat` span: the tool schemas the call offered the model. */
|
|
12754
|
+
toolDefinitions: "gen_ai.tool.definitions"
|
|
12502
12755
|
};
|
|
12503
12756
|
/**
|
|
12504
12757
|
* Read-side aliases for content attributes: canonical semconv name to vendor-namespaced spellings a stock instrumentation uses.
|
|
@@ -14454,6 +14707,39 @@ function unknownCatalogToolMessage(toolId) {
|
|
|
14454
14707
|
return `Tool "${toolId}" is not a built-in or platform catalog tool. Check the id against the tool catalog (list_tools), or point toolId at a saved tool's "tool_..." id or its "tool:<name>" reference.`;
|
|
14455
14708
|
}
|
|
14456
14709
|
/**
|
|
14710
|
+
* Name-based references to saved agents and flows (agent:<name>, flow:<name>) for portable ensure/config-as-code definitions.
|
|
14711
|
+
* The name is canonical: hashed and persisted verbatim so definitions converge to the same content hash across environments.
|
|
14712
|
+
* Grammar mirrors parseNamedToolRef: split on the first colon only, exact case-sensitive match, no trimming.
|
|
14713
|
+
* The flow: prefix also names a template-variable namespace, but the two grammars never collide.
|
|
14714
|
+
*/
|
|
14715
|
+
/** Reserved prefix for a name-based saved-agent reference (`agent:<name>`). */
|
|
14716
|
+
const NAMED_AGENT_REF_PREFIX = "agent:";
|
|
14717
|
+
/** Reserved prefix for a name-based saved-flow reference (`flow:<name>`). */
|
|
14718
|
+
const NAMED_FLOW_REF_PREFIX = "flow:";
|
|
14719
|
+
function isNamedRef(value, prefix) {
|
|
14720
|
+
return value.startsWith(prefix) && value.length > prefix.length;
|
|
14721
|
+
}
|
|
14722
|
+
function parseNamedRef(value, prefix) {
|
|
14723
|
+
if (!isNamedRef(value, prefix)) return null;
|
|
14724
|
+
return { name: value.slice(prefix.length) };
|
|
14725
|
+
}
|
|
14726
|
+
/**
|
|
14727
|
+
* Parse an `agent:<name>` reference into its name. Split on the FIRST colon
|
|
14728
|
+
* only; the remainder is the agent name verbatim. Returns null when `value` is
|
|
14729
|
+
* not a valid named agent ref.
|
|
14730
|
+
*/
|
|
14731
|
+
function parseNamedAgentRef(value) {
|
|
14732
|
+
return parseNamedRef(value, NAMED_AGENT_REF_PREFIX);
|
|
14733
|
+
}
|
|
14734
|
+
/**
|
|
14735
|
+
* Parse a `flow:<name>` reference into its name. Split on the FIRST colon only;
|
|
14736
|
+
* the remainder is the flow name verbatim. Returns null when `value` is not a
|
|
14737
|
+
* valid named flow ref.
|
|
14738
|
+
*/
|
|
14739
|
+
function parseNamedFlowRef(value) {
|
|
14740
|
+
return parseNamedRef(value, NAMED_FLOW_REF_PREFIX);
|
|
14741
|
+
}
|
|
14742
|
+
/**
|
|
14457
14743
|
* Return whether an `execute-agent` step's `config.agentId` is a template
|
|
14458
14744
|
* expression rather than a fixed agent reference. No static gate can scan the
|
|
14459
14745
|
* agent a `{{...}}` ref substitutes to, so the runtime lane folds it as
|
|
@@ -14499,10 +14785,12 @@ function walkSteps$1(steps, basePath, context, state) {
|
|
|
14499
14785
|
}
|
|
14500
14786
|
function walkStep(node, path, context, state) {
|
|
14501
14787
|
if (!isRecord$1(node)) return;
|
|
14502
|
-
if (node.enabled === false) return;
|
|
14788
|
+
if (node.enabled === false && !state.includeDisabled) return;
|
|
14503
14789
|
const config = isRecord$1(node.config) ? node.config : void 0;
|
|
14504
14790
|
if (!config) return;
|
|
14791
|
+
state.visitor.visitStep?.(node, config, path, context);
|
|
14505
14792
|
if (node.type === "execute-agent") state.visitor.visitExecuteAgent(node, config, path, context);
|
|
14793
|
+
visitFlowFallbacks(config, path, context, state);
|
|
14506
14794
|
walkToolPayloads(config.tools, `${path}.config.tools`, context, state);
|
|
14507
14795
|
const deeper = {
|
|
14508
14796
|
...state,
|
|
@@ -14524,6 +14812,21 @@ function walkStep(node, path, context, state) {
|
|
|
14524
14812
|
});
|
|
14525
14813
|
for (const key of CONDITIONAL_BRANCH_STEP_KEYS$2) walkSteps$1(config[key], `${path}.config.${key}`, context, nested);
|
|
14526
14814
|
}
|
|
14815
|
+
function visitFlowFallbacks(config, path, context, state) {
|
|
14816
|
+
const { visitFlowFallback, visitStepFallback } = state.visitor;
|
|
14817
|
+
if (!visitFlowFallback && !visitStepFallback || !isRecord$1(config.errorHandling)) return;
|
|
14818
|
+
const fallbacks = config.errorHandling.fallbacks;
|
|
14819
|
+
if (!Array.isArray(fallbacks)) return;
|
|
14820
|
+
fallbacks.forEach((fallback, index) => {
|
|
14821
|
+
if (!isRecord$1(fallback)) return;
|
|
14822
|
+
const fallbackPath = `${path}.config.errorHandling.fallbacks[${index}]`;
|
|
14823
|
+
if (fallback.type === "flow") visitFlowFallback?.(fallback, fallbackPath, context);
|
|
14824
|
+
else if (fallback.type === "step" && visitStepFallback && isRecord$1(fallback.stepConfig)) {
|
|
14825
|
+
visitStepFallback(fallback, fallback.stepConfig, fallbackPath, context);
|
|
14826
|
+
walkToolPayloads(fallback.stepConfig.tools, `${fallbackPath}.stepConfig.tools`, context, state);
|
|
14827
|
+
}
|
|
14828
|
+
});
|
|
14829
|
+
}
|
|
14527
14830
|
const TOOL_BAG_KEYS = ["runtimeTools", "inlineTools"];
|
|
14528
14831
|
/** Descend the inline flow-as-tool and skill payloads a step's tool bag can carry. */
|
|
14529
14832
|
function walkToolPayloads(tools, path, context, state) {
|
|
@@ -14546,7 +14849,7 @@ function walkRuntimeTools(runtimeTools, basePath, context, state) {
|
|
|
14546
14849
|
if (!config) return;
|
|
14547
14850
|
const toolPath = `${basePath}[${index}].config`;
|
|
14548
14851
|
if (isRecord$1(config.skill) && Array.isArray(config.skill.resolvedTools)) walkRuntimeTools(config.skill.resolvedTools, `${toolPath}.skill.resolvedTools`, context, deeper);
|
|
14549
|
-
if (isRecord$1(config.flow) && Array.isArray(config.flow.steps)) {
|
|
14852
|
+
if (!state.skipInlineFlows && isRecord$1(config.flow) && Array.isArray(config.flow.steps)) {
|
|
14550
14853
|
const nestedContext = state.visitor.enterNestedFlow?.(config.flow, context) ?? context;
|
|
14551
14854
|
walkSteps$1(config.flow.steps, `${toolPath}.flow.steps`, nestedContext, {
|
|
14552
14855
|
...deeper,
|
|
@@ -18560,39 +18863,6 @@ function normalizeMemorySummaryConfig(config) {
|
|
|
18560
18863
|
defaultValue: config.defaultValue ?? config.default_value
|
|
18561
18864
|
};
|
|
18562
18865
|
}
|
|
18563
|
-
/**
|
|
18564
|
-
* Name-based references to saved agents and flows (agent:<name>, flow:<name>) for portable ensure/config-as-code definitions.
|
|
18565
|
-
* The name is canonical: hashed and persisted verbatim so definitions converge to the same content hash across environments.
|
|
18566
|
-
* Grammar mirrors parseNamedToolRef: split on the first colon only, exact case-sensitive match, no trimming.
|
|
18567
|
-
* The flow: prefix also names a template-variable namespace, but the two grammars never collide.
|
|
18568
|
-
*/
|
|
18569
|
-
/** Reserved prefix for a name-based saved-agent reference (`agent:<name>`). */
|
|
18570
|
-
const NAMED_AGENT_REF_PREFIX = "agent:";
|
|
18571
|
-
/** Reserved prefix for a name-based saved-flow reference (`flow:<name>`). */
|
|
18572
|
-
const NAMED_FLOW_REF_PREFIX = "flow:";
|
|
18573
|
-
function isNamedRef(value, prefix) {
|
|
18574
|
-
return value.startsWith(prefix) && value.length > prefix.length;
|
|
18575
|
-
}
|
|
18576
|
-
function parseNamedRef(value, prefix) {
|
|
18577
|
-
if (!isNamedRef(value, prefix)) return null;
|
|
18578
|
-
return { name: value.slice(prefix.length) };
|
|
18579
|
-
}
|
|
18580
|
-
/**
|
|
18581
|
-
* Parse an `agent:<name>` reference into its name. Split on the FIRST colon
|
|
18582
|
-
* only; the remainder is the agent name verbatim. Returns null when `value` is
|
|
18583
|
-
* not a valid named agent ref.
|
|
18584
|
-
*/
|
|
18585
|
-
function parseNamedAgentRef(value) {
|
|
18586
|
-
return parseNamedRef(value, NAMED_AGENT_REF_PREFIX);
|
|
18587
|
-
}
|
|
18588
|
-
/**
|
|
18589
|
-
* Parse a `flow:<name>` reference into its name. Split on the FIRST colon only;
|
|
18590
|
-
* the remainder is the flow name verbatim. Returns null when `value` is not a
|
|
18591
|
-
* valid named flow ref.
|
|
18592
|
-
*/
|
|
18593
|
-
function parseNamedFlowRef(value) {
|
|
18594
|
-
return parseNamedRef(value, NAMED_FLOW_REF_PREFIX);
|
|
18595
|
-
}
|
|
18596
18866
|
function sanitizeCodeToolIdentifier(name) {
|
|
18597
18867
|
let safe = name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
18598
18868
|
if (/^[0-9]/.test(safe)) safe = "_" + safe;
|
|
@@ -22149,7 +22419,8 @@ _enum([
|
|
|
22149
22419
|
"saved_from_run",
|
|
22150
22420
|
"generated",
|
|
22151
22421
|
"imported",
|
|
22152
|
-
"fpo"
|
|
22422
|
+
"fpo",
|
|
22423
|
+
"ensure"
|
|
22153
22424
|
]);
|
|
22154
22425
|
/**
|
|
22155
22426
|
* Where a saved_from_run case was forked from.
|
|
@@ -22919,7 +23190,9 @@ const capabilitySchema = object({
|
|
|
22919
23190
|
flow: flowDefinitionSchema.optional(),
|
|
22920
23191
|
agent: union([agentDefinitionSchema, agentDefinitionSchemaV2]).optional(),
|
|
22921
23192
|
existingFlowId: string().optional(),
|
|
22922
|
-
existingAgentId: string().optional()
|
|
23193
|
+
existingAgentId: string().optional(),
|
|
23194
|
+
/** Whether the product link is enabled; omitted preserves the live value (new links start enabled). */
|
|
23195
|
+
enabled: boolean().optional()
|
|
22923
23196
|
}).refine((cap) => {
|
|
22924
23197
|
return [
|
|
22925
23198
|
cap.flow,
|
|
@@ -23098,6 +23371,11 @@ const fpoSkillCapabilitiesSchema = object({
|
|
|
23098
23371
|
inlineTools: array(fpoSkillInlineToolSchema).optional(),
|
|
23099
23372
|
mcpServers: never({ error: "Skill capabilities cannot declare `mcpServers` — MCP server bindings are rejected at write for v1 skills. Bind MCP tools via a saved tool id or an inlineTool instead." }).optional()
|
|
23100
23373
|
});
|
|
23374
|
+
/** A `bindTo` entry that pins the binding to one skill versionNumber instead of following published. */
|
|
23375
|
+
const fpoSkillBindTargetSchema = object({
|
|
23376
|
+
capability: string().min(1),
|
|
23377
|
+
version: number$1().int().positive().optional()
|
|
23378
|
+
}).strict();
|
|
23101
23379
|
const fpoSkillSchema = object({
|
|
23102
23380
|
id: string().min(1),
|
|
23103
23381
|
slug: string().regex(SKILL_NAME_PATTERN, "Skill slug must start with a letter and contain only lowercase letters, digits, underscores, and hyphens (max 64 chars).").optional(),
|
|
@@ -23110,7 +23388,7 @@ const fpoSkillSchema = object({
|
|
|
23110
23388
|
"community"
|
|
23111
23389
|
]).optional(),
|
|
23112
23390
|
capabilities: fpoSkillCapabilitiesSchema.optional(),
|
|
23113
|
-
bindTo: array(string()).optional(),
|
|
23391
|
+
bindTo: array(union([string(), fpoSkillBindTargetSchema])).optional(),
|
|
23114
23392
|
createPolicy: createPolicySchema
|
|
23115
23393
|
});
|
|
23116
23394
|
const metaSchema = object({
|
|
@@ -24099,8 +24377,17 @@ function validateFPO(fpo) {
|
|
|
24099
24377
|
if (!skill || typeof skill !== "object") continue;
|
|
24100
24378
|
const s = skill;
|
|
24101
24379
|
if (Array.isArray(s.bindTo)) {
|
|
24102
|
-
|
|
24103
|
-
|
|
24380
|
+
const boundRefs = /* @__PURE__ */ new Set();
|
|
24381
|
+
for (const [bi, entry] of s.bindTo.entries()) {
|
|
24382
|
+
const ref = entry && typeof entry === "object" ? entry.capability : entry;
|
|
24383
|
+
if (boundRefs.has(ref)) {
|
|
24384
|
+
result.errors.push(createIssue("error", "SKILL_BIND_DUPLICATE_CAPABILITY", `Skill "${id ?? ""}" binds to capability "${ref}" more than once; list each capability once with the version it should load`, `skills[${i}].bindTo[${bi}]`));
|
|
24385
|
+
continue;
|
|
24386
|
+
}
|
|
24387
|
+
boundRefs.add(ref);
|
|
24388
|
+
if (!capIds.has(ref)) result.errors.push(createIssue("error", "SKILL_BIND_CAPABILITY_NOT_FOUND", `Skill "${id ?? ""}" binds to capability "${ref}" which does not exist`, `skills[${i}].bindTo[${bi}]`));
|
|
24389
|
+
else if (!agentBackedCapIds.has(ref)) result.errors.push(createIssue("error", "SKILL_BIND_TARGET_NOT_AGENT", `Skill "${id ?? ""}" binds to capability "${ref}" which is not agent-backed; skills can only be bound to agent capabilities`, `skills[${i}].bindTo[${bi}]`));
|
|
24390
|
+
}
|
|
24104
24391
|
}
|
|
24105
24392
|
const capabilityRefs = s.capabilities && typeof s.capabilities === "object" ? s.capabilities.capabilityRefs : void 0;
|
|
24106
24393
|
if (Array.isArray(capabilityRefs)) {
|
|
@@ -27170,18 +27457,18 @@ const VERIFY_NEXT = "Ask the user for the 6-digit code from their email, then ru
|
|
|
27170
27457
|
function claimNext(email) {
|
|
27171
27458
|
return email ? `Send the verification email with: runtype auth claim ${email}` : "Ask the user for their email address, then run: runtype auth claim <email>";
|
|
27172
27459
|
}
|
|
27173
|
-
function fail$
|
|
27460
|
+
function fail$4(payload) {
|
|
27174
27461
|
console.error(JSON.stringify(payload, null, 2));
|
|
27175
27462
|
process.exit(1);
|
|
27176
27463
|
}
|
|
27177
27464
|
function failFromError(error, fallbackNext) {
|
|
27178
|
-
if (error instanceof AgentAuthError) fail$
|
|
27465
|
+
if (error instanceof AgentAuthError) fail$4({
|
|
27179
27466
|
status: "error",
|
|
27180
27467
|
code: error.code,
|
|
27181
27468
|
error: error.message,
|
|
27182
27469
|
...error.hint || fallbackNext ? { next: error.hint || fallbackNext } : {}
|
|
27183
27470
|
});
|
|
27184
|
-
fail$
|
|
27471
|
+
fail$4({
|
|
27185
27472
|
status: "error",
|
|
27186
27473
|
error: error instanceof Error ? error.message : String(error),
|
|
27187
27474
|
...fallbackNext ? { next: fallbackNext } : {}
|
|
@@ -27214,7 +27501,7 @@ async function registerAction(options) {
|
|
|
27214
27501
|
return;
|
|
27215
27502
|
}
|
|
27216
27503
|
}
|
|
27217
|
-
if (hasCredentials && !existingClaim && !options.force) fail$
|
|
27504
|
+
if (hasCredentials && !existingClaim && !options.force) fail$4({
|
|
27218
27505
|
status: "error",
|
|
27219
27506
|
code: "already_authenticated",
|
|
27220
27507
|
error: "Credentials are already stored for a claimed account.",
|
|
@@ -27266,7 +27553,7 @@ async function registerAction(options) {
|
|
|
27266
27553
|
});
|
|
27267
27554
|
return;
|
|
27268
27555
|
} catch (error) {
|
|
27269
|
-
if (error instanceof AgentAuthError) fail$
|
|
27556
|
+
if (error instanceof AgentAuthError) fail$4({
|
|
27270
27557
|
...base,
|
|
27271
27558
|
status: "registered_claim_failed",
|
|
27272
27559
|
code: error.code,
|
|
@@ -27286,7 +27573,7 @@ async function claimAction(email, options) {
|
|
|
27286
27573
|
const apiUrl = options.apiUrl || claimState?.apiUrl || (await store.getCredentials())?.apiUrl || getApiUrl();
|
|
27287
27574
|
if (!claimState) {
|
|
27288
27575
|
const hasCredentials = await store.hasCredentials();
|
|
27289
|
-
fail$
|
|
27576
|
+
fail$4({
|
|
27290
27577
|
status: "error",
|
|
27291
27578
|
code: "no_pending_registration",
|
|
27292
27579
|
error: hasCredentials ? "Stored credentials are already claimed; there is nothing to claim." : "No registration found.",
|
|
@@ -27314,13 +27601,13 @@ async function verifyAction(code, options) {
|
|
|
27314
27601
|
const store = new CredentialStore();
|
|
27315
27602
|
const claimState = await store.getClaimState();
|
|
27316
27603
|
const apiUrl = options.apiUrl || claimState?.apiUrl || (await store.getCredentials())?.apiUrl || getApiUrl();
|
|
27317
|
-
if (!claimState) fail$
|
|
27604
|
+
if (!claimState) fail$4({
|
|
27318
27605
|
status: "error",
|
|
27319
27606
|
code: "no_pending_registration",
|
|
27320
27607
|
error: "No claim in progress.",
|
|
27321
27608
|
next: "Run: runtype auth register --email <email> first."
|
|
27322
27609
|
});
|
|
27323
|
-
if (!claimState.claimEmail) fail$
|
|
27610
|
+
if (!claimState.claimEmail) fail$4({
|
|
27324
27611
|
status: "error",
|
|
27325
27612
|
code: "claim_not_initiated",
|
|
27326
27613
|
error: "No verification email has been sent yet, so there is no code to verify.",
|
|
@@ -27923,6 +28210,283 @@ function resolveJsonPayload(inline, fromFile, label) {
|
|
|
27923
28210
|
return parseJsonObject$1(inline, label);
|
|
27924
28211
|
}
|
|
27925
28212
|
//#endregion
|
|
28213
|
+
//#region src/lib/ensure-output.ts
|
|
28214
|
+
/**
|
|
28215
|
+
* Emit a pulled definition as JSON: to `out` when given, otherwise to stdout so
|
|
28216
|
+
* `runtype <entity> pull <name> > file.json` works. Hash and warnings go to stderr.
|
|
28217
|
+
*/
|
|
28218
|
+
function writePulledDefinition(data, meta) {
|
|
28219
|
+
const json = `${JSON.stringify(data, null, 2)}\n`;
|
|
28220
|
+
if (meta.out) {
|
|
28221
|
+
writeFileSync$1(meta.out, json);
|
|
28222
|
+
console.error(chalk.green(`Wrote ${meta.out}`));
|
|
28223
|
+
} else process.stdout.write(json);
|
|
28224
|
+
if (meta.contentHash) console.error(chalk.gray(`contentHash ${meta.contentHash}`));
|
|
28225
|
+
for (const warning of meta.warnings ?? []) console.error(chalk.yellow(`warning: ${warning}`));
|
|
28226
|
+
}
|
|
28227
|
+
//#endregion
|
|
28228
|
+
//#region src/commands/entity-ensure.ts
|
|
28229
|
+
function namespaceTransport(client) {
|
|
28230
|
+
const transport = {
|
|
28231
|
+
get: (path, params) => client.get(path, params),
|
|
28232
|
+
post: (path, body) => client.post(path, body)
|
|
28233
|
+
};
|
|
28234
|
+
return () => transport;
|
|
28235
|
+
}
|
|
28236
|
+
/** The SDK ensure options these flags ask for, shared by every entity. */
|
|
28237
|
+
function buildEntityEnsureOptions(flags, support) {
|
|
28238
|
+
if (flags.release !== void 0 && !["none", "publish"].includes(flags.release)) throw new Error("--release must be none or publish");
|
|
28239
|
+
const version = support.version && (flags.label || flags.notes) ? {
|
|
28240
|
+
...flags.label ? { label: flags.label } : {},
|
|
28241
|
+
...flags.notes ? { notes: flags.notes } : {}
|
|
28242
|
+
} : void 0;
|
|
28243
|
+
return {
|
|
28244
|
+
...flags.dryRun ? { dryRun: true } : {},
|
|
28245
|
+
...flags.expectNoChanges ? { expectNoChanges: true } : {},
|
|
28246
|
+
...flags.overwriteManaged ? { onConflict: "overwrite" } : {},
|
|
28247
|
+
...flags.expectedRemoteHash ? { expectedRemoteHash: flags.expectedRemoteHash } : {},
|
|
28248
|
+
...support.release && flags.release ? { release: flags.release } : {},
|
|
28249
|
+
...version ? { version } : {}
|
|
28250
|
+
};
|
|
28251
|
+
}
|
|
28252
|
+
const define = (fn, definition) => fn(definition);
|
|
28253
|
+
const SKILL_ENSURE_SPEC = {
|
|
28254
|
+
noun: "skill",
|
|
28255
|
+
idKey: "skillId",
|
|
28256
|
+
release: true,
|
|
28257
|
+
version: true,
|
|
28258
|
+
productScoped: false,
|
|
28259
|
+
ensure: async (t, def, options) => new SkillsNamespace(t).ensure(define(defineSkill, def), options),
|
|
28260
|
+
pull: (t, name) => new SkillsNamespace(t).pull(name)
|
|
28261
|
+
};
|
|
28262
|
+
const FLOW_ENSURE_SPEC = {
|
|
28263
|
+
noun: "flow",
|
|
28264
|
+
idKey: "flowId",
|
|
28265
|
+
release: true,
|
|
28266
|
+
version: true,
|
|
28267
|
+
productScoped: false,
|
|
28268
|
+
ensure: async (t, def, options) => new FlowsNamespace(t).ensure(define(defineFlow, def), options),
|
|
28269
|
+
pull: (t, name) => new FlowsNamespace(t).pull(name)
|
|
28270
|
+
};
|
|
28271
|
+
const TOOL_ENSURE_SPEC = {
|
|
28272
|
+
noun: "tool",
|
|
28273
|
+
idKey: "toolId",
|
|
28274
|
+
release: false,
|
|
28275
|
+
version: false,
|
|
28276
|
+
productScoped: false,
|
|
28277
|
+
ensure: async (t, def, options) => new ToolsNamespace(t).ensure(define(defineTool, def), options),
|
|
28278
|
+
pull: (t, name) => new ToolsNamespace(t).pull(name)
|
|
28279
|
+
};
|
|
28280
|
+
const PRODUCT_ENSURE_SPEC = {
|
|
28281
|
+
noun: "product",
|
|
28282
|
+
idKey: "productId",
|
|
28283
|
+
release: false,
|
|
28284
|
+
version: false,
|
|
28285
|
+
productScoped: false,
|
|
28286
|
+
ensure: async (t, def, options) => new ProductsNamespace(t).ensure(define(defineProduct, def), options),
|
|
28287
|
+
pull: (t, name) => new ProductsNamespace(t).pull(name)
|
|
28288
|
+
};
|
|
28289
|
+
const SURFACE_ENSURE_SPEC = {
|
|
28290
|
+
noun: "surface",
|
|
28291
|
+
idKey: "surfaceId",
|
|
28292
|
+
release: false,
|
|
28293
|
+
version: false,
|
|
28294
|
+
productScoped: true,
|
|
28295
|
+
ensure: async (t, def, options, productId) => new SurfacesNamespace(t).ensure(productId, define(defineSurface, def), options),
|
|
28296
|
+
pull: (t, name, productId) => new SurfacesNamespace(t).pull(productId, name)
|
|
28297
|
+
};
|
|
28298
|
+
function isDriftError(error) {
|
|
28299
|
+
return error instanceof Error && error.name.endsWith("DriftError") && "plan" in error;
|
|
28300
|
+
}
|
|
28301
|
+
function printEnsureOutcome(spec, name, result) {
|
|
28302
|
+
if (result.result === "plan") {
|
|
28303
|
+
console.log(chalk.cyan(`Plan for ${spec.noun} "${name}": ${result.changes ?? "n/a"}`));
|
|
28304
|
+
for (const key of result.changedKeys ?? []) console.log(` ${key}`);
|
|
28305
|
+
if (result.remoteHash) console.log(chalk.gray(`Apply this plan with --expected-remote-hash ${result.remoteHash}`));
|
|
28306
|
+
return;
|
|
28307
|
+
}
|
|
28308
|
+
const id = typeof result[spec.idKey] === "string" ? ` (${result[spec.idKey]})` : "";
|
|
28309
|
+
console.log(chalk.green(`${spec.noun[0].toUpperCase()}${spec.noun.slice(1)} "${name}" ${result.result}${id}`));
|
|
28310
|
+
if (result.versionId) console.log(chalk.gray(`Version ${result.versionId}`));
|
|
28311
|
+
}
|
|
28312
|
+
function fail$3(message, detail) {
|
|
28313
|
+
console.error(chalk.red(message));
|
|
28314
|
+
if (detail !== void 0) console.error(chalk.red(detail instanceof Error ? detail.message : String(detail)));
|
|
28315
|
+
process.exit(1);
|
|
28316
|
+
}
|
|
28317
|
+
/** `ensure <file>`: converge a definition file (a pull's output, or its `definition`). */
|
|
28318
|
+
function buildEntityEnsureCommand(spec) {
|
|
28319
|
+
const command = new Command("ensure").description(`Converge a ${spec.noun} definition from a JSON file (config-as-code)`).argument("<file>", `${spec.noun} definition JSON file, or - to read from stdin`);
|
|
28320
|
+
if (spec.productScoped) command.requiredOption("--product <productId>", "Product the surface belongs to");
|
|
28321
|
+
command.option("--dry-run", "Plan without writing").option("--expect-no-changes", "Plan and exit 1 on any drift (the CI gate)").option("--overwrite-managed", "Converge even when the last write came from the dashboard").option("--expected-remote-hash <hash>", "Apply only if the remote still hashes to this value (the remoteHash a dry run printed)");
|
|
28322
|
+
if (spec.release) command.option("--release <mode>", "none or publish");
|
|
28323
|
+
if (spec.version) command.option("--label <text>", "Version label recorded on the version this converge appends").option("--notes <text>", "Version notes recorded on the version this converge appends");
|
|
28324
|
+
command.option("--json", "Output as JSON").action(async (file, flags) => {
|
|
28325
|
+
let options;
|
|
28326
|
+
let definition;
|
|
28327
|
+
try {
|
|
28328
|
+
options = buildEntityEnsureOptions(flags, spec);
|
|
28329
|
+
definition = readJsonFile(file, {
|
|
28330
|
+
label: `${spec.noun} definition`,
|
|
28331
|
+
unwrapKey: "definition"
|
|
28332
|
+
});
|
|
28333
|
+
} catch (error) {
|
|
28334
|
+
fail$3(error instanceof Error ? error.message : String(error));
|
|
28335
|
+
}
|
|
28336
|
+
const name = typeof definition.name === "string" ? definition.name : "";
|
|
28337
|
+
const apiKey = await ensureAuth();
|
|
28338
|
+
if (!apiKey) return;
|
|
28339
|
+
const transport = namespaceTransport(createCliClient(apiKey));
|
|
28340
|
+
try {
|
|
28341
|
+
const result = await spec.ensure(transport, definition, options, flags.product);
|
|
28342
|
+
if (flags.json) printJson(result);
|
|
28343
|
+
else printEnsureOutcome(spec, name, result);
|
|
28344
|
+
} catch (error) {
|
|
28345
|
+
if (isDriftError(error)) {
|
|
28346
|
+
if (flags.json) printJson({
|
|
28347
|
+
drift: true,
|
|
28348
|
+
plan: error.plan
|
|
28349
|
+
});
|
|
28350
|
+
else console.error(chalk.yellow(error.message));
|
|
28351
|
+
process.exit(1);
|
|
28352
|
+
}
|
|
28353
|
+
fail$3(`Failed to converge the ${spec.noun}`, error);
|
|
28354
|
+
}
|
|
28355
|
+
});
|
|
28356
|
+
return command;
|
|
28357
|
+
}
|
|
28358
|
+
/** `pull <name>`: print the definition file `ensure` reads back (dashboard wins). */
|
|
28359
|
+
function buildEntityPullCommand(spec) {
|
|
28360
|
+
const command = new Command("pull").description(`Print a ${spec.noun} as the definition file \`ensure\` reads (dashboard wins)`).argument("<name>", `${spec.noun} name`);
|
|
28361
|
+
if (spec.productScoped) command.requiredOption("--product <productId>", "Product the surface belongs to");
|
|
28362
|
+
command.option("-o, --out <file>", "Write the definition file here instead of stdout").option("--json", "Print the full pull response (definition, contentHash, provenance)").action(async (name, flags) => {
|
|
28363
|
+
const apiKey = await ensureAuth();
|
|
28364
|
+
if (!apiKey) return;
|
|
28365
|
+
const transport = namespaceTransport(createCliClient(apiKey));
|
|
28366
|
+
try {
|
|
28367
|
+
const pulled = await spec.pull(transport, name, flags.product);
|
|
28368
|
+
writePulledDefinition(flags.json ? pulled : pulled.definition, {
|
|
28369
|
+
out: flags.out,
|
|
28370
|
+
contentHash: pulled.contentHash,
|
|
28371
|
+
warnings: pulled.warnings
|
|
28372
|
+
});
|
|
28373
|
+
} catch (error) {
|
|
28374
|
+
fail$3(`Failed to pull ${spec.noun} "${name}"`, error);
|
|
28375
|
+
}
|
|
28376
|
+
});
|
|
28377
|
+
return command;
|
|
28378
|
+
}
|
|
28379
|
+
function buildFpoEnsureOptions(flags) {
|
|
28380
|
+
if (flags.release !== void 0 && !["none", "publish"].includes(flags.release)) throw new Error("--release must be none or publish");
|
|
28381
|
+
return {
|
|
28382
|
+
...flags.dryRun ? { dryRun: true } : {},
|
|
28383
|
+
...flags.expectNoChanges ? { expectNoChanges: true } : {},
|
|
28384
|
+
...flags.overwriteManaged ? { onConflict: "overwrite" } : {},
|
|
28385
|
+
...flags.expectedRemoteHash ? { expectedRemoteHash: flags.expectedRemoteHash } : {},
|
|
28386
|
+
...flags.prune ? { prune: true } : {},
|
|
28387
|
+
...flags.hashProbe ? { hashProbe: true } : {},
|
|
28388
|
+
...flags.release ? { release: flags.release } : {}
|
|
28389
|
+
};
|
|
28390
|
+
}
|
|
28391
|
+
/** `ensure-fpo <file>`: converge a whole Full Product Object. */
|
|
28392
|
+
const productEnsureFpoCommand = new Command("ensure-fpo").description("Converge a whole Full Product Object (product, capabilities, surfaces, ...) from a JSON file").argument("<file>", "FPO JSON file (a pull-fpo output, or its fpo), or - to read from stdin").option("--dry-run", "Plan without writing").option("--expect-no-changes", "Plan and exit 1 when any entity would change (the CI gate)").option("--overwrite-managed", "Converge over dashboard/API edits").option("--expected-remote-hash <hash>", "Apply only if the live product still hashes to this value (the remoteHash a dry run printed)").option("--prune", "Remove product-scoped capabilities and surfaces absent from the FPO").option("--release <mode>", "publish (default: converged agents and flows go live) or none (save drafts)").option("--hash-probe", "With --release none, skip the upload when the live product already matches (best for pulled FPOs)").option("--json", "Output as JSON").action(async (file, flags) => {
|
|
28393
|
+
let fpo;
|
|
28394
|
+
try {
|
|
28395
|
+
fpo = defineFpo(readJsonFile(file, {
|
|
28396
|
+
label: "FPO",
|
|
28397
|
+
unwrapKey: "fpo"
|
|
28398
|
+
}));
|
|
28399
|
+
} catch (error) {
|
|
28400
|
+
fail$3(error instanceof Error ? error.message : String(error));
|
|
28401
|
+
}
|
|
28402
|
+
const apiKey = await ensureAuth();
|
|
28403
|
+
if (!apiKey) return;
|
|
28404
|
+
const products = new ProductsNamespace(namespaceTransport(createCliClient(apiKey)));
|
|
28405
|
+
try {
|
|
28406
|
+
const result = await products.ensureFpo(fpo, buildFpoEnsureOptions(flags));
|
|
28407
|
+
if (flags.json) printJson(result);
|
|
28408
|
+
else {
|
|
28409
|
+
console.log(chalk.cyan(`Product FPO ${result.result}${result.productId ? ` (${result.productId})` : ""}`));
|
|
28410
|
+
for (const entity of result.entities) {
|
|
28411
|
+
const line = ` ${entity.kind}:${entity.name} ${entity.result}`;
|
|
28412
|
+
console.log(entity.result === "failed" ? chalk.red(`${line} — ${entity.error ?? ""}`) : line);
|
|
28413
|
+
}
|
|
28414
|
+
if (result.remoteHash) console.log(chalk.gray(`Apply this plan with --expected-remote-hash ${result.remoteHash}`));
|
|
28415
|
+
}
|
|
28416
|
+
if (result.hasFailures) process.exit(1);
|
|
28417
|
+
} catch (error) {
|
|
28418
|
+
if (isDriftError(error)) {
|
|
28419
|
+
if (flags.json) printJson({
|
|
28420
|
+
drift: true,
|
|
28421
|
+
plan: error.plan
|
|
28422
|
+
});
|
|
28423
|
+
else console.error(chalk.yellow(error.message));
|
|
28424
|
+
process.exit(1);
|
|
28425
|
+
}
|
|
28426
|
+
fail$3("Failed to converge the product FPO", error);
|
|
28427
|
+
}
|
|
28428
|
+
});
|
|
28429
|
+
/** Runtype Agent Skills live under `agents` because `runtype skills` installs coding-agent skills. */
|
|
28430
|
+
const agentSkillsCommand = new Command("skills").description("Runtype Agent Skills: context bundles deployed agents load (for coding-agent skills see `runtype skills`)");
|
|
28431
|
+
agentSkillsCommand.addCommand(buildEntityEnsureCommand(SKILL_ENSURE_SPEC));
|
|
28432
|
+
agentSkillsCommand.addCommand(buildEntityPullCommand(SKILL_ENSURE_SPEC));
|
|
28433
|
+
/** `pull-fpo <name>`: print the whole product as the FPO `ensure-fpo` reads. */
|
|
28434
|
+
const productPullFpoCommand = new Command("pull-fpo").description("Print a product graph as the FPO `ensure-fpo` reads (dashboard wins)").argument("<name>", "Product name").option("-o, --out <file>", "Write the FPO here instead of stdout").option("--json", "Print the full pull response (fpo, contentHash, warnings)").action(async (name, flags) => {
|
|
28435
|
+
const apiKey = await ensureAuth();
|
|
28436
|
+
if (!apiKey) return;
|
|
28437
|
+
const products = new ProductsNamespace(namespaceTransport(createCliClient(apiKey)));
|
|
28438
|
+
try {
|
|
28439
|
+
const pulled = await products.pullFpo(name);
|
|
28440
|
+
writePulledDefinition(flags.json ? pulled : pulled.fpo, {
|
|
28441
|
+
out: flags.out,
|
|
28442
|
+
contentHash: pulled.contentHash,
|
|
28443
|
+
warnings: pulled.warnings
|
|
28444
|
+
});
|
|
28445
|
+
} catch (error) {
|
|
28446
|
+
fail$3(`Failed to pull product "${name}"`, error);
|
|
28447
|
+
}
|
|
28448
|
+
});
|
|
28449
|
+
/** Converge a client token file through the SDK; it keys on `name` and never deletes or regenerates. */
|
|
28450
|
+
async function ensureClientTokenFromFile(client, definition, flags) {
|
|
28451
|
+
const getClient = (() => client);
|
|
28452
|
+
return new ClientTokensNamespace(getClient).ensure(definition, {
|
|
28453
|
+
...flags.dryRun ? { dryRun: true } : {},
|
|
28454
|
+
...flags.expectNoChanges ? { expectNoChanges: true } : {}
|
|
28455
|
+
});
|
|
28456
|
+
}
|
|
28457
|
+
/** `client-tokens ensure <file>`: the widget token step of the agent-plus-token workflow. */
|
|
28458
|
+
const clientTokenEnsureCommand = new Command("ensure").description("Converge a client token definition from a JSON file, keyed by its name").argument("<file>", "Client token definition JSON file, or - to read from stdin").option("--dry-run", "Plan without writing").option("--expect-no-changes", "Plan and exit 1 on any drift (the CI gate)").option("--json", "Output as JSON").action(async (file, flags) => {
|
|
28459
|
+
let definition;
|
|
28460
|
+
try {
|
|
28461
|
+
definition = readJsonFile(file, { label: "client token definition" });
|
|
28462
|
+
} catch (error) {
|
|
28463
|
+
fail$3(error instanceof Error ? error.message : String(error));
|
|
28464
|
+
}
|
|
28465
|
+
const apiKey = await ensureAuth();
|
|
28466
|
+
if (!apiKey) return;
|
|
28467
|
+
try {
|
|
28468
|
+
const result = await ensureClientTokenFromFile(createCliClient(apiKey), definition, flags);
|
|
28469
|
+
if (flags.json) printJson(result);
|
|
28470
|
+
else if (result.result === "plan") {
|
|
28471
|
+
console.log(chalk.cyan(`Plan for client token "${String(definition.name)}": ${result.changes}`));
|
|
28472
|
+
for (const key of result.changedKeys) console.log(` ${key}`);
|
|
28473
|
+
} else {
|
|
28474
|
+
console.log(chalk.green(`Client token "${result.clientToken.name}" ${result.result} (${result.clientToken.id})`));
|
|
28475
|
+
if (result.token) console.log(`Token ${result.token}`);
|
|
28476
|
+
}
|
|
28477
|
+
} catch (error) {
|
|
28478
|
+
if (isDriftError(error)) {
|
|
28479
|
+
if (flags.json) printJson({
|
|
28480
|
+
drift: true,
|
|
28481
|
+
plan: error.plan
|
|
28482
|
+
});
|
|
28483
|
+
else console.error(chalk.yellow(error.message));
|
|
28484
|
+
process.exit(1);
|
|
28485
|
+
}
|
|
28486
|
+
fail$3("Failed to converge the client token", error);
|
|
28487
|
+
}
|
|
28488
|
+
});
|
|
28489
|
+
//#endregion
|
|
27926
28490
|
//#region src/lib/execution-input.ts
|
|
27927
28491
|
function addExecutionInputOptions(command) {
|
|
27928
28492
|
return command.option("--inputs <json>", "Typed input variables as a JSON object").option("--inputs-file <file|->", "Read input variables from a JSON object file or stdin").option("-v, --variable <key=value>", "Set a string variable (repeatable)", collectVariables, []).option("--messages-file <file|->", "Read structured messages from a JSON array file or stdin");
|
|
@@ -28248,6 +28812,8 @@ function flowsListParams(options) {
|
|
|
28248
28812
|
};
|
|
28249
28813
|
}
|
|
28250
28814
|
const flowsCommand = new Command("flows").description("Manage flows");
|
|
28815
|
+
flowsCommand.addCommand(buildEntityEnsureCommand(FLOW_ENSURE_SPEC));
|
|
28816
|
+
flowsCommand.addCommand(buildEntityPullCommand(FLOW_ENSURE_SPEC));
|
|
28251
28817
|
flowsCommand.command("list").description("List all flows").option("--limit <n>", "Limit results (defaults to the API page size)").option("--cursor <cursor>", "Pagination cursor for the next page").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
|
|
28252
28818
|
const apiKey = await ensureAuth();
|
|
28253
28819
|
if (!apiKey) return;
|
|
@@ -31461,6 +32027,8 @@ function formatToolMetadata(metadata) {
|
|
|
31461
32027
|
return parts.join("; ");
|
|
31462
32028
|
}
|
|
31463
32029
|
const toolsCommand = new Command("tools").description("Manage runtime tools (saved tools)");
|
|
32030
|
+
toolsCommand.addCommand(buildEntityEnsureCommand(TOOL_ENSURE_SPEC));
|
|
32031
|
+
toolsCommand.addCommand(buildEntityPullCommand(TOOL_ENSURE_SPEC));
|
|
31464
32032
|
toolsCommand.command("list").description("List your tools").option("--limit <n>", "Limit results (defaults to the API page size)").option("--cursor <cursor>", "Pagination cursor for the next page").option("--json", "Output as JSON").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
|
|
31465
32033
|
const apiKey = await ensureAuth();
|
|
31466
32034
|
if (!apiKey) return;
|
|
@@ -32255,6 +32823,8 @@ function parsePositiveInteger$1(value, option) {
|
|
|
32255
32823
|
return parsed;
|
|
32256
32824
|
}
|
|
32257
32825
|
const surfacesCommand = new Command("surfaces").description("View product surfaces");
|
|
32826
|
+
surfacesCommand.addCommand(buildEntityEnsureCommand(SURFACE_ENSURE_SPEC));
|
|
32827
|
+
surfacesCommand.addCommand(buildEntityPullCommand(SURFACE_ENSURE_SPEC));
|
|
32258
32828
|
surfacesCommand.command("list").description("List surfaces across your products").option("--json", "Output as JSON").option("--limit <n>", "Maximum number of surfaces to return").option("--cursor <cursor>", "Pagination cursor for the next page").option("--type <type>", "Filter by surface type").option("--status <status>", "Filter by surface status").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
|
|
32259
32829
|
const apiKey = await ensureAuth();
|
|
32260
32830
|
if (!apiKey) return;
|
|
@@ -32928,6 +33498,10 @@ async function validateUrl(url, apiUrl) {
|
|
|
32928
33498
|
return validateQuickStartUrl(url, apiUrl);
|
|
32929
33499
|
}
|
|
32930
33500
|
const productsCommand = new Command("products").description("Manage products");
|
|
33501
|
+
productsCommand.addCommand(buildEntityEnsureCommand(PRODUCT_ENSURE_SPEC));
|
|
33502
|
+
productsCommand.addCommand(buildEntityPullCommand(PRODUCT_ENSURE_SPEC));
|
|
33503
|
+
productsCommand.addCommand(productEnsureFpoCommand);
|
|
33504
|
+
productsCommand.addCommand(productPullFpoCommand);
|
|
32931
33505
|
productsCommand.command("init").description("Initialize a product from an external resource").option("--from <url>", "URL to validate and import").option("--name <name>", "Custom product name (defaults to agent name)").option("--open", "Open browser to dashboard instead of creating via API").option("--json", "Output as JSON").option("--api-url <url>", "Override API URL").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
|
|
32932
33506
|
const isJsonMode = !!options.json;
|
|
32933
33507
|
if (!isTTY(options) || isJsonMode) {
|
|
@@ -46416,7 +46990,6 @@ function fail$2(headline, error) {
|
|
|
46416
46990
|
const message = error instanceof Error ? error.message : String(error);
|
|
46417
46991
|
console.error(chalk.red(headline));
|
|
46418
46992
|
console.error(chalk.red(message));
|
|
46419
|
-
if (error instanceof AgentAliasPreviewLimitError) console.error(chalk.gray(`${error.active} of ${error.limit} preview aliases active for this ${error.scope}. Archive one with: runtype agents aliases archive <alias> --all`));
|
|
46420
46993
|
process.exit(1);
|
|
46421
46994
|
}
|
|
46422
46995
|
function describeAlias(row) {
|
|
@@ -46658,28 +47231,113 @@ agentDeploymentsCommand.command("list <agentId>").description("List deployment r
|
|
|
46658
47231
|
* The activation the flags ask for. `release` and `deploy` are two spellings of
|
|
46659
47232
|
* one thing, so supplying both is refused before any request is sent.
|
|
46660
47233
|
*/
|
|
46661
|
-
function buildEnsureActivation(flags) {
|
|
47234
|
+
function buildEnsureActivation(flags, bindings) {
|
|
46662
47235
|
if (flags.release !== void 0 && flags.deploy !== void 0) throw new Error(ENSURE_RELEASE_DEPLOY_CONFLICT_MESSAGE);
|
|
46663
|
-
if (flags.deploy
|
|
47236
|
+
if (bindings !== void 0 && flags.deploy === void 0) throw new Error("--bindings-file sets per-alias secret bindings, so it needs --deploy <alias>.");
|
|
47237
|
+
if (flags.deploy !== void 0) return { deploy: {
|
|
47238
|
+
alias: flags.deploy,
|
|
47239
|
+
...bindings !== void 0 ? { bindings } : {}
|
|
47240
|
+
} };
|
|
46664
47241
|
if (flags.release !== void 0) return { release: flags.release };
|
|
46665
47242
|
return {};
|
|
46666
47243
|
}
|
|
46667
|
-
|
|
47244
|
+
/** The API refuses expectedRemoteHash on a preview deploy, since it guards live-row writes only. */
|
|
47245
|
+
function acceptsExpectedRemoteHash(deploy) {
|
|
47246
|
+
return deploy === void 0 || deploy === "live";
|
|
47247
|
+
}
|
|
47248
|
+
/**
|
|
47249
|
+
* Parse a `--bindings-file` object of `{ SECRET_NAME: value }`. The whole object
|
|
47250
|
+
* replaces the alias's bindings, so `{}` clears them.
|
|
47251
|
+
*/
|
|
47252
|
+
function parseAliasBindings(file) {
|
|
47253
|
+
const bindings = {};
|
|
47254
|
+
for (const [name, value] of Object.entries(file)) {
|
|
47255
|
+
if (typeof value !== "string") throw new Error(`Binding "${name}" must be a string secret value.`);
|
|
47256
|
+
bindings[name] = value;
|
|
47257
|
+
}
|
|
47258
|
+
return bindings;
|
|
47259
|
+
}
|
|
47260
|
+
/** The file `agents ensure` reads back: the definition with its skills and capabilities at the top level. */
|
|
47261
|
+
function agentPullToDefinitionFile(pulled) {
|
|
47262
|
+
return {
|
|
47263
|
+
...pulled.definition,
|
|
47264
|
+
...pulled.skills !== void 0 ? { skills: pulled.skills } : {},
|
|
47265
|
+
...pulled.capabilities !== void 0 ? { capabilities: pulled.capabilities } : {}
|
|
47266
|
+
};
|
|
47267
|
+
}
|
|
47268
|
+
/**
|
|
47269
|
+
* Lift a definition file's top-level `skills` (the agent's complete binding set)
|
|
47270
|
+
* out of the definition, which ensure hashes, and into the request.
|
|
47271
|
+
*/
|
|
47272
|
+
function splitDefinitionSkills(file) {
|
|
47273
|
+
if (!("skills" in file)) return { definition: file };
|
|
47274
|
+
const { skills, ...definition } = file;
|
|
47275
|
+
if (!Array.isArray(skills)) throw new Error("\"skills\" must be an array of { name, version?, enabled? } bindings.");
|
|
47276
|
+
return {
|
|
47277
|
+
definition,
|
|
47278
|
+
skills
|
|
47279
|
+
};
|
|
47280
|
+
}
|
|
47281
|
+
/**
|
|
47282
|
+
* Lift a definition file's top-level `skills` and `capabilities` (each the agent's
|
|
47283
|
+
* complete set) out of the definition and into the request.
|
|
47284
|
+
*/
|
|
47285
|
+
function splitDefinitionAgentState(file) {
|
|
47286
|
+
const { definition: withCapabilities, skills } = splitDefinitionSkills(file);
|
|
47287
|
+
if (!("capabilities" in withCapabilities)) return {
|
|
47288
|
+
definition: withCapabilities,
|
|
47289
|
+
...skills !== void 0 ? { skills } : {}
|
|
47290
|
+
};
|
|
47291
|
+
const { capabilities, ...definition } = withCapabilities;
|
|
47292
|
+
if (!Array.isArray(capabilities)) throw new Error("\"capabilities\" must be an array of { toolName, flow | agent | tool, description?, parametersSchema?, enabled? } entries.");
|
|
47293
|
+
return {
|
|
47294
|
+
definition,
|
|
47295
|
+
...skills !== void 0 ? { skills } : {},
|
|
47296
|
+
capabilities
|
|
47297
|
+
};
|
|
47298
|
+
}
|
|
47299
|
+
function formatCapabilityChange(change) {
|
|
47300
|
+
const target = change.kind && change.source ? ` (${change.kind}:${change.source})` : "";
|
|
47301
|
+
return ` capability:${change.toolName} ${change.action}${target}`;
|
|
47302
|
+
}
|
|
47303
|
+
/**
|
|
47304
|
+
* Whether a dry-run plan drifts from the definition: content, skill bindings, capabilities, or,
|
|
47305
|
+
* when the flags ask for a deploy, the release pointer or its binding names (#9371).
|
|
47306
|
+
*/
|
|
47307
|
+
function ensurePlanHasDrift(result, activation) {
|
|
47308
|
+
if (result.result !== "plan") return false;
|
|
47309
|
+
if (result.changes !== "none") return true;
|
|
47310
|
+
if ((result.skillBindings ?? []).some((b) => b.action !== "unchanged")) return true;
|
|
47311
|
+
if ((result.capabilityChanges ?? []).some((c) => c.action !== "unchanged")) return true;
|
|
47312
|
+
return (activation.deploy !== void 0 || activation.release === "publish") && (result.deployment?.changed === true || result.deployment?.bindingsChanged === true);
|
|
47313
|
+
}
|
|
47314
|
+
function formatSkillBinding(binding) {
|
|
47315
|
+
const pin = binding.version === null ? "published" : `v${binding.version}`;
|
|
47316
|
+
return ` skill:${binding.name} ${binding.action} (${pin})`;
|
|
47317
|
+
}
|
|
47318
|
+
const agentEnsureCommand = new Command("ensure").description("Converge an agent definition from a JSON file, optionally deploying it").argument("<file>", "Agent definition JSON file, or - to read from stdin").option("--deploy <alias>", "Activate the converged version at this release alias").option("--release <mode>", "Compatibility spelling of deploy: none or publish").option("--dry-run", "Plan without writing").option("--expect-no-changes", "Plan and exit 1 on any drift (the CI gate)").option("--overwrite-managed", "Converge even when the last write came from the dashboard").option("--label <text>", "Version label recorded on the version this converge appends").option("--notes <text>", "Version notes recorded on the version this converge appends").option("--expected-remote-hash <hash>", "Apply only if the remote still hashes to this value (the remoteHash a dry run printed)").option("--bindings-file <file>", "JSON object of { SECRET_NAME: value } bound to the --deploy alias (replaces the whole set)").option("--json", "Output as JSON").action(async (file, options) => {
|
|
46668
47319
|
if (options.release !== void 0 && !["none", "publish"].includes(options.release)) {
|
|
46669
47320
|
console.error(chalk.red("--release must be none or publish"));
|
|
46670
47321
|
process.exit(1);
|
|
46671
47322
|
}
|
|
46672
47323
|
let activation;
|
|
46673
47324
|
try {
|
|
46674
|
-
activation = buildEnsureActivation(options);
|
|
47325
|
+
activation = buildEnsureActivation(options, options.bindingsFile !== void 0 ? parseAliasBindings(readJsonFile(options.bindingsFile, { label: "alias bindings" })) : void 0);
|
|
46675
47326
|
} catch (error) {
|
|
46676
47327
|
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
46677
47328
|
process.exit(1);
|
|
46678
47329
|
}
|
|
46679
|
-
|
|
46680
|
-
|
|
46681
|
-
|
|
46682
|
-
|
|
47330
|
+
let parsed;
|
|
47331
|
+
try {
|
|
47332
|
+
parsed = splitDefinitionAgentState(readJsonFile(file, {
|
|
47333
|
+
label: "agent definition",
|
|
47334
|
+
unwrapKey: "agent"
|
|
47335
|
+
}));
|
|
47336
|
+
} catch (error) {
|
|
47337
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
47338
|
+
process.exit(1);
|
|
47339
|
+
}
|
|
47340
|
+
const { definition, skills, capabilities } = parsed;
|
|
46683
47341
|
const name = definition.name;
|
|
46684
47342
|
if (typeof name !== "string" || name.length === 0) {
|
|
46685
47343
|
console.error(chalk.red("The agent definition needs a non-empty \"name\"."));
|
|
@@ -46700,22 +47358,35 @@ const agentEnsureCommand = new Command("ensure").description("Converge an agent
|
|
|
46700
47358
|
...dryRun ? { dryRun: true } : {},
|
|
46701
47359
|
...options.overwriteManaged ? { onConflict: "overwrite" } : {},
|
|
46702
47360
|
...version ? { version } : {},
|
|
47361
|
+
...options.expectedRemoteHash ? { expectedRemoteHash: options.expectedRemoteHash } : {},
|
|
47362
|
+
...skills !== void 0 ? { skills } : {},
|
|
47363
|
+
...capabilities !== void 0 ? { capabilities } : {},
|
|
46703
47364
|
...activation
|
|
46704
47365
|
});
|
|
46705
47366
|
if (options.json) printJson(result);
|
|
46706
47367
|
else if (result.result === "plan") {
|
|
46707
47368
|
console.log(chalk.cyan(`Plan for "${name}": ${result.changes}`));
|
|
46708
47369
|
for (const key of result.changedKeys ?? []) console.log(` ${key}`);
|
|
47370
|
+
if (result.remoteHash && acceptsExpectedRemoteHash(options.deploy)) console.log(chalk.gray(`Apply this plan with --expected-remote-hash ${result.remoteHash}`));
|
|
46709
47371
|
} else {
|
|
46710
47372
|
console.log(chalk.green(`Agent "${name}" ${result.result} (${result.agentId})`));
|
|
46711
47373
|
if (result.versionId) console.log(chalk.gray(`Version ${result.versionId}`));
|
|
46712
47374
|
}
|
|
47375
|
+
const skillChanges = (result.skillBindings ?? []).filter((b) => b.action !== "unchanged");
|
|
47376
|
+
const capabilityChanges = (result.capabilityChanges ?? []).filter((change) => change.action !== "unchanged");
|
|
47377
|
+
if (!options.json) {
|
|
47378
|
+
for (const binding of skillChanges) console.log(formatSkillBinding(binding));
|
|
47379
|
+
for (const change of capabilityChanges) console.log(formatCapabilityChange(change));
|
|
47380
|
+
}
|
|
46713
47381
|
if (result.deployment && !options.json) {
|
|
46714
|
-
const { alias, versionId, revision, changed } = result.deployment;
|
|
46715
|
-
|
|
47382
|
+
const { alias, versionId, revision, changed, bindingsChanged } = result.deployment;
|
|
47383
|
+
const planned = result.result === "plan";
|
|
47384
|
+
const verb = changed ? planned ? "Would deploy over" : "Deployed" : "Already deployed";
|
|
47385
|
+
console.log(chalk.green(`${verb} ${versionId} at "${alias}" (revision ${revision})`));
|
|
47386
|
+
if (planned && bindingsChanged) console.log(` deploy:${alias} (bindings)`);
|
|
46716
47387
|
if (alias !== "live") console.log(chalk.gray("Live unchanged."));
|
|
46717
47388
|
}
|
|
46718
|
-
if (options.expectNoChanges && result
|
|
47389
|
+
if (options.expectNoChanges && ensurePlanHasDrift(result, activation)) process.exit(1);
|
|
46719
47390
|
} catch (error) {
|
|
46720
47391
|
const message = error instanceof Error ? error.message : String(error);
|
|
46721
47392
|
console.error(chalk.red("Failed to converge the agent"));
|
|
@@ -46723,6 +47394,23 @@ const agentEnsureCommand = new Command("ensure").description("Converge an agent
|
|
|
46723
47394
|
process.exit(1);
|
|
46724
47395
|
}
|
|
46725
47396
|
});
|
|
47397
|
+
const agentPullCommand = new Command("pull").description("Print an agent as the definition file `agents ensure` reads (dashboard wins)").argument("<name>", "Agent name").option("-o, --out <file>", "Write the definition file here instead of stdout").option("--json", "Print the full pull response (definition, contentHash, provenance)").action(async (name, options) => {
|
|
47398
|
+
const apiKey = await ensureAuth();
|
|
47399
|
+
if (!apiKey) return;
|
|
47400
|
+
const client = createCliClient(apiKey);
|
|
47401
|
+
try {
|
|
47402
|
+
const pulled = await client.get("/agents/pull", { name });
|
|
47403
|
+
writePulledDefinition(options.json ? pulled : agentPullToDefinitionFile(pulled), {
|
|
47404
|
+
out: options.out,
|
|
47405
|
+
contentHash: pulled.contentHash,
|
|
47406
|
+
warnings: pulled.warnings
|
|
47407
|
+
});
|
|
47408
|
+
} catch (error) {
|
|
47409
|
+
console.error(chalk.red(`Failed to pull agent "${name}"`));
|
|
47410
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
47411
|
+
process.exit(1);
|
|
47412
|
+
}
|
|
47413
|
+
});
|
|
46726
47414
|
//#endregion
|
|
46727
47415
|
//#region src/commands/agent-promote.ts
|
|
46728
47416
|
const DEFAULT_SOURCE_KEY_ENV = "RUNTYPE_SOURCE_API_KEY";
|
|
@@ -48225,6 +48913,8 @@ agentsCommand.addCommand(taskCommand);
|
|
|
48225
48913
|
agentsCommand.addCommand(agentAliasesCommand);
|
|
48226
48914
|
agentsCommand.addCommand(agentDeploymentsCommand);
|
|
48227
48915
|
agentsCommand.addCommand(agentEnsureCommand);
|
|
48916
|
+
agentsCommand.addCommand(agentPullCommand);
|
|
48917
|
+
agentsCommand.addCommand(agentSkillsCommand);
|
|
48228
48918
|
agentsCommand.addCommand(agentPromoteCommand);
|
|
48229
48919
|
agentsCommand.addCommand(agentExportRuntimeCommand);
|
|
48230
48920
|
agentsCommand.command("list").description("List all agents").option("--json", "Output as JSON").option("--limit <n>", "Limit results", "20").option("--cursor <cursor>", "Pagination cursor for the next page").option("--tty", "Force TTY mode").option("--no-tty", "Force non-TTY mode").action(async (options) => {
|
|
@@ -49893,7 +50583,7 @@ function toJUnitSuite(outcome) {
|
|
|
49893
50583
|
}))
|
|
49894
50584
|
};
|
|
49895
50585
|
}
|
|
49896
|
-
evalCommand.command("run [idOrDirPrefix]").description("Run code-colocated eval suites (**/*.eval.ts) as a CI gate (exit 0 pass / 1 fail / 2 config)").option("--strict", "Fail the exit code on soft grader misses too (default: soft misses are reported but do not fail)").option("--virtual", "Run inline without persisting a suite/batch to the dashboard").option("--junit <path>", "Write JUnit XML results to <path>").option("--url <api>", "Override the API base URL (e.g. staging)").option("--cwd <dir>", "Directory to discover *.eval.ts under (default: current directory)").option("--alias <name>", "Run an Agent-target suite against whichever version this release alias points at").option("--version-id <id>", "Run an Agent-target suite against this exact version snapshot").action(async (idOrDirPrefix, options) => {
|
|
50586
|
+
evalCommand.command("run [idOrDirPrefix]").description("Run code-colocated eval suites (**/*.eval.ts) as a CI gate (exit 0 pass / 1 fail / 2 config)").option("--strict", "Fail the exit code on soft grader misses too (default: soft misses are reported but do not fail)").option("--virtual", "Run inline without persisting a suite/batch to the dashboard").option("--junit <path>", "Write JUnit XML results to <path>").option("--url <api>", "Override the API base URL (e.g. staging)").option("--cwd <dir>", "Directory to discover *.eval.ts under (default: current directory)").option("--alias <name>", "Run an Agent-target suite against whichever version this release alias points at").option("--version-id <id>", "Run an Agent-target suite against this exact version snapshot").option("--overwrite-managed", "Converge each suite even when its last write came from the dashboard or API (replaces edited ensure-owned cases)").action(async (idOrDirPrefix, options) => {
|
|
49897
50587
|
const apiKey = await ensureAuth();
|
|
49898
50588
|
if (!apiKey) failConfig("Not authenticated. Set RUNTYPE_API_KEY or run `runtype login`.");
|
|
49899
50589
|
let agentSelector = {};
|
|
@@ -49938,7 +50628,8 @@ evalCommand.command("run [idOrDirPrefix]").description("Run code-colocated eval
|
|
|
49938
50628
|
else {
|
|
49939
50629
|
const ensured = await client.post("/eval/ensure", {
|
|
49940
50630
|
name: def.name,
|
|
49941
|
-
definition: def
|
|
50631
|
+
definition: def,
|
|
50632
|
+
...options.overwriteManaged ? { onConflict: "overwrite" } : {}
|
|
49942
50633
|
});
|
|
49943
50634
|
if (!ensured.suiteId) throw new Error(`ensure did not return a suiteId (result: ${ensured.result})`);
|
|
49944
50635
|
result = await client.post("/eval/run", {
|
|
@@ -51509,6 +52200,7 @@ clientTokensCommand.command("regenerate <id>").description("Regenerate a client
|
|
|
51509
52200
|
const { waitUntilExit } = render(React.createElement(App));
|
|
51510
52201
|
await waitUntilExit();
|
|
51511
52202
|
});
|
|
52203
|
+
clientTokensCommand.addCommand(clientTokenEnsureCommand);
|
|
51512
52204
|
//#endregion
|
|
51513
52205
|
//#region ../../node_modules/.pnpm/@runtypelabs+persona@4.24.0/node_modules/@runtypelabs/persona/dist/codegen.js
|
|
51514
52206
|
var j = "4.24.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.46.0",
|
|
4
4
|
"description": "Command-line interface for Runtype AI platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"@modelcontextprotocol/client": "2.0.0",
|
|
12
12
|
"@modelcontextprotocol/core": "2.0.0",
|
|
13
13
|
"@runtypelabs/ink-components": "0.3.9",
|
|
14
|
-
"@runtypelabs/sdk": "10.
|
|
14
|
+
"@runtypelabs/sdk": "10.11.0",
|
|
15
15
|
"@runtypelabs/terminal-animations": "0.2.4",
|
|
16
16
|
"add-mcp": "2.0.0",
|
|
17
17
|
"chalk": "^5.3.0",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@modelcontextprotocol/server": "2.0.0",
|
|
35
35
|
"@runtypelabs/persona": "4.24.0",
|
|
36
|
-
"@runtypelabs/shared": "3.
|
|
36
|
+
"@runtypelabs/shared": "3.71.0",
|
|
37
37
|
"@types/express": "^5.0.6",
|
|
38
38
|
"@types/micromatch": "^4.0.9",
|
|
39
39
|
"@types/node": "^25.3.3",
|