feat: add gated Itô inference skill lifecycle

This commit is contained in:
Affaan Mustafa
2026-08-07 14:57:38 -04:00
parent a73deb211e
commit c0e532d8f6
9 changed files with 221 additions and 220 deletions
+8
View File
@@ -202,6 +202,14 @@
"ito-compute"
]
},
{
"id": "capability:ito-inference",
"family": "capability",
"description": "Gated Itô inference preflight, deployment, observability, and cleanup on existing entitled clusters.",
"modules": [
"ito-inference"
]
},
{
"id": "capability:social",
"family": "capability",
+30
View File
@@ -634,6 +634,36 @@
"cost": "light",
"stability": "beta"
},
{
"id": "ito-inference",
"kind": "skills",
"description": "Gated Itô inference eligibility, entitlement, deployment, observability, and cleanup on existing clusters.",
"paths": [
"skills/ito-inference"
],
"targets": [
"claude",
"claude-project",
"cursor",
"antigravity",
"codex",
"gemini",
"opencode",
"codebuddy",
"joycode",
"qwen",
"zed",
"hermes",
"openclaw",
"kimi"
],
"dependencies": [
"ito-compute"
],
"defaultInstall": false,
"cost": "light",
"stability": "experimental"
},
{
"id": "social-distribution",
"kind": "skills",
+1
View File
@@ -89,6 +89,7 @@
"optimization-workflows",
"prediction-market-skills",
"ito-compute",
"ito-inference",
"social-distribution",
"media-generation",
"orchestration",
+1
View File
@@ -224,6 +224,7 @@
"skills/inventory-demand-planning/",
"skills/ito-basket-compare/",
"skills/ito-compute/",
"skills/ito-inference/",
"skills/ito-data-atlas-agent/",
"skills/ito-inference/",
"skills/ito-market-intelligence/",
+12 -4
View File
@@ -10,7 +10,7 @@ const {
getInvocationCommand,
} = require("./lib/ito-environment");
const SUPPORTED_COMMANDS = Object.freeze(["login", "logout", "auth", "find", "status", "evals"]);
const SUPPORTED_COMMANDS = Object.freeze(["login", "logout", "auth", "find", "status", "evals", "inference"]);
const CANONICAL_REPOSITORY = "https://github.com/Ito-Markets/ito-cloud-runtime.git";
const CANONICAL_PACKAGE_PATH = "cli/ito-compute-cli";
const CANONICAL_ENTRY_SEGMENTS = Object.freeze([
@@ -34,14 +34,18 @@ Usage:
ecc ito find <all required RFQ options>
ecc ito status
ecc ito evals --cluster <id> --live-sixtytwo --nodes <list> --config-dir <dir>
ecc ito <login|logout|auth|find|status|evals> --json
ecc ito inference preflight --cluster <id> --model <owner/model> --revision <commit> --engine vllm --max-runtime-hours <hours>
ecc ito inference deploy <same options> --confirm-cost-usd <exact preflight amount>
ecc ito inference <status|logs> --deployment <id>
ecc ito inference stop --deployment <id> --confirm
ecc ito <login|logout|auth|find|status|evals|inference> --json
The bridge invokes the separately installed canonical Itô CLI and returns its
real stdout, stderr, and exit code unchanged. "ecc ito login" delegates to the
canonical CLI's device authorization. It opens the Itô verification page by default
and persists its device token in macOS Keychain. Pass --no-browser to
suppress that handoff. ECC itself performs no browser automation and adds no
lock, workload, inference, or purchase path.
lock, workload, or purchase path.
"ecc ito auth" is validation-only and never starts device login.
"ecc ito logout" asks the canonical CLI to revoke the current device credential
and remove its local copy only after remote revocation is confirmed.
@@ -52,6 +56,10 @@ Important:
- "status" reads live RFQ and procurement status.
- "evals" invokes only the canonical CLI's double-opt-in, pinned
sixtytwo-cli node-qualification adapter against explicit nodes.
- "inference preflight" is read-only; deploy and stop require the canonical
CLI's exact cost and explicit cleanup confirmations.
- Inference operates only on an existing active, entitled cluster. It cannot
find, book, reserve, purchase, resize, or delete compute capacity.
- Node qualification cannot rent, launch, recover, repair, or purchase.
- Inventory and RFQs are not reservations; only a returned firm quote is firm.
@@ -164,7 +172,7 @@ function parseArgs(argv, environment = process.env) {
const command = withoutJson.shift();
if (!SUPPORTED_COMMANDS.includes(command)) {
throw new Error(
`Unsupported Itô command "${command || "(missing)"}"; ECC permits only login, logout, auth, find, status, and evals.`
`Unsupported Itô command "${command || "(missing)"}"; ECC permits only login, logout, auth, find, status, evals, and inference.`
);
}
if (command === "auth" && withoutJson.includes("--no-browser")) {
+2 -2
View File
@@ -45,7 +45,7 @@ const ECC_ITO_CONTROL_KEYS = Object.freeze([
"ECC_ITO_CLI_EXECUTABLE",
"NODE_ENV",
]);
const ITO_RUNTIME_COMMANDS = new Set(["login", "logout", "auth", "find", "status"]);
const ITO_RUNTIME_COMMANDS = new Set(["login", "logout", "auth", "find", "status", "inference"]);
function copyDefined(source, target, key) {
if (typeof source[key] === "string") {
@@ -97,7 +97,7 @@ function createSafeItoInvocationEnvironment(
return createSafeItoEnvironment(source, {
includeControls: options.includeControls === true,
includeItoRuntime: ITO_RUNTIME_COMMANDS.has(command),
includeItoApiKey: ["auth", "find", "status"].includes(command),
includeItoApiKey: ["auth", "find", "status", "inference"].includes(command),
includeItoEvals: command === "evals",
});
}
+69 -90
View File
@@ -1,119 +1,98 @@
---
name: ito-inference
description: Inspect the availability of model serving on a completed Itô compute booking and, when the canonical backend becomes available, hand off an explicitly confirmed serving manifest. Use after ito-compute has booked GPU nodes and the user asks for an OpenAI-compatible endpoint, ito-serve, hosted Kimi, or self-hosted open-weights inference. ECC implements no serving stack of its own.
metadata:
origin: ECC
status: scaffold
aliases: ito-serve, hosted-open-weights
description: Preflight, deploy, observe, and stop self-hosted open-weights inference on an existing eligible and entitled Itô compute cluster. Use when a user asks to deploy, host, serve, run inference, check health or logs, or tear down a Kimi or other open-weights model on Itô GPUs.
---
# Itô Inference
`ito-inference` is the sole canonical ECC skill for inference serving on Itô
compute. Requests naming `ito-serve` route here; do not create or install a
second `ito-serve` skill. ECC never SSHes to nodes, downloads weights, launches
an engine, or exposes an endpoint; it never books, reserves, or spends.
Use only the reviewed canonical `ecc ito inference` lifecycle. This skill never
finds, books, reserves, resizes, purchases, or deletes compute capacity. Never
substitute SSH, systemd, a local vLLM runner, browser automation, or an invented
API/MCP tool.
## Current production boundary
## Establish intent and authority
Managed serving is unavailable today. The ECC bridge exposes only `login`,
`auth`, `find`, `status`, and explicitly gated `evals`. It has no `serve` verb.
The canonical runtime documents `inference` only as an unsupported compatibility
probe; ECC does not invoke or depend on it. The MCP surface exposes only auth,
find, and status. The locally enforceable guarantee is that ECC rejects `serve`
before resolving or spawning the credential-bearing canonical client.
Require the originating agent's completed Itô booking record: account, cluster
id, active status, nodes, GPU topology, service start, and entitlement context.
Collect an exact Hugging Face `owner/model`, a pinned 764 character hexadecimal
revision, `vllm`, quantization (`none`, `awq`, or `gptq`), and a bounded runtime
of 1168 hours. Return to the originating agent for missing booking data; do not
select another cluster.
Therefore stop before authentication or any command invocation. Report the
missing capability and return to the originating agent. Never substitute a
local runner, SSH helper, browser workflow, purchase endpoint, or any untracked
local `ito-serve` draft.
Deployment and teardown are separate state-changing actions. Obtain explicit
user confirmation immediately before each. A compute booking confirmation does
not authorize serving, and a deployment confirmation does not authorize stop.
## Required entitlement
## Authenticate
When serving is implemented, its first gate is a server-verified completed
booking. Harness memory, an RFQ, a quote, node IPs, or SSH access are not proof
of entitlement. The backend must return fresh serving eligibility bound to the
authenticated account, booking, GPU topology, region, fabric, term, and model
policy. Expired, revoked, mismatched, incomplete, or already-released bookings
fail closed before confirmation.
Run `ecc ito auth --json`. If authentication is missing, run `ecc ito login`,
present the canonical device URL/code handoff, and return control to the
originating agent while the user completes authorization. Then retry
`ecc ito auth --json`. Never place a key, token, or device code in chat, files,
screenshots, or logs. Timeout, denial, or revocation is not success; repeat the
device flow and never silently switch accounts.
## Future CLI and API contract
## Preflight
The intended command name is `serve`; `inference` may remain only as an
explicitly deprecated compatibility alias after the production contract lands.
The future handoff must be equivalent to:
Run the fresh, read-only check:
```sh
ecc ito serve \
--booking <server-verified-booking-id> \
--manifest <absolute-reviewed-json-file> \
--confirmation-ref <opaque-non-authorizing-reference> \
--idempotency-key <stable-retry-key> \
--json
```text
ecc ito inference preflight --cluster <cluster-id> --model <owner/model> --revision <commit> --engine vllm --quantization <none|awq|gptq> --max-runtime-hours <1-168> --json
```
The reviewed manifest must identify the model revision, engine and version,
quantization, tensor/pipeline topology, endpoint exposure policy, artifact
checksums, storage ceiling, runtime limits, optional TTFT/TPOT objectives, and
maximum incremental cost. No raw API key, SSH key, node password, or bearer
token belongs in arguments, manifests, logs, MCP results, or chat.
Proceed only when the response says the authenticated account is entitled, the
exact cluster is active, every contracted node is present and up, service has
started, and the model configuration is valid. Record the returned exact
`estimated_cost_usd`. This is a conservative serving ceiling on already-booked
capacity; it is not authority to buy capacity.
The client must canonicalize the manifest path, reject symlinks, open a regular
file without following links, require appropriate ownership and restrictive
permissions, enforce a bounded size, and hash bytes from the opened descriptor.
That digest must exactly equal the digest bound into confirmation before any
workload mutation. A path swap, digest mismatch, oversized file, or mutable
unsafe file fails closed.
## Deploy
The canonical API—not ECC—must own workload creation and return structured JSON
with `ok`, `live_api_contacted`, `notice`, and either `data` or `error`. Serving
data must include stable booking, workload, manifest, and idempotency IDs plus a
state enum; it must not claim an endpoint is live until health and model checks
pass. Errors must include a stable code and safe message without secrets.
After explicit user confirmation, bind that confirmation to the exact current
preflight amount:
## Confirmation and execution gates
```text
ecc ito inference deploy --cluster <cluster-id> --model <owner/model> --revision <commit> --engine vllm --quantization <none|awq|gptq> --max-runtime-hours <1-168> --confirm-cost-usd <exact-amount> --json
```
Before workload creation, require all of the following:
Do not round, edit, or reuse a stale amount. The canonical client supplies an
idempotency key and rejects missing/mismatched confirmation. On timeout or an
ambiguous response, do not deploy again blindly; query status using any returned
deployment id and otherwise report the ambiguity for operator reconciliation.
1. Fresh entitlement and serving eligibility from the canonical backend.
2. A reviewable immutable manifest and deterministic digest.
3. A separate single-use confirmation bound to account, action, manifest, and
cost, with a short expiry and replay protection. CLI arguments carry only an
opaque, non-authorizing confirmation reference; the server resolves and
consumes the bearer capability out of band.
4. A caller-supplied idempotency key reserved atomically with the workload.
5. Server-side fabric, capacity, model-policy, storage, and cost validation.
## Observe
Authentication is identity, not workload authority. A login, API key, quote,
or completed booking never substitutes for the serving confirmation. Inspection
and plan generation must not create a workload. Cancel and cleanup are separate
mutations with their own scoped confirmation and idempotency boundaries.
Read canonical state; never infer readiness from process exit alone:
## Lifecycle and recovery
```text
ecc ito inference status --deployment <deployment-id> --json
ecc ito inference logs --deployment <deployment-id> --limit <1-500> --json
```
The production surface is incomplete until the same canonical client exposes
tenant-scoped status, logs, metrics, cancel, and cleanup operations. Every
operation needs bounded connect and overall timeouts, revocation-aware errors,
and structured output. After an ambiguous transport failure, query status by
the idempotency key before retrying; never create a second workload merely
because the first response was lost. A revoked credential stops polling and
returns control to the originating agent without starting login automatically.
Claim readiness only when status reports the pinned model revision, a healthy
deployment, and its endpoint. Redact credentials and signed endpoint material.
Logs are bounded and read-only. A provider timeout, invalid status, or revoked
authentication is an error, not cached success.
Only report `ready` after endpoint health, model identity, and canary inference
all pass. Report intermediate and terminal failure states honestly. Cleanup must
be observable and must not release or modify the underlying booking unless that
separate economic action was explicitly authorized.
## Stop and verify cleanup
## Proposed backend stages
After separate explicit teardown confirmation:
These stages describe the future backend, not code that exists in ECC:
```text
ecc ito inference stop --deployment <deployment-id> --confirm --json
ecc ito inference status --deployment <deployment-id> --json
```
1. Verify entitlement, topology, fabric, and cost gates.
2. Fetch checksum-pinned weights into backend-managed storage.
3. Emit and validate a reviewable topology/engine plan.
4. Launch through the provider control plane, never direct root SSH from ECC.
5. Warm up, test health and model identity, run an SLO canary, then register the
endpoint and redacted configuration.
Report cleanup complete only after canonical state is `stopped` with no active
endpoint. Stopping serving must not cancel or delete the underlying compute
booking. If stop is ambiguous, retain `cleanup.required=true` and escalate for
status reconciliation rather than claiming completion.
Until every gate and lifecycle operation above exists in the canonical runtime,
this skill remains a fail-closed availability check and documentation handoff.
## Structured result
Return one secret-free object with `status` (`PASS` or `BLOCK`), phase, account
and cluster ids, authenticated/eligible/entitled flags, model and revision,
estimated cost, deployment id, health, endpoint, bounded log evidence, cleanup
required/completed, error code, and next action. Distinguish code existence,
local test pass, merge, deployment, and observed live behavior. Use `PASS` only
for the lifecycle stage actually observed.
+66 -119
View File
@@ -1,130 +1,77 @@
/**
* Contract tests for the installable, fail-closed Itô inference handoff.
*/
/** Contract tests for the installable, fail-closed Itô inference skill. */
const assert = require("assert");
const fs = require("fs");
const os = require("os");
const path = require("path");
const { spawnSync } = require("child_process");
const REPO_ROOT = path.join(__dirname, "..", "..");
const ROOT = path.join(__dirname, "..", "..");
const read = (file) => fs.readFileSync(path.join(ROOT, file), "utf8");
const json = (file) => JSON.parse(read(file));
const skill = read("skills/ito-inference/SKILL.md");
function read(relativePath) {
return fs.readFileSync(path.join(REPO_ROOT, relativePath), "utf8");
}
function readJson(relativePath) {
return JSON.parse(read(relativePath));
}
function test(name, fn) {
try {
fn();
console.log(`${name}`);
return true;
} catch (error) {
console.log(`${name}`);
console.error(` ${error.message}`);
return false;
}
}
console.log("\n=== Testing Itô inference skill lifecycle ===\n");
const results = [
test("uses the canonical serving trigger and fails closed while unavailable", () => {
const skill = read("skills/ito-inference/SKILL.md");
assert.match(skill, /^name: ito-inference$/m);
assert.match(skill, /self-host|serve a model|OpenAI-compatible endpoint/i);
assert.match(skill, /requests naming .*ito-serve/i);
assert.match(skill, /completed booking/i);
assert.match(skill, /never books, reserves,\s+or spends/i);
assert.match(skill, /serving is unavailable today/i);
assert.match(skill, /report the\s+missing capability and return/i);
assert.match(skill, /stop before authentication/i);
assert.match(skill, /no `serve` verb/i);
assert.match(skill, /`inference`.*unsupported compatibility\s+probe/i);
assert.match(skill, /never substitute a\s+local runner, SSH helper, browser workflow, purchase endpoint/i);
assert.doesNotMatch(skill, /ssh\s+root@|serve-status\.sh/i);
for (const gate of [
/server-verified completed\s+booking/i,
/fresh serving eligibility/i,
/single-use confirmation/i,
/account, action, manifest, and\s+cost/i,
/idempotency/i,
/status, logs, metrics, cancel, and cleanup/i,
/structured JSON/i,
/ambiguous transport/i,
/reject symlinks/i,
/without following links/i,
/hash bytes from the opened descriptor/i,
/digest must exactly equal/i,
]) assert.match(skill, gate);
assert.match(skill, /--confirmation-ref <opaque-non-authorizing-reference>/i);
assert.doesNotMatch(skill, /--confirmation-token|--api-key|--access-token/i);
}),
test("keeps unsupported serving outside the executable bridge", () => {
const bridge = read("scripts/ito.js");
assert.match(bridge, /SUPPORTED_COMMANDS[^\n]+login[^\n]+auth[^\n]+find[^\n]+status[^\n]+evals/);
assert.doesNotMatch(bridge, /SUPPORTED_COMMANDS[^\n]+serve/);
assert.match(bridge, /Unsupported Itô command/);
const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), "ecc-ito-serve-reject-"));
try {
const canonicalDir = path.join(fixtureRoot, "cli", "ito-compute-cli", "dist", "bin");
fs.mkdirSync(canonicalDir, { recursive: true });
const marker = path.join(fixtureRoot, "spawned");
const executable = path.join(canonicalDir, "ito.js");
fs.writeFileSync(executable, `require("fs").writeFileSync(${JSON.stringify(marker)}, "spawned");\n`);
const result = spawnSync(process.execPath, [
path.join(REPO_ROOT, "scripts", "ecc.js"), "ito", "serve",
"--booking", "booking_test", "--model", "model_test",
], {
encoding: "utf8",
env: { ...process.env, ECC_ITO_CLI_EXECUTABLE: executable },
});
assert.notStrictEqual(result.status, 0);
assert.match(result.stderr, /Unsupported Itô command "serve"/);
assert.ok(!fs.existsSync(marker), "unsupported serve spawned the canonical child");
} finally {
fs.rmSync(fixtureRoot, { recursive: true, force: true });
const tests = [
["has trigger-rich two-field frontmatter", () => {
const frontmatter = skill.match(/^---\n([\s\S]*?)\n---/)[1];
const keys = [...frontmatter.matchAll(/^([a-z_-]+):/gm)].map((match) => match[1]);
assert.deepStrictEqual(keys, ["name", "description"]);
for (const phrase of ["deploy", "host", "serve", "run inference", "check", "tear down"]) {
assert.match(frontmatter, new RegExp(phrase, "i"));
}
}),
test("ships canonical inference through the existing opt-in compute module", () => {
const modules = readJson("manifests/install-modules.json").modules;
const module = modules.find((candidate) => candidate.id === "ito-compute");
assert.ok(module, "ito-compute install module is missing");
assert.deepStrictEqual(module.paths, [
"skills/ito-compute",
"skills/ito-inference",
"skills/ito-training",
]);
assert.deepStrictEqual(module.dependencies, ["platform-configs"]);
}],
["requires originating booking, exact eligibility, and no capacity selection", () => {
for (const pattern of [
/originating agent/, /completed Itô booking/, /cluster id/, /active status/,
/entitlement/, /do not\s+select another cluster/, /never\s+finds, books, reserves/,
]) assert.match(skill, pattern);
}],
["documents device login return and revocation recovery without secrets", () => {
for (const pattern of [
/ecc ito auth --json/, /ecc ito login/, /device URL\/code handoff/,
/return control to the\s+originating agent/, /Timeout, denial, or revocation/,
/Never place a key, token, or device code/,
]) assert.match(skill, pattern);
}],
["documents the complete canonical lifecycle and validation bounds", () => {
for (const pattern of [
/inference preflight/, /inference deploy/, /inference status/,
/inference logs/, /inference stop/, /owner\/model/, /pinned 764/,
/1168 hours/, /exact current\s+preflight amount/, /idempotency key/,
]) assert.match(skill, pattern);
}],
["enforces separate confirmations, ambiguity recovery, and safe cleanup", () => {
for (const pattern of [
/separate state-changing actions/, /explicit\s+user confirmation immediately before each/,
/do not deploy again blindly/, /provider timeout[\s\S]*not cached success/,
/separate explicit teardown confirmation/, /must not cancel or delete.*compute\s+booking/,
/cleanup.required=true/,
]) assert.match(skill, pattern);
}],
["defines structured results and honest capability-state distinctions", () => {
for (const field of [
"status", "phase", "cluster", "authenticated", "eligible", "entitled",
"model", "revision", "estimated cost", "deployment id", "health",
"endpoint", "cleanup", "error code", "next action",
]) assert.match(skill, new RegExp(field, "i"));
assert.match(skill, /code existence,\s+local test pass, merge, deployment, and observed live behavior/);
}],
["registers package, module, dependency, capability, and full profile", () => {
const module = json("manifests/install-modules.json").modules.find((item) => item.id === "ito-inference");
assert.deepStrictEqual(module.paths, ["skills/ito-inference"]);
assert.deepStrictEqual(module.dependencies, ["ito-compute"]);
assert.strictEqual(module.defaultInstall, false);
assert.strictEqual(module.stability, "beta");
const components = readJson("manifests/install-components.json").components;
assert.deepStrictEqual(
components.find((candidate) => candidate.id === "capability:ito-compute"),
{
id: "capability:ito-compute",
family: "capability",
description: "Authenticated Itô GPU inventory, RFQ, status, and explicitly gated node-qualification workflows through the separately installed canonical CLI.",
modules: ["ito-compute"],
}
);
const profiles = readJson("manifests/install-profiles.json").profiles;
assert.ok(profiles.full.modules.includes("ito-compute"));
const packageFiles = readJson("package.json").files;
assert.ok(packageFiles.includes("skills/ito-inference/"));
assert.ok(packageFiles.includes("skills/ito-training/"));
}),
assert.strictEqual(module.stability, "experimental");
assert.ok(module.targets.includes("codex") && module.targets.includes("kimi"));
assert.deepStrictEqual(json("manifests/install-components.json").components.find((item) => item.id === "capability:ito-inference").modules, ["ito-inference"]);
assert.ok(json("manifests/install-profiles.json").profiles.full.modules.includes("ito-inference"));
assert.ok(json("package.json").files.includes("skills/ito-inference/"));
}],
];
const failed = results.filter((passed) => !passed).length;
console.log(`\nPassed: ${results.length - failed}`);
let failed = 0;
for (const [name, test] of tests) {
try { test(); console.log(`${name}`); }
catch (error) { failed += 1; console.error(`${name}: ${error.message}`); }
}
console.log(`Passed: ${tests.length - failed}`);
console.log(`Failed: ${failed}`);
process.exit(failed > 0 ? 1 : 0);
process.exitCode = failed ? 1 : 0;
+32 -5
View File
@@ -118,7 +118,7 @@ async function main() {
const tests = [
["forwards only the reviewed RFQ CLI surface to an explicit local executable", () => {
for (const command of ["login", "logout", "auth", "find", "status"]) {
for (const command of ["login", "logout", "auth", "find", "status", "inference"]) {
const probe = makeItoProbe();
try {
const result = runCli(["ito", command], {
@@ -241,8 +241,8 @@ async function main() {
fs.rmSync(probe.directory, { recursive: true, force: true });
}
}],
["forwards ITO_API_KEY directly to auth, find, and status without legacy mode", () => {
for (const command of ["auth", "find", "status"]) {
["forwards ITO_API_KEY directly to authenticated runtime commands without legacy mode", () => {
for (const command of ["auth", "find", "status", "inference"]) {
const probe = makeItoProbe();
try {
const result = runCli(["ito", command], {
@@ -256,6 +256,33 @@ async function main() {
}
}
}],
["passes inference lifecycle arguments and explicit confirmation boundaries unchanged", () => {
const probe = makeItoProbe();
try {
const args = [
"ito", "inference", "deploy",
"--cluster", "clu_1",
"--model", "Qwen/Qwen3-8B",
"--revision", "a1b2c3d4",
"--engine", "vllm",
"--max-runtime-hours", "2",
"--confirm-cost-usd", "32.00",
"--json",
];
const result = runCli(args, {
ECC_ITO_CLI_EXECUTABLE: probe.executable,
ITO_API_KEY: "ito_test_key",
OPENAI_API_KEY: "must-not-cross",
});
assert.strictEqual(result.status, 0, result.stderr);
const invocation = readInvocation(probe);
assert.deepStrictEqual(invocation.argv, ["--json", ...args.slice(1, -1)]);
assert.strictEqual(invocation.env.ITO_API_KEY, "ito_test_key");
assert.strictEqual(invocation.env.OPENAI_API_KEY, undefined);
} finally {
fs.rmSync(probe.directory, { recursive: true, force: true });
}
}],
["streams device login output before completion and propagates its exit status", async () => {
const probe = makeItoProbe(7);
try {
@@ -473,14 +500,14 @@ async function main() {
);
}],
["rejects unsupported browser, paper, and execution operations before spawning", () => {
for (const command of ["rent", "lock", "purchase", "run", "inference", "mcp"]) {
for (const command of ["rent", "lock", "purchase", "run", "mcp"]) {
const probe = makeItoProbe();
try {
const result = runCli(["ito", command], {
ECC_ITO_CLI_EXECUTABLE: probe.executable,
});
assert.notStrictEqual(result.status, 0, command);
assert.match(result.stderr, /only login, logout, auth, find, status, and evals/i);
assert.match(result.stderr, /only login, logout, auth, find, status, evals, and inference/i);
assert.ok(!fs.existsSync(probe.log), `${command} must not spawn the Itô CLI`);
} finally {
fs.rmSync(probe.directory, { recursive: true, force: true });