@runtypelabs/cli 2.44.0 → 2.45.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 +43 -1
- package/dist/index.js +749 -67
- 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
|
|
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 { AgentAliasPreviewLimitError, DEFAULT_STALL_STOP_AFTER, ENSURE_RELEASE_DEPLOY_CONFLICT_MESSAGE, LEDGER_ARTIFACT_LINE_PREFIX, RuntypeApiError, RuntypeClient, activateAgentPromotion, buildLedgerOffloadReference, compileWorkflowConfig, createClient, defaultWorkflow, deployWorkflow, ensureDefaultWorkflowHooks, evaluateGeneratedRuntimeToolProposal, gameWorkflow, getDefaultPlanPath, isBuiltinToolExecuteResponse, isWorkflowHookRef, parseFinalBuffer, parseLedgerArtifactRelativePath, parseOffloadedOutputId, parseSSEChunk, prepareAgentPromotion, processStream, registerWorkflowHook, validateAgentPromotion } from "@runtypelabs/sdk";
|
|
14
|
+
import { AgentAliasPreviewLimitError, 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);
|
|
@@ -12279,7 +12472,7 @@ const CREATOR_TO_PROVIDER = {
|
|
|
12279
12472
|
"unknown": "openai"
|
|
12280
12473
|
};
|
|
12281
12474
|
//#endregion
|
|
12282
|
-
//#region ../shared/dist/chunk-
|
|
12475
|
+
//#region ../shared/dist/chunk-BFlozpfw.mjs
|
|
12283
12476
|
/**
|
|
12284
12477
|
* The reserved model-facing name of the agent state write tool
|
|
12285
12478
|
* (`packages/runtime/src/engine/state-write-tool.ts`). The model is told to call
|
|
@@ -12478,7 +12671,48 @@ const GEN_AI_ATTRIBUTES = {
|
|
|
12478
12671
|
toolCallId: "gen_ai.tool.call.id"
|
|
12479
12672
|
};
|
|
12480
12673
|
//#endregion
|
|
12481
|
-
//#region ../shared/dist/
|
|
12674
|
+
//#region ../shared/dist/context-manifest-wire.mjs
|
|
12675
|
+
const entryTupleSchema = union([tuple([
|
|
12676
|
+
string(),
|
|
12677
|
+
number$1().int(),
|
|
12678
|
+
number$1().int(),
|
|
12679
|
+
number$1().int(),
|
|
12680
|
+
number$1().int(),
|
|
12681
|
+
number$1().int(),
|
|
12682
|
+
number$1().int()
|
|
12683
|
+
]), tuple([
|
|
12684
|
+
string(),
|
|
12685
|
+
number$1().int(),
|
|
12686
|
+
number$1().int(),
|
|
12687
|
+
number$1().int(),
|
|
12688
|
+
number$1().int(),
|
|
12689
|
+
number$1().int(),
|
|
12690
|
+
number$1().int(),
|
|
12691
|
+
string(),
|
|
12692
|
+
string()
|
|
12693
|
+
])]);
|
|
12694
|
+
object({
|
|
12695
|
+
v: literal(1),
|
|
12696
|
+
s: number$1().int().nonnegative(),
|
|
12697
|
+
e: array(entryTupleSchema),
|
|
12698
|
+
b: array(number$1().int().nonnegative()),
|
|
12699
|
+
x: array(tuple([entryTupleSchema, number$1().int()]))
|
|
12700
|
+
});
|
|
12701
|
+
object({
|
|
12702
|
+
v: literal(1),
|
|
12703
|
+
b: array(tuple([
|
|
12704
|
+
string(),
|
|
12705
|
+
string(),
|
|
12706
|
+
union([literal(0), literal(1)])
|
|
12707
|
+
]))
|
|
12708
|
+
});
|
|
12709
|
+
object({
|
|
12710
|
+
hash: string(),
|
|
12711
|
+
content: string(),
|
|
12712
|
+
truncated: boolean()
|
|
12713
|
+
});
|
|
12714
|
+
//#endregion
|
|
12715
|
+
//#region ../shared/dist/chunk-_ZhmfUpr.mjs
|
|
12482
12716
|
/**
|
|
12483
12717
|
* GenAI semconv content attributes, set by a customer's own instrumentation when it opts into content export.
|
|
12484
12718
|
* 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 +12732,9 @@ const GEN_AI_CONTENT_ATTRIBUTES = {
|
|
|
12498
12732
|
/** `execute_tool` span: the arguments the tool was called with. */
|
|
12499
12733
|
toolCallArguments: GEN_AI_TOOL_CALL_ARGUMENTS,
|
|
12500
12734
|
/** `execute_tool` span: the result the tool returned. */
|
|
12501
|
-
toolCallResult: GEN_AI_TOOL_CALL_RESULT
|
|
12735
|
+
toolCallResult: GEN_AI_TOOL_CALL_RESULT,
|
|
12736
|
+
/** `chat` span: the tool schemas the call offered the model. */
|
|
12737
|
+
toolDefinitions: "gen_ai.tool.definitions"
|
|
12502
12738
|
};
|
|
12503
12739
|
/**
|
|
12504
12740
|
* Read-side aliases for content attributes: canonical semconv name to vendor-namespaced spellings a stock instrumentation uses.
|
|
@@ -14406,6 +14642,11 @@ function agentSelectorViolation(agent) {
|
|
|
14406
14642
|
}
|
|
14407
14643
|
return null;
|
|
14408
14644
|
}
|
|
14645
|
+
const AGENT_TARGET_RESOLUTIONS = [
|
|
14646
|
+
"alias",
|
|
14647
|
+
"version",
|
|
14648
|
+
"legacy-live-row"
|
|
14649
|
+
];
|
|
14409
14650
|
const CLIENT_EXECUTED_TOOL_TYPES = /* @__PURE__ */ new Set(["local", "webmcp"]);
|
|
14410
14651
|
/** Is this tool executed by the CLIENT rather than the host? */
|
|
14411
14652
|
function isClientExecutedToolType(toolType) {
|
|
@@ -14449,6 +14690,39 @@ function unknownCatalogToolMessage(toolId) {
|
|
|
14449
14690
|
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.`;
|
|
14450
14691
|
}
|
|
14451
14692
|
/**
|
|
14693
|
+
* Name-based references to saved agents and flows (agent:<name>, flow:<name>) for portable ensure/config-as-code definitions.
|
|
14694
|
+
* The name is canonical: hashed and persisted verbatim so definitions converge to the same content hash across environments.
|
|
14695
|
+
* Grammar mirrors parseNamedToolRef: split on the first colon only, exact case-sensitive match, no trimming.
|
|
14696
|
+
* The flow: prefix also names a template-variable namespace, but the two grammars never collide.
|
|
14697
|
+
*/
|
|
14698
|
+
/** Reserved prefix for a name-based saved-agent reference (`agent:<name>`). */
|
|
14699
|
+
const NAMED_AGENT_REF_PREFIX = "agent:";
|
|
14700
|
+
/** Reserved prefix for a name-based saved-flow reference (`flow:<name>`). */
|
|
14701
|
+
const NAMED_FLOW_REF_PREFIX = "flow:";
|
|
14702
|
+
function isNamedRef(value, prefix) {
|
|
14703
|
+
return value.startsWith(prefix) && value.length > prefix.length;
|
|
14704
|
+
}
|
|
14705
|
+
function parseNamedRef(value, prefix) {
|
|
14706
|
+
if (!isNamedRef(value, prefix)) return null;
|
|
14707
|
+
return { name: value.slice(prefix.length) };
|
|
14708
|
+
}
|
|
14709
|
+
/**
|
|
14710
|
+
* Parse an `agent:<name>` reference into its name. Split on the FIRST colon
|
|
14711
|
+
* only; the remainder is the agent name verbatim. Returns null when `value` is
|
|
14712
|
+
* not a valid named agent ref.
|
|
14713
|
+
*/
|
|
14714
|
+
function parseNamedAgentRef(value) {
|
|
14715
|
+
return parseNamedRef(value, NAMED_AGENT_REF_PREFIX);
|
|
14716
|
+
}
|
|
14717
|
+
/**
|
|
14718
|
+
* Parse a `flow:<name>` reference into its name. Split on the FIRST colon only;
|
|
14719
|
+
* the remainder is the flow name verbatim. Returns null when `value` is not a
|
|
14720
|
+
* valid named flow ref.
|
|
14721
|
+
*/
|
|
14722
|
+
function parseNamedFlowRef(value) {
|
|
14723
|
+
return parseNamedRef(value, NAMED_FLOW_REF_PREFIX);
|
|
14724
|
+
}
|
|
14725
|
+
/**
|
|
14452
14726
|
* Return whether an `execute-agent` step's `config.agentId` is a template
|
|
14453
14727
|
* expression rather than a fixed agent reference. No static gate can scan the
|
|
14454
14728
|
* agent a `{{...}}` ref substitutes to, so the runtime lane folds it as
|
|
@@ -14494,10 +14768,12 @@ function walkSteps$1(steps, basePath, context, state) {
|
|
|
14494
14768
|
}
|
|
14495
14769
|
function walkStep(node, path, context, state) {
|
|
14496
14770
|
if (!isRecord$1(node)) return;
|
|
14497
|
-
if (node.enabled === false) return;
|
|
14771
|
+
if (node.enabled === false && !state.includeDisabled) return;
|
|
14498
14772
|
const config = isRecord$1(node.config) ? node.config : void 0;
|
|
14499
14773
|
if (!config) return;
|
|
14774
|
+
state.visitor.visitStep?.(node, config, path, context);
|
|
14500
14775
|
if (node.type === "execute-agent") state.visitor.visitExecuteAgent(node, config, path, context);
|
|
14776
|
+
visitFlowFallbacks(config, path, context, state);
|
|
14501
14777
|
walkToolPayloads(config.tools, `${path}.config.tools`, context, state);
|
|
14502
14778
|
const deeper = {
|
|
14503
14779
|
...state,
|
|
@@ -14519,6 +14795,21 @@ function walkStep(node, path, context, state) {
|
|
|
14519
14795
|
});
|
|
14520
14796
|
for (const key of CONDITIONAL_BRANCH_STEP_KEYS$2) walkSteps$1(config[key], `${path}.config.${key}`, context, nested);
|
|
14521
14797
|
}
|
|
14798
|
+
function visitFlowFallbacks(config, path, context, state) {
|
|
14799
|
+
const { visitFlowFallback, visitStepFallback } = state.visitor;
|
|
14800
|
+
if (!visitFlowFallback && !visitStepFallback || !isRecord$1(config.errorHandling)) return;
|
|
14801
|
+
const fallbacks = config.errorHandling.fallbacks;
|
|
14802
|
+
if (!Array.isArray(fallbacks)) return;
|
|
14803
|
+
fallbacks.forEach((fallback, index) => {
|
|
14804
|
+
if (!isRecord$1(fallback)) return;
|
|
14805
|
+
const fallbackPath = `${path}.config.errorHandling.fallbacks[${index}]`;
|
|
14806
|
+
if (fallback.type === "flow") visitFlowFallback?.(fallback, fallbackPath, context);
|
|
14807
|
+
else if (fallback.type === "step" && visitStepFallback && isRecord$1(fallback.stepConfig)) {
|
|
14808
|
+
visitStepFallback(fallback, fallback.stepConfig, fallbackPath, context);
|
|
14809
|
+
walkToolPayloads(fallback.stepConfig.tools, `${fallbackPath}.stepConfig.tools`, context, state);
|
|
14810
|
+
}
|
|
14811
|
+
});
|
|
14812
|
+
}
|
|
14522
14813
|
const TOOL_BAG_KEYS = ["runtimeTools", "inlineTools"];
|
|
14523
14814
|
/** Descend the inline flow-as-tool and skill payloads a step's tool bag can carry. */
|
|
14524
14815
|
function walkToolPayloads(tools, path, context, state) {
|
|
@@ -14541,7 +14832,7 @@ function walkRuntimeTools(runtimeTools, basePath, context, state) {
|
|
|
14541
14832
|
if (!config) return;
|
|
14542
14833
|
const toolPath = `${basePath}[${index}].config`;
|
|
14543
14834
|
if (isRecord$1(config.skill) && Array.isArray(config.skill.resolvedTools)) walkRuntimeTools(config.skill.resolvedTools, `${toolPath}.skill.resolvedTools`, context, deeper);
|
|
14544
|
-
if (isRecord$1(config.flow) && Array.isArray(config.flow.steps)) {
|
|
14835
|
+
if (!state.skipInlineFlows && isRecord$1(config.flow) && Array.isArray(config.flow.steps)) {
|
|
14545
14836
|
const nestedContext = state.visitor.enterNestedFlow?.(config.flow, context) ?? context;
|
|
14546
14837
|
walkSteps$1(config.flow.steps, `${toolPath}.flow.steps`, nestedContext, {
|
|
14547
14838
|
...deeper,
|
|
@@ -15412,7 +15703,8 @@ const agentRuntimeConfigSchema = object({
|
|
|
15412
15703
|
toolPool: array(string()),
|
|
15413
15704
|
description: string().optional(),
|
|
15414
15705
|
timeoutMs: number$1().int().min(1e3).max(6e5).optional()
|
|
15415
|
-
}).optional()
|
|
15706
|
+
}).optional(),
|
|
15707
|
+
toolSearch: toolSearchConfigSchema.optional()
|
|
15416
15708
|
}).optional(),
|
|
15417
15709
|
artifacts: artifactConfigSchema.optional(),
|
|
15418
15710
|
contextManagement: contextManagementAgentConfigSchema.optional(),
|
|
@@ -18554,39 +18846,6 @@ function normalizeMemorySummaryConfig(config) {
|
|
|
18554
18846
|
defaultValue: config.defaultValue ?? config.default_value
|
|
18555
18847
|
};
|
|
18556
18848
|
}
|
|
18557
|
-
/**
|
|
18558
|
-
* Name-based references to saved agents and flows (agent:<name>, flow:<name>) for portable ensure/config-as-code definitions.
|
|
18559
|
-
* The name is canonical: hashed and persisted verbatim so definitions converge to the same content hash across environments.
|
|
18560
|
-
* Grammar mirrors parseNamedToolRef: split on the first colon only, exact case-sensitive match, no trimming.
|
|
18561
|
-
* The flow: prefix also names a template-variable namespace, but the two grammars never collide.
|
|
18562
|
-
*/
|
|
18563
|
-
/** Reserved prefix for a name-based saved-agent reference (`agent:<name>`). */
|
|
18564
|
-
const NAMED_AGENT_REF_PREFIX = "agent:";
|
|
18565
|
-
/** Reserved prefix for a name-based saved-flow reference (`flow:<name>`). */
|
|
18566
|
-
const NAMED_FLOW_REF_PREFIX = "flow:";
|
|
18567
|
-
function isNamedRef(value, prefix) {
|
|
18568
|
-
return value.startsWith(prefix) && value.length > prefix.length;
|
|
18569
|
-
}
|
|
18570
|
-
function parseNamedRef(value, prefix) {
|
|
18571
|
-
if (!isNamedRef(value, prefix)) return null;
|
|
18572
|
-
return { name: value.slice(prefix.length) };
|
|
18573
|
-
}
|
|
18574
|
-
/**
|
|
18575
|
-
* Parse an `agent:<name>` reference into its name. Split on the FIRST colon
|
|
18576
|
-
* only; the remainder is the agent name verbatim. Returns null when `value` is
|
|
18577
|
-
* not a valid named agent ref.
|
|
18578
|
-
*/
|
|
18579
|
-
function parseNamedAgentRef(value) {
|
|
18580
|
-
return parseNamedRef(value, NAMED_AGENT_REF_PREFIX);
|
|
18581
|
-
}
|
|
18582
|
-
/**
|
|
18583
|
-
* Parse a `flow:<name>` reference into its name. Split on the FIRST colon only;
|
|
18584
|
-
* the remainder is the flow name verbatim. Returns null when `value` is not a
|
|
18585
|
-
* valid named flow ref.
|
|
18586
|
-
*/
|
|
18587
|
-
function parseNamedFlowRef(value) {
|
|
18588
|
-
return parseNamedRef(value, NAMED_FLOW_REF_PREFIX);
|
|
18589
|
-
}
|
|
18590
18849
|
function sanitizeCodeToolIdentifier(name) {
|
|
18591
18850
|
let safe = name.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
18592
18851
|
if (/^[0-9]/.test(safe)) safe = "_" + safe;
|
|
@@ -22143,7 +22402,8 @@ _enum([
|
|
|
22143
22402
|
"saved_from_run",
|
|
22144
22403
|
"generated",
|
|
22145
22404
|
"imported",
|
|
22146
|
-
"fpo"
|
|
22405
|
+
"fpo",
|
|
22406
|
+
"ensure"
|
|
22147
22407
|
]);
|
|
22148
22408
|
/**
|
|
22149
22409
|
* Where a saved_from_run case was forked from.
|
|
@@ -23092,6 +23352,11 @@ const fpoSkillCapabilitiesSchema = object({
|
|
|
23092
23352
|
inlineTools: array(fpoSkillInlineToolSchema).optional(),
|
|
23093
23353
|
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()
|
|
23094
23354
|
});
|
|
23355
|
+
/** A `bindTo` entry that pins the binding to one skill versionNumber instead of following published. */
|
|
23356
|
+
const fpoSkillBindTargetSchema = object({
|
|
23357
|
+
capability: string().min(1),
|
|
23358
|
+
version: number$1().int().positive().optional()
|
|
23359
|
+
}).strict();
|
|
23095
23360
|
const fpoSkillSchema = object({
|
|
23096
23361
|
id: string().min(1),
|
|
23097
23362
|
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(),
|
|
@@ -23104,7 +23369,7 @@ const fpoSkillSchema = object({
|
|
|
23104
23369
|
"community"
|
|
23105
23370
|
]).optional(),
|
|
23106
23371
|
capabilities: fpoSkillCapabilitiesSchema.optional(),
|
|
23107
|
-
bindTo: array(string()).optional(),
|
|
23372
|
+
bindTo: array(union([string(), fpoSkillBindTargetSchema])).optional(),
|
|
23108
23373
|
createPolicy: createPolicySchema
|
|
23109
23374
|
});
|
|
23110
23375
|
const metaSchema = object({
|
|
@@ -24093,8 +24358,17 @@ function validateFPO(fpo) {
|
|
|
24093
24358
|
if (!skill || typeof skill !== "object") continue;
|
|
24094
24359
|
const s = skill;
|
|
24095
24360
|
if (Array.isArray(s.bindTo)) {
|
|
24096
|
-
|
|
24097
|
-
|
|
24361
|
+
const boundRefs = /* @__PURE__ */ new Set();
|
|
24362
|
+
for (const [bi, entry] of s.bindTo.entries()) {
|
|
24363
|
+
const ref = entry && typeof entry === "object" ? entry.capability : entry;
|
|
24364
|
+
if (boundRefs.has(ref)) {
|
|
24365
|
+
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}]`));
|
|
24366
|
+
continue;
|
|
24367
|
+
}
|
|
24368
|
+
boundRefs.add(ref);
|
|
24369
|
+
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}]`));
|
|
24370
|
+
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}]`));
|
|
24371
|
+
}
|
|
24098
24372
|
}
|
|
24099
24373
|
const capabilityRefs = s.capabilities && typeof s.capabilities === "object" ? s.capabilities.capabilityRefs : void 0;
|
|
24100
24374
|
if (Array.isArray(capabilityRefs)) {
|
|
@@ -25114,10 +25388,12 @@ object({
|
|
|
25114
25388
|
contextId: string().optional(),
|
|
25115
25389
|
taskId: string().optional()
|
|
25116
25390
|
}).optional(),
|
|
25117
|
-
/** The
|
|
25391
|
+
/** The version recorded for the run; on `legacy-live-row` it is the row's version, not what ran. */
|
|
25118
25392
|
agentVersionId: string().optional(),
|
|
25119
25393
|
/** The release alias the version was resolved through (`live`, `staging`, ...), when one was. */
|
|
25120
|
-
alias: string().optional()
|
|
25394
|
+
alias: string().optional(),
|
|
25395
|
+
/** How the run selected its definition, as `GET /v1/executions/{id}/status` reports it. */
|
|
25396
|
+
agentTargetResolution: _enum(AGENT_TARGET_RESOLUTIONS).optional()
|
|
25121
25397
|
});
|
|
25122
25398
|
object({
|
|
25123
25399
|
recordId: string().optional(),
|
|
@@ -27162,18 +27438,18 @@ const VERIFY_NEXT = "Ask the user for the 6-digit code from their email, then ru
|
|
|
27162
27438
|
function claimNext(email) {
|
|
27163
27439
|
return email ? `Send the verification email with: runtype auth claim ${email}` : "Ask the user for their email address, then run: runtype auth claim <email>";
|
|
27164
27440
|
}
|
|
27165
|
-
function fail$
|
|
27441
|
+
function fail$4(payload) {
|
|
27166
27442
|
console.error(JSON.stringify(payload, null, 2));
|
|
27167
27443
|
process.exit(1);
|
|
27168
27444
|
}
|
|
27169
27445
|
function failFromError(error, fallbackNext) {
|
|
27170
|
-
if (error instanceof AgentAuthError) fail$
|
|
27446
|
+
if (error instanceof AgentAuthError) fail$4({
|
|
27171
27447
|
status: "error",
|
|
27172
27448
|
code: error.code,
|
|
27173
27449
|
error: error.message,
|
|
27174
27450
|
...error.hint || fallbackNext ? { next: error.hint || fallbackNext } : {}
|
|
27175
27451
|
});
|
|
27176
|
-
fail$
|
|
27452
|
+
fail$4({
|
|
27177
27453
|
status: "error",
|
|
27178
27454
|
error: error instanceof Error ? error.message : String(error),
|
|
27179
27455
|
...fallbackNext ? { next: fallbackNext } : {}
|
|
@@ -27206,7 +27482,7 @@ async function registerAction(options) {
|
|
|
27206
27482
|
return;
|
|
27207
27483
|
}
|
|
27208
27484
|
}
|
|
27209
|
-
if (hasCredentials && !existingClaim && !options.force) fail$
|
|
27485
|
+
if (hasCredentials && !existingClaim && !options.force) fail$4({
|
|
27210
27486
|
status: "error",
|
|
27211
27487
|
code: "already_authenticated",
|
|
27212
27488
|
error: "Credentials are already stored for a claimed account.",
|
|
@@ -27258,7 +27534,7 @@ async function registerAction(options) {
|
|
|
27258
27534
|
});
|
|
27259
27535
|
return;
|
|
27260
27536
|
} catch (error) {
|
|
27261
|
-
if (error instanceof AgentAuthError) fail$
|
|
27537
|
+
if (error instanceof AgentAuthError) fail$4({
|
|
27262
27538
|
...base,
|
|
27263
27539
|
status: "registered_claim_failed",
|
|
27264
27540
|
code: error.code,
|
|
@@ -27278,7 +27554,7 @@ async function claimAction(email, options) {
|
|
|
27278
27554
|
const apiUrl = options.apiUrl || claimState?.apiUrl || (await store.getCredentials())?.apiUrl || getApiUrl();
|
|
27279
27555
|
if (!claimState) {
|
|
27280
27556
|
const hasCredentials = await store.hasCredentials();
|
|
27281
|
-
fail$
|
|
27557
|
+
fail$4({
|
|
27282
27558
|
status: "error",
|
|
27283
27559
|
code: "no_pending_registration",
|
|
27284
27560
|
error: hasCredentials ? "Stored credentials are already claimed; there is nothing to claim." : "No registration found.",
|
|
@@ -27306,13 +27582,13 @@ async function verifyAction(code, options) {
|
|
|
27306
27582
|
const store = new CredentialStore();
|
|
27307
27583
|
const claimState = await store.getClaimState();
|
|
27308
27584
|
const apiUrl = options.apiUrl || claimState?.apiUrl || (await store.getCredentials())?.apiUrl || getApiUrl();
|
|
27309
|
-
if (!claimState) fail$
|
|
27585
|
+
if (!claimState) fail$4({
|
|
27310
27586
|
status: "error",
|
|
27311
27587
|
code: "no_pending_registration",
|
|
27312
27588
|
error: "No claim in progress.",
|
|
27313
27589
|
next: "Run: runtype auth register --email <email> first."
|
|
27314
27590
|
});
|
|
27315
|
-
if (!claimState.claimEmail) fail$
|
|
27591
|
+
if (!claimState.claimEmail) fail$4({
|
|
27316
27592
|
status: "error",
|
|
27317
27593
|
code: "claim_not_initiated",
|
|
27318
27594
|
error: "No verification email has been sent yet, so there is no code to verify.",
|
|
@@ -27915,6 +28191,283 @@ function resolveJsonPayload(inline, fromFile, label) {
|
|
|
27915
28191
|
return parseJsonObject$1(inline, label);
|
|
27916
28192
|
}
|
|
27917
28193
|
//#endregion
|
|
28194
|
+
//#region src/lib/ensure-output.ts
|
|
28195
|
+
/**
|
|
28196
|
+
* Emit a pulled definition as JSON: to `out` when given, otherwise to stdout so
|
|
28197
|
+
* `runtype <entity> pull <name> > file.json` works. Hash and warnings go to stderr.
|
|
28198
|
+
*/
|
|
28199
|
+
function writePulledDefinition(data, meta) {
|
|
28200
|
+
const json = `${JSON.stringify(data, null, 2)}\n`;
|
|
28201
|
+
if (meta.out) {
|
|
28202
|
+
writeFileSync$1(meta.out, json);
|
|
28203
|
+
console.error(chalk.green(`Wrote ${meta.out}`));
|
|
28204
|
+
} else process.stdout.write(json);
|
|
28205
|
+
if (meta.contentHash) console.error(chalk.gray(`contentHash ${meta.contentHash}`));
|
|
28206
|
+
for (const warning of meta.warnings ?? []) console.error(chalk.yellow(`warning: ${warning}`));
|
|
28207
|
+
}
|
|
28208
|
+
//#endregion
|
|
28209
|
+
//#region src/commands/entity-ensure.ts
|
|
28210
|
+
function namespaceTransport(client) {
|
|
28211
|
+
const transport = {
|
|
28212
|
+
get: (path, params) => client.get(path, params),
|
|
28213
|
+
post: (path, body) => client.post(path, body)
|
|
28214
|
+
};
|
|
28215
|
+
return () => transport;
|
|
28216
|
+
}
|
|
28217
|
+
/** The SDK ensure options these flags ask for, shared by every entity. */
|
|
28218
|
+
function buildEntityEnsureOptions(flags, support) {
|
|
28219
|
+
if (flags.release !== void 0 && !["none", "publish"].includes(flags.release)) throw new Error("--release must be none or publish");
|
|
28220
|
+
const version = support.version && (flags.label || flags.notes) ? {
|
|
28221
|
+
...flags.label ? { label: flags.label } : {},
|
|
28222
|
+
...flags.notes ? { notes: flags.notes } : {}
|
|
28223
|
+
} : void 0;
|
|
28224
|
+
return {
|
|
28225
|
+
...flags.dryRun ? { dryRun: true } : {},
|
|
28226
|
+
...flags.expectNoChanges ? { expectNoChanges: true } : {},
|
|
28227
|
+
...flags.overwriteManaged ? { onConflict: "overwrite" } : {},
|
|
28228
|
+
...flags.expectedRemoteHash ? { expectedRemoteHash: flags.expectedRemoteHash } : {},
|
|
28229
|
+
...support.release && flags.release ? { release: flags.release } : {},
|
|
28230
|
+
...version ? { version } : {}
|
|
28231
|
+
};
|
|
28232
|
+
}
|
|
28233
|
+
const define = (fn, definition) => fn(definition);
|
|
28234
|
+
const SKILL_ENSURE_SPEC = {
|
|
28235
|
+
noun: "skill",
|
|
28236
|
+
idKey: "skillId",
|
|
28237
|
+
release: true,
|
|
28238
|
+
version: true,
|
|
28239
|
+
productScoped: false,
|
|
28240
|
+
ensure: async (t, def, options) => new SkillsNamespace(t).ensure(define(defineSkill, def), options),
|
|
28241
|
+
pull: (t, name) => new SkillsNamespace(t).pull(name)
|
|
28242
|
+
};
|
|
28243
|
+
const FLOW_ENSURE_SPEC = {
|
|
28244
|
+
noun: "flow",
|
|
28245
|
+
idKey: "flowId",
|
|
28246
|
+
release: true,
|
|
28247
|
+
version: true,
|
|
28248
|
+
productScoped: false,
|
|
28249
|
+
ensure: async (t, def, options) => new FlowsNamespace(t).ensure(define(defineFlow, def), options),
|
|
28250
|
+
pull: (t, name) => new FlowsNamespace(t).pull(name)
|
|
28251
|
+
};
|
|
28252
|
+
const TOOL_ENSURE_SPEC = {
|
|
28253
|
+
noun: "tool",
|
|
28254
|
+
idKey: "toolId",
|
|
28255
|
+
release: false,
|
|
28256
|
+
version: false,
|
|
28257
|
+
productScoped: false,
|
|
28258
|
+
ensure: async (t, def, options) => new ToolsNamespace(t).ensure(define(defineTool, def), options),
|
|
28259
|
+
pull: (t, name) => new ToolsNamespace(t).pull(name)
|
|
28260
|
+
};
|
|
28261
|
+
const PRODUCT_ENSURE_SPEC = {
|
|
28262
|
+
noun: "product",
|
|
28263
|
+
idKey: "productId",
|
|
28264
|
+
release: false,
|
|
28265
|
+
version: false,
|
|
28266
|
+
productScoped: false,
|
|
28267
|
+
ensure: async (t, def, options) => new ProductsNamespace(t).ensure(define(defineProduct, def), options),
|
|
28268
|
+
pull: (t, name) => new ProductsNamespace(t).pull(name)
|
|
28269
|
+
};
|
|
28270
|
+
const SURFACE_ENSURE_SPEC = {
|
|
28271
|
+
noun: "surface",
|
|
28272
|
+
idKey: "surfaceId",
|
|
28273
|
+
release: false,
|
|
28274
|
+
version: false,
|
|
28275
|
+
productScoped: true,
|
|
28276
|
+
ensure: async (t, def, options, productId) => new SurfacesNamespace(t).ensure(productId, define(defineSurface, def), options),
|
|
28277
|
+
pull: (t, name, productId) => new SurfacesNamespace(t).pull(productId, name)
|
|
28278
|
+
};
|
|
28279
|
+
function isDriftError(error) {
|
|
28280
|
+
return error instanceof Error && error.name.endsWith("DriftError") && "plan" in error;
|
|
28281
|
+
}
|
|
28282
|
+
function printEnsureOutcome(spec, name, result) {
|
|
28283
|
+
if (result.result === "plan") {
|
|
28284
|
+
console.log(chalk.cyan(`Plan for ${spec.noun} "${name}": ${result.changes ?? "n/a"}`));
|
|
28285
|
+
for (const key of result.changedKeys ?? []) console.log(` ${key}`);
|
|
28286
|
+
if (result.remoteHash) console.log(chalk.gray(`Apply this plan with --expected-remote-hash ${result.remoteHash}`));
|
|
28287
|
+
return;
|
|
28288
|
+
}
|
|
28289
|
+
const id = typeof result[spec.idKey] === "string" ? ` (${result[spec.idKey]})` : "";
|
|
28290
|
+
console.log(chalk.green(`${spec.noun[0].toUpperCase()}${spec.noun.slice(1)} "${name}" ${result.result}${id}`));
|
|
28291
|
+
if (result.versionId) console.log(chalk.gray(`Version ${result.versionId}`));
|
|
28292
|
+
}
|
|
28293
|
+
function fail$3(message, detail) {
|
|
28294
|
+
console.error(chalk.red(message));
|
|
28295
|
+
if (detail !== void 0) console.error(chalk.red(detail instanceof Error ? detail.message : String(detail)));
|
|
28296
|
+
process.exit(1);
|
|
28297
|
+
}
|
|
28298
|
+
/** `ensure <file>`: converge a definition file (a pull's output, or its `definition`). */
|
|
28299
|
+
function buildEntityEnsureCommand(spec) {
|
|
28300
|
+
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`);
|
|
28301
|
+
if (spec.productScoped) command.requiredOption("--product <productId>", "Product the surface belongs to");
|
|
28302
|
+
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)");
|
|
28303
|
+
if (spec.release) command.option("--release <mode>", "none or publish");
|
|
28304
|
+
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");
|
|
28305
|
+
command.option("--json", "Output as JSON").action(async (file, flags) => {
|
|
28306
|
+
let options;
|
|
28307
|
+
let definition;
|
|
28308
|
+
try {
|
|
28309
|
+
options = buildEntityEnsureOptions(flags, spec);
|
|
28310
|
+
definition = readJsonFile(file, {
|
|
28311
|
+
label: `${spec.noun} definition`,
|
|
28312
|
+
unwrapKey: "definition"
|
|
28313
|
+
});
|
|
28314
|
+
} catch (error) {
|
|
28315
|
+
fail$3(error instanceof Error ? error.message : String(error));
|
|
28316
|
+
}
|
|
28317
|
+
const name = typeof definition.name === "string" ? definition.name : "";
|
|
28318
|
+
const apiKey = await ensureAuth();
|
|
28319
|
+
if (!apiKey) return;
|
|
28320
|
+
const transport = namespaceTransport(createCliClient(apiKey));
|
|
28321
|
+
try {
|
|
28322
|
+
const result = await spec.ensure(transport, definition, options, flags.product);
|
|
28323
|
+
if (flags.json) printJson(result);
|
|
28324
|
+
else printEnsureOutcome(spec, name, result);
|
|
28325
|
+
} catch (error) {
|
|
28326
|
+
if (isDriftError(error)) {
|
|
28327
|
+
if (flags.json) printJson({
|
|
28328
|
+
drift: true,
|
|
28329
|
+
plan: error.plan
|
|
28330
|
+
});
|
|
28331
|
+
else console.error(chalk.yellow(error.message));
|
|
28332
|
+
process.exit(1);
|
|
28333
|
+
}
|
|
28334
|
+
fail$3(`Failed to converge the ${spec.noun}`, error);
|
|
28335
|
+
}
|
|
28336
|
+
});
|
|
28337
|
+
return command;
|
|
28338
|
+
}
|
|
28339
|
+
/** `pull <name>`: print the definition file `ensure` reads back (dashboard wins). */
|
|
28340
|
+
function buildEntityPullCommand(spec) {
|
|
28341
|
+
const command = new Command("pull").description(`Print a ${spec.noun} as the definition file \`ensure\` reads (dashboard wins)`).argument("<name>", `${spec.noun} name`);
|
|
28342
|
+
if (spec.productScoped) command.requiredOption("--product <productId>", "Product the surface belongs to");
|
|
28343
|
+
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) => {
|
|
28344
|
+
const apiKey = await ensureAuth();
|
|
28345
|
+
if (!apiKey) return;
|
|
28346
|
+
const transport = namespaceTransport(createCliClient(apiKey));
|
|
28347
|
+
try {
|
|
28348
|
+
const pulled = await spec.pull(transport, name, flags.product);
|
|
28349
|
+
writePulledDefinition(flags.json ? pulled : pulled.definition, {
|
|
28350
|
+
out: flags.out,
|
|
28351
|
+
contentHash: pulled.contentHash,
|
|
28352
|
+
warnings: pulled.warnings
|
|
28353
|
+
});
|
|
28354
|
+
} catch (error) {
|
|
28355
|
+
fail$3(`Failed to pull ${spec.noun} "${name}"`, error);
|
|
28356
|
+
}
|
|
28357
|
+
});
|
|
28358
|
+
return command;
|
|
28359
|
+
}
|
|
28360
|
+
function buildFpoEnsureOptions(flags) {
|
|
28361
|
+
if (flags.release !== void 0 && !["none", "publish"].includes(flags.release)) throw new Error("--release must be none or publish");
|
|
28362
|
+
return {
|
|
28363
|
+
...flags.dryRun ? { dryRun: true } : {},
|
|
28364
|
+
...flags.expectNoChanges ? { expectNoChanges: true } : {},
|
|
28365
|
+
...flags.overwriteManaged ? { onConflict: "overwrite" } : {},
|
|
28366
|
+
...flags.expectedRemoteHash ? { expectedRemoteHash: flags.expectedRemoteHash } : {},
|
|
28367
|
+
...flags.prune ? { prune: true } : {},
|
|
28368
|
+
...flags.hashProbe ? { hashProbe: true } : {},
|
|
28369
|
+
...flags.release ? { release: flags.release } : {}
|
|
28370
|
+
};
|
|
28371
|
+
}
|
|
28372
|
+
/** `ensure-fpo <file>`: converge a whole Full Product Object. */
|
|
28373
|
+
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) => {
|
|
28374
|
+
let fpo;
|
|
28375
|
+
try {
|
|
28376
|
+
fpo = defineFpo(readJsonFile(file, {
|
|
28377
|
+
label: "FPO",
|
|
28378
|
+
unwrapKey: "fpo"
|
|
28379
|
+
}));
|
|
28380
|
+
} catch (error) {
|
|
28381
|
+
fail$3(error instanceof Error ? error.message : String(error));
|
|
28382
|
+
}
|
|
28383
|
+
const apiKey = await ensureAuth();
|
|
28384
|
+
if (!apiKey) return;
|
|
28385
|
+
const products = new ProductsNamespace(namespaceTransport(createCliClient(apiKey)));
|
|
28386
|
+
try {
|
|
28387
|
+
const result = await products.ensureFpo(fpo, buildFpoEnsureOptions(flags));
|
|
28388
|
+
if (flags.json) printJson(result);
|
|
28389
|
+
else {
|
|
28390
|
+
console.log(chalk.cyan(`Product FPO ${result.result}${result.productId ? ` (${result.productId})` : ""}`));
|
|
28391
|
+
for (const entity of result.entities) {
|
|
28392
|
+
const line = ` ${entity.kind}:${entity.name} ${entity.result}`;
|
|
28393
|
+
console.log(entity.result === "failed" ? chalk.red(`${line} — ${entity.error ?? ""}`) : line);
|
|
28394
|
+
}
|
|
28395
|
+
if (result.remoteHash) console.log(chalk.gray(`Apply this plan with --expected-remote-hash ${result.remoteHash}`));
|
|
28396
|
+
}
|
|
28397
|
+
if (result.hasFailures) process.exit(1);
|
|
28398
|
+
} catch (error) {
|
|
28399
|
+
if (isDriftError(error)) {
|
|
28400
|
+
if (flags.json) printJson({
|
|
28401
|
+
drift: true,
|
|
28402
|
+
plan: error.plan
|
|
28403
|
+
});
|
|
28404
|
+
else console.error(chalk.yellow(error.message));
|
|
28405
|
+
process.exit(1);
|
|
28406
|
+
}
|
|
28407
|
+
fail$3("Failed to converge the product FPO", error);
|
|
28408
|
+
}
|
|
28409
|
+
});
|
|
28410
|
+
/** Runtype Agent Skills live under `agents` because `runtype skills` installs coding-agent skills. */
|
|
28411
|
+
const agentSkillsCommand = new Command("skills").description("Runtype Agent Skills: context bundles deployed agents load (for coding-agent skills see `runtype skills`)");
|
|
28412
|
+
agentSkillsCommand.addCommand(buildEntityEnsureCommand(SKILL_ENSURE_SPEC));
|
|
28413
|
+
agentSkillsCommand.addCommand(buildEntityPullCommand(SKILL_ENSURE_SPEC));
|
|
28414
|
+
/** `pull-fpo <name>`: print the whole product as the FPO `ensure-fpo` reads. */
|
|
28415
|
+
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) => {
|
|
28416
|
+
const apiKey = await ensureAuth();
|
|
28417
|
+
if (!apiKey) return;
|
|
28418
|
+
const products = new ProductsNamespace(namespaceTransport(createCliClient(apiKey)));
|
|
28419
|
+
try {
|
|
28420
|
+
const pulled = await products.pullFpo(name);
|
|
28421
|
+
writePulledDefinition(flags.json ? pulled : pulled.fpo, {
|
|
28422
|
+
out: flags.out,
|
|
28423
|
+
contentHash: pulled.contentHash,
|
|
28424
|
+
warnings: pulled.warnings
|
|
28425
|
+
});
|
|
28426
|
+
} catch (error) {
|
|
28427
|
+
fail$3(`Failed to pull product "${name}"`, error);
|
|
28428
|
+
}
|
|
28429
|
+
});
|
|
28430
|
+
/** Converge a client token file through the SDK; it keys on `name` and never deletes or regenerates. */
|
|
28431
|
+
async function ensureClientTokenFromFile(client, definition, flags) {
|
|
28432
|
+
const getClient = (() => client);
|
|
28433
|
+
return new ClientTokensNamespace(getClient).ensure(definition, {
|
|
28434
|
+
...flags.dryRun ? { dryRun: true } : {},
|
|
28435
|
+
...flags.expectNoChanges ? { expectNoChanges: true } : {}
|
|
28436
|
+
});
|
|
28437
|
+
}
|
|
28438
|
+
/** `client-tokens ensure <file>`: the widget token step of the agent-plus-token workflow. */
|
|
28439
|
+
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) => {
|
|
28440
|
+
let definition;
|
|
28441
|
+
try {
|
|
28442
|
+
definition = readJsonFile(file, { label: "client token definition" });
|
|
28443
|
+
} catch (error) {
|
|
28444
|
+
fail$3(error instanceof Error ? error.message : String(error));
|
|
28445
|
+
}
|
|
28446
|
+
const apiKey = await ensureAuth();
|
|
28447
|
+
if (!apiKey) return;
|
|
28448
|
+
try {
|
|
28449
|
+
const result = await ensureClientTokenFromFile(createCliClient(apiKey), definition, flags);
|
|
28450
|
+
if (flags.json) printJson(result);
|
|
28451
|
+
else if (result.result === "plan") {
|
|
28452
|
+
console.log(chalk.cyan(`Plan for client token "${String(definition.name)}": ${result.changes}`));
|
|
28453
|
+
for (const key of result.changedKeys) console.log(` ${key}`);
|
|
28454
|
+
} else {
|
|
28455
|
+
console.log(chalk.green(`Client token "${result.clientToken.name}" ${result.result} (${result.clientToken.id})`));
|
|
28456
|
+
if (result.token) console.log(`Token ${result.token}`);
|
|
28457
|
+
}
|
|
28458
|
+
} catch (error) {
|
|
28459
|
+
if (isDriftError(error)) {
|
|
28460
|
+
if (flags.json) printJson({
|
|
28461
|
+
drift: true,
|
|
28462
|
+
plan: error.plan
|
|
28463
|
+
});
|
|
28464
|
+
else console.error(chalk.yellow(error.message));
|
|
28465
|
+
process.exit(1);
|
|
28466
|
+
}
|
|
28467
|
+
fail$3("Failed to converge the client token", error);
|
|
28468
|
+
}
|
|
28469
|
+
});
|
|
28470
|
+
//#endregion
|
|
27918
28471
|
//#region src/lib/execution-input.ts
|
|
27919
28472
|
function addExecutionInputOptions(command) {
|
|
27920
28473
|
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");
|
|
@@ -28240,6 +28793,8 @@ function flowsListParams(options) {
|
|
|
28240
28793
|
};
|
|
28241
28794
|
}
|
|
28242
28795
|
const flowsCommand = new Command("flows").description("Manage flows");
|
|
28796
|
+
flowsCommand.addCommand(buildEntityEnsureCommand(FLOW_ENSURE_SPEC));
|
|
28797
|
+
flowsCommand.addCommand(buildEntityPullCommand(FLOW_ENSURE_SPEC));
|
|
28243
28798
|
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) => {
|
|
28244
28799
|
const apiKey = await ensureAuth();
|
|
28245
28800
|
if (!apiKey) return;
|
|
@@ -31453,6 +32008,8 @@ function formatToolMetadata(metadata) {
|
|
|
31453
32008
|
return parts.join("; ");
|
|
31454
32009
|
}
|
|
31455
32010
|
const toolsCommand = new Command("tools").description("Manage runtime tools (saved tools)");
|
|
32011
|
+
toolsCommand.addCommand(buildEntityEnsureCommand(TOOL_ENSURE_SPEC));
|
|
32012
|
+
toolsCommand.addCommand(buildEntityPullCommand(TOOL_ENSURE_SPEC));
|
|
31456
32013
|
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) => {
|
|
31457
32014
|
const apiKey = await ensureAuth();
|
|
31458
32015
|
if (!apiKey) return;
|
|
@@ -32247,6 +32804,8 @@ function parsePositiveInteger$1(value, option) {
|
|
|
32247
32804
|
return parsed;
|
|
32248
32805
|
}
|
|
32249
32806
|
const surfacesCommand = new Command("surfaces").description("View product surfaces");
|
|
32807
|
+
surfacesCommand.addCommand(buildEntityEnsureCommand(SURFACE_ENSURE_SPEC));
|
|
32808
|
+
surfacesCommand.addCommand(buildEntityPullCommand(SURFACE_ENSURE_SPEC));
|
|
32250
32809
|
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) => {
|
|
32251
32810
|
const apiKey = await ensureAuth();
|
|
32252
32811
|
if (!apiKey) return;
|
|
@@ -32920,6 +33479,10 @@ async function validateUrl(url, apiUrl) {
|
|
|
32920
33479
|
return validateQuickStartUrl(url, apiUrl);
|
|
32921
33480
|
}
|
|
32922
33481
|
const productsCommand = new Command("products").description("Manage products");
|
|
33482
|
+
productsCommand.addCommand(buildEntityEnsureCommand(PRODUCT_ENSURE_SPEC));
|
|
33483
|
+
productsCommand.addCommand(buildEntityPullCommand(PRODUCT_ENSURE_SPEC));
|
|
33484
|
+
productsCommand.addCommand(productEnsureFpoCommand);
|
|
33485
|
+
productsCommand.addCommand(productPullFpoCommand);
|
|
32923
33486
|
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) => {
|
|
32924
33487
|
const isJsonMode = !!options.json;
|
|
32925
33488
|
if (!isTTY(options) || isJsonMode) {
|
|
@@ -46650,28 +47213,113 @@ agentDeploymentsCommand.command("list <agentId>").description("List deployment r
|
|
|
46650
47213
|
* The activation the flags ask for. `release` and `deploy` are two spellings of
|
|
46651
47214
|
* one thing, so supplying both is refused before any request is sent.
|
|
46652
47215
|
*/
|
|
46653
|
-
function buildEnsureActivation(flags) {
|
|
47216
|
+
function buildEnsureActivation(flags, bindings) {
|
|
46654
47217
|
if (flags.release !== void 0 && flags.deploy !== void 0) throw new Error(ENSURE_RELEASE_DEPLOY_CONFLICT_MESSAGE);
|
|
46655
|
-
if (flags.deploy
|
|
47218
|
+
if (bindings !== void 0 && flags.deploy === void 0) throw new Error("--bindings-file sets per-alias secret bindings, so it needs --deploy <alias>.");
|
|
47219
|
+
if (flags.deploy !== void 0) return { deploy: {
|
|
47220
|
+
alias: flags.deploy,
|
|
47221
|
+
...bindings !== void 0 ? { bindings } : {}
|
|
47222
|
+
} };
|
|
46656
47223
|
if (flags.release !== void 0) return { release: flags.release };
|
|
46657
47224
|
return {};
|
|
46658
47225
|
}
|
|
46659
|
-
|
|
47226
|
+
/** The API refuses expectedRemoteHash on a preview deploy, since it guards live-row writes only. */
|
|
47227
|
+
function acceptsExpectedRemoteHash(deploy) {
|
|
47228
|
+
return deploy === void 0 || deploy === "live";
|
|
47229
|
+
}
|
|
47230
|
+
/**
|
|
47231
|
+
* Parse a `--bindings-file` object of `{ SECRET_NAME: value }`. The whole object
|
|
47232
|
+
* replaces the alias's bindings, so `{}` clears them.
|
|
47233
|
+
*/
|
|
47234
|
+
function parseAliasBindings(file) {
|
|
47235
|
+
const bindings = {};
|
|
47236
|
+
for (const [name, value] of Object.entries(file)) {
|
|
47237
|
+
if (typeof value !== "string") throw new Error(`Binding "${name}" must be a string secret value.`);
|
|
47238
|
+
bindings[name] = value;
|
|
47239
|
+
}
|
|
47240
|
+
return bindings;
|
|
47241
|
+
}
|
|
47242
|
+
/** The file `agents ensure` reads back: the definition with its skills and capabilities at the top level. */
|
|
47243
|
+
function agentPullToDefinitionFile(pulled) {
|
|
47244
|
+
return {
|
|
47245
|
+
...pulled.definition,
|
|
47246
|
+
...pulled.skills !== void 0 ? { skills: pulled.skills } : {},
|
|
47247
|
+
...pulled.capabilities !== void 0 ? { capabilities: pulled.capabilities } : {}
|
|
47248
|
+
};
|
|
47249
|
+
}
|
|
47250
|
+
/**
|
|
47251
|
+
* Lift a definition file's top-level `skills` (the agent's complete binding set)
|
|
47252
|
+
* out of the definition, which ensure hashes, and into the request.
|
|
47253
|
+
*/
|
|
47254
|
+
function splitDefinitionSkills(file) {
|
|
47255
|
+
if (!("skills" in file)) return { definition: file };
|
|
47256
|
+
const { skills, ...definition } = file;
|
|
47257
|
+
if (!Array.isArray(skills)) throw new Error("\"skills\" must be an array of { name, version?, enabled? } bindings.");
|
|
47258
|
+
return {
|
|
47259
|
+
definition,
|
|
47260
|
+
skills
|
|
47261
|
+
};
|
|
47262
|
+
}
|
|
47263
|
+
/**
|
|
47264
|
+
* Lift a definition file's top-level `skills` and `capabilities` (each the agent's
|
|
47265
|
+
* complete set) out of the definition and into the request.
|
|
47266
|
+
*/
|
|
47267
|
+
function splitDefinitionAgentState(file) {
|
|
47268
|
+
const { definition: withCapabilities, skills } = splitDefinitionSkills(file);
|
|
47269
|
+
if (!("capabilities" in withCapabilities)) return {
|
|
47270
|
+
definition: withCapabilities,
|
|
47271
|
+
...skills !== void 0 ? { skills } : {}
|
|
47272
|
+
};
|
|
47273
|
+
const { capabilities, ...definition } = withCapabilities;
|
|
47274
|
+
if (!Array.isArray(capabilities)) throw new Error("\"capabilities\" must be an array of { toolName, flow | agent | tool, description?, parametersSchema?, enabled? } entries.");
|
|
47275
|
+
return {
|
|
47276
|
+
definition,
|
|
47277
|
+
...skills !== void 0 ? { skills } : {},
|
|
47278
|
+
capabilities
|
|
47279
|
+
};
|
|
47280
|
+
}
|
|
47281
|
+
function formatCapabilityChange(change) {
|
|
47282
|
+
const target = change.kind && change.source ? ` (${change.kind}:${change.source})` : "";
|
|
47283
|
+
return ` capability:${change.toolName} ${change.action}${target}`;
|
|
47284
|
+
}
|
|
47285
|
+
/**
|
|
47286
|
+
* Whether a dry-run plan drifts from the definition: content, skill bindings, capabilities, or,
|
|
47287
|
+
* when the flags ask for a deploy, the release pointer or its binding names (#9371).
|
|
47288
|
+
*/
|
|
47289
|
+
function ensurePlanHasDrift(result, activation) {
|
|
47290
|
+
if (result.result !== "plan") return false;
|
|
47291
|
+
if (result.changes !== "none") return true;
|
|
47292
|
+
if ((result.skillBindings ?? []).some((b) => b.action !== "unchanged")) return true;
|
|
47293
|
+
if ((result.capabilityChanges ?? []).some((c) => c.action !== "unchanged")) return true;
|
|
47294
|
+
return (activation.deploy !== void 0 || activation.release === "publish") && (result.deployment?.changed === true || result.deployment?.bindingsChanged === true);
|
|
47295
|
+
}
|
|
47296
|
+
function formatSkillBinding(binding) {
|
|
47297
|
+
const pin = binding.version === null ? "published" : `v${binding.version}`;
|
|
47298
|
+
return ` skill:${binding.name} ${binding.action} (${pin})`;
|
|
47299
|
+
}
|
|
47300
|
+
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) => {
|
|
46660
47301
|
if (options.release !== void 0 && !["none", "publish"].includes(options.release)) {
|
|
46661
47302
|
console.error(chalk.red("--release must be none or publish"));
|
|
46662
47303
|
process.exit(1);
|
|
46663
47304
|
}
|
|
46664
47305
|
let activation;
|
|
46665
47306
|
try {
|
|
46666
|
-
activation = buildEnsureActivation(options);
|
|
47307
|
+
activation = buildEnsureActivation(options, options.bindingsFile !== void 0 ? parseAliasBindings(readJsonFile(options.bindingsFile, { label: "alias bindings" })) : void 0);
|
|
46667
47308
|
} catch (error) {
|
|
46668
47309
|
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
46669
47310
|
process.exit(1);
|
|
46670
47311
|
}
|
|
46671
|
-
|
|
46672
|
-
|
|
46673
|
-
|
|
46674
|
-
|
|
47312
|
+
let parsed;
|
|
47313
|
+
try {
|
|
47314
|
+
parsed = splitDefinitionAgentState(readJsonFile(file, {
|
|
47315
|
+
label: "agent definition",
|
|
47316
|
+
unwrapKey: "agent"
|
|
47317
|
+
}));
|
|
47318
|
+
} catch (error) {
|
|
47319
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
47320
|
+
process.exit(1);
|
|
47321
|
+
}
|
|
47322
|
+
const { definition, skills, capabilities } = parsed;
|
|
46675
47323
|
const name = definition.name;
|
|
46676
47324
|
if (typeof name !== "string" || name.length === 0) {
|
|
46677
47325
|
console.error(chalk.red("The agent definition needs a non-empty \"name\"."));
|
|
@@ -46692,22 +47340,35 @@ const agentEnsureCommand = new Command("ensure").description("Converge an agent
|
|
|
46692
47340
|
...dryRun ? { dryRun: true } : {},
|
|
46693
47341
|
...options.overwriteManaged ? { onConflict: "overwrite" } : {},
|
|
46694
47342
|
...version ? { version } : {},
|
|
47343
|
+
...options.expectedRemoteHash ? { expectedRemoteHash: options.expectedRemoteHash } : {},
|
|
47344
|
+
...skills !== void 0 ? { skills } : {},
|
|
47345
|
+
...capabilities !== void 0 ? { capabilities } : {},
|
|
46695
47346
|
...activation
|
|
46696
47347
|
});
|
|
46697
47348
|
if (options.json) printJson(result);
|
|
46698
47349
|
else if (result.result === "plan") {
|
|
46699
47350
|
console.log(chalk.cyan(`Plan for "${name}": ${result.changes}`));
|
|
46700
47351
|
for (const key of result.changedKeys ?? []) console.log(` ${key}`);
|
|
47352
|
+
if (result.remoteHash && acceptsExpectedRemoteHash(options.deploy)) console.log(chalk.gray(`Apply this plan with --expected-remote-hash ${result.remoteHash}`));
|
|
46701
47353
|
} else {
|
|
46702
47354
|
console.log(chalk.green(`Agent "${name}" ${result.result} (${result.agentId})`));
|
|
46703
47355
|
if (result.versionId) console.log(chalk.gray(`Version ${result.versionId}`));
|
|
46704
47356
|
}
|
|
47357
|
+
const skillChanges = (result.skillBindings ?? []).filter((b) => b.action !== "unchanged");
|
|
47358
|
+
const capabilityChanges = (result.capabilityChanges ?? []).filter((change) => change.action !== "unchanged");
|
|
47359
|
+
if (!options.json) {
|
|
47360
|
+
for (const binding of skillChanges) console.log(formatSkillBinding(binding));
|
|
47361
|
+
for (const change of capabilityChanges) console.log(formatCapabilityChange(change));
|
|
47362
|
+
}
|
|
46705
47363
|
if (result.deployment && !options.json) {
|
|
46706
|
-
const { alias, versionId, revision, changed } = result.deployment;
|
|
46707
|
-
|
|
47364
|
+
const { alias, versionId, revision, changed, bindingsChanged } = result.deployment;
|
|
47365
|
+
const planned = result.result === "plan";
|
|
47366
|
+
const verb = changed ? planned ? "Would deploy over" : "Deployed" : "Already deployed";
|
|
47367
|
+
console.log(chalk.green(`${verb} ${versionId} at "${alias}" (revision ${revision})`));
|
|
47368
|
+
if (planned && bindingsChanged) console.log(` deploy:${alias} (bindings)`);
|
|
46708
47369
|
if (alias !== "live") console.log(chalk.gray("Live unchanged."));
|
|
46709
47370
|
}
|
|
46710
|
-
if (options.expectNoChanges && result
|
|
47371
|
+
if (options.expectNoChanges && ensurePlanHasDrift(result, activation)) process.exit(1);
|
|
46711
47372
|
} catch (error) {
|
|
46712
47373
|
const message = error instanceof Error ? error.message : String(error);
|
|
46713
47374
|
console.error(chalk.red("Failed to converge the agent"));
|
|
@@ -46715,6 +47376,23 @@ const agentEnsureCommand = new Command("ensure").description("Converge an agent
|
|
|
46715
47376
|
process.exit(1);
|
|
46716
47377
|
}
|
|
46717
47378
|
});
|
|
47379
|
+
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) => {
|
|
47380
|
+
const apiKey = await ensureAuth();
|
|
47381
|
+
if (!apiKey) return;
|
|
47382
|
+
const client = createCliClient(apiKey);
|
|
47383
|
+
try {
|
|
47384
|
+
const pulled = await client.get("/agents/pull", { name });
|
|
47385
|
+
writePulledDefinition(options.json ? pulled : agentPullToDefinitionFile(pulled), {
|
|
47386
|
+
out: options.out,
|
|
47387
|
+
contentHash: pulled.contentHash,
|
|
47388
|
+
warnings: pulled.warnings
|
|
47389
|
+
});
|
|
47390
|
+
} catch (error) {
|
|
47391
|
+
console.error(chalk.red(`Failed to pull agent "${name}"`));
|
|
47392
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
|
|
47393
|
+
process.exit(1);
|
|
47394
|
+
}
|
|
47395
|
+
});
|
|
46718
47396
|
//#endregion
|
|
46719
47397
|
//#region src/commands/agent-promote.ts
|
|
46720
47398
|
const DEFAULT_SOURCE_KEY_ENV = "RUNTYPE_SOURCE_API_KEY";
|
|
@@ -48217,6 +48895,8 @@ agentsCommand.addCommand(taskCommand);
|
|
|
48217
48895
|
agentsCommand.addCommand(agentAliasesCommand);
|
|
48218
48896
|
agentsCommand.addCommand(agentDeploymentsCommand);
|
|
48219
48897
|
agentsCommand.addCommand(agentEnsureCommand);
|
|
48898
|
+
agentsCommand.addCommand(agentPullCommand);
|
|
48899
|
+
agentsCommand.addCommand(agentSkillsCommand);
|
|
48220
48900
|
agentsCommand.addCommand(agentPromoteCommand);
|
|
48221
48901
|
agentsCommand.addCommand(agentExportRuntimeCommand);
|
|
48222
48902
|
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) => {
|
|
@@ -49885,7 +50565,7 @@ function toJUnitSuite(outcome) {
|
|
|
49885
50565
|
}))
|
|
49886
50566
|
};
|
|
49887
50567
|
}
|
|
49888
|
-
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) => {
|
|
50568
|
+
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) => {
|
|
49889
50569
|
const apiKey = await ensureAuth();
|
|
49890
50570
|
if (!apiKey) failConfig("Not authenticated. Set RUNTYPE_API_KEY or run `runtype login`.");
|
|
49891
50571
|
let agentSelector = {};
|
|
@@ -49930,7 +50610,8 @@ evalCommand.command("run [idOrDirPrefix]").description("Run code-colocated eval
|
|
|
49930
50610
|
else {
|
|
49931
50611
|
const ensured = await client.post("/eval/ensure", {
|
|
49932
50612
|
name: def.name,
|
|
49933
|
-
definition: def
|
|
50613
|
+
definition: def,
|
|
50614
|
+
...options.overwriteManaged ? { onConflict: "overwrite" } : {}
|
|
49934
50615
|
});
|
|
49935
50616
|
if (!ensured.suiteId) throw new Error(`ensure did not return a suiteId (result: ${ensured.result})`);
|
|
49936
50617
|
result = await client.post("/eval/run", {
|
|
@@ -51501,6 +52182,7 @@ clientTokensCommand.command("regenerate <id>").description("Regenerate a client
|
|
|
51501
52182
|
const { waitUntilExit } = render(React.createElement(App));
|
|
51502
52183
|
await waitUntilExit();
|
|
51503
52184
|
});
|
|
52185
|
+
clientTokensCommand.addCommand(clientTokenEnsureCommand);
|
|
51504
52186
|
//#endregion
|
|
51505
52187
|
//#region ../../node_modules/.pnpm/@runtypelabs+persona@4.24.0/node_modules/@runtypelabs/persona/dist/codegen.js
|
|
51506
52188
|
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.45.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.9.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.69.0",
|
|
37
37
|
"@types/express": "^5.0.6",
|
|
38
38
|
"@types/micromatch": "^4.0.9",
|
|
39
39
|
"@types/node": "^25.3.3",
|