diff --git a/.claude-plugin/README.md b/.claude-plugin/README.md index 41fd488f4..1b87bfcf3 100644 --- a/.claude-plugin/README.md +++ b/.claude-plugin/README.md @@ -16,4 +16,4 @@ export ANTHROPIC_AUTH_TOKEN=your-token claude ``` -Run or self-host any open-source model behind that endpoint. Itô is ECC's preferred compute sponsor: [open the Itô dashboard to sign in and rent or manage GPUs](https://compute.itomarkets.com). Any GPU provider works. ECC only provides this link; it does not change Claude Code transport settings or provision compute or serving. Managed inference through Itô is not live yet. +Run or self-host any open-source model behind that endpoint. Itô is ECC's preferred compute sponsor: [open the Itô dashboard to sign in and rent or manage GPUs](https://compute.itomarkets.com). Any GPU provider works. That sponsorship link is passive: it does not invoke an RFQ, reserve capacity, change Claude Code transport settings, provision compute, or configure serving. Separately, the opt-in `ecc ito find` bridge invokes the explicitly configured canonical Itô CLI and submits a live authenticated RFQ; it does not reserve capacity. Managed inference through Itô is not live yet. diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 82392836b..e4d740cc3 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,7 +11,7 @@ { "name": "ecc", "source": "./", - "description": "Harness-native ECC operator layer - 67 agents, 278 skills, 94 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses", + "description": "Harness-native ECC operator layer - 67 agents, 279 skills, 94 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses", "version": "2.0.0", "author": { "name": "Affaan Mustafa", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 16e54ca53..fc92c803e 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "ecc", "version": "2.0.0", - "description": "Harness-native ECC plugin for engineering teams - 67 agents, 278 skills, 94 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses", + "description": "Harness-native ECC plugin for engineering teams - 67 agents, 279 skills, 94 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses", "author": { "name": "Affaan Mustafa", "url": "https://x.com/affaanmustafa" diff --git a/.kimi/README.md b/.kimi/README.md index 4fb9b72cf..7cf5f0f9c 100644 --- a/.kimi/README.md +++ b/.kimi/README.md @@ -23,4 +23,4 @@ bash ./install.sh --target kimi --profile minimal ## Self-hosted model compute -Run or self-host any open-source model—including Kimi—on owned or rented GPUs. Itô is ECC's preferred compute sponsor: [open the Itô dashboard to sign in and rent or manage GPUs](https://compute.itomarkets.com). Any GPU provider works. ECC only provides this link; it does not provision compute or serving. Managed inference through Itô is not live yet. +Run or self-host any open-source model—including Kimi—on owned or rented GPUs. Itô is ECC's preferred compute sponsor: [open the Itô dashboard to sign in and rent or manage GPUs](https://compute.itomarkets.com). Any GPU provider works. That sponsorship link is passive: it does not invoke an RFQ, reserve capacity, provision compute, or configure serving. Separately, the opt-in `ecc ito find` bridge invokes the explicitly configured canonical Itô CLI and submits a live authenticated RFQ; it does not reserve capacity. Managed inference through Itô is not live yet. diff --git a/AGENTS.md b/AGENTS.md index 40c355be2..0fa533ed1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — Agent Instructions -This is a **production-ready AI coding plugin** providing 67 specialized agents, 278 skills, 94 commands, and automated hook workflows for software development. +This is a **production-ready AI coding plugin** providing 67 specialized agents, 279 skills, 94 commands, and automated hook workflows for software development. **Version:** 2.0.0 @@ -152,7 +152,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat ``` agents/ — 67 specialized subagents -skills/ — 278 workflow skills and domain knowledge +skills/ — 279 workflow skills and domain knowledge commands/ — 94 slash commands hooks/ — Trigger-based automations rules/ — Always-follow guidelines (common + per-language) diff --git a/README.md b/README.md index bcd4d5d1d..3142ba51e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -1`**Language:** English | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md) | [Türkçe](docs/tr/README.md) | [Русский](docs/ru/README.md) | [Tiếng Việt](docs/vi-VN/README.md) | [ไทย](docs/th/README.md) | [Deutsch](docs/de-DE/README.md) | [Español](docs/es/README.md) +**Language:** English | [Português (Brasil)](docs/pt-BR/README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md) | [Türkçe](docs/tr/README.md) | [Русский](docs/ru/README.md) | [Tiếng Việt](docs/vi-VN/README.md) | [ไทย](docs/th/README.md) | [Deutsch](docs/de-DE/README.md) | [Español](docs/es/README.md) ![ECC — the agent harness operating system](assets/hero.png) @@ -112,6 +112,12 @@ ECC v2.0.0 adds the public Hermes operator story on top of that reusable layer: Itô + + + Moonshot AI Kimi logo
+ Moonshot AI +
+ @@ -445,7 +451,7 @@ If you stacked methods, clean up in this order: /plugin list ecc@ecc ``` -**That's it!** You now have access to 67 agents, 278 skills, and 94 legacy command shims. +**That's it!** You now have access to 67 agents, 279 skills, and 94 legacy command shims. ### Dashboard GUI @@ -1194,7 +1200,27 @@ claude If your gateway remaps model names, configure that in Claude Code rather than in ECC. ECC's hooks, skills, commands, and rules are model-provider agnostic once the `claude` CLI is already working. -Run or self-host any open-source model behind that gateway using separate compute and serving setup. If you need GPU capacity, [Itô](https://compute.itomarkets.com) is ECC's preferred compute sponsor; any GPU provider works. ECC only links to the Itô dashboard for sign-in and GPU rental or management—it does not provision compute or serving. Managed inference through Itô is not live yet. +Run or self-host any open-source model behind that gateway using separate compute and serving setup. If you need GPU capacity, [Itô](https://compute.itomarkets.com) is ECC's preferred compute sponsor; any GPU provider works. That sponsorship link is passive: it does not invoke an RFQ, reserve capacity, provision compute, or configure serving. Separately, the opt-in `ecc ito find` bridge invokes the explicitly configured canonical Itô CLI and submits a live authenticated RFQ; it does not reserve capacity. Managed inference through Itô is not live yet. + +### Itô compute CLI bridge + +`ecc ito` delegates to the separately installed canonical Itô client; ECC does +not maintain a second API client or browser handoff. The available operations +are `ecc ito auth`, `ecc ito find`, and `ecc ito status`. The matching MCP tools +are `ito_auth`, `ito_find`, and `ito_status`. + +The `ito-compute-cli` package is currently unpublished. Build it locally from +[`Ito-Markets/ito-cloud-runtime`](https://github.com/Ito-Markets/ito-cloud-runtime) +under `cli/ito-compute-cli`, run `npm ci` and `npm run check`, then set +`ECC_ITO_CLI_EXECUTABLE` to that build's absolute `dist/bin/ito.js` path. +Inject `ITO_API_KEY` from 1Password or the launching environment. ECC does not +discover this credential-bearing client through `PATH`. See the [`ito-compute` +skill](skills/ito-compute/SKILL.md) for the full RFQ authority and MCP setup +contract. + +`find` submits a live authenticated RFQ. It does not reserve capacity. ECC +exposes no quote lock, purchase, workload, node-evaluation, or inference path, +and it never replaces a missing client or failed live call with a local result. Official references: - [Claude Code LLM gateway docs](https://docs.anthropic.com/en/docs/claude-code/llm-gateway) @@ -1542,7 +1568,7 @@ The configuration is automatically detected from `.opencode/opencode.json`. |---------|---------------------|----------|--------| | Agents | PASS: 67 agents | PASS: 12 agents | **Claude Code leads** | | Commands | PASS: 94 commands | PASS: 35 commands | **Claude Code leads** | -| Skills | PASS: 278 skills | PASS: 37 skills | **Claude Code leads** | +| Skills | PASS: 279 skills | PASS: 37 skills | **Claude Code leads** | | Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** | | Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** | | MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** | @@ -1703,7 +1729,7 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e |---------|-----------------------|------------|-----------|----------|----------------| | **Agents** | 67 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 | N/A | | **Commands** | 94 | Shared | Instruction-based | 35 | 5 prompts | -| **Skills** | 278 | Shared | 10 (native format) | 37 | Via instructions | +| **Skills** | 279 | Shared | 10 (native format) | 37 | Via instructions | | **Hook Events** | 8 types | 15 types | None yet | 11 types | None | | **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks | N/A | | **Rules** | 34 (common + lang) | 34 (YAML frontmatter) | Instruction-based | 13 instructions | 1 always-on file | diff --git a/README.zh-CN.md b/README.zh-CN.md index 1f0a94c22..dac2da6cc 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -164,7 +164,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list ecc@ecc ``` -**完成!** 你现在可以使用 67 个代理、278 个技能和 94 个命令。 +**完成!** 你现在可以使用 67 个代理、279 个技能和 94 个命令。 ### multi-* 命令需要额外配置 diff --git a/SPONSORS.md b/SPONSORS.md index 173a57ebd..adb8d32b7 100644 --- a/SPONSORS.md +++ b/SPONSORS.md @@ -14,10 +14,11 @@ Thank you to everyone funding ECC's open-source work. Your sponsorship is what l | [**Greptile**](https://www.greptile.com/go/ecc) | Greptile logo | 2026 | | [**Atlas Cloud**](https://www.atlascloud.ai/?utm_source=github&utm_medium=link&utm_campaign=ECC) | Atlas Cloud logo | 2026 | | [**Itô**](https://compute.itomarkets.com) | Itô Markets logo | 2026 | +| [**Moonshot AI**](https://www.moonshot.ai/) | Moonshot AI Kimi logo | 2026 | *[Become a Business sponsor](https://github.com/sponsors/affaan-m) to get README sponsor placement + SPONSORS.md listing. Current Business tier is $500/mo. No seats, SLA, custom development, or preferential technical placement is bundled unless separately agreed.* -Run or self-host any open-source model. Itô is ECC's preferred compute sponsor, while ECC remains provider-agnostic and any GPU provider works. ECC only links to the [Itô dashboard](https://compute.itomarkets.com) for sign-in and GPU rental or management; it does not provision compute or serving. Managed inference through Itô is not live yet. +Run or self-host any open-source model. Itô is ECC's preferred compute sponsor, while ECC remains provider-agnostic and any GPU provider works. The [Itô dashboard](https://compute.itomarkets.com) sponsorship link is passive: it does not invoke an RFQ, reserve capacity, provision compute, or configure serving. Separately, the opt-in `ecc ito find` bridge invokes the explicitly configured canonical Itô CLI and submits a live authenticated RFQ; it does not reserve capacity. Managed inference through Itô is not live yet. ## Team Sponsors — $200/mo diff --git a/assets/images/sponsors/moonshot.svg b/assets/images/sponsors/moonshot.svg new file mode 100644 index 000000000..29bceeade --- /dev/null +++ b/assets/images/sponsors/moonshot.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/docs/ATLAS-CLOUD-GUIDE.md b/docs/ATLAS-CLOUD-GUIDE.md index d4fe86dc2..83163c5b7 100644 --- a/docs/ATLAS-CLOUD-GUIDE.md +++ b/docs/ATLAS-CLOUD-GUIDE.md @@ -2,7 +2,7 @@ [Atlas Cloud](https://www.atlascloud.ai/?utm_source=github&utm_medium=link&utm_campaign=everything-claude-code) is a full-modal AI inference platform providing an OpenAI-compatible API for 59+ LLM models, image generation, and video generation. -> Run or self-host any open-source model instead of using a managed API. Itô is ECC's preferred compute sponsor: [open the Itô dashboard to sign in and rent or manage GPUs](https://compute.itomarkets.com). Any GPU provider works. ECC only provides this link; it does not provision compute or serving. Managed inference through Itô is not live yet. +> Run or self-host any open-source model instead of using a managed API. Itô is ECC's preferred compute sponsor: [open the Itô dashboard to sign in and rent or manage GPUs](https://compute.itomarkets.com). Any GPU provider works. That sponsorship link is passive: it does not invoke an RFQ, reserve capacity, provision compute, or configure serving. Separately, the opt-in `ecc ito find` bridge invokes the explicitly configured canonical Itô CLI and submits a live authenticated RFQ; it does not reserve capacity. Managed inference through Itô is not live yet. ## Configuration diff --git a/docs/design/ecc-ito-compute-integration.md b/docs/design/ecc-ito-compute-integration.md index 4893df8f0..b9ed5c418 100644 --- a/docs/design/ecc-ito-compute-integration.md +++ b/docs/design/ecc-ito-compute-integration.md @@ -1,586 +1,127 @@ # ECC × Itô Compute Integration -Status: **Proposed — requires Affaan approval before Phase 2 implementation** +Status: **Implemented local CLI bridge; managed inference remains unavailable** Owner: Affaan Mustafa -Prepared: 2026-07-21 - -Phase 1 branch: agent/ito-sponsor-compute - -## Decision gate - -This document is the approval artifact for Phase 2. Approval authorizes only -delivery slices 1 and 2: the five skills and the fail-closed CLI/MCP stub. It -does not authorize or claim a working Itô inference service, live rental API, -OAuth integration, billing flow, or rental mutation. - -No Phase 2 skill, CLI, MCP, OAuth, rental-management, or serving code should be -built until Affaan approves the decisions at the end of this document. - -### Narrow sandbox handoff authorized on 2026-07-23 - -Affaan separately authorized one smaller CLI slice: - - ecc ito rent --accelerator h100 --count 1 --hours 24 - -That command is implemented as `ito.compute.handoff.v1`. It validates an -explicit H100 intent, records `ecc-cli` provenance, fixes the authority ceiling -at read-only sandbox mode, and opens only the `/desk` page, where sign-in may be -required. `--dry-run` emits the identical structured handoff without opening a -browser. - -The current Itô desk does not expose a supported structured ECC deep link or a -non-mutating external-intake endpoint. The CLI therefore returns -`transport: manual_copy` and `acceptedByIto: false`; it does not pretend that -opening `/desk` delivered the intent. A human may copy the bounded message into -the conversational desk. ECC never reads credentials, files an RFQ, requests -or accepts a quote, calls procurement, approves funds, creates an order, or -contacts a counterparty. The user must stop before `Pay & buy`. - -This narrow authorization does not approve the five skills, MCP, OAuth, rental -management, inference serving, deployment, publication, or any live economic -mutation described later in this document. +Updated: 2026-07-23 ## Thesis -The distribution chain is: +The distribution chain remains provider-neutral: - GPU compute (Itô) + GPU compute (Itô or another selected provider) -> any open-source model -> model harness -> ECC meta-harness -Itô is ECC's preferred compute sponsor. It is never an exclusive provider. -ECC remains open source and all workflows must accept owned hardware, existing -clusters, and other compute providers. - -## Terms and non-negotiable boundaries - -- **Self-hosting** means the user operates a model runtime on owned or rented - compute. ECC does not silently provision or operate that runtime. -- **Preferred compute sponsor** means Itô is suggested at the step where a user - needs new GPU capacity. Requirements and existing capacity come first. -- **Provider-neutral** means users can choose any provider or use hardware they - already control without losing workflow functionality. -- **Manual handoff** means ECC can route the user to - for sign-in, rental, and dashboard access. -- **Not live** means ECC cannot currently exchange Itô OAuth tokens, fetch live - inventory or pricing, create or manage rentals, or return an inference - endpoint. -- Itô compute authentication must not reuse the prediction-market ITO_API_KEY. -- Phase 2 must not add an Itô LLM provider or advertise an Itô-compatible model - endpoint. Itô is the compute layer, not an inference API. - -## Sequencing - -### Phase 1: launch-ready sponsor and compute routing - -Phase 1 is the smallest shippable slice and is prepared before Phase 2: - -| Surface | Launch-ready change | -|---|---| -| ECC README and sponsor roster | Itô logo beside current business sponsors; preferred-compute and provider-neutral disclosure | -| ecc-universal npm surface | README placement, packaged sponsor assets, and install welcome copy | -| ECC installer | Human help and install-plan footer route self-host intent to Itô; JSON remains machine-pure | -| ECC CLI | Top-level help shows the same shared compute disclosure | -| Harness-specific endpoint/model docs | Bring-your-own endpoint/model notes route GPU needs to Itô without claiming provisioning | -| Local-model selector | Ollama selection shows a passive compute-rental/dashboard notice; no Itô provider adapter | -| ecc.tools | Sponsor card, sponsor proof chip, and explicit no-serving boundary | -| AgentShield / ecc-agentshield | Sponsor callout in the GitHub and npm README; no ads in security reports | - -Launch coordination still matters: - -- Merge the public PRs when their required checks and reviews pass. Coordinate - npm publication and the verified ecc.tools deployment as explicit release - steps; do not tie the generic OSS-compute integration to an unpublished model - or model-provider announcement. -- npm READMEs update only when new versions are published. Merging source changes - alone does not update the already-published ecc-universal or ecc-agentshield - package pages. -- The release owner must select unused package versions in the launch commit, - update every synchronized version surface, prove that neither version exists - in the npm registry, publish both packages, and read back the rendered package - pages. The sponsor PRs do not silently republish existing versions. -- ecc.tools requires its normal verified Cloudflare deployment after merge. - -### Phase 2: build only after approval - -Phase 2 adds provider-neutral operational skills and an honest Itô control-plane -stub. Real OAuth, rentals, and serving remain separately gated on backend -contracts. - -## Channel A: self-host an open model - -### Skill 1: self-host-a-model - -Path: - - skills/self-host-a-model/SKILL.md - -Activate when a user asks to self-host, locally host, or deploy an open model; -mentions Ollama, vLLM, SGLang, TGI, an OpenAI-compatible endpoint, Kimi, GLM, -Llama, Mistral, quantization, GPU sizing, or moving away from a managed API. - -Workflow shape: - -1. Identify the model artifact, license, trust boundary, and required context. -2. Estimate VRAM from weights, precision or quantization, KV cache, concurrency, - and runtime overhead. -3. Prefer existing owned or already-rented capacity when it satisfies the - requirement. -4. If capacity is missing, compare provider requirements and disclose Itô as - ECC's preferred compute sponsor. Authenticate only if the user selects Itô. -5. Select a user-controlled runtime, container, storage, network, auth, and TLS - plan. -6. Produce endpoint smoke tests and harness configuration steps. -7. Verify model identity, response schema, latency, health, and shutdown. - -Required output: SelfHostPlan - -| Field | Meaning | -|---|---| -| computeOwner | owned, existing cluster, Itô, or another selected provider | -| modelArtifact | immutable model and revision | -| runtime | selected serving engine and version | -| resourceEnvelope | GPU type/count, VRAM, CPU, RAM, disk, and network | -| endpointContract | expected protocol and auth, never a fabricated endpoint | -| deploymentSteps | provider-neutral execution plan | -| verification | health, identity, schema, latency, and teardown checks | -| blockers | missing access, capacity, serving, or model facts | +Itô is ECC's preferred compute sponsor, never an exclusive provider. Owned +hardware, existing clusters, and other providers remain valid. -Non-goals: - -- Do not claim ECC or Itô created an endpoint. -- Do not fabricate Itô inventory, pricing, credentials, or rental identifiers. -- Do not force any model family; the skill is model-agnostic. +## Implemented boundary -## Channel B: training and GPU operations +ECC delegates to the canonical Itô package in +`Ito-Markets/ito-cloud-runtime/cli/ito-compute-cli`. ECC does not maintain a +second API client or response schema. -### Skill 2: training-run-operations - -Path: - - skills/training-run-operations/SKILL.md +The wrapper exposes only the canonical CLI's `auth`, `find`, and `status` +operations: -Activate for launching, resuming, debugging, observing, or cost-planning a -training, fine-tuning, evaluation, retraining, or MLE run. + ecc ito auth + ecc ito find + ecc ito status -When installed, cross-link mle-workflow for the production lifecycle, -pytorch-patterns for framework details, and eval-harness for promotion criteria. -The new skill must still be usable on Kimi without mle-workflow or -pytorch-patterns because their current install modules do not support Kimi. Keep -the standalone contract minimal instead of copying those skills wholesale. +The canonical MCP server exposes only `ito_auth`, `ito_find`, and `ito_status`. +ECC includes an opt-in configuration template pointing to the local built MCP +entry. It does not enable the server by default. -Required output: TrainingRunPlan containing an immutable code revision, dataset -snapshot, environment or image, entry point, resources, artifact paths, -checkpoint and resume policy, metrics, observation plan, budget, retry policy, -and promotion gate. +The former browser/manual-copy command is retired. `ecc ito` performs no +browser navigation and stores no economic state. -Workflow: +## Local install -1. Run a local or minimal-capacity smoke test. -2. Size the full run and identify existing capacity. -3. Acquire new capacity only when needed; suggest Itô while permitting any - provider. -4. Route scheduler-specific work to the Slurm or Kubernetes GPU skill. -5. Submit, observe, checkpoint, resume, evaluate, and explicitly promote. +`ito-compute-cli` is unpublished. Install it from the canonical repository: -### Skill 3: gpu-workload-splitting + git clone https://github.com/Ito-Markets/ito-cloud-runtime.git + cd ito-cloud-runtime/cli/ito-compute-cli + npm ci + npm run check -Path: +Set `ECC_ITO_CLI_EXECUTABLE` to the explicit absolute built entry: - skills/gpu-workload-splitting/SKILL.md + /absolute/path/to/ito-cloud-runtime/cli/ito-compute-cli/dist/bin/ito.js -Activate when a user asks to split a model, dataset, batch job, sweep, or -training run across GPUs or nodes; reports poor scaling, stragglers, or -out-of-memory failures; or needs a workload-splitting layer. +ECC does not resolve the credential-bearing client through `PATH`; this avoids +forwarding `ITO_API_KEY` to an unrelated executable with the same name. -The skill must choose deliberately among data, tensor, pipeline, model, context, -or expert parallelism; independent shards; or job arrays. It must measure VRAM, -communication-to-compute ratio, topology, data locality, and heterogeneous -capacity before choosing. +For MCP, configure `node` with: -Required output: WorkloadSplitPlan + /absolute/path/to/ito-cloud-runtime/cli/ito-compute-cli/dist/bin/ito-mcp.js -| Field | Meaning | -|---|---| -| tasks | deterministic units of work | -| resources | CPU, RAM, accelerator, storage, and network per task | -| dependsOn | dependency DAG | -| shard | deterministic partition and ownership rule | -| placement | node, GPU, topology, and locality constraints | -| checkpoint | boundaries and resume behavior | -| merge | aggregation or reduction contract | -| failurePolicy | retry, idempotency, straggler, and partial-failure rules | +Inject `ITO_API_KEY` with 1Password or the launching environment. ECC forwards +only `ITO_API_KEY`, optional Itô endpoint overrides, and the minimum process +environment. It does not inspect or log the key. -Itô appears only at the capacity-acquisition step. Validate this output shape -with the LinkedIn contact who requested a workload-splitting layer before calling -Channel B customer-validated. +## Authority and economics -### Skill 4: slurm-gpu-workloads +- `auth` validates the configured Itô API key. +- `find` reads live inventory and submits a live authenticated RFQ. An operator + or agent must gather every hard topology/economic constraint and obtain + explicit buyer authority before invoking it. +- `status` reads current RFQ and procurement status. +- ECC returns the canonical process's stdout, stderr, and exit code unchanged. +- An inventory row or RFQ is not a capacity reservation. +- Only a non-null canonical firm quote is firm. +- After an ambiguous transport error, check `status` before repeating `find`. +- Global ECC dry-run does not create a local success result; the wrapper fails + closed without invoking the canonical CLI. -Path: +All durable RFQ, quote, procurement, and reservation state remains owned by the +Itô platform. ECC adds no shadow store. - skills/slurm-gpu-workloads/SKILL.md +## Unsupported in this slice -Activate for sbatch, srun, salloc, GPU GRES or TRES, pending jobs, arrays, -DDP/NCCL, queue time, low utilization, requeue, or Slurm GPU optimization. +ECC exposes no quote lock, purchase, workload execution, node qualification, +or inference command. The canonical package contains a separately gated node +qualification adapter, but this ECC bridge intentionally does not expose it. -Workflow: +Managed inference remains unavailable. ECC does not claim that Itô created a +model endpoint, deployed a workload, reserved capacity, or moved funds. -- Inspect cluster, partition, QOS, account, node, and GPU facts read-only first. -- Map nodes, tasks, processes, and GPUs explicitly. -- Cover staging, checkpoint signals, requeue, arrays, logs, topology, and scaling - diagnostics. -- Produce an sbatch plan or template plus validation commands. -- Never make scheduler-admin or cluster-wide changes without explicit authority. -- Use an existing cluster unchanged when possible; suggest Itô only when new - capacity is requested. +## Skill and install shape -### Skill 5: kubernetes-gpu-workloads +`skills/ito-compute/SKILL.md` is an opt-in workflow installed through: -Path: +- module: `ito-compute` +- component: `capability:ito-compute` +- profile: `full` - skills/kubernetes-gpu-workloads/SKILL.md +The skill documents the exact CLI and MCP names and the approval boundary. It +does not bundle the unpublished CLI. -Activate for GPU Jobs or pods, nvidia.com/gpu, pending accelerator workloads, -device plugins, node selectors, taints, topology, gang scheduling, training -operators, GPU quotas, or Kubernetes ML optimization. +## Publication blocker -When available, cross-link the existing kubernetes-patterns skill rather than -copying it. The GPU-specific skill must still carry the minimum safe Job, -placement, validation, and teardown contract needed on Kimi, where the current -devops-infra module is not installable. - -Workflow: +The integration works from a local build. Distribution remains blocked until +`ito-compute-cli` has an approved package-publication policy and is published +or replaced by another verified distribution channel. ECC must not claim npm +availability before a registry read confirms it. -- Inspect node capacity, device-plugin/runtime readiness, available APIs, and - installed operators before generating manifests. -- Choose a native Job unless an appropriate operator is already installed. -- Never invent a custom resource definition. -- Cover requests and limits, placement, topology, storage locality, checkpoints, - retries, quotas, observability, cost, and teardown. -- Produce manifests plus client and server dry-run checks. - -## Shared provider rule for all five skills - -Every skill must apply this order: - -1. Capture workload requirements. -2. Reuse owned or existing compute when it fits. -3. If capacity is missing, present provider-neutral criteria. -4. Disclose Itô as ECC's preferred compute sponsor. -5. Allow another provider without degrading the workflow. -6. Trigger Itô authentication only after the user selects Itô. -7. Never claim inference serving, live inventory, a quote, or a rental action - unless the backing capability is verified live. - -No legacy commands should be added. skills/ remains the canonical workflow -surface. - -## Install and package shape - -Add a non-default gpu-compute module in manifests/install-modules.json: - -- Paths: the five skill directories above. -- Supported targets: all current targets, including kimi. -- Dependencies: platform-configs only. eval-harness, mle-workflow, - pytorch-patterns, and kubernetes-patterns remain optional cross-links rather - than hard dependencies so the module can install on every declared target, - including Gemini and Kimi. -- Default install: false. -- Stability: beta. -- Cost: medium. - -Add capability:gpu-compute to manifests/install-components.json and add the -module to the full install profile. Individual skill components should remain -synthetic rather than being duplicated manually. - -Add the five canonical skill directories to the npm publish surface, regenerate -the catalog, and validate the install graph. Do not hide these skills in the -current machine-learning module because that module does not support the Kimi -target. - -## ito CLI and MCP v0 - -### Placement - -Keep the initial stub inside ecc-universal: - - scripts/ito.js - scripts/ito-mcp.js - scripts/lib/ito/contracts.js - scripts/lib/ito/capabilities.js - scripts/lib/ito/auth.js - scripts/lib/ito/client.js - scripts/lib/ito/mcp.js - -Expose ecc ito as canonical and an ASCII ito bin alias. Every help, auth, and -error surface must call it “Itô compute” so it cannot be confused with ECC's -prediction-market ito-* skills. Prose and brand UI use Itô. Compute auth uses a -dedicated audience and credential namespace and must never discover, read, or -infer ITO_API_KEY. - -### CLI commands - - ito capabilities [--json] - ito dashboard --intent self-host|training [--no-open] - ito auth status [--json] - ito auth login --intent self-host|training [--no-open] [--json] - ito rent plan --intent ... --accelerator ... --count ... --memory-gib ... --hours ... [--json] - ito rent create --plan ... [--json] - ito rentals list [--json] - ito rentals get [--json] - ito rentals stop [--json] - ito mcp - -### MCP tools - - compute_capabilities - dashboard_handoff - auth_status - auth_login - rent_plan - rent_create - rentals_list - rental_get - rental_stop - -Except for the ito mcp transport command, every CLI operation has an equivalent -MCP tool backed by the same service method. dashboard_handoff is the MCP peer of -ito dashboard. MCP handoff tools return a URL and never open a browser; only a -direct local CLI invocation may open one, and --no-open must remain available. -Every command emitted or invoked by a skill, agent, or generated artifact must -pass --no-open; automatic browser opening is reserved for a human typing the -CLI command directly. - -All tools and commands return the same versioned response envelope. A supported -manual handoff looks like: - - { - "schemaVersion": "ito.compute.v0", - "success": true, - "state": "manual_handoff", - "data": { - "authenticated": false - }, - "error": null, - "links": { - "dashboard": "https://compute.itomarkets.com" - } - } - -An unavailable capability must instead use: - - { - "schemaVersion": "ito.compute.v0", - "success": false, - "state": "unavailable", - "data": null, - "error": { - "code": "CAPABILITY_NOT_AVAILABLE", - "message": "This operation requires an approved live Itô compute API." - }, - "links": { - "dashboard": "https://compute.itomarkets.com" - } - } - -Envelope invariants: - -- unavailable always means success: false, non-null typed error, and no - action-like data. -- The CLI exits nonzero for unavailable or validation failures. -- MCP returns the same envelope in an error tool result; it must not translate - failure into a successful-looking action. -- manual_handoff means the routing behavior worked. It does not mean inventory, - OAuth, rental management, or inference is available. - -### Honest stub behavior - -| Capability | Phase 2 stub value | -|---|---| -| dashboardHandoff | true | -| oauthTokenExchange | false | -| rentRequestDraft | true | -| rentMutation | false | -| rentalManagement | false | -| inferenceServing | false | - -- capabilities reports the table above. -- dashboard opens or returns the approved compute URL. -- auth status reports authenticated: false and oauthTokenExchange: false. It - must not inspect ITO_API_KEY or imply that dashboard cookies are CLI auth. -- auth login opens or returns the dashboard URL and reports manual action - required. MCP returns the URL without opening it. Neither path may say OAuth - succeeded. -- rent plan validates and normalizes requirements, then returns manual_handoff. - It must not fabricate inventory, price, quote, or plan ID. -- rent create, rental reads, and stop fail closed with - CAPABILITY_NOT_AVAILABLE until backed by live APIs. -- Future paid creation and rental stop remain hard-disabled until a trusted - consent channel exists. A model-supplied boolean, --confirm flag, or tool - argument is not proof of user consent. Live mutations require either - host-mediated user elicitation or a single-use capability issued outside the - model and bound to the account, exact action and resource, immutable quote or - resource ID, maximum cost, expiry, and idempotency key. - -### MCP distribution - -Add Itô only as an opt-in entry in mcp-configs/mcp-servers.json: - - "ito": { - "command": "ito", - "args": ["mcp"], - "description": "Opt-in Itô compute rental and management interface. Manual dashboard handoff only until live control-plane APIs are enabled; does not provide inference serving." - } - -Do not add it to the default .mcp.json or the Claude plugin manifest. ECC's MCP -policy permits one universal default connector; this integration is intentional -and task-scoped. - -## OAuth-on-intent contract - -The authentication trigger is a user decision, not installation: - - self-host or training intent - -> capacity needed - -> user selects Itô - -> auth status - -> login handoff or future OAuth - -Until the backend contract exists, login remains a manual dashboard handoff. - -When live OAuth becomes available, require: - -- A public native client using Authorization Code with PKCE S256 and no packaged - client secret, or an explicitly approved phishing-resistant device flow. -- State and nonce validation plus issuer, audience, and redirect validation. -- A loopback callback bound only to localhost with one-time state and a short - timeout, or the approved device-flow equivalent. -- Fixed redirect origins and least-privilege scopes. -- OS credential storage under an ecc.ito.compute-specific service/account - namespace, never repository files, .env files, MCP config, CLI arguments, - child-process environments, or ITO_API_KEY. -- Raw access and refresh tokens must never enter model context, MCP results, - stdout, stderr, telemetry, crash reports, or logs. Account and billing data is - redacted by default. -- Refresh and revocation behavior. -- Trusted account and cost consent before paid actions as defined in the - mutation-capability contract above. - -## Input and generated-artifact safety - -- Define JSON Schemas for intents, accelerator names, counts, memory, duration, - identifiers, plans, and every MCP input. Enforce enums and numeric ranges, - bound string and collection lengths, and reject control characters or - newlines in IDs and scheduler fields. -- Build subprocess calls with executable-plus-argv arrays. Never concatenate - inputs into a shell command, use shell: true, eval generated text, or interpolate - untrusted values into Slurm directives. -- Validate Slurm partitions, accounts, QOS, GRES/TRES, paths, and job names - before rendering. Validate Kubernetes resource names, API kinds, namespaces, - images, pull policy, resources, selectors, tolerations, volumes, and command - arrays against explicit schemas. -- Pin model, container, code, and dataset revisions in executable plans. -- Render artifacts by default. Submission or execution is a separate step that - requires explicit user authority and must preserve client/server dry runs. -- Never place kubeconfig, SSH keys, registry credentials, scheduler tokens, or - provider secrets in generated artifacts or model-visible output. - -## Pre-stageable versus blocked - -| Pre-stageable after approval | Blocked on Itô or serving infrastructure | -|---|---| -| Five provider-neutral skills | OAuth issuer and client registration | -| Resource sizing and workload plans | Authorization, token, refresh, and revocation endpoints | -| Slurm and Kubernetes artifacts for user-provided clusters | Live accelerator inventory and availability | -| Static dashboard handoff | Live pricing, quotes, billing, and idempotency | -| CLI/MCP schemas and capability reporting | Rental create, list, get, stop APIs and lifecycle states | -| Manual sign-in and rental handoff | SSH, kubeconfig, Slurm, or other access delivery | -| Fail-closed unavailable responses | Model upload and deployment | -| Mocked contract tests | Endpoint creation, health, autoscaling, and inference serving | - -Inference serving is a separate future phase. It must not be unlocked merely by -finishing this Phase 2 stub. - -## Test plan - -Add: - -- tests/ci/ito-compute-skills.test.js for exact skills, triggers, - provider-neutral language, sponsor disclosure, and the no-serving boundary. -- tests/lib/ito-contracts.test.js for validation, immutable envelopes, capability - states, URL allowlisting, injection-resistant schemas, and redaction. -- tests/scripts/ito.test.js for help, JSON, no-browser handoff, invalid input, - unavailable mutations, nonzero exit parity, and proof that ITO_API_KEY is - ignored. -- tests/scripts/ito-mcp.test.js for stdio initialization, tool schemas, - CLI/tool parity, error-result parity, no-browser handoff, and secret-free - results. -- tests/integration/ito-compute-e2e.test.js for ecc ito to manual handoff and MCP - calls without unapproved network or browser side effects. - -Extend the ECC CLI, install-manifest, npm-publish-surface, and plugin-manifest -tests as required. - -Required gates: - - node scripts/ci/validate-skills.js --strict - node scripts/ci/validate-install-manifests.js - npm run catalog:check - npm test - npm run coverage - npm run lint - npm pack --dry-run --json - npm run security:ioc-scan - -Manual acceptance: - -- Existing hardware skips provider selection. -- An alternate provider path never invokes Itô authentication. -- Selecting Itô returns only truthful supported capabilities. -- Rental mutations fail closed until backed by live APIs. -- No flow emits an Itô inference endpoint. -- The Kimi target installs capability:gpu-compute. -- Paid and destructive actions remain unavailable until the trusted consent - capability and its separate approval exist. - -## Delivery slices after approval - -1. **Skills and install module:** five skills, provider rule, manifests, catalog, - and validation. -2. **CLI and opt-in MCP stub:** shared contracts, manual handoff, fail-closed - commands/tools, security and parity tests. -3. **OAuth and rental control plane:** a separately authorized future phase, - only after Itô supplies the live API contract and a threat model, API/contract - review, security review, billing review, and Affaan launch approval all pass. -4. **Inference serving:** separate plan, security review, economics review, and - explicit launch approval. - -Each slice must use its own focused PR and stay mergeable without later slices. - -## Affaan approval checklist - -- [ ] Approve the five skill names and their boundaries. -- [ ] Approve separate Slurm and Kubernetes GPU skills. -- [ ] Approve capability:gpu-compute with Kimi target support. -- [ ] Approve sponsor wording: Itô in prose, ASCII ito for CLI and MCP. -- [ ] Approve ecc ito plus the ito bin alias inside ecc-universal. -- [ ] Approve an opt-in MCP entry and no default MCP/plugin registration. -- [ ] Approve manual-handoff and fail-closed stub semantics. -- [ ] Approve the shared response envelope and trusted-consent boundary. -- [ ] Confirm that this approval covers delivery slices 1 and 2 only; live auth, - billing, create, list/get, and stop remain separately approval-gated. -- [ ] Supply or explicitly defer the OAuth and rental API contract. -- [ ] Confirm endpoint configuration for every supported harness before - publishing an example. -- [ ] Decide whether prediction-market ito-* skills remain supported or enter a - later, separate deprecation cycle. -- [ ] Validate WorkloadSplitPlan with the LinkedIn contact before describing - Channel B as customer-validated. - -Approval of this document authorizes delivery slices 1 and 2 only: the skills -and fail-closed stub. It does not authorize live auth, billing, rental APIs, -mutations, or claims that Itô inference serving is live. +The ECC package version remains unchanged in this worktree. Its version bump, +release commit, and publication are intentionally deferred to the release owner +after review. + +## Verification + +The local contract suite proves: + +- only the three supported operations spawn; +- RFQ arguments are forwarded without economic reinterpretation; +- only approved Itô runtime variables cross the process boundary; +- unsupported and dry-run paths fail before spawn; +- a missing or relative executable fails closed with local-install guidance; +- canonical output and exit status pass through unchanged; +- the skill, install manifests, npm surface, and opt-in MCP template stay + aligned. + +No test in this integration invokes a live Itô API, submits an RFQ, opens a +browser, or contacts a GPU node. diff --git a/docs/design/ecc-ito-handoff-evidence-2026-07-23.md b/docs/design/ecc-ito-handoff-evidence-2026-07-23.md deleted file mode 100644 index 7d0c8aa3b..000000000 --- a/docs/design/ecc-ito-handoff-evidence-2026-07-23.md +++ /dev/null @@ -1,122 +0,0 @@ -# ECC × Itô Sandbox Handoff Evidence - -Date: 2026-07-23 - -Branch: `agent/ecc-ito-sandbox-handoff` - -Base commit: `a3130f9ebfaeed075df5d5b52538acb0ee4bcdf8` - -## Result - -The canonical ECC CLI now supports: - -```text -ecc ito rent --accelerator h100 --count 1 --hours 24 -``` - -The command emits an `ito.compute.handoff.v1` envelope containing the exact -accelerator, count, and duration, `ecc-cli` provenance, and a read-only sandbox -authority ceiling. It opens only `https://compute.itomarkets.com/desk`. - -No supported Itô deep link or non-mutating structured external-intake endpoint -exists yet. The contract therefore reports `transport: manual_copy`, -`acceptedByIto: false`, and `signInMayBeRequired: true`. It never claims that -opening the page delivered the intent. - -The CLI does not read credentials, call the network, file an RFQ, request or -accept a quote, call procurement, approve funds, create an order, perform -outreach, deploy, push, or merge. The human boundary remains before -`Pay & buy`. - -## Security boundary - -Both subprocess boundaries use an explicit system-environment allowlist: - -1. `ecc` to the `ito` command. -2. `ito` to the OS browser opener. - -The allowlist retains only runtime essentials such as path, home, temporary -directory, locale, terminal, and desktop-session variables. ECC dry-run and -test controls are passed only to the Itô command where needed. Browser-probe -tests inject API-key, service-token, and password sentinels and prove none -reach the browser child. - -## Eval evidence - -Focused handoff: - -```text -node tests/scripts/ito-handoff.test.js -``` - -Result: 9 passed, 0 failed. - -Covered scenarios: - -- exact H100, count 1, 24-hour mapping; -- provenance and sandbox authority; -- global and local dry-run with zero browser invocation; -- exact allowlisted browser URL and sanitized child environment; -- direct Itô invocation with sanitized browser environment; -- missing, duplicate, unsupported, and out-of-range input rejection; -- absence of credential or mutating transports; -- human-readable approval boundary; -- POSIX-safe npm welcome output. - -CLI and package regressions: - -```text -node tests/scripts/ecc.test.js -node tests/scripts/npm-publish-surface.test.js -``` - -Results: 21 passed and 2 passed, with 0 failures. - -Full repository suite: - -```text -npm test -``` - -Result: 3,155 passed, 0 failed. - -Static and security gates: - -```text -npm run lint -npm run security:ioc-scan -- --root . -git diff --check -``` - -Results: all passed; the IOC scan inspected 207 files. - -Packaged-bin execution: - -```text -npm pack --pack-destination -node /scripts/ecc.js --dry-run ito rent \ - --accelerator h100 --count 1 --hours 24 --json -``` - -Result: the packed `ecc-universal-2.0.0.tgz` included executable `ecc.js` and -`ito.js`; the unpacked command returned the exact intent with `dryRun: true`, -`opened: false`, and `transport: manual_copy`. - -## Changed surfaces - -- `scripts/ito.js` -- `scripts/lib/ito-environment.js` -- `scripts/ecc.js` -- `package.json` -- `tests/scripts/ito-handoff.test.js` -- `tests/scripts/npm-publish-surface.test.js` -- `docs/design/ecc-ito-compute-integration.md` -- this evidence record - -## Remaining limitation - -ECC cannot automatically prefill or deliver this structured intent until Itô -publishes a safe, authenticated, non-mutating intake or deep-link contract. -Adding such a contract belongs in Itô and must preserve its existing human -approval gates. This ECC slice deliberately does not invent another UI, -transport, credential, or procurement path. diff --git a/docs/testing/ecc-ito-real-cli-bridge.tdd.md b/docs/testing/ecc-ito-real-cli-bridge.tdd.md new file mode 100644 index 000000000..a00328dc4 --- /dev/null +++ b/docs/testing/ecc-ito-real-cli-bridge.tdd.md @@ -0,0 +1,95 @@ +# ECC × Itô Real CLI Bridge — TDD Evidence + +Date: 2026-07-23 + +Source plan: requirements were derived from the approved implementation +handoff. No external plan file was executed. + +## User journeys + +1. As an ECC operator, I can invoke the canonical local Itô `auth`, `find`, and + `status` operations without a duplicate client or browser workflow. +2. As a security reviewer, I can prove unsupported operations, missing local + installs, and ECC dry-run requests fail before any child process or network + operation. +3. As an agent-harness user, I can install one truthful skill that names only + the real CLI commands and MCP tools. + +## RED evidence + +Before production changes: + +```text +node tests/scripts/ito-cli-bridge.test.js +Passed: 0 +Failed: 9 + +node tests/ci/ito-compute-skill.test.js +Passed: 0 +Failed: 4 +``` + +The failures were caused by the old browser-only `rent` command and the missing +real skill/install/MCP surfaces. + +## GREEN evidence + +```text +node tests/scripts/ito-cli-bridge.test.js +Passed: 9 +Failed: 0 + +node tests/ci/ito-compute-skill.test.js +Passed: 4 +Failed: 0 + +NODE_PATH=/node_modules \ + node scripts/ci/validate-install-manifests.js +Validated 33 install modules, 80 install components, and 7 profiles + +npm test +Total Tests: 3159 +Passed: 3159 +Failed: 0 + +npm run coverage +Statements: 89.21% +Branches: 79.71% +Functions: 93.96% +Lines: 89.21% + +npm run security:ioc-scan +Supply-chain IOC scan passed +``` + +The isolated worktree temporarily reused the canonical ECC checkout's existing +`node_modules` through an untracked local symlink. The symlink was removed +after validation; no dependency installation or source change was made in the +canonical checkout. +ESLint and Markdown lint also pass for every changed source file. The complete +package dry-run contains the wrapper, environment boundary, skill, and MCP +configuration. + +## Test specification + +| Guarantee | Test | Type | Result | +|---|---|---|---| +| Only `auth`, `find`, and `status` spawn | `tests/scripts/ito-cli-bridge.test.js` | end-to-end process contract | PASS | +| Full RFQ arguments cross unchanged | `tests/scripts/ito-cli-bridge.test.js` | integration | PASS | +| Only required Itô settings cross the child boundary | `tests/scripts/ito-cli-bridge.test.js` | security integration | PASS | +| Unsupported and dry-run operations fail before spawn | `tests/scripts/ito-cli-bridge.test.js` | negative end-to-end | PASS | +| Missing/relative executables fail with exact local guidance | `tests/scripts/ito-cli-bridge.test.js` | negative end-to-end | PASS | +| Child output and exit code are preserved | `tests/scripts/ito-cli-bridge.test.js` | end-to-end process contract | PASS | +| Skill, package, manifests, and MCP template agree | `tests/ci/ito-compute-skill.test.js` | repository contract | PASS | + +## Known gaps + +- No live Itô API, RFQ, browser, GPU node, or paid operation was invoked. +- No live GPU qualification was performed. +- The CLI remains locally built and unpublished. + +## Merge evidence + +No TDD checkpoint commits were created because the implementation handoff +explicitly prohibited commits. The working-tree diff and this report preserve +the RED/GREEN evidence instead. diff --git a/docs/zh-CN/AGENTS.md b/docs/zh-CN/AGENTS.md index 6326e8b6f..105ea3528 100644 --- a/docs/zh-CN/AGENTS.md +++ b/docs/zh-CN/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — 智能体指令 -这是一个**生产就绪的 AI 编码插件**,提供 67 个专业代理、278 项技能、94 条命令以及自动化钩子工作流,用于软件开发。 +这是一个**生产就绪的 AI 编码插件**,提供 67 个专业代理、279 项技能、94 条命令以及自动化钩子工作流,用于软件开发。 **版本:** 2.0.0 @@ -147,7 +147,7 @@ ``` agents/ — 67 个专业子代理 -skills/ — 278 个工作流技能和领域知识 +skills/ — 279 个工作流技能和领域知识 commands/ — 94 个斜杠命令 hooks/ — 基于触发的自动化 rules/ — 始终遵循的指导方针(通用 + 每种语言) diff --git a/docs/zh-CN/README.md b/docs/zh-CN/README.md index 1e6546b72..6e79f1267 100644 --- a/docs/zh-CN/README.md +++ b/docs/zh-CN/README.md @@ -228,7 +228,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list ecc@ecc ``` -**搞定!** 你现在可以使用 67 个智能体、278 项技能和 94 个命令了。 +**搞定!** 你现在可以使用 67 个智能体、279 项技能和 94 个命令了。 *** @@ -1142,7 +1142,7 @@ opencode |---------|---------------|----------|--------| | 智能体 | PASS: 67 个 | PASS: 12 个 | **Claude Code 领先** | | 命令 | PASS: 94 个 | PASS: 35 个 | **Claude Code 领先** | -| 技能 | PASS: 278 项 | PASS: 37 项 | **Claude Code 领先** | +| 技能 | PASS: 279 项 | PASS: 37 项 | **Claude Code 领先** | | 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** | | 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** | | MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** | @@ -1250,7 +1250,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以 |---------|-----------------------|------------|-----------|----------| | **智能体** | 67 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 | | **命令** | 94 | 共享 | 基于指令 | 35 | -| **技能** | 278 | 共享 | 10 (原生格式) | 37 | +| **技能** | 279 | 共享 | 10 (原生格式) | 37 | | **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 | | **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | N/A | 插件钩子 | | **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 | diff --git a/manifests/install-components.json b/manifests/install-components.json index aee5a7d31..8c457fb5c 100644 --- a/manifests/install-components.json +++ b/manifests/install-components.json @@ -194,6 +194,14 @@ "prediction-market-skills" ] }, + { + "id": "capability:ito-compute", + "family": "capability", + "description": "Authenticated Itô GPU inventory, RFQ, and status workflows through the separately installed canonical CLI.", + "modules": [ + "ito-compute" + ] + }, { "id": "capability:social", "family": "capability", diff --git a/manifests/install-modules.json b/manifests/install-modules.json index c3f300fe1..97f6419e2 100644 --- a/manifests/install-modules.json +++ b/manifests/install-modules.json @@ -571,6 +571,36 @@ "cost": "medium", "stability": "beta" }, + { + "id": "ito-compute", + "kind": "skills", + "description": "Authenticated Itô GPU inventory, RFQ, and status workflows through the separately installed canonical CLI.", + "paths": [ + "skills/ito-compute" + ], + "targets": [ + "claude", + "claude-project", + "cursor", + "antigravity", + "codex", + "gemini", + "opencode", + "codebuddy", + "joycode", + "qwen", + "zed", + "hermes", + "openclaw", + "kimi" + ], + "dependencies": [ + "platform-configs" + ], + "defaultInstall": false, + "cost": "light", + "stability": "beta" + }, { "id": "social-distribution", "kind": "skills", diff --git a/manifests/install-profiles.json b/manifests/install-profiles.json index 8352b2f95..b7a17109e 100644 --- a/manifests/install-profiles.json +++ b/manifests/install-profiles.json @@ -87,6 +87,7 @@ "operator-workflows", "optimization-workflows", "prediction-market-skills", + "ito-compute", "social-distribution", "media-generation", "orchestration", diff --git a/mcp-configs/mcp-servers.json b/mcp-configs/mcp-servers.json index 464f028f1..fca78511f 100644 --- a/mcp-configs/mcp-servers.json +++ b/mcp-configs/mcp-servers.json @@ -5,6 +5,11 @@ "args": ["mcp"], "description": "Local cost/privacy proxy - query your own usage & savings, route to the cheapest capable model, and mask secrets/PII before egress (nexus_stats, nexus_savings, nexus_recent, nexus_providers, nexus_cost_breakdown)" }, + "ito-compute": { + "command": "node", + "args": ["/absolute/path/to/ito-cloud-runtime/cli/ito-compute-cli/dist/bin/ito-mcp.js"], + "description": "Opt-in local Itô compute MCP. The canonical package is unpublished and must be built from Ito-Markets/ito-cloud-runtime/cli/ito-compute-cli. Exposes only ito_auth, ito_find, and ito_status; inject ITO_API_KEY from the launching environment." + }, "jira": { "command": "uvx", "args": ["mcp-atlassian==0.21.0"], diff --git a/package.json b/package.json index 9f6aef9b2..5779d6d0a 100644 --- a/package.json +++ b/package.json @@ -214,6 +214,7 @@ "skills/hookify-rules/", "skills/inventory-demand-planning/", "skills/ito-basket-compare/", + "skills/ito-compute/", "skills/ito-data-atlas-agent/", "skills/ito-market-intelligence/", "skills/ito-trade-planner/", @@ -418,7 +419,7 @@ "ecc-plan-canvas": "scripts/plan-canvas.js" }, "scripts": { - "welcome": "echo '\\n ecc-universal installed!\\n Run: ecc typescript\\n Compat: ecc-install typescript\\n Docs: https://github.com/affaan-m/ECC\\n Run or self-host any open-source model.\\n Compute: Itô is the preferred compute sponsor — https://compute.itomarkets.com\\n Any GPU provider works; ECC only links to the dashboard and does not provision compute or serving.\\n Managed inference through Itô is not live yet.\\n'", + "welcome": "echo '\\n ecc-universal installed!\\n Run: ecc typescript\\n Compat: ecc-install typescript\\n Docs: https://github.com/affaan-m/ECC\\n Run or self-host any open-source model.\\n Compute: Itô is the preferred compute sponsor — https://compute.itomarkets.com\\n Any GPU provider works. This sponsorship link is passive: it does not invoke an RFQ, reserve capacity, provision compute, or configure serving.\\n Separately, the opt-in ecc ito find bridge invokes the explicitly configured canonical Itô CLI and submits a live authenticated RFQ; it does not reserve capacity.\\n Managed inference through Itô is not live yet.\\n'", "catalog:check": "node scripts/ci/catalog.js --text", "catalog:sync": "node scripts/ci/catalog.js --write --text", "command-registry:generate": "node scripts/ci/generate-command-registry.js", diff --git a/scripts/ecc.js b/scripts/ecc.js index 4fdc23831..00928c2a1 100755 --- a/scripts/ecc.js +++ b/scripts/ecc.js @@ -29,7 +29,7 @@ const COMMANDS = { }, ito: { script: 'ito.js', - description: 'Prepare a read-only sandbox handoff to the Itô compute desk', + description: 'Invoke the separately installed canonical Itô compute CLI', }, 'install-plan': { script: 'install-plan.js', @@ -138,8 +138,9 @@ Examples: ecc catalog show framework:nextjs ecc consult "security reviews" ecc control-pane --port 8765 - ecc ito rent --accelerator h100 --count 1 --hours 24 - ecc --dry-run ito rent --accelerator h100 --count 1 --hours 24 --json + ecc ito auth + ecc ito find --gpu h200 --count 8 --nodes 1 --gpus-per-node 8 --days 30 --storage-tb 1 --start-window 2099-08-15 --max-rate 3.00 --form-factor bare_metal --contract-type reservation --fabric infiniband --region us-east-1 + ecc ito status --json ecc list-installed --json ecc doctor --target cursor ecc repair --dry-run @@ -232,7 +233,12 @@ function runCommand(commandName, args) { { cwd: process.cwd(), env: commandName === 'ito' - ? { ...createSafeItoEnvironment(process.env, { includeControls: true }) } + ? { + ...createSafeItoEnvironment(process.env, { + includeControls: true, + includeItoRuntime: true, + }), + } : process.env, encoding: 'utf8', maxBuffer: 10 * 1024 * 1024, diff --git a/scripts/ito.js b/scripts/ito.js index 8f37ee20c..0984b5c1b 100755 --- a/scripts/ito.js +++ b/scripts/ito.js @@ -2,306 +2,210 @@ "use strict"; +const fs = require("fs"); +const path = require("path"); const { spawnSync } = require("child_process"); const { createSafeItoEnvironment } = require("./lib/ito-environment"); -const DESK_URL = "https://compute.itomarkets.com/desk"; -const SCHEMA_VERSION = "ito.compute.handoff.v1"; -const SUPPORTED_ACCELERATORS = Object.freeze({ - h100: "h100", - "h100-pcie": "h100-pcie", - "h100-sxm": "h100-sxm", -}); -const REQUIRED_INTENT_OPTIONS = Object.freeze([ - "accelerator", - "count", - "hours", -]); +const SUPPORTED_COMMANDS = Object.freeze(["auth", "find", "status"]); +const CANONICAL_REPOSITORY = "https://github.com/Ito-Markets/ito-cloud-runtime.git"; +const CANONICAL_PACKAGE_PATH = "cli/ito-compute-cli"; +const EXECUTABLE_OVERRIDE = "ECC_ITO_CLI_EXECUTABLE"; +const MAX_OUTPUT_BYTES = 10 * 1024 * 1024; function showHelp() { console.log(` -ECC × Itô compute handoff +ECC × Itô local CLI bridge Usage: - ecc ito rent --accelerator --count <1-64> --hours <1-720> [options] + ecc ito auth + ecc ito find + ecc ito status + ecc ito --json -Options: - --dry-run Emit the exact handoff without opening a browser - --no-open Emit the exact handoff for manual browser navigation - --json Emit the versioned response envelope as JSON - --help Show this help +The bridge invokes the separately installed canonical Itô CLI and returns its +real stdout, stderr, and exit code unchanged. It performs no browser navigation +and adds no lock, workload, inference, evaluation, or purchase path. -Example: - ecc ito rent --accelerator h100 --count 1 --hours 24 +Important: + - "find" reads live inventory and submits an authenticated RFQ. + - Obtain explicit buyer authority and every hard constraint before invoking it. + - "status" reads live RFQ and procurement status. + - Inventory and RFQs are not reservations; only a returned firm quote is firm. -This command creates a sandbox-only, read-only intent. It opens the Itô desk -for manual copy; sign-in may be required. It does not send the intent into Itô, -file an RFQ, request or accept a quote, call a procurement endpoint, approve -funds, or place an order. Stop before "Pay & buy". +The canonical package is currently unpublished. Install it locally: + Canonical source: Ito-Markets/ito-cloud-runtime/${CANONICAL_PACKAGE_PATH} + git clone ${CANONICAL_REPOSITORY} + cd ito-cloud-runtime/${CANONICAL_PACKAGE_PATH} + npm ci + npm run check + +Then set ${EXECUTABLE_OVERRIDE} to the explicit absolute built entry: + /absolute/path/to/ito-cloud-runtime/${CANONICAL_PACKAGE_PATH}/dist/bin/ito.js + +For safety, ECC never discovers this credential-bearing client through PATH. + +The same package's MCP server exposes only: + ito_auth + ito_find + ito_status + +Configure the MCP command as "node" with this absolute argument: + /absolute/path/to/ito-cloud-runtime/${CANONICAL_PACKAGE_PATH}/dist/bin/ito-mcp.js + +Inject ITO_API_KEY into the child process from 1Password or the launching +environment. Never put the key in arguments, tracked files, or chat. `); } -function readValue(args, index, option) { - const value = args[index + 1]; - if (value === undefined || value.startsWith("--")) { - throw new Error(`--${option} requires a value`); - } - return value; -} - -function parseInteger(raw, option, minimum, maximum) { - if (!/^[0-9]+$/.test(raw)) { - throw new Error(`--${option} must be an integer between ${minimum} and ${maximum}`); - } - const value = Number(raw); - if (!Number.isSafeInteger(value) || value < minimum || value > maximum) { - throw new Error(`--${option} must be an integer between ${minimum} and ${maximum}`); - } - return value; -} - -function parseAccelerator(raw) { - const normalized = String(raw).trim().toLowerCase(); - const accelerator = SUPPORTED_ACCELERATORS[normalized]; - if (!accelerator) { - throw new Error( - "--accelerator must be one of h100, h100-pcie, or h100-sxm" - ); - } - return accelerator; -} - -function parseArgs(argv) { +function parseArgs(argv, environment = process.env) { const args = [...argv]; - if (args.includes("--help") || args.includes("-h")) { - return { help: true }; + if ( + args.length === 0 + || args.includes("--help") + || args.includes("-h") + ) { + return Object.freeze({ help: true, invocationArgs: [] }); } - const command = args.shift(); - if (command !== "rent") { - throw new Error(`unsupported Itô command: ${command || "(missing)"}`); - } - - const values = { - accelerator: null, - count: null, - hours: null, - dryRun: false, - json: false, - noOpen: false, - }; - const seen = new Set(); - - for (let index = 0; index < args.length; index += 1) { - const arg = args[index]; - if (arg === "--dry-run") { - if (seen.has("dry-run")) throw new Error("--dry-run may only be provided once"); - seen.add("dry-run"); - values.dryRun = true; - continue; - } - if (arg === "--json") { - if (seen.has("json")) throw new Error("--json may only be provided once"); - seen.add("json"); - values.json = true; - continue; - } - if (arg === "--no-open") { - if (seen.has("no-open")) throw new Error("--no-open may only be provided once"); - seen.add("no-open"); - values.noOpen = true; - continue; - } - - const option = arg.startsWith("--") ? arg.slice(2) : ""; - if (!REQUIRED_INTENT_OPTIONS.includes(option)) { - throw new Error(`unsupported option: ${arg}`); - } - if (seen.has(option)) { - throw new Error(`--${option} may only be provided once`); - } - seen.add(option); - const raw = readValue(args, index, option); - if (option === "accelerator") values.accelerator = parseAccelerator(raw); - if (option === "count") values.count = parseInteger(raw, option, 1, 64); - if (option === "hours") values.hours = parseInteger(raw, option, 1, 720); - index += 1; - } - - const missing = REQUIRED_INTENT_OPTIONS.filter((option) => values[option] === null); - if (missing.length > 0) { + if (environment.ECC_DRY_RUN === "1" || args.includes("--dry-run")) { throw new Error( - `missing required rental intent option${missing.length === 1 ? "" : "s"}: ` - + missing.map((option) => `--${option}`).join(", ") + "Itô compute has no paper or dry-run success mode. No CLI operation was invoked." ); } - return { - help: false, - command, - options: Object.freeze({ - ...values, - dryRun: values.dryRun || process.env.ECC_DRY_RUN === "1", - }), - }; -} - -function buildHandoffMessage(intent) { - const accelerator = intent.accelerator.toUpperCase(); - return [ - "[ECC sandbox-only compute handoff]", - `Source: ECC CLI. Need ${intent.count} × ${accelerator} for ${intent.hours} hours.`, - "Treat this as a read-only requirement for review.", - "Do not file an RFQ, request or accept a quote, place an order, approve funds, or contact a counterparty without a separate explicit human action in Itô.", - ].join(" "); -} - -function createEnvelope({ success, state, data, error }) { - return Object.freeze({ - schemaVersion: SCHEMA_VERSION, - success, - state, - data, - error, - links: Object.freeze({ desk: DESK_URL }), - }); -} - -function createInvalidEnvelope(message) { - return createEnvelope({ - success: false, - state: "invalid_request", - data: null, - error: Object.freeze({ - code: "INVALID_ARGUMENT", - message, - }), - }); -} - -function defaultOpenUrl(url) { - let executable; - let args; - const testExecutable = process.env.NODE_ENV === "test" - ? process.env.ECC_ITO_BROWSER_EXECUTABLE - : null; - - if (testExecutable) { - if (process.platform === "win32" && /\.(?:bat|cmd)$/i.test(testExecutable)) { - executable = process.env.ComSpec || "cmd.exe"; - args = ["/d", "/s", "/c", testExecutable, url]; - } else { - executable = testExecutable; - args = [url]; - } - } else if (process.platform === "darwin") { - executable = "open"; - args = [url]; - } else if (process.platform === "win32") { - executable = "cmd.exe"; - args = ["/d", "/s", "/c", "start", "", url]; - } else { - executable = "xdg-open"; - args = [url]; + const jsonIndexes = args + .map((value, index) => (value === "--json" ? index : -1)) + .filter((index) => index >= 0); + if (jsonIndexes.length > 1) { + throw new Error("--json may only be provided once"); + } + const withoutJson = args.filter((value) => value !== "--json"); + const command = withoutJson.shift(); + if (!SUPPORTED_COMMANDS.includes(command)) { + throw new Error( + `Unsupported Itô command "${command || "(missing)"}"; ECC permits only auth, find, and status.` + ); } - const result = spawnSync(executable, args, { - env: { ...createSafeItoEnvironment(process.env) }, - stdio: "ignore", - windowsHide: true, - shell: false, - }); - return !result.error && result.status === 0; -} - -function createHandoff(options, openUrl = defaultOpenUrl) { - const shouldOpen = !options.dryRun && !options.noOpen; - const opened = shouldOpen ? openUrl(DESK_URL) : false; - const intent = Object.freeze({ - accelerator: options.accelerator, - count: options.count, - hours: options.hours, - }); - - return createEnvelope({ - success: true, - state: "manual_handoff", - data: Object.freeze({ - authenticated: null, - dryRun: options.dryRun, - opened, - intent, - provenance: Object.freeze({ - source: "ecc-cli", - command: "ecc ito rent", - }), - authority: Object.freeze({ - environment: "sandbox", - readOnly: true, - liveRfq: false, - procurementMutation: false, - quoteAcceptance: false, - fundsApproval: false, - orderCreation: false, - outreach: false, - }), - handoff: Object.freeze({ - transport: "manual_copy", - destination: "ito-desk", - acceptedByIto: false, - signInMayBeRequired: true, - message: buildHandoffMessage(intent), - limitation: "Itô /desk currently exposes no supported structured ECC intake or deep-link contract.", - }), - approvalGate: 'Stop before "Pay & buy".', - priceQuote: null, - orderId: null, - }), - error: null, + return Object.freeze({ + help: false, + invocationArgs: Object.freeze([ + ...(jsonIndexes.length === 1 ? ["--json"] : []), + command, + ...withoutJson, + ]), }); } -function renderText(payload) { - const { data } = payload; - const browserState = data.dryRun - ? "Dry-run: browser not opened." - : data.opened - ? "Opened the Itô desk; sign-in may be required." - : "Browser not opened; sign-in may be required at the Itô desk."; - return [ - "ECC × Itô sandbox compute handoff", - "", - browserState, - `Desk: ${payload.links.desk}`, - "Transport: manual copy; Itô has not accepted this intent.", - `Message: ${data.handoff.message}`, - `Limitation: ${data.handoff.limitation}`, - `Approval gate: ${data.approvalGate}`, - "", - "ECC does not file an RFQ, request or accept a quote, use credentials, approve funds, create an order, or contact a counterparty.", - ].join("\n"); +function resolveItoExecutable(environment = process.env) { + const configured = environment[EXECUTABLE_OVERRIDE]?.trim(); + if (!configured) { + throw new Error([ + "The canonical ito-compute-cli is unpublished and ECC will not resolve", + `a credential-bearing "ito" executable from PATH. Build it from`, + `${CANONICAL_REPOSITORY.replace(/\.git$/, "")}/${CANONICAL_PACKAGE_PATH},`, + "run npm ci and npm run check, then set", + `${EXECUTABLE_OVERRIDE} to the explicit absolute dist/bin/ito.js path.`, + ].join(" ")); + } + + if (!path.isAbsolute(configured)) { + throw new Error( + `${EXECUTABLE_OVERRIDE} must be an absolute path explicitly configured by the operator.` + ); + } + return assertUsableExecutable(configured); } -function main(argv = process.argv.slice(2), dependencies = {}) { - let parsed; +function assertUsableExecutable(candidate) { + let canonicalCandidate; try { - parsed = parseArgs(argv); + canonicalCandidate = fs.realpathSync.native(candidate); + } catch { + throw new Error( + `${EXECUTABLE_OVERRIDE} does not point to a readable local Itô CLI file.` + ); + } + if (!isUsableExecutable(canonicalCandidate)) { + throw new Error( + `${EXECUTABLE_OVERRIDE} does not point to a readable local Itô CLI file.` + ); + } + return canonicalCandidate; +} + +function isUsableExecutable(candidate) { + try { + const info = fs.statSync(candidate); + if (!info.isFile()) return false; + if (process.platform !== "win32" && path.extname(candidate) !== ".js") { + fs.accessSync(candidate, fs.constants.X_OK); + } else { + fs.accessSync(candidate, fs.constants.R_OK); + } + return true; + } catch { + return false; + } +} + +function buildInvocation(executable, args) { + if (path.extname(executable).toLowerCase() === ".js") { + return Object.freeze({ + executable: process.execPath, + args: Object.freeze([executable, ...args]), + }); + } + if ( + process.platform === "win32" + && /\.(?:bat|cmd|ps1)$/i.test(executable) + ) { + throw new Error( + `Refusing to invoke the Itô CLI through a shell shim. Set ${EXECUTABLE_OVERRIDE} to the absolute dist/bin/ito.js path.` + ); + } + return Object.freeze({ executable, args: Object.freeze([...args]) }); +} + +function invokeIto(executable, args, environment = process.env) { + const invocation = buildInvocation(executable, args); + const result = spawnSync(invocation.executable, invocation.args, { + cwd: process.cwd(), + encoding: "utf8", + env: { + ...createSafeItoEnvironment(environment, { includeItoRuntime: true }), + }, + maxBuffer: MAX_OUTPUT_BYTES, + shell: false, + windowsHide: true, + }); + + if (result.stdout) process.stdout.write(result.stdout); + if (result.stderr) process.stderr.write(result.stderr); + if (result.error) { + throw new Error(`The local Itô CLI could not be started: ${result.error.message}`); + } + if (typeof result.status === "number") return result.status; + if (result.signal) { + throw new Error(`The local Itô CLI terminated by signal ${result.signal}.`); + } + return 1; +} + +function main(argv = process.argv.slice(2), environment = process.env) { + try { + const parsed = parseArgs(argv, environment); + if (parsed.help) { + showHelp(); + return 0; + } + const executable = resolveItoExecutable(environment); + return invokeIto(executable, parsed.invocationArgs, environment); } catch (error) { - const payload = createInvalidEnvelope(error.message); - if (argv.includes("--json")) console.log(JSON.stringify(payload, null, 2)); - else console.error(`Error: ${error.message}`); + console.error(`Error: ${error.message}`); return 1; } - - if (parsed.help) { - showHelp(); - return 0; - } - - const payload = createHandoff(parsed.options, dependencies.openUrl); - if (parsed.options.json) console.log(JSON.stringify(payload, null, 2)); - else console.log(renderText(payload)); - return 0; } if (require.main === module) { @@ -309,13 +213,13 @@ if (require.main === module) { } module.exports = Object.freeze({ - DESK_URL, - SCHEMA_VERSION, - buildHandoffMessage, - createHandoff, - createInvalidEnvelope, + CANONICAL_PACKAGE_PATH, + CANONICAL_REPOSITORY, + EXECUTABLE_OVERRIDE, + SUPPORTED_COMMANDS, + buildInvocation, + invokeIto, main, - parseAccelerator, parseArgs, - renderText, + resolveItoExecutable, }); diff --git a/scripts/lib/compute-sponsor.js b/scripts/lib/compute-sponsor.js index a5235485c..98b1951ba 100644 --- a/scripts/lib/compute-sponsor.js +++ b/scripts/lib/compute-sponsor.js @@ -6,8 +6,11 @@ function getComputeSponsorCopy() { return "Run or self-host any open-source model. Itô is ECC's preferred compute sponsor: " + 'open its dashboard to sign in and rent or manage GPUs at ' + ITO_COMPUTE_URL - + '. Any GPU provider works. ECC only provides this link; it does not provision ' - + 'compute or serving. Managed inference through Itô is not live yet.'; + + '. Any GPU provider works. This sponsorship link is passive: it does not invoke ' + + 'an RFQ, reserve capacity, provision compute, or configure serving. Separately, ' + + 'the opt-in "ecc ito find" bridge invokes the explicitly configured canonical ' + + 'Itô CLI and submits a live authenticated RFQ; it does not reserve capacity. ' + + 'Managed inference through Itô is not live yet.'; } module.exports = Object.freeze({ diff --git a/scripts/lib/ito-environment.js b/scripts/lib/ito-environment.js index 3be2f5be2..d0afa8653 100644 --- a/scripts/lib/ito-environment.js +++ b/scripts/lib/ito-environment.js @@ -23,6 +23,18 @@ const SYSTEM_ENVIRONMENT_KEYS = Object.freeze([ "XDG_RUNTIME_DIR", ]); +const ITO_RUNTIME_ENVIRONMENT_KEYS = Object.freeze([ + "ITO_API_KEY", + "ITO_API_URL", + "ITO_INVENTORY_URL", +]); + +const ECC_ITO_CONTROL_KEYS = Object.freeze([ + "ECC_DRY_RUN", + "ECC_ITO_CLI_EXECUTABLE", + "NODE_ENV", +]); + function copyDefined(source, target, key) { if (typeof source[key] === "string") { target[key] = source[key]; @@ -38,11 +50,15 @@ function createSafeItoEnvironment(source = process.env, options = {}) { if (key.startsWith("LC_")) copyDefined(source, safe, key); } + if (options.includeItoRuntime) { + for (const key of ITO_RUNTIME_ENVIRONMENT_KEYS) { + copyDefined(source, safe, key); + } + } + if (options.includeControls) { - copyDefined(source, safe, "ECC_DRY_RUN"); - copyDefined(source, safe, "NODE_ENV"); - if (source.NODE_ENV === "test") { - copyDefined(source, safe, "ECC_ITO_BROWSER_EXECUTABLE"); + for (const key of ECC_ITO_CONTROL_KEYS) { + copyDefined(source, safe, key); } } @@ -50,6 +66,8 @@ function createSafeItoEnvironment(source = process.env, options = {}) { } module.exports = Object.freeze({ + ECC_ITO_CONTROL_KEYS, + ITO_RUNTIME_ENVIRONMENT_KEYS, SYSTEM_ENVIRONMENT_KEYS, createSafeItoEnvironment, }); diff --git a/skills/ito-compute/SKILL.md b/skills/ito-compute/SKILL.md new file mode 100644 index 000000000..11181ad14 --- /dev/null +++ b/skills/ito-compute/SKILL.md @@ -0,0 +1,103 @@ +--- +name: ito-compute +description: Query live GPU inventory, submit an authenticated Itô fixed-rate RFQ, and inspect RFQ or procurement status through the separately installed canonical CLI or its exact MCP tools. Use when a user asks to find H100/H200 capacity, request a fixed compute rate, or check Itô compute status. +metadata: + origin: ECC +--- + +# Itô Compute + +Use the canonical Itô compute CLI or MCP server. ECC does not implement a +parallel client, browser handoff, local simulation, reservation, workload +runner, or inference server. + +## Install the canonical local package + +`ito-compute-cli` is currently unpublished. Build it from its canonical +repository instead of using `npx`, `npm exec`, or an unverified package: + +```sh +git clone https://github.com/Ito-Markets/ito-cloud-runtime.git +cd ito-cloud-runtime/cli/ito-compute-cli +npm ci +npm run check +``` + +Set `ECC_ITO_CLI_EXECUTABLE` to the explicit absolute built entry: + +```text +/absolute/path/to/ito-cloud-runtime/cli/ito-compute-cli/dist/bin/ito.js +``` + +ECC never discovers this credential-bearing client through `PATH`. +Inject `ITO_API_KEY` through 1Password or the launching process environment. +Never put it in arguments, tracked files, MCP results, logs, or chat. + +## CLI workflow + +1. Run `ecc ito auth` before the first operation. +2. Before `ecc ito find`, obtain explicit buyer authority to submit an RFQ. + - Require `gpu`, `count`, whole `days`, `max-rate`, `nodes`, + `gpus-per-node`, `storage-tb`, `start-window`, `form-factor`, + `contract-type`, `fabric`, `region`, and the split-fill decision. + - Require `count == nodes * gpus-per-node`; never derive topology. + - Use `any` only when the buyer explicitly accepts any fabric or region. + - Omitted `--allow-split` means false. +3. Run the live RFQ command: + + ```sh + ecc ito find \ + --gpu h200 \ + --count 8 \ + --nodes 1 \ + --gpus-per-node 8 \ + --days 30 \ + --storage-tb 1 \ + --start-window 2099-08-15 \ + --max-rate 3.00 \ + --form-factor bare_metal \ + --contract-type reservation \ + --fabric infiniband \ + --region us-east-1 + ``` + +4. Run `ecc ito status` to inspect RFQs and procurement orders. + After an ambiguous transport failure, check status before repeating `find`. + +Inventory prices are indicative. An RFQ is not reserved capacity. Treat a rate +as fixed only when the canonical result contains a non-null firm quote. + +## MCP workflow + +Build the canonical package, then configure the stdio server with an absolute +path: + +```json +{ + "mcpServers": { + "ito-compute": { + "command": "node", + "args": [ + "/absolute/path/to/ito-cloud-runtime/cli/ito-compute-cli/dist/bin/ito-mcp.js" + ] + } + } +} +``` + +The server exposes only: + +- `ito_auth` +- `ito_find` +- `ito_status` + +Use `ito_auth`, gather explicit buyer authority and every hard constraint, call +`ito_find`, then poll with `ito_status` when needed. + +## Unsupported operations + +The supported client surface cannot lock quotes, reserve capacity, execute +workloads, qualify nodes through ECC, or serve inference. Do not invent +additional tools or a purchase path. Do not substitute a browser or fixture +when the local CLI is missing or a live operation fails. Report the missing +capability and stop. diff --git a/src/llm/cli/selector.py b/src/llm/cli/selector.py index 7a5777461..3430bb2d5 100644 --- a/src/llm/cli/selector.py +++ b/src/llm/cli/selector.py @@ -41,8 +41,11 @@ def print_self_host_compute_notice(provider: str) -> None: "https://compute.itomarkets.com" ) print( - "Any GPU provider works. ECC only links to the Itô dashboard; it does not " - "provision compute or serving. Managed inference through Itô is not live yet." + "Any GPU provider works. This sponsorship link is passive: it does not invoke " + "an RFQ, reserve capacity, provision compute, or configure serving. Separately, " + 'the opt-in "ecc ito find" bridge invokes the explicitly configured canonical ' + "Itô CLI and submits a live authenticated RFQ; it does not reserve capacity. " + "Managed inference through Itô is not live yet." ) diff --git a/tests/ci/ito-compute-skill.test.js b/tests/ci/ito-compute-skill.test.js new file mode 100644 index 000000000..4d4d283a9 --- /dev/null +++ b/tests/ci/ito-compute-skill.test.js @@ -0,0 +1,115 @@ +/** + * Contract tests for the installable Itô compute skill and MCP documentation. + */ + +const assert = require("assert"); +const fs = require("fs"); +const path = require("path"); + +const REPO_ROOT = path.join(__dirname, "..", ".."); + +function read(relativePath) { + return fs.readFileSync(path.join(REPO_ROOT, relativePath), "utf8"); +} + +function readJson(relativePath) { + return JSON.parse(read(relativePath)); +} + +function runTest(name, fn) { + try { + fn(); + console.log(` ✓ ${name}`); + return true; + } catch (error) { + console.log(` ✗ ${name}`); + console.error(` ${error.message}`); + return false; + } +} + +function main() { + console.log("\n=== Testing Itô compute skill surface ===\n"); + + const tests = [ + ["documents only the real CLI commands and MCP tools", () => { + const skill = read("skills/ito-compute/SKILL.md"); + for (const command of ["ecc ito auth", "ecc ito find", "ecc ito status"]) { + assert.match(skill, new RegExp(command.replace(" ", "\\s+"))); + } + assert.doesNotMatch(skill, /^\s*ito (?:auth|find|status)\b/m); + for (const tool of ["ito_auth", "ito_find", "ito_status"]) { + assert.match(skill, new RegExp(`\\b${tool}\\b`)); + } + assert.doesNotMatch( + skill, + /ito_lock|ito_run|ITO_CLI_DEMO|paper mode|simulated|live on the registry|publishing soon/i + ); + assert.match(skill, /unpublished/i); + assert.match(skill, /Ito-Markets\/ito-cloud-runtime/); + assert.match(skill, /cli\/ito-compute-cli/); + assert.match(skill, /npm run check/); + assert.match(skill, /ECC_ITO_CLI_EXECUTABLE/); + assert.match(skill, /explicit absolute built entry/); + assert.match(skill, /never discovers[^\n]*through `PATH`/); + assert.doesNotMatch(skill, /npm link/); + }], + ["registers one opt-in install module and capability", () => { + 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"]); + assert.deepStrictEqual(module.dependencies, ["platform-configs"]); + assert.strictEqual(module.defaultInstall, false); + assert.strictEqual(module.stability, "beta"); + for (const target of ["claude", "codex", "opencode", "hermes", "kimi"]) { + assert.ok(module.targets.includes(target), `${target} target is missing`); + } + + 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, and status 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")); + }], + ["publishes the skill but never bundles the Itô CLI", () => { + const packageJson = readJson("package.json"); + assert.ok(packageJson.files.includes("skills/ito-compute/")); + assert.ok(!packageJson.dependencies?.["ito-compute-cli"]); + assert.ok(!packageJson.optionalDependencies?.["ito-compute-cli"]); + assert.ok(!packageJson.bin?.ito); + }], + ["offers an opt-in local MCP template with the exact real tool boundary", () => { + const mcpConfig = readJson("mcp-configs/mcp-servers.json"); + const server = mcpConfig.mcpServers["ito-compute"]; + assert.ok(server, "ito-compute MCP template is missing"); + assert.strictEqual(server.command, "node"); + assert.deepStrictEqual(server.args, [ + "/absolute/path/to/ito-cloud-runtime/cli/ito-compute-cli/dist/bin/ito-mcp.js", + ]); + assert.doesNotMatch(JSON.stringify(server), /npx|ito_lock|ito_run|paper|simulat/i); + assert.match(server.description, /ito_auth, ito_find, and ito_status/); + assert.match(server.description, /unpublished/i); + }], + ]; + + let passed = 0; + let failed = 0; + for (const [name, fn] of tests) { + if (runTest(name, fn)) passed += 1; + else failed += 1; + } + + console.log(`\nPassed: ${passed}`); + console.log(`Failed: ${failed}`); + process.exit(failed > 0 ? 1 : 0); +} + +main(); diff --git a/tests/scripts/install-apply.test.js b/tests/scripts/install-apply.test.js index dc7b6408c..9cc17d903 100644 --- a/tests/scripts/install-apply.test.js +++ b/tests/scripts/install-apply.test.js @@ -39,6 +39,7 @@ function run(args = [], options = {}) { env, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], + maxBuffer: 4 * 1024 * 1024, timeout: options.timeout || DEFAULT_INSTALL_APPLY_TIMEOUT_MS, }); diff --git a/tests/scripts/ito-cli-bridge.test.js b/tests/scripts/ito-cli-bridge.test.js new file mode 100644 index 000000000..d170def00 --- /dev/null +++ b/tests/scripts/ito-cli-bridge.test.js @@ -0,0 +1,293 @@ +/** + * End-to-end contract tests for ECC's real local Itô CLI bridge. + * + * The executable used here is a process-boundary probe. It never contacts an + * Itô API, submits an RFQ, opens a browser, or reaches a GPU node. + */ + +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 ECC_SCRIPT = path.join(REPO_ROOT, "scripts", "ecc.js"); +const CANONICAL_PACKAGE = "Ito-Markets/ito-cloud-runtime/cli/ito-compute-cli"; + +function runCli(args, environment = {}) { + return spawnSync(process.execPath, [ECC_SCRIPT, ...args], { + cwd: REPO_ROOT, + encoding: "utf8", + env: { + ...process.env, + NODE_ENV: "test", + ...environment, + }, + }); +} + +function makeItoProbe(exitCode = 0) { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), "ecc-ito-cli-")); + const log = path.join(directory, "invocation.json"); + const script = path.join(directory, "ito-probe.js"); + const executable = script; + fs.writeFileSync( + script, + [ + `#!${process.execPath}`, + '"use strict";', + 'const fs = require("fs");', + `fs.writeFileSync(${JSON.stringify(log)}, JSON.stringify({ argv: process.argv.slice(2), env: process.env }));`, + 'process.stdout.write(`ito-probe:${process.argv.slice(2).join("|")}\\n`);', + 'process.stderr.write("ito-probe-stderr\\n");', + `process.exit(${exitCode});`, + "", + ].join("\n") + ); + if (process.platform !== "win32") { + fs.chmodSync(script, 0o755); + } + return Object.freeze({ directory, executable, log }); +} + +function readInvocation(probe) { + return JSON.parse(fs.readFileSync(probe.log, "utf8")); +} + +function runTest(name, fn) { + try { + fn(); + console.log(` ✓ ${name}`); + return true; + } catch (error) { + console.log(` ✗ ${name}`); + console.error(` ${error.message}`); + return false; + } +} + +function main() { + console.log("\n=== Testing ECC × Itô real CLI bridge ===\n"); + + const tests = [ + ["forwards only auth, find, and status to an explicit local executable", () => { + for (const command of ["auth", "find", "status"]) { + const probe = makeItoProbe(); + try { + const result = runCli(["ito", command], { + ECC_ITO_CLI_EXECUTABLE: probe.executable, + }); + assert.strictEqual(result.status, 0, result.stderr); + assert.deepStrictEqual(readInvocation(probe).argv, [command]); + assert.match(result.stdout, new RegExp(`ito-probe:${command}`)); + } finally { + fs.rmSync(probe.directory, { recursive: true, force: true }); + } + } + }], + ["normalizes JSON and forwards every RFQ constraint without interpretation", () => { + const probe = makeItoProbe(); + try { + const args = [ + "ito", + "find", + "--gpu", "h200", + "--count", "8", + "--nodes", "1", + "--gpus-per-node", "8", + "--days", "30", + "--storage-tb", "1", + "--start-window", "2099-08-15", + "--max-rate", "3.00", + "--form-factor", "bare_metal", + "--contract-type", "reservation", + "--fabric", "infiniband", + "--region", "us-east-1", + "--json", + ]; + const result = runCli(args, { + ECC_ITO_CLI_EXECUTABLE: probe.executable, + }); + assert.strictEqual(result.status, 0, result.stderr); + assert.deepStrictEqual(readInvocation(probe).argv, [ + "--json", + ...args.slice(1, -1), + ]); + } finally { + fs.rmSync(probe.directory, { recursive: true, force: true }); + } + }], + ["passes only the required Itô runtime settings across the process boundary", () => { + const probe = makeItoProbe(); + try { + const result = runCli(["ito", "auth"], { + ECC_ITO_CLI_EXECUTABLE: probe.executable, + ITO_API_KEY: "ito_test_key", + ITO_API_URL: "https://compute.example.test", + ITO_INVENTORY_URL: "https://edge.example.test", + AWS_SECRET_ACCESS_KEY: "must-not-cross", + OPENAI_API_KEY: "must-not-cross", + TEST_PASSWORD: "must-not-cross", + }); + assert.strictEqual(result.status, 0, result.stderr); + const childEnvironment = readInvocation(probe).env; + assert.strictEqual(childEnvironment.ITO_API_KEY, "ito_test_key"); + assert.strictEqual(childEnvironment.ITO_API_URL, "https://compute.example.test"); + assert.strictEqual(childEnvironment.ITO_INVENTORY_URL, "https://edge.example.test"); + assert.strictEqual(childEnvironment.AWS_SECRET_ACCESS_KEY, undefined); + assert.strictEqual(childEnvironment.OPENAI_API_KEY, undefined); + assert.strictEqual(childEnvironment.TEST_PASSWORD, undefined); + assert.strictEqual(childEnvironment.ECC_ITO_CLI_EXECUTABLE, undefined); + } finally { + fs.rmSync(probe.directory, { recursive: true, force: true }); + } + }], + ["rejects unsupported, browser, simulated, and node operations before spawning", () => { + for (const command of ["rent", "lock", "run", "inference", "evals", "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 auth, find, and status/i); + assert.ok(!fs.existsSync(probe.log), `${command} must not spawn the Itô CLI`); + } finally { + fs.rmSync(probe.directory, { recursive: true, force: true }); + } + } + }], + ["fails closed rather than simulating a dry-run RFQ", () => { + const probe = makeItoProbe(); + try { + const result = runCli(["--dry-run", "ito", "find"], { + ECC_ITO_CLI_EXECUTABLE: probe.executable, + }); + assert.notStrictEqual(result.status, 0); + assert.match(result.stderr, /no paper or dry-run success mode/i); + assert.ok(!fs.existsSync(probe.log)); + } finally { + fs.rmSync(probe.directory, { recursive: true, force: true }); + } + }], + ["fails closed with exact local install guidance when the explicit CLI is absent", () => { + const emptyPath = fs.mkdtempSync(path.join(os.tmpdir(), "ecc-empty-path-")); + try { + const result = runCli(["ito", "status"], { + ECC_ITO_CLI_EXECUTABLE: "", + PATH: emptyPath, + }); + assert.notStrictEqual(result.status, 0); + assert.match(result.stderr, /canonical ito-compute-cli is unpublished/i); + assert.match(result.stderr, new RegExp(CANONICAL_PACKAGE.replaceAll("/", "\\/"))); + assert.match(result.stderr, /npm run check/); + assert.match(result.stderr, /ECC_ITO_CLI_EXECUTABLE/); + assert.match(result.stderr, /explicit absolute/i); + assert.match(result.stderr, /unpublished/i); + assert.doesNotMatch(result.stderr, /npx|npm exec|npm link|install -g/i); + } finally { + fs.rmSync(emptyPath, { recursive: true, force: true }); + } + }], + ["never forwards Itô credentials to an unverified PATH collision", () => { + const collisionDirectory = fs.mkdtempSync( + path.join(os.tmpdir(), "ecc-hostile-ito-path-") + ); + const stolenEnvironment = path.join(collisionDirectory, "stolen.json"); + const executable = path.join( + collisionDirectory, + process.platform === "win32" ? "ito.exe" : "ito" + ); + try { + fs.writeFileSync( + executable, + [ + `#!${process.execPath}`, + '"use strict";', + 'const fs = require("fs");', + `fs.writeFileSync(${JSON.stringify(stolenEnvironment)}, JSON.stringify(process.env));`, + "", + ].join("\n") + ); + if (process.platform !== "win32") { + fs.chmodSync(executable, 0o755); + } + + const result = runCli(["ito", "auth"], { + ECC_ITO_CLI_EXECUTABLE: "", + ITO_API_KEY: "must-never-reach-path-collision", + PATH: collisionDirectory, + }); + + assert.notStrictEqual(result.status, 0); + assert.match(result.stderr, /explicit absolute|ECC_ITO_CLI_EXECUTABLE/i); + assert.ok( + !fs.existsSync(stolenEnvironment), + "an unverified PATH executable must never receive the Itô credential" + ); + } finally { + fs.rmSync(collisionDirectory, { recursive: true, force: true }); + } + }], + ["rejects a relative executable override instead of searching or guessing", () => { + const result = runCli(["ito", "status"], { + ECC_ITO_CLI_EXECUTABLE: "ito", + }); + assert.notStrictEqual(result.status, 0); + assert.match(result.stderr, /must be an absolute path/i); + }], + ["preserves the real CLI exit code and output without a success wrapper", () => { + const probe = makeItoProbe(7); + try { + const result = runCli(["ito", "status"], { + ECC_ITO_CLI_EXECUTABLE: probe.executable, + }); + assert.strictEqual(result.status, 7); + assert.match(result.stdout, /ito-probe:status/); + assert.match(result.stderr, /ito-probe-stderr/); + assert.doesNotMatch(result.stdout, /manual_handoff|simulated|paper/i); + } finally { + fs.rmSync(probe.directory, { recursive: true, force: true }); + } + }], + ["help exposes the truthful CLI and MCP surface without a browser path", () => { + const probe = makeItoProbe(); + try { + const result = runCli(["ito", "--help"], { + ECC_ITO_CLI_EXECUTABLE: probe.executable, + }); + assert.strictEqual(result.status, 0, result.stderr); + assert.match(result.stdout, /ecc ito auth/); + assert.match(result.stdout, /ecc ito find/); + assert.match(result.stdout, /ecc ito status/); + assert.match(result.stdout, /ito_auth/); + assert.match(result.stdout, /ito_find/); + assert.match(result.stdout, /ito_status/); + assert.match(result.stdout, new RegExp(CANONICAL_PACKAGE.replaceAll("/", "\\/"))); + assert.match(result.stdout, /unpublished/i); + assert.match(result.stdout, /never discovers[^\n]*through PATH/i); + assert.doesNotMatch( + result.stdout, + /manual copy|open(?:s)? (?:a )?browser|ito_lock|ito_run|npm link|paper|simulat/i + ); + assert.ok(!fs.existsSync(probe.log)); + } finally { + fs.rmSync(probe.directory, { recursive: true, force: true }); + } + }], + ]; + + let passed = 0; + let failed = 0; + for (const [name, fn] of tests) { + if (runTest(name, fn)) passed += 1; + else failed += 1; + } + + console.log(`\nPassed: ${passed}`); + console.log(`Failed: ${failed}`); + process.exit(failed > 0 ? 1 : 0); +} + +main(); diff --git a/tests/scripts/ito-compute-sponsor.test.js b/tests/scripts/ito-compute-sponsor.test.js index ea0ca72ff..fe878afa5 100644 --- a/tests/scripts/ito-compute-sponsor.test.js +++ b/tests/scripts/ito-compute-sponsor.test.js @@ -61,7 +61,13 @@ function assertHonestComputeCopy(content) { assert.match(content, /preferred compute sponsor/i); assert.match(content, /run or self-host any open-source model/i); assert.match(content, /any GPU provider/i); + assert.match(content, /sponsorship link is passive/i); + assert.match(content, /ecc ito find/i); + assert.match(content, /explicitly configured canonical Itô CLI/i); + assert.match(content, /submits a live authenticated RFQ/i); + assert.match(content, /does not reserve capacity/i); assert.match(content, /managed inference[^\n.]*not live/i); + assert.doesNotMatch(content, /ECC only (?:links|provides this link)/i); } function main() { @@ -90,7 +96,7 @@ function main() { assertHonestComputeCopy(readme); assert.match( readme, - /custom API endpoint or model gateway[\s\S]*Run or self-host any open-source model behind that gateway[\s\S]*ECC only links to the Itô dashboard/ + /custom API endpoint or model gateway[\s\S]*Run or self-host any open-source model behind that gateway[\s\S]*sponsorship link is passive/ ); const sponsorMark = read('assets/images/sponsors/ito.svg'); assert.match(sponsorMark, / { + ['sponsor roster keeps Itô and Moonshot distinct from node tooling', () => { const sponsors = read('SPONSORS.md'); assert.ok(sponsors.includes('[**Itô**]')); assert.ok(sponsors.includes('assets/images/sponsors/ito.svg')); + assert.ok(sponsors.includes('[**Moonshot AI**]')); + assert.ok(sponsors.includes('assets/images/sponsors/moonshot.svg')); + assert.doesNotMatch(sponsors, /sixtytwo|sixty.?two/i); assertExactComputeRoute(sponsors); }], ['inference guide distinguishes rental compute from managed serving', () => { @@ -113,11 +122,17 @@ function main() { assertHonestComputeCopy(read('.claude-plugin/README.md')); assertHonestComputeCopy(read('.kimi/README.md')); }], - ['Phase 2 plan keeps its thesis and release framing generic', () => { - const plan = read('docs/design/ecc-ito-compute-integration.md'); - assert.match(plan, /-> any open-source model/); - assert.doesNotMatch(plan, /public Kimi|Moonshot|video and sponsorship/i); - assert.match(plan, /Status: \*\*Proposed/); + ['integration record keeps the thesis and real client boundary honest', () => { + const record = read('docs/design/ecc-ito-compute-integration.md'); + assert.match(record, /-> any open-source model/); + assert.doesNotMatch(record, /public Kimi|Moonshot|video and sponsorship/i); + assert.match(record, /Status: \*\*Implemented local CLI bridge/i); + assert.match(record, /auth`, `find`, and `status/); + assert.match(record, /ito_auth`, `ito_find`, and `ito_status/); + assert.match(record, /unpublished/i); + assert.match(record, /managed inference remains unavailable/i); + assert.match(record, /version bump[\s\S]*intentionally deferred/i); + assert.doesNotMatch(record, /manual_copy|ito\.compute\.handoff|ecc ito rent/i); }], ['top-level CLI help exposes the provider-neutral compute route', () => { const result = spawnSync('node', ['scripts/ecc.js', '--help'], { @@ -159,7 +174,12 @@ function main() { assert.ok(packageJson.files.includes('assets/images/sponsors/')); assertExactComputeRoute(packageJson.scripts.welcome); assert.match(packageJson.scripts.welcome, /run or self-host any open-source model/i); + assert.match(packageJson.scripts.welcome, /sponsorship link is passive/i); + assert.match(packageJson.scripts.welcome, /ecc ito find/i); + assert.match(packageJson.scripts.welcome, /submits a live authenticated RFQ/i); + assert.match(packageJson.scripts.welcome, /does not reserve capacity/i); assert.ok(fs.existsSync(path.join(REPO_ROOT, 'assets', 'images', 'sponsors', 'ito.svg'))); + assert.ok(fs.existsSync(path.join(REPO_ROOT, 'assets', 'images', 'sponsors', 'moonshot.svg'))); }], ]; diff --git a/tests/scripts/ito-handoff.test.js b/tests/scripts/ito-handoff.test.js deleted file mode 100644 index 349ee5386..000000000 --- a/tests/scripts/ito-handoff.test.js +++ /dev/null @@ -1,294 +0,0 @@ -/** - * End-to-end contract tests for ECC's read-only Itô compute handoff. - */ - -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 ECC_SCRIPT = path.join(REPO_ROOT, "scripts", "ecc.js") -const ITO_SCRIPT = path.join(REPO_ROOT, "scripts", "ito.js") -const DESK_URL = "https://compute.itomarkets.com/desk" - -function runCli(args, options = {}) { - return spawnSync(process.execPath, [ECC_SCRIPT, ...args], { - cwd: REPO_ROOT, - encoding: "utf8", - env: { - ...process.env, - NODE_ENV: "test", - ...(options.env || {}), - }, - }) -} - -function parseJson(result, expectedStatus = 0) { - assert.strictEqual(result.status, expectedStatus, result.stderr) - return JSON.parse(result.stdout) -} - -function runTest(name, fn) { - try { - fn() - console.log(` ✓ ${name}`) - return true - } catch (error) { - console.log(` ✗ ${name}`) - console.error(` ${error.message}`) - return false - } -} - -function makeBrowserProbe() { - const dir = fs.mkdtempSync(path.join(os.tmpdir(), "ecc-ito-browser-")) - const log = path.join(dir, "opened-url.txt") - const envLog = path.join(dir, "browser-env.txt") - const executable = path.join(dir, process.platform === "win32" ? "browser-probe.cmd" : "browser-probe") - if (process.platform === "win32") { - fs.writeFileSync(executable, `@echo off\r\n"${log}"\r\nset >"${envLog}"\r\n`) - } else { - fs.writeFileSync(executable, `#!/bin/sh\nprintf '%s' "$1" > "${log}"\nenv > "${envLog}"\n`) - fs.chmodSync(executable, 0o755) - } - return { dir, envLog, executable, log } -} - -function main() { - console.log("\n=== Testing ECC × Itô sandbox handoff ===\n") - - const tests = [ - ["maps the exact CLI request into a structured sandbox intent", () => { - const payload = parseJson(runCli([ - "ito", - "rent", - "--accelerator", "h100", - "--count", "1", - "--hours", "24", - "--no-open", - "--json", - ])) - - assert.strictEqual(payload.schemaVersion, "ito.compute.handoff.v1") - assert.strictEqual(payload.success, true) - assert.strictEqual(payload.state, "manual_handoff") - assert.deepStrictEqual(payload.data.intent, { - accelerator: "h100", - count: 1, - hours: 24, - }) - assert.deepStrictEqual(payload.data.provenance, { - source: "ecc-cli", - command: "ecc ito rent", - }) - assert.strictEqual(payload.data.authority.environment, "sandbox") - assert.strictEqual(payload.data.authority.readOnly, true) - assert.strictEqual(payload.data.authority.liveRfq, false) - assert.strictEqual(payload.data.authority.procurementMutation, false) - assert.strictEqual(payload.data.authority.quoteAcceptance, false) - assert.strictEqual(payload.data.authority.fundsApproval, false) - assert.strictEqual(payload.data.authority.orderCreation, false) - assert.strictEqual(payload.data.handoff.transport, "manual_copy") - assert.strictEqual(payload.data.handoff.destination, "ito-desk") - assert.strictEqual(payload.data.handoff.acceptedByIto, false) - assert.strictEqual(payload.data.handoff.signInMayBeRequired, true) - assert.match(payload.data.handoff.message, /1 × H100/) - assert.match(payload.data.handoff.message, /24 hours/) - assert.match(payload.data.handoff.message, /ECC CLI/) - assert.match(payload.data.handoff.message, /sandbox-only/) - assert.strictEqual(payload.links.desk, DESK_URL) - assert.strictEqual(payload.data.opened, false) - assert.strictEqual(payload.data.priceQuote, null) - assert.strictEqual(payload.data.orderId, null) - }], - ["global dry-run emits the same handoff and never opens a browser", () => { - const probe = makeBrowserProbe() - try { - const payload = parseJson(runCli([ - "--dry-run", - "ito", - "rent", - "--accelerator", "h100", - "--count", "1", - "--hours", "24", - "--json", - ], { - env: { ECC_ITO_BROWSER_EXECUTABLE: probe.executable }, - })) - - assert.strictEqual(payload.data.dryRun, true) - assert.strictEqual(payload.data.opened, false) - assert.ok(!fs.existsSync(probe.log), "dry-run must not invoke the browser executable") - } finally { - fs.rmSync(probe.dir, { recursive: true, force: true }) - } - }], - ["local dry-run is equivalent and rejects an open request", () => { - const probe = makeBrowserProbe() - try { - const payload = parseJson(runCli([ - "ito", - "rent", - "--accelerator", "h100", - "--count", "1", - "--hours", "24", - "--dry-run", - "--json", - ], { - env: { ECC_ITO_BROWSER_EXECUTABLE: probe.executable }, - })) - - assert.strictEqual(payload.data.dryRun, true) - assert.strictEqual(payload.data.opened, false) - assert.ok(!fs.existsSync(probe.log), "local dry-run must not invoke the browser executable") - } finally { - fs.rmSync(probe.dir, { recursive: true, force: true }) - } - }], - ["browser handoff opens only the allowlisted desk URL without inherited secrets", () => { - const probe = makeBrowserProbe() - try { - const payload = parseJson(runCli([ - "ito", - "rent", - "--accelerator", "h100", - "--count", "1", - "--hours", "24", - "--json", - ], { - env: { - ECC_ITO_BROWSER_EXECUTABLE: probe.executable, - ITO_API_KEY: "parent-api-key-must-not-cross", - ITO_SERVICE_TOKEN: "parent-token-must-not-cross", - TEST_PASSWORD: "parent-password-must-not-cross", - }, - })) - - assert.strictEqual(payload.data.opened, true) - assert.strictEqual(fs.readFileSync(probe.log, "utf8"), DESK_URL) - assert.strictEqual(payload.links.desk, DESK_URL) - const childEnvironment = fs.readFileSync(probe.envLog, "utf8") - assert.doesNotMatch(childEnvironment, /parent-api-key-must-not-cross/) - assert.doesNotMatch(childEnvironment, /parent-token-must-not-cross/) - assert.doesNotMatch(childEnvironment, /parent-password-must-not-cross/) - } finally { - fs.rmSync(probe.dir, { recursive: true, force: true }) - } - }], - ["direct Ito execution strips secrets from the browser child", () => { - const probe = makeBrowserProbe() - try { - const result = spawnSync(process.execPath, [ - ITO_SCRIPT, - "rent", - "--accelerator", "h100", - "--count", "1", - "--hours", "24", - "--json", - ], { - cwd: REPO_ROOT, - encoding: "utf8", - env: { - ...process.env, - NODE_ENV: "test", - ECC_ITO_BROWSER_EXECUTABLE: probe.executable, - ITO_API_KEY: "direct-api-key-must-not-cross", - ITO_SERVICE_TOKEN: "direct-token-must-not-cross", - TEST_PASSWORD: "direct-password-must-not-cross", - }, - }) - const payload = parseJson(result) - assert.strictEqual(payload.data.opened, true) - const childEnvironment = fs.readFileSync(probe.envLog, "utf8") - assert.doesNotMatch(childEnvironment, /direct-api-key-must-not-cross/) - assert.doesNotMatch(childEnvironment, /direct-token-must-not-cross/) - assert.doesNotMatch(childEnvironment, /direct-password-must-not-cross/) - } finally { - fs.rmSync(probe.dir, { recursive: true, force: true }) - } - }], - ["fails closed on missing, unsupported, duplicate, and out-of-range intent", () => { - for (const args of [ - ["ito", "rent", "--count", "1", "--hours", "24", "--no-open", "--json"], - ["ito", "rent", "--accelerator", "a100", "--count", "1", "--hours", "24", "--no-open", "--json"], - ["ito", "rent", "--accelerator", "h100", "--count", "0", "--hours", "24", "--no-open", "--json"], - ["ito", "rent", "--accelerator", "h100", "--count", "1", "--hours", "721", "--no-open", "--json"], - ["ito", "rent", "--accelerator", "h100", "--accelerator", "h100-sxm", "--count", "1", "--hours", "24", "--no-open", "--json"], - ["ito", "dashboard", "--accelerator", "h100", "--count", "1", "--hours", "24", "--no-open", "--json"], - ]) { - const result = runCli(args) - assert.notStrictEqual(result.status, 0, args.join(" ")) - const payload = JSON.parse(result.stdout) - assert.strictEqual(payload.success, false) - assert.strictEqual(payload.state, "invalid_request") - assert.strictEqual(payload.data, null) - assert.strictEqual(payload.error.code, "INVALID_ARGUMENT") - } - }], - ["does not inspect credentials or contain a mutating transport", () => { - const payload = parseJson(runCli([ - "ito", - "rent", - "--accelerator", "h100", - "--count", "1", - "--hours", "24", - "--no-open", - "--json", - ], { - env: { - ITO_API_KEY: "must-not-be-read", - ITO_SERVICE_TOKEN: "must-not-be-read", - }, - })) - - assert.strictEqual(payload.data.authenticated, null) - const source = fs.readFileSync(path.join(REPO_ROOT, "scripts", "ito.js"), "utf8") - assert.doesNotMatch(source, /ITO_API_KEY|ITO_SERVICE_TOKEN|procurement\/orders|authorization/i) - assert.doesNotMatch(source, /\bfetch\s*\(|https?\.request|child_process\.exec\b/) - }], - ["human output stops before the economic boundary", () => { - const result = runCli([ - "ito", - "rent", - "--accelerator", "h100", - "--count", "1", - "--hours", "24", - "--no-open", - ]) - - assert.strictEqual(result.status, 0, result.stderr) - assert.match(result.stdout, /manual copy/i) - assert.match(result.stdout, /sandbox-only/i) - assert.match(result.stdout, /sign-in may be required/i) - assert.doesNotMatch(result.stdout, /signed-in/i) - assert.match(result.stdout, /does not file an RFQ/i) - assert.match(result.stdout, /Stop before ["“]Pay & buy["”]/i) - assert.match(result.stdout, /no supported structured ECC intake/i) - }], - ["npm welcome remains POSIX-safe around apostrophes", () => { - const npmExecutable = process.platform === "win32" ? "npm.cmd" : "npm" - const result = spawnSync(npmExecutable, ["run", "welcome"], { - cwd: REPO_ROOT, - encoding: "utf8", - shell: process.platform === "win32", - }) - assert.strictEqual(result.status, 0, result.stderr) - assert.match(result.stdout, /Itô/) - }], - ] - - let passed = 0 - let failed = 0 - for (const [name, fn] of tests) { - if (runTest(name, fn)) passed += 1 - else failed += 1 - } - - console.log(`\nPassed: ${passed}`) - console.log(`Failed: ${failed}`) - process.exit(failed > 0 ? 1 : 0) -} - -main() diff --git a/tests/test_selector.py b/tests/test_selector.py index 5f1441041..af8023bcc 100644 --- a/tests/test_selector.py +++ b/tests/test_selector.py @@ -50,6 +50,11 @@ def test_ollama_notice_routes_to_ito_without_claiming_serving(capsys): assert_exact_compute_route(output) assert "preferred compute sponsor" in output assert "Any GPU provider works" in output + assert "sponsorship link is passive" in output + assert "ecc ito find" in output + assert "explicitly configured canonical Itô CLI" in output + assert "submits a live authenticated RFQ" in output + assert "does not reserve capacity" in output assert "Managed inference through Itô is not live yet" in output