@runtypelabs/cli 2.42.3 → 2.42.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +318 -93
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -14828,6 +14828,11 @@ _enum([
|
|
|
14828
14828
|
"pending"
|
|
14829
14829
|
]);
|
|
14830
14830
|
/**
|
|
14831
|
+
* Pattern enforced by `SecretStoreService.validateSecretKey`:
|
|
14832
|
+
* `/^[A-Z][A-Z0-9_]{0,98}[A-Z0-9]$/`. Keep in sync if that validator changes.
|
|
14833
|
+
*/
|
|
14834
|
+
const SECRET_KEY_PATTERN = /^[A-Z][A-Z0-9_]{0,98}[A-Z0-9]$/;
|
|
14835
|
+
/**
|
|
14831
14836
|
* Operator allowlist for a single filter condition. No raw SQL passthrough —
|
|
14832
14837
|
* the SQL compiler maps each operator to a parameterized fragment.
|
|
14833
14838
|
*/
|
|
@@ -15221,7 +15226,7 @@ const memoryConfigSchema = object({
|
|
|
15221
15226
|
* With 'open', a prompt-injected agent can exfiltrate context to any host; no separate plan entitlement restricts it.
|
|
15222
15227
|
* Revisit with a Schematic entitlement if/when sandbox egress becomes plan-differentiated.
|
|
15223
15228
|
*/
|
|
15224
|
-
const sandboxNetworkAccessSchema = union([_enum([
|
|
15229
|
+
const sandboxNetworkAccessSchema$1 = union([_enum([
|
|
15225
15230
|
"none",
|
|
15226
15231
|
"essentials",
|
|
15227
15232
|
"open"
|
|
@@ -15253,7 +15258,7 @@ const sandboxConfigSchema = object({
|
|
|
15253
15258
|
"standard-3",
|
|
15254
15259
|
"standard-4"
|
|
15255
15260
|
]).optional().describe("Only standard-4 is supported on the hosted Agent Sandbox path; other sizes cannot be enabled."),
|
|
15256
|
-
networkAccess: sandboxNetworkAccessSchema.optional(),
|
|
15261
|
+
networkAccess: sandboxNetworkAccessSchema$1.optional(),
|
|
15257
15262
|
ttl: _enum([
|
|
15258
15263
|
"5m",
|
|
15259
15264
|
"1h",
|
|
@@ -15428,6 +15433,288 @@ const agentRuntimeConfigSchema = object({
|
|
|
15428
15433
|
durability: agentDurabilityConfigSchema.optional(),
|
|
15429
15434
|
state: agentStateChannelSchema.optional()
|
|
15430
15435
|
}).strict();
|
|
15436
|
+
const injectionModeSchema = _enum([
|
|
15437
|
+
"bearer_header",
|
|
15438
|
+
"api_key_header",
|
|
15439
|
+
"query_param",
|
|
15440
|
+
"basic_auth"
|
|
15441
|
+
]);
|
|
15442
|
+
const credentialProxyConfigSchema = object({
|
|
15443
|
+
secretKey: string().min(1),
|
|
15444
|
+
hostnamePattern: string().min(1),
|
|
15445
|
+
pathPattern: string().default("/*"),
|
|
15446
|
+
injectionMode: injectionModeSchema,
|
|
15447
|
+
injectionConfig: record(string(), unknown()).default({}),
|
|
15448
|
+
envVarName: string().optional()
|
|
15449
|
+
});
|
|
15450
|
+
/**
|
|
15451
|
+
* Sandbox provider capability table, the single source of truth for which `transform-data` sandbox provider runs which language.
|
|
15452
|
+
* #6521 needed the same provider/language relation in four places; the first draft forked it into four unpinned hand-copies.
|
|
15453
|
+
* This module is a pure leaf (no imports, no I/O) so every layer can depend on it; the table MIRRORS `TransformSandboxRouter`, asserted entry-for-entry by `apps/api/tests/flow-eligibility-facts.test.ts`.
|
|
15454
|
+
* Since `transform-data` continues on error by default, anything this table cannot vouch for must route to the legacy engine rather than silently produce wrong data.
|
|
15455
|
+
*/
|
|
15456
|
+
/**
|
|
15457
|
+
* The provider a `transform-data` step gets when `config.sandboxProvider` is
|
|
15458
|
+
* unset. Mirrors the default in `TransformSandboxRouter.selectExecutor` and in
|
|
15459
|
+
* the legacy `ApiContextStepExecutor.executeJavaScriptTransform`.
|
|
15460
|
+
*/
|
|
15461
|
+
const DEFAULT_SANDBOX_PROVIDER = "cloudflare-worker";
|
|
15462
|
+
/**
|
|
15463
|
+
* Every routable provider mapped to the languages its executor declares.
|
|
15464
|
+
* PROTOTYPE-LESS ON PURPOSE: the provider string is attacker-controlled on unauthenticated validation routes with no enum check in front.
|
|
15465
|
+
* On a normal object literal, `SANDBOX_PROVIDER_LANGUAGES['constructor']` resolves truthy and the following `.includes` throws, surfacing as a 500.
|
|
15466
|
+
* `Object.create(null)` plus an `Object.hasOwn` gate in `lookupLanguages` both close it; kept together since this table is read from four packages.
|
|
15467
|
+
*/
|
|
15468
|
+
const SANDBOX_PROVIDER_LANGUAGES = Object.assign(Object.create(null), {
|
|
15469
|
+
quickjs: ["javascript"],
|
|
15470
|
+
"cloudflare-worker": ["javascript"],
|
|
15471
|
+
"runtype-sandbox": [
|
|
15472
|
+
"javascript",
|
|
15473
|
+
"typescript",
|
|
15474
|
+
"python"
|
|
15475
|
+
],
|
|
15476
|
+
"cloudflare-sandbox": [
|
|
15477
|
+
"javascript",
|
|
15478
|
+
"typescript",
|
|
15479
|
+
"python"
|
|
15480
|
+
],
|
|
15481
|
+
daytona: [
|
|
15482
|
+
"javascript",
|
|
15483
|
+
"typescript",
|
|
15484
|
+
"python"
|
|
15485
|
+
]
|
|
15486
|
+
});
|
|
15487
|
+
/** Normalize an unset / non-string provider to the documented default. */
|
|
15488
|
+
function resolveSandboxProvider(provider) {
|
|
15489
|
+
return typeof provider === "string" ? provider : DEFAULT_SANDBOX_PROVIDER;
|
|
15490
|
+
}
|
|
15491
|
+
/**
|
|
15492
|
+
* The one lookup every predicate below goes through. Returns `undefined` for
|
|
15493
|
+
* anything that is not an OWN entry, so an inherited key can never be mistaken
|
|
15494
|
+
* for a provider — see the note on {@link SANDBOX_PROVIDER_LANGUAGES}.
|
|
15495
|
+
*/
|
|
15496
|
+
function lookupLanguages(provider) {
|
|
15497
|
+
const resolved = resolveSandboxProvider(provider);
|
|
15498
|
+
if (!Object.prototype.hasOwnProperty.call(SANDBOX_PROVIDER_LANGUAGES, resolved)) return void 0;
|
|
15499
|
+
const languages = SANDBOX_PROVIDER_LANGUAGES[resolved];
|
|
15500
|
+
return Array.isArray(languages) ? languages : void 0;
|
|
15501
|
+
}
|
|
15502
|
+
/** Providers whose executor runs JavaScript and nothing else — the Cloudflare Worker Loader and in-process QuickJS. */
|
|
15503
|
+
function isJsOnlySandboxProvider(provider) {
|
|
15504
|
+
const languages = lookupLanguages(provider);
|
|
15505
|
+
return languages !== void 0 && languages.length === 1 && languages[0] === "javascript";
|
|
15506
|
+
}
|
|
15507
|
+
/**
|
|
15508
|
+
* Can a `transform-data` step on this provider bind a code-mode tool pool? ONLY `cloudflare-worker`.
|
|
15509
|
+
* This is narrower than `isJsOnlySandboxProvider` and the gap is deliberate: the seam is the api's `SANDBOX.workerLoaderExecuteTransformWithTools` RPC, which exists for Worker Loader alone.
|
|
15510
|
+
* Legacy and the dashboard both gate on this same single provider.
|
|
15511
|
+
*/
|
|
15512
|
+
function supportsCodeModeToolPool(provider) {
|
|
15513
|
+
return resolveSandboxProvider(provider) === "cloudflare-worker";
|
|
15514
|
+
}
|
|
15515
|
+
/**
|
|
15516
|
+
* Sandbox egress posture shared by `transform-data` steps and `custom` tools:
|
|
15517
|
+
* `'off'` (the default when omitted), `'on'`, or an explicit hostname allowlist.
|
|
15518
|
+
*/
|
|
15519
|
+
const sandboxNetworkAccessSchema = union([
|
|
15520
|
+
literal("off"),
|
|
15521
|
+
literal("on"),
|
|
15522
|
+
object({ allowedHostnames: array(string()).min(1) })
|
|
15523
|
+
]);
|
|
15524
|
+
/**
|
|
15525
|
+
* One managed secret a `custom` tool may send, and where. Code sees only the
|
|
15526
|
+
* phantom token as `secrets.<key>`; the sandbox egress proxy swaps in the real
|
|
15527
|
+
* credential for requests that match `hostnamePattern` and `pathPattern`.
|
|
15528
|
+
*/
|
|
15529
|
+
const customToolSecretSchema = object({
|
|
15530
|
+
key: string().regex(SECRET_KEY_PATTERN, "key must be a managed secret name, e.g. GITHUB_TOKEN"),
|
|
15531
|
+
hostnamePattern: string().min(1),
|
|
15532
|
+
pathPattern: string().min(1).optional(),
|
|
15533
|
+
injectionMode: injectionModeSchema,
|
|
15534
|
+
injectionConfig: record(string(), unknown()).optional()
|
|
15535
|
+
});
|
|
15536
|
+
/** Every code {@link collectCustomToolEgressIssues} can emit, for validators that forward core codes. */
|
|
15537
|
+
const CUSTOM_TOOL_EGRESS_ISSUE_CODES = [
|
|
15538
|
+
"CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
|
|
15539
|
+
"CUSTOM_TOOL_EGRESS_PROVIDER_UNSUPPORTED",
|
|
15540
|
+
"CUSTOM_TOOL_SECRET_HOST_NOT_ALLOWED"
|
|
15541
|
+
];
|
|
15542
|
+
/**
|
|
15543
|
+
* Shared-hosting suffixes where every subdomain belongs to a different tenant, so
|
|
15544
|
+
* `*.<suffix>` would send a credential to strangers. Curated from the Public Suffix
|
|
15545
|
+
* List's private section; extend it when a new host platform shows up.
|
|
15546
|
+
*/
|
|
15547
|
+
const SHARED_HOSTING_SUFFIXES = /* @__PURE__ */ new Set([
|
|
15548
|
+
"github.io",
|
|
15549
|
+
"gitlab.io",
|
|
15550
|
+
"pages.dev",
|
|
15551
|
+
"workers.dev",
|
|
15552
|
+
"vercel.app",
|
|
15553
|
+
"netlify.app",
|
|
15554
|
+
"herokuapp.com",
|
|
15555
|
+
"azurewebsites.net",
|
|
15556
|
+
"cloudfront.net",
|
|
15557
|
+
"appspot.com",
|
|
15558
|
+
"firebaseapp.com",
|
|
15559
|
+
"web.app",
|
|
15560
|
+
"onrender.com",
|
|
15561
|
+
"fly.dev",
|
|
15562
|
+
"glitch.me",
|
|
15563
|
+
"repl.co",
|
|
15564
|
+
"replit.app",
|
|
15565
|
+
"deno.dev",
|
|
15566
|
+
"railway.app",
|
|
15567
|
+
"ngrok.io",
|
|
15568
|
+
"ngrok-free.app",
|
|
15569
|
+
"trycloudflare.com",
|
|
15570
|
+
"blogspot.com",
|
|
15571
|
+
"wordpress.com",
|
|
15572
|
+
"s3.amazonaws.com",
|
|
15573
|
+
"amplifyapp.com",
|
|
15574
|
+
"azurestaticapps.net",
|
|
15575
|
+
"surge.sh",
|
|
15576
|
+
"myshopify.com"
|
|
15577
|
+
]);
|
|
15578
|
+
/** Second-level labels that registries sell under a two-letter country code (co.uk, com.au, ne.jp). */
|
|
15579
|
+
const COUNTRY_SECOND_LEVELS = /* @__PURE__ */ new Set([
|
|
15580
|
+
"ac",
|
|
15581
|
+
"co",
|
|
15582
|
+
"com",
|
|
15583
|
+
"edu",
|
|
15584
|
+
"gov",
|
|
15585
|
+
"go",
|
|
15586
|
+
"ne",
|
|
15587
|
+
"net",
|
|
15588
|
+
"or",
|
|
15589
|
+
"org",
|
|
15590
|
+
"ltd",
|
|
15591
|
+
"plc",
|
|
15592
|
+
"nom",
|
|
15593
|
+
"gob",
|
|
15594
|
+
"mil"
|
|
15595
|
+
]);
|
|
15596
|
+
/** Is `domain` a public suffix (a registry or shared-hosting zone), not one owner's domain? */
|
|
15597
|
+
function isPublicSuffixDomain(domain) {
|
|
15598
|
+
const host = domain.toLowerCase().replace(/\.+$/, "");
|
|
15599
|
+
if (SHARED_HOSTING_SUFFIXES.has(host)) return true;
|
|
15600
|
+
const labels = host.split(".");
|
|
15601
|
+
if (labels.length === 1) return true;
|
|
15602
|
+
if (labels.length !== 2) return false;
|
|
15603
|
+
const [second = "", top = ""] = labels;
|
|
15604
|
+
return top.length === 2 && COUNTRY_SECOND_LEVELS.has(second);
|
|
15605
|
+
}
|
|
15606
|
+
/**
|
|
15607
|
+
* A secret's hostname pattern must name one public DNS host (`api.github.com`) or one
|
|
15608
|
+
* domain's subdomains (`*.github.com`): never a TLD, a public suffix (`*.co.uk`,
|
|
15609
|
+
* `*.github.io`), everything, an IP literal, `localhost`, or an `.internal` name.
|
|
15610
|
+
*/
|
|
15611
|
+
function isConcreteSecretHostnamePattern(pattern) {
|
|
15612
|
+
const wildcard = pattern.startsWith("*.");
|
|
15613
|
+
const host = (wildcard ? pattern.slice(2) : pattern).toLowerCase();
|
|
15614
|
+
if (host.includes("*")) return false;
|
|
15615
|
+
const labels = host.split(".");
|
|
15616
|
+
if (labels.length < 2) return false;
|
|
15617
|
+
if (labels.some((label) => !/^[a-z0-9-]+$/.test(label))) return false;
|
|
15618
|
+
const topLevel = labels[labels.length - 1] ?? "";
|
|
15619
|
+
if (!/[a-z]/.test(topLevel)) return false;
|
|
15620
|
+
if (topLevel === "internal" || topLevel === "local" || topLevel === "localhost") return false;
|
|
15621
|
+
return !(wildcard && isPublicSuffixDomain(host));
|
|
15622
|
+
}
|
|
15623
|
+
/** Is every host `secretPattern` can match also allowed by the tool's network posture? */
|
|
15624
|
+
function networkAccessCoversHostnamePattern(networkAccess, secretPattern) {
|
|
15625
|
+
if (networkAccess === "on") return true;
|
|
15626
|
+
if (networkAccess === "off") return false;
|
|
15627
|
+
return networkAccess.allowedHostnames.some((allowed) => {
|
|
15628
|
+
if (allowed === secretPattern) return true;
|
|
15629
|
+
if (!allowed.startsWith("*.")) return false;
|
|
15630
|
+
const suffix = allowed.slice(1);
|
|
15631
|
+
return (secretPattern.startsWith("*.") ? secretPattern.slice(1) : secretPattern).endsWith(suffix);
|
|
15632
|
+
});
|
|
15633
|
+
}
|
|
15634
|
+
const PROVIDERS_WITHOUT_EGRESS_PROXY = /* @__PURE__ */ new Set(["quickjs"]);
|
|
15635
|
+
const PROVIDERS_WITHOUT_SECRET_PROXY = /* @__PURE__ */ new Set(["quickjs", "daytona"]);
|
|
15636
|
+
const EGRESS_PROVIDER_REMEDIATION = "Set sandboxProvider to \"cloudflare-worker\" (JavaScript) or \"runtype-sandbox\" (JavaScript, TypeScript, Python).";
|
|
15637
|
+
/**
|
|
15638
|
+
* Pure checks on a custom tool's `networkAccess` / `secrets` against its provider.
|
|
15639
|
+
* Returns every issue so validators can surface them together.
|
|
15640
|
+
*/
|
|
15641
|
+
function collectCustomToolEgressIssues(config) {
|
|
15642
|
+
if (!isRecord$1(config)) return [];
|
|
15643
|
+
const issues = [];
|
|
15644
|
+
const provider = resolveSandboxProvider(config.sandboxProvider);
|
|
15645
|
+
const networkParse = config.networkAccess === void 0 ? void 0 : sandboxNetworkAccessSchema.safeParse(config.networkAccess);
|
|
15646
|
+
if (networkParse && !networkParse.success) issues.push({
|
|
15647
|
+
code: "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
|
|
15648
|
+
message: `networkAccess must be 'off', 'on', or { allowedHostnames: [...] } with at least one hostname.`,
|
|
15649
|
+
path: "networkAccess"
|
|
15650
|
+
});
|
|
15651
|
+
const networkAccess = networkParse?.success === true ? networkParse.data : "off";
|
|
15652
|
+
if (networkAccess !== "off" && PROVIDERS_WITHOUT_EGRESS_PROXY.has(provider)) issues.push({
|
|
15653
|
+
code: "CUSTOM_TOOL_EGRESS_PROVIDER_UNSUPPORTED",
|
|
15654
|
+
message: `sandboxProvider "${provider}" has no fetch and no egress proxy, so networkAccess cannot be enabled. ${EGRESS_PROVIDER_REMEDIATION}`,
|
|
15655
|
+
path: "networkAccess"
|
|
15656
|
+
});
|
|
15657
|
+
if (typeof networkAccess === "object" && provider === "daytona") issues.push({
|
|
15658
|
+
code: "CUSTOM_TOOL_EGRESS_PROVIDER_UNSUPPORTED",
|
|
15659
|
+
message: `sandboxProvider "daytona" manages its own egress and cannot enforce an allowedHostnames list. ${EGRESS_PROVIDER_REMEDIATION}`,
|
|
15660
|
+
path: "networkAccess"
|
|
15661
|
+
});
|
|
15662
|
+
if (config.secrets === void 0) return issues;
|
|
15663
|
+
if (!Array.isArray(config.secrets)) {
|
|
15664
|
+
issues.push({
|
|
15665
|
+
code: "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
|
|
15666
|
+
message: "secrets must be an array of { key, hostnamePattern, injectionMode } entries.",
|
|
15667
|
+
path: "secrets"
|
|
15668
|
+
});
|
|
15669
|
+
return issues;
|
|
15670
|
+
}
|
|
15671
|
+
if (config.secrets.length === 0) return issues;
|
|
15672
|
+
if (PROVIDERS_WITHOUT_SECRET_PROXY.has(provider)) issues.push({
|
|
15673
|
+
code: "CUSTOM_TOOL_EGRESS_PROVIDER_UNSUPPORTED",
|
|
15674
|
+
message: `sandboxProvider "${provider}" has no credential proxy, so managed secrets cannot be injected. ${EGRESS_PROVIDER_REMEDIATION}`,
|
|
15675
|
+
path: "secrets"
|
|
15676
|
+
});
|
|
15677
|
+
if (config.secrets.length > 10) issues.push({
|
|
15678
|
+
code: "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
|
|
15679
|
+
message: `A custom tool can declare at most 10 secrets.`,
|
|
15680
|
+
path: "secrets"
|
|
15681
|
+
});
|
|
15682
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
15683
|
+
config.secrets.forEach((entry, index) => {
|
|
15684
|
+
const path = `secrets[${index}]`;
|
|
15685
|
+
const parsed = customToolSecretSchema.safeParse(entry);
|
|
15686
|
+
if (!parsed.success) {
|
|
15687
|
+
const first = parsed.error.issues[0];
|
|
15688
|
+
issues.push({
|
|
15689
|
+
code: "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
|
|
15690
|
+
message: `Invalid secret entry: ${first?.path.join(".") || "entry"} ${first?.message ?? "is invalid"}. Use the bare secret name as key (GITHUB_TOKEN, not {{secret:GITHUB_TOKEN}}).`,
|
|
15691
|
+
path
|
|
15692
|
+
});
|
|
15693
|
+
return;
|
|
15694
|
+
}
|
|
15695
|
+
const secret = parsed.data;
|
|
15696
|
+
if (seenKeys.has(secret.key)) issues.push({
|
|
15697
|
+
code: "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
|
|
15698
|
+
message: `Secret "${secret.key}" is declared twice. Declare each secret once; it is injected for one hostname pattern.`,
|
|
15699
|
+
path: `${path}.key`
|
|
15700
|
+
});
|
|
15701
|
+
seenKeys.add(secret.key);
|
|
15702
|
+
if (!isConcreteSecretHostnamePattern(secret.hostnamePattern)) {
|
|
15703
|
+
issues.push({
|
|
15704
|
+
code: "CUSTOM_TOOL_EGRESS_CONFIG_INVALID",
|
|
15705
|
+
message: `Secret "${secret.key}" hostnamePattern "${secret.hostnamePattern}" is not a single public host. Name the API host (api.github.com) or one domain's subdomains (*.github.com); public suffixes (*.co.uk, *.github.io), IP literals, localhost and internal names are refused.`,
|
|
15706
|
+
path: `${path}.hostnamePattern`
|
|
15707
|
+
});
|
|
15708
|
+
return;
|
|
15709
|
+
}
|
|
15710
|
+
if (!networkAccessCoversHostnamePattern(networkAccess, secret.hostnamePattern)) issues.push({
|
|
15711
|
+
code: "CUSTOM_TOOL_SECRET_HOST_NOT_ALLOWED",
|
|
15712
|
+
message: `Secret "${secret.key}" is sent to "${secret.hostnamePattern}", but networkAccess does not allow that host. Add it to networkAccess.allowedHostnames.`,
|
|
15713
|
+
path: `${path}.hostnamePattern`
|
|
15714
|
+
});
|
|
15715
|
+
});
|
|
15716
|
+
return issues;
|
|
15717
|
+
}
|
|
15431
15718
|
/**
|
|
15432
15719
|
* A single entry in a context-step fallback chain. Mirrors `ContextFallback`
|
|
15433
15720
|
* (`retry` | `step` | `flow`) in `error-handling.ts`. Discriminated by `type`.
|
|
@@ -16220,16 +16507,6 @@ const updateRecordConfigSchema = object({
|
|
|
16220
16507
|
defaultValue: any().optional()
|
|
16221
16508
|
});
|
|
16222
16509
|
/**
|
|
16223
|
-
* Network-access policy for sandboxed transform-data execution (cloudflare-worker/runtype-sandbox providers).
|
|
16224
|
-
* Mirrors the inline union on TransformDataStep.config.networkAccess: 'off' (default), 'on', or an explicit allowlist.
|
|
16225
|
-
* Lives here rather than the api validator because it's transform-data-specific.
|
|
16226
|
-
*/
|
|
16227
|
-
const transformDataNetworkAccessSchema = union([
|
|
16228
|
-
literal("off"),
|
|
16229
|
-
literal("on"),
|
|
16230
|
-
object({ allowedHostnames: array(string()).min(1) })
|
|
16231
|
-
]);
|
|
16232
|
-
/**
|
|
16233
16510
|
* Canonical transform-data config, the strict field set only; superset of the prior validator and TransformDataStep.config.
|
|
16234
16511
|
* errorHandling tightens to the canonical union (prod-verified safe: of 1654 steps, only 4 carry it, all valid).
|
|
16235
16512
|
* inputMode/packageJson/persistSandbox/reuseSandboxId are recovered fields the validator silently stripped.
|
|
@@ -16256,7 +16533,7 @@ const transformDataConfigSchema = object({
|
|
|
16256
16533
|
packageJson: string().optional(),
|
|
16257
16534
|
persistSandbox: boolean().optional(),
|
|
16258
16535
|
reuseSandboxId: string().optional(),
|
|
16259
|
-
networkAccess:
|
|
16536
|
+
networkAccess: sandboxNetworkAccessSchema.optional(),
|
|
16260
16537
|
inference: boolean().optional(),
|
|
16261
16538
|
errorHandling: contextErrorHandlingConfigSchema.optional(),
|
|
16262
16539
|
defaultValue: any().optional(),
|
|
@@ -18302,71 +18579,6 @@ function sanitizeCodeToolIdentifier(name) {
|
|
|
18302
18579
|
return safe;
|
|
18303
18580
|
}
|
|
18304
18581
|
/**
|
|
18305
|
-
* Sandbox provider capability table, the single source of truth for which `transform-data` sandbox provider runs which language.
|
|
18306
|
-
* #6521 needed the same provider/language relation in four places; the first draft forked it into four unpinned hand-copies.
|
|
18307
|
-
* This module is a pure leaf (no imports, no I/O) so every layer can depend on it; the table MIRRORS `TransformSandboxRouter`, asserted entry-for-entry by `apps/api/tests/flow-eligibility-facts.test.ts`.
|
|
18308
|
-
* Since `transform-data` continues on error by default, anything this table cannot vouch for must route to the legacy engine rather than silently produce wrong data.
|
|
18309
|
-
*/
|
|
18310
|
-
/**
|
|
18311
|
-
* The provider a `transform-data` step gets when `config.sandboxProvider` is
|
|
18312
|
-
* unset. Mirrors the default in `TransformSandboxRouter.selectExecutor` and in
|
|
18313
|
-
* the legacy `ApiContextStepExecutor.executeJavaScriptTransform`.
|
|
18314
|
-
*/
|
|
18315
|
-
const DEFAULT_SANDBOX_PROVIDER = "cloudflare-worker";
|
|
18316
|
-
/**
|
|
18317
|
-
* Every routable provider mapped to the languages its executor declares.
|
|
18318
|
-
* PROTOTYPE-LESS ON PURPOSE: the provider string is attacker-controlled on unauthenticated validation routes with no enum check in front.
|
|
18319
|
-
* On a normal object literal, `SANDBOX_PROVIDER_LANGUAGES['constructor']` resolves truthy and the following `.includes` throws, surfacing as a 500.
|
|
18320
|
-
* `Object.create(null)` plus an `Object.hasOwn` gate in `lookupLanguages` both close it; kept together since this table is read from four packages.
|
|
18321
|
-
*/
|
|
18322
|
-
const SANDBOX_PROVIDER_LANGUAGES = Object.assign(Object.create(null), {
|
|
18323
|
-
quickjs: ["javascript"],
|
|
18324
|
-
"cloudflare-worker": ["javascript"],
|
|
18325
|
-
"runtype-sandbox": [
|
|
18326
|
-
"javascript",
|
|
18327
|
-
"typescript",
|
|
18328
|
-
"python"
|
|
18329
|
-
],
|
|
18330
|
-
"cloudflare-sandbox": [
|
|
18331
|
-
"javascript",
|
|
18332
|
-
"typescript",
|
|
18333
|
-
"python"
|
|
18334
|
-
],
|
|
18335
|
-
daytona: [
|
|
18336
|
-
"javascript",
|
|
18337
|
-
"typescript",
|
|
18338
|
-
"python"
|
|
18339
|
-
]
|
|
18340
|
-
});
|
|
18341
|
-
/** Normalize an unset / non-string provider to the documented default. */
|
|
18342
|
-
function resolveSandboxProvider(provider) {
|
|
18343
|
-
return typeof provider === "string" ? provider : DEFAULT_SANDBOX_PROVIDER;
|
|
18344
|
-
}
|
|
18345
|
-
/**
|
|
18346
|
-
* The one lookup every predicate below goes through. Returns `undefined` for
|
|
18347
|
-
* anything that is not an OWN entry, so an inherited key can never be mistaken
|
|
18348
|
-
* for a provider — see the note on {@link SANDBOX_PROVIDER_LANGUAGES}.
|
|
18349
|
-
*/
|
|
18350
|
-
function lookupLanguages(provider) {
|
|
18351
|
-
const resolved = resolveSandboxProvider(provider);
|
|
18352
|
-
if (!Object.prototype.hasOwnProperty.call(SANDBOX_PROVIDER_LANGUAGES, resolved)) return void 0;
|
|
18353
|
-
const languages = SANDBOX_PROVIDER_LANGUAGES[resolved];
|
|
18354
|
-
return Array.isArray(languages) ? languages : void 0;
|
|
18355
|
-
}
|
|
18356
|
-
/** Providers whose executor runs JavaScript and nothing else — the Cloudflare Worker Loader and in-process QuickJS. */
|
|
18357
|
-
function isJsOnlySandboxProvider(provider) {
|
|
18358
|
-
const languages = lookupLanguages(provider);
|
|
18359
|
-
return languages !== void 0 && languages.length === 1 && languages[0] === "javascript";
|
|
18360
|
-
}
|
|
18361
|
-
/**
|
|
18362
|
-
* Can a `transform-data` step on this provider bind a code-mode tool pool? ONLY `cloudflare-worker`.
|
|
18363
|
-
* This is narrower than `isJsOnlySandboxProvider` and the gap is deliberate: the seam is the api's `SANDBOX.workerLoaderExecuteTransformWithTools` RPC, which exists for Worker Loader alone.
|
|
18364
|
-
* Legacy and the dashboard both gate on this same single provider.
|
|
18365
|
-
*/
|
|
18366
|
-
function supportsCodeModeToolPool(provider) {
|
|
18367
|
-
return resolveSandboxProvider(provider) === "cloudflare-worker";
|
|
18368
|
-
}
|
|
18369
|
-
/**
|
|
18370
18582
|
* Convert snake_case string to camelCase
|
|
18371
18583
|
* @example snakeToCamel('flow_name') // 'flowName'
|
|
18372
18584
|
* @example snakeToCamel('user_id') // 'userId'
|
|
@@ -20825,6 +21037,25 @@ function validateCodeModeConfig(value, stepTools, stepIndex, stepRef) {
|
|
|
20825
21037
|
}
|
|
20826
21038
|
return null;
|
|
20827
21039
|
}
|
|
21040
|
+
/**
|
|
21041
|
+
* Pure half of the custom-tool egress rules (#8103): a runtime `custom` tool whose
|
|
21042
|
+
* `networkAccess` / `secrets` its provider cannot honor, or whose secret is sent to
|
|
21043
|
+
* a host its own networkAccess blocks. The api's validateRuntimeTools enforces the same rules at dispatch.
|
|
21044
|
+
*/
|
|
21045
|
+
function validateCustomToolEgress(tools, stepIndex, stepRef, buckets) {
|
|
21046
|
+
if (!Array.isArray(tools.runtimeTools)) return;
|
|
21047
|
+
tools.runtimeTools.forEach((tool, toolIndex) => {
|
|
21048
|
+
if (!isRecord$1(tool) || tool.toolType !== "custom") return;
|
|
21049
|
+
const toolName = typeof tool.name === "string" ? tool.name : `#${toolIndex}`;
|
|
21050
|
+
for (const issue of collectCustomToolEgressIssues(tool.config)) addIssue("error", {
|
|
21051
|
+
code: issue.code,
|
|
21052
|
+
message: `Custom tool "${toolName}": ${issue.message}`,
|
|
21053
|
+
path: `flowSteps[${stepIndex}].config.tools.runtimeTools[${toolIndex}].config.${issue.path}`,
|
|
21054
|
+
step: stepRef,
|
|
21055
|
+
details: { toolName }
|
|
21056
|
+
}, buckets);
|
|
21057
|
+
});
|
|
21058
|
+
}
|
|
20828
21059
|
function validateTransformDataCodeTools(toolsConfig, stepIndex, stepRef, buckets) {
|
|
20829
21060
|
const runtimeTools = Array.isArray(toolsConfig.runtimeTools) ? toolsConfig.runtimeTools : [];
|
|
20830
21061
|
const runtimeToolNames = [];
|
|
@@ -21008,6 +21239,7 @@ function collectFlowStructureIssues(flowData, deps, buckets) {
|
|
|
21008
21239
|
step: stepRef
|
|
21009
21240
|
}, buckets);
|
|
21010
21241
|
}
|
|
21242
|
+
validateCustomToolEgress(step.config.tools, stepIndex, stepRef, buckets);
|
|
21011
21243
|
if (step.type === "transform-data") validateTransformDataCodeTools(step.config.tools, stepIndex, stepRef, buckets);
|
|
21012
21244
|
const subagentCfg = step.config.tools.subagentConfig;
|
|
21013
21245
|
if (subagentCfg !== void 0) {
|
|
@@ -23204,7 +23436,8 @@ const FORWARDED_CORE_CODES = /* @__PURE__ */ new Set([
|
|
|
23204
23436
|
TOOL_CALL_ERROR_HANDLING_IGNORED_CODE,
|
|
23205
23437
|
EXECUTE_AGENT_TEMPLATED_REF_UNRUNNABLE_CODE,
|
|
23206
23438
|
PROVIDER_TOOLS_MIXED_OWNERS_CODE,
|
|
23207
|
-
FLOW_TOOL_OUTPUT_VARIABLE_RESERVED_CODE
|
|
23439
|
+
FLOW_TOOL_OUTPUT_VARIABLE_RESERVED_CODE,
|
|
23440
|
+
...CUSTOM_TOOL_EGRESS_ISSUE_CODES
|
|
23208
23441
|
]);
|
|
23209
23442
|
function validateFlowDefinition(flow, capIndex, context) {
|
|
23210
23443
|
const result = emptyResult();
|
|
@@ -23408,6 +23641,10 @@ function memoryPointerIssues(memory, context, path) {
|
|
|
23408
23641
|
* v2 nested-`config` shape. The v2 branch reads the equivalent fields from
|
|
23409
23642
|
* `agent.config` and reports them under `.config.*` paths.
|
|
23410
23643
|
*/
|
|
23644
|
+
/** Same egress rules the pure flow validator's validateCustomToolEgress applies (#8103), for AGENT-level runtimeTools. */
|
|
23645
|
+
function pushCustomToolEgressIssues(config, configPath, result) {
|
|
23646
|
+
for (const issue of collectCustomToolEgressIssues(config)) result.errors.push(createIssue("error", issue.code, issue.message, `${configPath}.${issue.path}`));
|
|
23647
|
+
}
|
|
23411
23648
|
function validateAgentDefinition(agent, capIndex) {
|
|
23412
23649
|
const result = emptyResult();
|
|
23413
23650
|
const base = `capabilities[${capIndex}].agent`;
|
|
@@ -23451,6 +23688,7 @@ function validateFlatAgent(agent, base, result) {
|
|
|
23451
23688
|
const outputVariableIssue = flowToolOutputVariableIssue(tool.config?.outputVariable, `${base}.toolsConfig.runtimeTools[${i}].config.outputVariable`);
|
|
23452
23689
|
if (outputVariableIssue) result.errors.push(outputVariableIssue);
|
|
23453
23690
|
}
|
|
23691
|
+
if (tool.toolType === "custom") pushCustomToolEgressIssues(tool.config, `${base}.toolsConfig.runtimeTools[${i}].config`, result);
|
|
23454
23692
|
}
|
|
23455
23693
|
}
|
|
23456
23694
|
function validateNestedAgent(agent, base, result) {
|
|
@@ -23476,6 +23714,7 @@ function validateNestedAgent(agent, base, result) {
|
|
|
23476
23714
|
const outputVariableIssue = flowToolOutputVariableIssue(tool.config?.outputVariable, `${base}.config.tools.runtimeTools[${i}].config.outputVariable`);
|
|
23477
23715
|
if (outputVariableIssue) result.errors.push(outputVariableIssue);
|
|
23478
23716
|
}
|
|
23717
|
+
if (tool.toolType === "custom") pushCustomToolEgressIssues(tool.config, `${base}.config.tools.runtimeTools[${i}].config`, result);
|
|
23479
23718
|
}
|
|
23480
23719
|
}
|
|
23481
23720
|
const KEBAB_CASE_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
@@ -24226,20 +24465,6 @@ function isNonAIProvider(id) {
|
|
|
24226
24465
|
if (colonIndex > 0) return NON_AI_PROVIDER_IDS.has(normalized.slice(0, colonIndex));
|
|
24227
24466
|
return false;
|
|
24228
24467
|
}
|
|
24229
|
-
const injectionModeSchema = _enum([
|
|
24230
|
-
"bearer_header",
|
|
24231
|
-
"api_key_header",
|
|
24232
|
-
"query_param",
|
|
24233
|
-
"basic_auth"
|
|
24234
|
-
]);
|
|
24235
|
-
const credentialProxyConfigSchema = object({
|
|
24236
|
-
secretKey: string().min(1),
|
|
24237
|
-
hostnamePattern: string().min(1),
|
|
24238
|
-
pathPattern: string().default("/*"),
|
|
24239
|
-
injectionMode: injectionModeSchema,
|
|
24240
|
-
injectionConfig: record(string(), unknown()).default({}),
|
|
24241
|
-
envVarName: string().optional()
|
|
24242
|
-
});
|
|
24243
24468
|
const LocalInferenceSurfaceSchema = _enum([
|
|
24244
24469
|
"playground",
|
|
24245
24470
|
"flows",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runtypelabs/cli",
|
|
3
|
-
"version": "2.42.
|
|
3
|
+
"version": "2.42.4",
|
|
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.6.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.67.0",
|
|
37
37
|
"@types/express": "^5.0.6",
|
|
38
38
|
"@types/micromatch": "^4.0.9",
|
|
39
39
|
"@types/node": "^25.3.3",
|