diff --git a/.github/workflows/openshell-scaffold.yml b/.github/workflows/openshell-scaffold.yml new file mode 100644 index 00000000..0cb869ff --- /dev/null +++ b/.github/workflows/openshell-scaffold.yml @@ -0,0 +1,39 @@ +name: OpenShell offline scaffold + +on: + pull_request: + paths: + - 'agent-os/openshell/**' + - '.github/workflows/openshell-scaffold.yml' + push: + branches: [main] + paths: + - 'agent-os/openshell/**' + - '.github/workflows/openshell-scaffold.yml' + +permissions: + contents: read + +jobs: + offline-contract: + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + node: ['20', '22'] + steps: + - uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-node@v7.0.0 + with: + node-version: ${{ matrix.node }} + - name: Validate offline contracts; do not install OpenShell + run: | + node --check agent-os/openshell/scaffold.mjs + node --check agent-os/openshell/preview.mjs + node --test agent-os/openshell/scaffold.test.mjs + node agent-os/openshell/preview.mjs agent-os/openshell/fixture.json + node agent-os/openshell/preview.mjs --readiness diff --git a/agent-os/README.md b/agent-os/README.md index 90fd5c72..a8de132a 100644 --- a/agent-os/README.md +++ b/agent-os/README.md @@ -143,3 +143,13 @@ Use this to track recurring spend, success rate, receipt linkage, and budget pre - Agent OS quickstart: https://agoragentic.com/guides/agent-os-quickstart/ - API reference: https://agoragentic.com/docs.html - Discovery check: https://agoragentic.com/api/discovery/check + +## OpenShell preparation scaffold + +The [OpenShell scaffold](openshell/README.md) compiles offline sandbox-policy proposals for Cartographer, Emissary, and Steward against a pinned upstream source contract. It includes local tests and a [Codex integration handoff](openshell/CODEX_HANDOFF.md), but does not install OpenShell, connect a gateway, provision a sandbox, run an agent, or grant execution/payment authority. No live compatibility or production protection is claimed. + +```sh +node --test agent-os/openshell/scaffold.test.mjs +node agent-os/openshell/preview.mjs agent-os/openshell/fixture.json +node agent-os/openshell/preview.mjs --readiness +``` diff --git a/agent-os/openshell/CODEX_HANDOFF.md b/agent-os/openshell/CODEX_HANDOFF.md new file mode 100644 index 00000000..1c0e694e --- /dev/null +++ b/agent-os/openshell/CODEX_HANDOFF.md @@ -0,0 +1,59 @@ +# OpenShell augmentation and integration handoff + +## Goal and present state + +Integrate OpenShell below Agoragentic's existing control plane, preserving the agent-operated Interchange and all owner, financial, identity, trust, and evidence boundaries. The compiler, offline CLI, observation shaper, tests, and source pin are implemented. A live provider adapter is **not** implemented or qualified. No production migration is authorized by this document. + +## Work sequence + +### 1. Source and build qualification + +Re-read repository instructions and the pinned upstream contracts. Resolve the v0.1.2 release to the recorded commit; verify the SDK package and gateway build rather than trusting a version string. Install only in an owner-approved disposable lab. Verify the generated SDK types accept `createSpec` and verify server-side normalization has not weakened its policy. Upgrade the pin only in a reviewed change that updates the lock and conformance fixtures together. + +Build or select a digest-pinned minimal worker image with the `sandbox` user/group, explicitly required paths and binaries, no credentials, no host-runtime socket, and no production configuration. The example digest is fictional. Validate hard-required Landlock on the actual host. Failure must stop setup, not silently downgrade to best effort. + +Acceptance: recorded package/image/gateway integrity, generated type/schema checks, policy acceptance, and a reproducible host/image definition. These are not yet containment proof. + +### 2. Trusted host adapter + +Implement the adapter in the existing platform provider abstraction, not in an agent-importable SDK shim. Keep source activation default-off. Use explicit workspace-scoped OpenShell operations. `OpenShellClient.connect()` is lazy: a successful construction is not a health check. Require a real `health()` result and verified gateway identity/auth configuration. + +Bind authorization outside the sandbox to the exact owner, tenant/workspace, run, immutable sandbox ID, compiled plan/policy digest, image digest, command, quota, and expiry. Recheck the financial freeze and relevant mandate/consent controls. A caller-supplied `approved: true`, environment flag, self-hash, fake SDK client, or policy advisor proposal is not authorization. Do not accept agent-supplied method names, deployment contracts, policy overrides, `rawSpec`, provider lists, or arbitrary environment variables. + +Use the pinned SDK's `sandbox.create`, `waitReady`, `getConfig`, `exec`/`execStream`, `delete`, and identity-aware `waitDeleted` interfaces only after rechecking their actual types. Confirm effective static and network policy before any worker executes. Keep request data and authority/control-plane clients in separate trust domains. + +Acceptance: local tests show every provider side effect is preceded by the real host gate; denial results in zero provider calls. Runtime status is never inferred from generated plans or mocks. + +### 3. Bounded lab canary and failure cleanup + +Obtain separate owner approval for lab resources; no payment/custody enablement follows. Start with the fixed `/bin/true` canary, then a reviewed read-only worker. Enforce CPU, memory, storage, wall-clock, and output limits in the actual host/runtime, not just metadata. + +Test an approved read succeeds and unapproved hosts/methods/paths, private IPs, cloud metadata, redirects/rebinding, unauthorized files, credential reads, provider attachment, policy widening, and cross-workspace access fail. Use synthetic secrets only. Capture policy identity, sandbox identity, image/build identity, terminal status, and independently observed denials without raw sensitive logs. A network error alone is not proof that a policy denial occurred. + +Test create failure, timeout, cancellation, stream closure without terminal exit, gateway failure, policy drift, owner revocation, interrupted cleanup, and same-name sandbox replacement. Ambiguous create/exec responses are not safe to retry automatically. Track sandbox IDs durably so an orphan can be reconciled. `accepted` deletion stays pending; `unspecified`/unknown stays unknown. Abort of a client RPC is not proof the worker stopped. Verify termination/cleanup separately. + +Acceptance: authenticated, identity-bound live evidence for success, denials, and cleanup. Keep production and live-traffic qualification false until independently reviewed. + +### 4. Role integration, in order + +Cartographer first: supply only sanitized public-source inputs from the existing owner-approved source configuration. Preserve discovery provenance and quotas; no new trust, listing, peer-key, payment, or wallet mutation. Candidates discovered by an agent are not automatically approved egress destinations. + +Emissary second: isolate drafting/inspection. Send contact proposals to the existing host consent/mandate path; do not move sender credentials, key-pinning authority, or transport approval into the worker. Retain first-contact limits and independent owner gates. + +Steward third: consume minimal evidence references and propose bounded maintenance. Do not let it approve its own policy changes, reconfigure the gateway, grant credentials, or merge/deploy itself. Put approval and policy application in separate host components. + +Harness Core/ECF: connect through existing proposal/evidence contracts. Mark imported artifacts as local/unverified until a trusted evidence verifier promotes them. Never treat local plan hashes as portable authority. Risk Fork can evaluate candidate changes, but it is not a substitute for OpenShell containment or the owner's mandate. + +### 5. OpenShell-specific augmentation, after baseline qualification + +Evaluate supervisor middleware for pre-action checks and gateway interceptors for control-plane admission. First inspect the pinned upstream contracts and failure behavior. Keep policy admission fail-closed; do not assume extension existence supplies authenticated Agoragentic authority. Policy advisor/prover output can explain proposed access changes but must remain a recommendation until the independent host approval path authorizes the exact delta. Formal policy reasoning is not business-consent or settlement verification. + +Credential-backed inference/provider routes require a separate approved threat model and endpoint-scoped credential binding. Do not export signer/custody material. Any paid inference or external resource cost requires its own authorized budget. No generic provider attachment or arbitrary network widening is allowed. + +Acceptance: isolated integration tests for unavailable/bypassed middleware, expired/replayed approvals, widened policy, and non-mutating denied requests. No production claims based only on source inspection. + +## Completion report required from the implementation agent + +Report changed files, source/SDK/image/gateway pins, actual test commands and outcomes, evidence provenance, enabled versus default-off paths, outstanding gates, rollback/cleanup status, and the final reviewed commit. Separate local tests, mocked contract tests, live canaries, and production verification. Do not claim subagent review unless real subagent execution was available. + +Rollback before activation is simply leaving the provider unselected. After an approved lab activation, stop new dispatch, revoke the lab grant, terminate only the recorded sandbox IDs, verify cleanup, and preserve redacted evidence. Never delete another workspace's or a same-name replacement's resource. diff --git a/agent-os/openshell/README.md b/agent-os/openshell/README.md new file mode 100644 index 00000000..ff8a14e3 --- /dev/null +++ b/agent-os/openshell/README.md @@ -0,0 +1,75 @@ +# OpenShell integration scaffold + +**Source-only, offline, default-off. No OpenShell installation, gateway connection, sandbox provisioning, agent execution, package publication, or production activation is performed.** This is a preparation surface under the existing Agent OS examples, not a new supported integration or canonical tool ID. + +Agoragentic owns owner mandates, action approvals, identity, budgets, receipts, and reconciliation. OpenShell is a candidate execution-isolation layer underneath those controls. A sandbox policy must not grant payment, federation, publication, or trust authority. + +## Run the implemented scaffold + +Node.js 20+; no package installation or credentials required. From the repository root: + +```sh +node --test agent-os/openshell/scaffold.test.mjs +node agent-os/openshell/preview.mjs agent-os/openshell/fixture.json +node agent-os/openshell/preview.mjs --readiness +``` + +The fixture uses a **fictional image digest and example hostname**. It demonstrates the format, not an image that can be pulled or a live discovery target. The readiness command describes this scaffold only; it does not inspect a host. `activationSupported` remains false. There is no `apply` command or enabling environment variable. + +## What is implemented + +- A deterministic policy/plan compiler with strict input validation, bounded input, immutable output, and SHA-256 plan/policy digests. +- Separate Cartographer, Emissary, and Steward profiles. Cartographer can propose exact-host/exact-path HTTPS GET/HEAD egress. Emissary and Steward remain network-denied. All profiles are proposal-only. +- A TypeScript SDK `createSpec` candidate with create-time filesystem, process, hard-required Landlock, explicit L7 enforcement, no provider attachment, no environment injection, and no exposed service ports. +- An adapter preparation interface whose invocation path always refuses activation. +- Offline observation shaping that requires a terminal integer exit code, binds an expected sandbox ID, distinguishes pending deletion, and never upgrades caller reports to verified receipts. +- A pinned upstream source record and a staged integration/qualification handoff. + +```js +import { OpenShellScaffoldAdapter } from './scaffold.mjs'; +const adapter = new OpenShellScaffoldAdapter(); +const plan = adapter.prepare(request); // pure local compilation +// await adapter.invoke(...); // always refuses; no live driver is bound +``` + +## Role boundaries + +| Role | Current proposal | Later host integration, not implemented here | +|---|---|---| +| Cartographer | Explicit public read candidates; no default egress | Observe approved sources, retain provenance, emit discovery candidates without promoting trust | +| Emissary | Offline drafting, no egress or credentials | Submit bounded contact proposals to the existing consent/mandate-controlled host path | +| Steward | Offline evidence review, no egress or mutation | Recommend maintenance and policy changes; owner gates remain outside the sandbox | + +This preserves agent-operated growth and maintenance without letting the agents approve their own authority expansion. These profiles do not contain or start the actual role agents. The only candidate create-time command is `/bin/true`; replacing it with a role worker is a separate reviewed change. + +## Upstream contract and important details + +Reviewed target: **OpenShell v0.1.2**, commit `6648bd0c290efbc41ba131ee9831ee45cd431f94`. See [upstream.lock.json](upstream.lock.json) for file/blob provenance and review scope. No upstream source is vendored. Runtime compatibility remains untested. + +Primary sources at that immutable revision: + +- [TypeScript SDK interface and distribution](https://github.com/NVIDIA/OpenShell/blob/6648bd0c290efbc41ba131ee9831ee45cd431f94/sdk/typescript/README.md) +- [SDK create/exec/deletion types](https://github.com/NVIDIA/OpenShell/blob/6648bd0c290efbc41ba131ee9831ee45cd431f94/sdk/typescript/src/client.ts) +- [Policy protobuf contract](https://github.com/NVIDIA/OpenShell/blob/6648bd0c290efbc41ba131ee9831ee45cd431f94/proto/sandbox.proto) + +`createSpec` uses **camelCase SDK message-init fields**, not CLI YAML. The policy protobuf specifies audit as the unspecified inspected-network enforcement mode; this compiler sets `enforcement: 1` explicitly. `tls: 0` is automatic TLS handling, not TLS bypass. The proposal has no `allowedIps`, uninspected-credential bypass, provider attachment, middleware registration, or `rawSpec` escape hatch. + +OpenShell filesystem/process/Landlock policy must be supplied at creation; sandbox-scoped policy updates cannot introduce static fields later. The proposed image must have the non-root `sandbox` account, `/bin/true`, `/usr/bin/curl`, required runtime paths, `/sandbox/input`, and `/sandbox/output`. None of those image properties are verified by a digest-shaped string. + +The SDK is documented as distributed through GitHub Packages at this release. Do not assume an identically named public npm package is the reviewed build. A later approved setup must authenticate outside the sandbox, pin SDK and gateway versions together, and record package/image integrity. Do not run an unpinned remote installation script as part of this scaffold. + +## Evidence and safety limits + +A local `planDigest` or `policyDigest` is **not** an OpenShell protobuf policy hash, signature, owner approval, attestation, settlement receipt, or proof of work. The digests bind the scaffold's canonical local JSON. Plan validation detects changes against this compiler; it does not authenticate who proposed the plan. + +Observation summaries accept only the small documented projection (`kind`, `sandboxId`, and `exitCode` or `outcome`). Do not feed raw SDK objects, stdout, stderr, prompts, environment variables, credentials, or customer data into them. Output remains `caller_reported_unverified` even when an exit code is zero. A deletion result of `accepted` is pending, not cleanup success. Missing or replaced sandbox identity must not be papered over by inserting an expected ID into a response that did not contain it. + +Hostname screening is syntactic only. It does not resolve DNS or establish SSRF protection, consent, endpoint semantics, redirect safety, or trust. GET/HEAD does not guarantee a remote application is side-effect-free. The host must validate destinations at connection time, control redirects, and test metadata/private-network denial. No real request is made here. + +The runtime deadline is a **host requirement, not an implemented timer**. All runtime-verification flags stay false. CPU/memory/storage quotas, lifecycle cancellation, tenant isolation, gateway identity/authentication, effective policy confirmation, and independent containment evidence remain qualification work. No Docker socket, host credentials, gateway admin token, signer, wallet key, or production database belongs in the worker image or mounts. + +## Integration ownership and next work + +See [CODEX_HANDOFF.md](CODEX_HANDOFF.md). Keep the reusable compiler here; host authority wiring belongs in the platform, while Harness Core should consume only a supported local evidence/proposal interface. Do not create another Harness Core implementation in this repository's migration-pointer directory. Risk Fork/ECF checks are additional gates, not reasons to skip sandbox or owner controls. + +No changes to the canonical integration index, integration counts, paid execution, custody state, trust vocabulary, public readiness claims, or existing runtime selection are made by this scaffold. diff --git a/agent-os/openshell/fixture.json b/agent-os/openshell/fixture.json new file mode 100644 index 00000000..cbbed856 --- /dev/null +++ b/agent-os/openshell/fixture.json @@ -0,0 +1,11 @@ +{ + "schema": "agoragentic.openshell.request.v1", + "role": "cartographer", + "runId": "offline-demo", + "workspace": "agoragentic-lab", + "image": "registry.example.com/agoragentic/worker@sha256:1111111111111111111111111111111111111111111111111111111111111111", + "readEndpoints": [ + { "host": "docs.example.com", "paths": ["/robots.txt", "/.well-known/agent-card.json"] } + ], + "maxRuntimeSeconds": 60 +} diff --git a/agent-os/openshell/preview.mjs b/agent-os/openshell/preview.mjs new file mode 100644 index 00000000..0cefda40 --- /dev/null +++ b/agent-os/openshell/preview.mjs @@ -0,0 +1,36 @@ +import { openSync, readSync, fstatSync, closeSync } from 'node:fs'; +import { buildOpenShellPlan, openShellReadiness } from './scaffold.mjs'; + +// Offline only. No installer, SDK import, subprocess, network, or apply command. +try { + const args = process.argv.slice(2); + if (args.length === 1 && args[0] === '--readiness') { + console.log(JSON.stringify(openShellReadiness(), null, 2)); + } else { + if (args.length !== 1 || args[0].startsWith('-')) throw new Error('usage: node preview.mjs REQUEST.json | --readiness'); + const fd = openSync(args[0], 'r'); + let text; + try { + if (!fstatSync(fd).isFile()) throw new Error('request_must_be_regular_file'); + const bytes = Buffer.alloc(65537); + let size = 0; + while (size < bytes.length) { + const count = readSync(fd, bytes, size, bytes.length - size, null); + if (!count) break; + size += count; + } + if (size > 65536) throw new Error('request_too_large'); + text = bytes.subarray(0, size).toString('utf8'); + } finally { closeSync(fd); } + let request; + try { request = JSON.parse(text); } catch { throw new Error('invalid_request_json'); } + console.log(JSON.stringify(buildOpenShellPlan(request), null, 2)); + } +} catch (error) { + // Do not echo request contents or filesystem paths from native errors. + const message = error.code && !error.code.startsWith('E') ? error.code + : ['invalid_request_json', 'request_too_large', 'request_must_be_regular_file'].includes(error.message) ? error.message + : 'preview_failed: use one request JSON path or --readiness'; + console.error(message); + process.exitCode = 2; +} diff --git a/agent-os/openshell/scaffold.mjs b/agent-os/openshell/scaffold.mjs new file mode 100644 index 00000000..c2afd0a7 --- /dev/null +++ b/agent-os/openshell/scaffold.mjs @@ -0,0 +1,224 @@ +import { createHash } from 'node:crypto'; + +// Source-contract target, NOT a claim of installed or runtime-tested compatibility. +export const UPSTREAM = Object.freeze({ + release: 'v0.1.2', + commit: '6648bd0c290efbc41ba131ee9831ee45cd431f94', + sdk: '@nvidia/openshell-sdk', +}); +export const ROLES = Object.freeze(['cartographer', 'emissary', 'steward']); +const REQUEST_SCHEMA = 'agoragentic.openshell.request.v1'; +const PLAN_SCHEMA = 'agoragentic.openshell.plan.v1'; +const FORBIDDEN_KEYS = new Set(['__proto__', 'prototype', 'constructor']); + +function fail(code) { + const error = new Error(code); + error.code = code; + throw error; +} + +// Do not invoke getters/toJSON, accept class instances, or copy dangerous keys. +function dataOnly(value, depth = 0, seen = new Set(), budget = { nodes: 0 }) { + if (++budget.nodes > 4096 || depth > 16) fail('input_too_complex'); + if (value === null || typeof value === 'boolean') return value; + if (typeof value === 'string') { + if (value.length > 4096) fail('input_string_too_long'); + return value; + } + if (typeof value === 'number' && Number.isFinite(value)) return value; + if (!value || typeof value !== 'object') fail('json_data_required'); + if (seen.has(value)) fail('cyclic_input'); + const array = Array.isArray(value); + if (!array && ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail('plain_object_required'); + if (Object.getOwnPropertySymbols(value).length) fail('symbol_keys_denied'); + seen.add(value); + const output = array ? [] : {}; + for (const key of Object.getOwnPropertyNames(value)) { + if (array && key === 'length') continue; + if (FORBIDDEN_KEYS.has(key)) fail('unsafe_key'); + const descriptor = Object.getOwnPropertyDescriptor(value, key); + if (!descriptor.enumerable || !Object.hasOwn(descriptor, 'value')) fail('accessor_or_hidden_field_denied'); + if (array && !/^(0|[1-9][0-9]*)$/.test(key)) fail('array_property_denied'); + output[key] = dataOnly(descriptor.value, depth + 1, seen, budget); + } + if (array && (value.length > 4096 || Object.keys(output).length !== value.length)) fail('sparse_array_denied'); + seen.delete(value); + return output; +} + +function fields(value, allowed, required = []) { + if (!value || Array.isArray(value) || typeof value !== 'object') fail('object_required'); + if (Object.keys(value).some((key) => !allowed.includes(key))) fail('unknown_field'); + if (required.some((key) => !Object.hasOwn(value, key))) fail('missing_field'); +} + +function slug(value, max = 63) { + if (typeof value !== 'string' || value.length > max || !/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(value)) fail('invalid_identifier'); + return value; +} + +function canonical(value) { + if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`; + if (value && typeof value === 'object') { + return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(',')}}`; + } + return JSON.stringify(value); +} + +function digest(value) { + return `sha256:${createHash('sha256').update(canonical(value)).digest('hex')}`; +} + +function freeze(value) { + if (value && typeof value === 'object') { + Object.values(value).forEach(freeze); + Object.freeze(value); + } + return value; +} + +function endpoint(value) { + fields(value, ['host', 'paths'], ['host', 'paths']); + const { host, paths } = value; + // Syntactic screening only: DNS resolution/rebinding checks belong to the host. + if (typeof host !== 'string' || host.length > 253 || host !== host.toLowerCase() + || !/^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/.test(host) + || /(?:^|\.)(?:localhost|local|internal|lan|home|onion)$/.test(host)) fail('invalid_public_hostname'); + if (!Array.isArray(paths) || paths.length < 1 || paths.length > 16) fail('invalid_paths'); + for (const path of paths) { + if (typeof path !== 'string' || path.length > 256 || !/^\/[A-Za-z0-9._~/-]*$/.test(path) + || path.includes('//') || path.split('/').some((part) => part === '.' || part === '..')) fail('invalid_exact_path'); + } + return { host, paths: [...new Set(paths)].sort() }; +} + +/** Compile an offline proposal. It is not an approval, executor, or sandbox. */ +export function buildOpenShellPlan(input) { + const request = dataOnly(input); + fields(request, ['schema', 'role', 'runId', 'workspace', 'image', 'readEndpoints', 'maxRuntimeSeconds'], + ['schema', 'role', 'runId', 'workspace', 'image']); + if (request.schema !== REQUEST_SCHEMA) fail('unsupported_request_schema'); + if (!ROLES.includes(request.role)) fail('unsupported_role'); + slug(request.runId, 24); + slug(request.workspace); + if (typeof request.image !== 'string' || request.image.length > 300 + || !/^[a-z0-9]+(?:[._-][a-z0-9]+)*(?:\/[a-z0-9]+(?:[._-][a-z0-9]+)*)+@sha256:[a-f0-9]{64}$/.test(request.image)) fail('digest_pinned_image_required'); + const seconds = Object.hasOwn(request, 'maxRuntimeSeconds') ? request.maxRuntimeSeconds : 60; + if (!Number.isInteger(seconds) || seconds < 1 || seconds > 300) fail('invalid_runtime_limit'); + const endpoints = Object.hasOwn(request, 'readEndpoints') ? request.readEndpoints : []; + if (!Array.isArray(endpoints) || endpoints.length > 16) fail('invalid_endpoints'); + if (request.role !== 'cartographer' && endpoints.length) fail('role_egress_denied'); + const normalized = endpoints.map(endpoint).sort((a, b) => a.host < b.host ? -1 : a.host > b.host ? 1 : 0); + if (new Set(normalized.map((item) => item.host)).size !== normalized.length) fail('duplicate_host'); + const normalizedRequest = { ...request, readEndpoints: normalized, maxRuntimeSeconds: seconds }; + const networkPolicies = {}; + normalized.forEach((item, index) => { + const name = `read_${index}`; + networkPolicies[name] = { + name, + binaries: [{ path: '/usr/bin/curl' }], + endpoints: [{ + host: item.host, port: 443, protocol: 'rest', + tls: 0, enforcement: 1, + rules: item.paths.flatMap((path) => ['GET', 'HEAD'].map((method) => ({ allow: { method, path } }))), + }], + }; + }); + const policy = { + version: 1, + filesystem: { + includeWorkdir: false, + readOnly: ['/usr', '/bin', '/lib', '/lib64', '/etc/ssl/certs', '/etc/hosts', '/etc/resolv.conf', '/sandbox/input'], + readWrite: ['/sandbox/output', '/tmp'], + }, + landlock: { compatibility: 'hard_requirement' }, + process: { runAsUser: 'sandbox', runAsGroup: 'sandbox' }, + networkPolicies, + }; + const plan = { + schema: PLAN_SCHEMA, + request: normalizedRequest, + upstream: { ...UPSTREAM }, + status: 'offline_proposal', + authority: { execution: false, provisioning: false, payment: false, trustMutation: false, policyApproval: false }, + // camelCase is the TypeScript SDK message-init shape, NOT CLI YAML. + createSpec: { + name: `ag-${request.role}-${request.runId}`, + workspace: request.workspace, + image: request.image, + command: ['/bin/true'], + tty: false, + gpu: false, + environment: {}, + providers: [], + serviceExposures: [], + policy, + }, + hostRequirements: { + maxRuntimeSeconds: seconds, + deadlineEnforced: false, + imageVerified: false, + gatewayVerified: false, + dnsAndRedirectContainmentVerified: false, + runtimePolicyVerified: false, + ownerApprovalRequired: true, + credentialBoundary: 'control_plane_only', + }, + policyDigest: digest(policy), + evidenceClass: 'local_plan_not_runtime_proof', + }; + return freeze({ ...plan, planDigest: digest(plan) }); +} + +/** Reject altered or extended plans, including self-rehashed policy overrides. */ +export function validateOpenShellPlan(input) { + const plan = dataOnly(input); + const rebuilt = buildOpenShellPlan(plan.request); + if (canonical(plan) !== canonical(rebuilt)) fail('plan_mismatch'); + return rebuilt; +} + +/** Offline SDK-result shaping. Caller reports NEVER become verified receipts. */ +export function summarizeOpenShellObservation(planInput, expectedSandboxId, input) { + const plan = validateOpenShellPlan(planInput); + if (typeof expectedSandboxId !== 'string' || !/^[A-Za-z0-9-]{1,80}$/.test(expectedSandboxId)) fail('invalid_sandbox_id'); + const observation = dataOnly(input); + fields(observation, ['kind', 'sandboxId', 'exitCode', 'outcome'], ['kind', 'sandboxId']); + if (observation.sandboxId !== expectedSandboxId) fail('sandbox_identity_mismatch'); + let reportedStatus; + if (observation.kind === 'exec') { + if (Object.hasOwn(observation, 'outcome')) fail('unexpected_outcome'); + if (!Number.isInteger(observation.exitCode) || observation.exitCode < 0 || observation.exitCode > 255) fail('terminal_exit_code_required'); + reportedStatus = observation.exitCode === 0 ? 'reported_success' : 'reported_failure'; + } else if (observation.kind === 'delete') { + if (Object.hasOwn(observation, 'exitCode')) fail('unexpected_exit_code'); + const states = { completed: 'reported_completed', accepted: 'reported_pending', already_absent: 'reported_absent_unverified', unspecified: 'unknown', unknown: 'unknown' }; + if (!Object.hasOwn(states, observation.outcome)) fail('unknown_deletion_outcome'); + reportedStatus = states[observation.outcome]; + } else fail('unsupported_observation'); + return freeze({ + schema: 'agoragentic.openshell.observation.v1', + runId: plan.request.runId, + planDigest: plan.planDigest, + sandboxId: expectedSandboxId, + kind: observation.kind, + reportedStatus, + evidenceClass: 'caller_reported_unverified', + verified: false, + settlementProof: false, + executionAuthority: false, + }); +} + +export function openShellReadiness() { + return freeze({ + status: 'scaffold_only', upstream: { ...UPSTREAM }, + activationSupported: false, liveTrafficProtected: false, + blockers: ['qualified_host_adapter_missing', 'image_and_gateway_not_verified', 'live_containment_not_tested', 'owner_activation_not_approved'], + }); +} + +export class OpenShellScaffoldAdapter { + prepare(request) { return buildOpenShellPlan(request); } + async invoke() { fail('openshell_activation_not_implemented'); } +} diff --git a/agent-os/openshell/scaffold.test.mjs b/agent-os/openshell/scaffold.test.mjs new file mode 100644 index 00000000..40176ca7 --- /dev/null +++ b/agent-os/openshell/scaffold.test.mjs @@ -0,0 +1,205 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync, mkdtempSync, writeFileSync, rmSync } from 'node:fs'; +import { execFileSync, spawnSync } from 'node:child_process'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { UPSTREAM, ROLES, buildOpenShellPlan, validateOpenShellPlan, summarizeOpenShellObservation, openShellReadiness, OpenShellScaffoldAdapter } from './scaffold.mjs'; + +const fixture = JSON.parse(readFileSync(new URL('./fixture.json', import.meta.url), 'utf8')); +const fresh = () => structuredClone(fixture); +const plan = () => buildOpenShellPlan(fresh()); +const rejects = (input, code) => assert.throws(() => buildOpenShellPlan(input), { code }); + +for (const role of ROLES) { + test(`${role}: offline, authority-free, immutable plan`, () => { + const input = { ...fresh(), role, readEndpoints: [] }; + const result = buildOpenShellPlan(input); + assert.equal(result.status, 'offline_proposal'); + assert.ok(Object.values(result.authority).every((value) => value === false)); + assert.deepEqual(result.createSpec.command, ['/bin/true']); + assert.deepEqual(result.createSpec.providers, []); + assert.deepEqual(result.createSpec.environment, {}); + assert.deepEqual(result.createSpec.serviceExposures, []); + assert.equal(result.hostRequirements.deadlineEnforced, false); + assert.equal(result.hostRequirements.ownerApprovalRequired, true); + assert.throws(() => { result.createSpec.policy.filesystem.readWrite.push('/'); }, TypeError); + assert.deepEqual(validateOpenShellPlan(JSON.parse(JSON.stringify(result))), result); + input.readEndpoints.push({ host: 'changed.example.com', paths: ['/'] }); + assert.equal(result.request.readEndpoints.length, 0); + }); +} + +test('cartographer emits exact GET/HEAD rules with explicit enforcement, no credential/IP escape hatch', () => { + const policy = plan().createSpec.policy; + assert.equal(policy.landlock.compatibility, 'hard_requirement'); + assert.equal(policy.filesystem.includeWorkdir, false); + assert.deepEqual(policy.filesystem.readWrite, ['/sandbox/output', '/tmp']); + assert.deepEqual(policy.process, { runAsUser: 'sandbox', runAsGroup: 'sandbox' }); + const rule = policy.networkPolicies.read_0; + assert.deepEqual(rule.binaries, [{ path: '/usr/bin/curl' }]); + const item = rule.endpoints[0]; + assert.equal(item.enforcement, 1); + assert.equal(item.protocol, 'rest'); + assert.equal(item.tls, 0); + assert.equal(item.port, 443); + assert.ok(item.rules.every(({ allow }) => ['GET', 'HEAD'].includes(allow.method))); + assert.deepEqual(Object.keys(item).sort(), ['host', 'port', 'protocol', 'tls', 'enforcement', 'rules'].sort()); +}); + +test('deterministic normalization and digest binds all policy and host-limit inputs', () => { + const a = fresh(); + a.readEndpoints.push({ host: 'a.example.com', paths: ['/z', '/a', '/a'] }); + const b = fresh(); + b.readEndpoints.unshift({ host: 'a.example.com', paths: ['/a', '/z'] }); + assert.deepEqual(buildOpenShellPlan(a), buildOpenShellPlan(b)); + const c = { ...b, maxRuntimeSeconds: 61 }; + assert.notEqual(buildOpenShellPlan(c).planDigest, buildOpenShellPlan(b).planDigest); + assert.equal(buildOpenShellPlan(c).policyDigest, buildOpenShellPlan(b).policyDigest); +}); + +test('optional omitted fields use bounded defaults, not permissive policy', () => { + const input = fresh(); + delete input.readEndpoints; + delete input.maxRuntimeSeconds; + const result = buildOpenShellPlan(input); + assert.deepEqual(result.createSpec.policy.networkPolicies, {}); + assert.equal(result.hostRequirements.maxRuntimeSeconds, 60); +}); + +for (const host of ['127.0.0.1', '169.254.169.254', '::1', '[::1]', '2130706433', '0177.0.0.1', '0x7f000001', 'localhost', 'foo.localhost', 'metadata.internal', 'foo.local', 'foo.lan', 'foo.onion', '*.example.com', '**.example.com', 'https://example.com', 'user@example.com', 'example.com:443', 'example.com.', 'EXAMPLE.com', 'exämple.com', 'example.com\n', 'a..example.com']) { + test(`reject hostname ${JSON.stringify(host)}`, () => { + rejects({ ...fresh(), readEndpoints: [{ host, paths: ['/'] }] }, 'invalid_public_hostname'); + }); +} +for (const path of ['/*', '/**', '//evil', '/x//y', '/../x', '/a/./b', '/a/../b', '/%2e%2e/x', '/x%2fy', '/x?token=secret', '/x#fragment', '/x\\y', '/x\n', 'relative', 7, null]) { + test(`reject path ${JSON.stringify(path)}`, () => { + rejects({ ...fresh(), readEndpoints: [{ host: 'docs.example.com', paths: [path] }] }, 'invalid_exact_path'); + }); +} +for (const value of [0, -1, 301, 1.5, '60', null, true]) { + test(`reject runtime limit ${JSON.stringify(value)}`, () => rejects({ ...fresh(), maxRuntimeSeconds: value }, 'invalid_runtime_limit')); +} +for (const field of ['enabled', 'approved', 'providers', 'environment', 'rawSpec', 'policy', 'command', 'gateway', 'token', 'filesystem']) { + test(`deny unrecognized authority/override field ${field}`, () => rejects({ ...fresh(), [field]: true }, 'unknown_field')); +} +for (const role of ['emissary', 'steward']) { + test(`${role} cannot inherit cartographer egress`, () => rejects({ ...fresh(), role }, 'role_egress_denied')); +} +for (const image of ['ubuntu:latest', 'ubuntu', 'repo/name@sha256:abc', 'repo/name@sha256:' + 'A'.repeat(64), 'repo/name@sha256:' + '1'.repeat(64) + '\n']) { + test(`reject unpinned/noncanonical image ${JSON.stringify(image)}`, () => rejects({ ...fresh(), image }, 'digest_pinned_image_required')); +} + +test('reject duplicate hosts, excessive endpoints, null endpoints and empty path lists', () => { + rejects({ ...fresh(), readEndpoints: [fixture.readEndpoints[0], fixture.readEndpoints[0]] }, 'duplicate_host'); + rejects({ ...fresh(), readEndpoints: Array(17).fill(fixture.readEndpoints[0]) }, 'invalid_endpoints'); + rejects({ ...fresh(), readEndpoints: null }, 'invalid_endpoints'); + rejects({ ...fresh(), readEndpoints: [{ host: 'example.com', paths: [] }] }, 'invalid_paths'); + rejects({ ...fresh(), readEndpoints: [{ host: 'example.com', paths: ['/'], allowedIps: ['10.0.0.1'] }] }, 'unknown_field'); +}); + +test('reject invalid schema, missing fields, role and identifiers', () => { + rejects({ ...fresh(), schema: 'unknown' }, 'unsupported_request_schema'); + const input = fresh(); delete input.image; + rejects(input, 'missing_field'); + rejects({ ...fresh(), role: 'admin' }, 'unsupported_role'); + for (const runId of ['../x', '', 'a'.repeat(25), 'a\n', '--option']) rejects({ ...fresh(), runId }, 'invalid_identifier'); +}); + +test('reject non-data objects, accessors without invoking them, cyclic and hostile keys', () => { + rejects(new Date(), 'plain_object_required'); + let touched = false; + const input = fresh(); + Object.defineProperty(input, 'token', { enumerable: true, get() { touched = true; return 'secret'; } }); + rejects(input, 'accessor_or_hidden_field_denied'); + assert.equal(touched, false); + const cyclic = fresh(); cyclic.extra = cyclic; + rejects(cyclic, 'cyclic_input'); + rejects(JSON.parse('{"__proto__":{"polluted":true}}'), 'unsafe_key'); + rejects({ ...fresh(), extra: () => true }, 'json_data_required'); + rejects({ ...fresh(), extra: Infinity }, 'json_data_required'); + rejects({ ...fresh(), readEndpoints: Array(2) }, 'sparse_array_denied'); + assert.equal({}.polluted, undefined); +}); + +test('reject edited, extended, and authority-bearing serialized plans', () => { + for (const change of [ + (p) => { p.authority.execution = true; }, + (p) => { p.createSpec.policy.networkPolicies.read_0.endpoints[0].enforcement = 0; }, + (p) => { p.createSpec.rawSpec = { policy: {} }; }, + (p) => { p.extra = true; }, + (p) => { p.planDigest = 'sha256:' + '0'.repeat(64); }, + ]) { + const changed = structuredClone(plan()); change(changed); + assert.throws(() => validateOpenShellPlan(changed), { code: 'plan_mismatch' }); + } +}); + +for (const [exitCode, expected] of [[0, 'reported_success'], [1, 'reported_failure'], [137, 'reported_failure']]) { + test(`execution report ${exitCode} remains unverified`, () => { + const report = summarizeOpenShellObservation(plan(), 'sandbox-123', { kind: 'exec', sandboxId: 'sandbox-123', exitCode }); + assert.equal(report.reportedStatus, expected); + assert.equal(report.verified, false); + assert.equal(report.settlementProof, false); + assert.equal(report.executionAuthority, false); + assert.equal(report.evidenceClass, 'caller_reported_unverified'); + }); +} +for (const exitCode of [undefined, null, '', '0', -1, 256, 0.5, false]) { + test(`missing/invalid terminal exit ${String(exitCode)} is not success`, () => { + const report = { kind: 'exec', sandboxId: 'sandbox-123' }; + if (exitCode !== undefined) report.exitCode = exitCode; + assert.throws(() => summarizeOpenShellObservation(plan(), 'sandbox-123', report), { code: 'terminal_exit_code_required' }); + }); +} +for (const [outcome, status] of [['completed', 'reported_completed'], ['accepted', 'reported_pending'], ['already_absent', 'reported_absent_unverified'], ['unspecified', 'unknown'], ['unknown', 'unknown']]) { + test(`deletion ${outcome} preserves pending/uncertainty`, () => { + const result = summarizeOpenShellObservation(plan(), 'original-id', { kind: 'delete', sandboxId: 'original-id', outcome }); + assert.equal(result.reportedStatus, status); + assert.equal(result.verified, false); + }); +} + +test('observations cannot change identity, carry raw logs, or mix terminal types', () => { + assert.throws(() => summarizeOpenShellObservation(plan(), 'original', { kind: 'exec', sandboxId: 'replacement', exitCode: 0 }), { code: 'sandbox_identity_mismatch' }); + assert.throws(() => summarizeOpenShellObservation(plan(), 'original', { kind: 'exec', sandboxId: 'original', exitCode: 0, stdout: 'secret' }), { code: 'unknown_field' }); + assert.throws(() => summarizeOpenShellObservation(plan(), 'original', { kind: 'delete', sandboxId: 'original', outcome: 'future' }), { code: 'unknown_deletion_outcome' }); + assert.throws(() => summarizeOpenShellObservation(plan(), 'original', { kind: 'delete', sandboxId: 'original', outcome: 'completed', exitCode: 0 }), { code: 'unexpected_exit_code' }); +}); + +test('adapter is hard-off regardless of flags, booleans or supplied client', async () => { + let calls = 0; + const adapter = new OpenShellScaffoldAdapter({ enabled: true, client: { create() { calls++; } } }); + assert.equal(adapter.prepare(fresh()).status, 'offline_proposal'); + await assert.rejects(adapter.invoke('create_sandbox', { enabled: true, approved: true, plan: plan() }), { code: 'openshell_activation_not_implemented' }); + assert.equal(calls, 0); + assert.equal(openShellReadiness().activationSupported, false); + assert.equal(openShellReadiness().liveTrafficProtected, false); +}); + +test('upstream pin matches checked-in lock; runtime proof is not claimed', () => { + const lock = JSON.parse(readFileSync(new URL('./upstream.lock.json', import.meta.url), 'utf8')); + for (const key of ['release', 'commit', 'sdk']) assert.equal(UPSTREAM[key], lock[key]); + assert.equal(lock.runtimeTested, false); +}); + +test('CLI is offline, reads fixture, denies apply, limits input, and does not echo malformed secrets', () => { + const cli = fileURLToPath(new URL('./preview.mjs', import.meta.url)); + const file = fileURLToPath(new URL('./fixture.json', import.meta.url)); + const result = JSON.parse(execFileSync(process.execPath, [cli, file], { encoding: 'utf8' })); + assert.deepEqual(result, plan()); + const ready = JSON.parse(execFileSync(process.execPath, [cli, '--readiness'], { encoding: 'utf8' })); + assert.equal(ready.activationSupported, false); + assert.equal(spawnSync(process.execPath, [cli, '--apply'], { encoding: 'utf8' }).status, 2); + const dir = mkdtempSync(join(tmpdir(), 'openshell-scaffold-')); + try { + const bad = join(dir, 'bad.json'); + writeFileSync(bad, '{"secret":"do-not-echo-this"'); + const failed = spawnSync(process.execPath, [cli, bad], { encoding: 'utf8' }); + assert.equal(failed.status, 2); + assert.equal(failed.stderr.includes('do-not-echo-this'), false); + writeFileSync(bad, 'x'.repeat(65537)); + assert.match(spawnSync(process.execPath, [cli, bad], { encoding: 'utf8' }).stderr, /request_too_large/); + } finally { rmSync(dir, { recursive: true, force: true }); } +}); diff --git a/agent-os/openshell/upstream.lock.json b/agent-os/openshell/upstream.lock.json new file mode 100644 index 00000000..b0c383cb --- /dev/null +++ b/agent-os/openshell/upstream.lock.json @@ -0,0 +1,16 @@ +{ + "schema": "agoragentic.openshell.upstream-lock.v1", + "reviewedAt": "2026-09-28", + "repository": "https://github.com/NVIDIA/OpenShell", + "release": "v0.1.2", + "commit": "6648bd0c290efbc41ba131ee9831ee45cd431f94", + "sdk": "@nvidia/openshell-sdk", + "sdkDistribution": "GitHub Packages at the reviewed release; no package installation performed", + "license": "Apache-2.0", + "runtimeTested": false, + "sourceFiles": [ + { "path": "sdk/typescript/README.md", "gitBlob": "99c4fac9d4d9dfe0904f6fb2510e22154e5b206f" }, + { "path": "sdk/typescript/src/client.ts", "gitBlob": "93ad8ba6d163f409e522a7e9bde53f13eafa9338", "reviewedLines": "1-280" }, + { "path": "proto/sandbox.proto", "gitBlob": "1d837b7f0e1d4e0e9d82763252f214804027c5b6", "reviewedLines": "1-260,300-430" } + ] +} diff --git a/claude-agent-sdk/package-lock.json b/claude-agent-sdk/package-lock.json index 7f11903f..8d7dd1e2 100644 --- a/claude-agent-sdk/package-lock.json +++ b/claude-agent-sdk/package-lock.json @@ -690,9 +690,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "dev": true, "funding": [ { @@ -893,9 +893,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", - "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", "dev": true, "license": "MIT", "engines": { diff --git a/integrations.json b/integrations.json index f3cb13fc..c876a69f 100644 --- a/integrations.json +++ b/integrations.json @@ -443,7 +443,7 @@ "reason": "The exact upstream source lock, closed representation contract, no-spend target policy, and deterministic fixture replay pass, but no live MatrAIx provider run or independent external compatibility review has occurred.", "owner": "repository-maintainers", "tracking_ref": "https://github.com/rhein1/agoragentic-integrations/pull/320", - "review_by": "2026-09-30", + "review_by": "2026-10-08", "published": false, "external_compatibility_verified": false, "ready_for_manifest": false, diff --git a/mcp/package-lock.json b/mcp/package-lock.json index 1cef2831..bb5eaaba 100644 --- a/mcp/package-lock.json +++ b/mcp/package-lock.json @@ -1041,9 +1041,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "dev": true, "funding": [ { @@ -1243,9 +1243,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz", - "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", "dev": true, "license": "MIT", "engines": { diff --git a/mcp/package.json b/mcp/package.json index c36ee9e5..6d6048af 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -64,7 +64,7 @@ }, "overrides": { "@hono/node-server": "2.0.11", - "fast-uri": "3.1.7", + "fast-uri": "3.1.8", "qs": "6.16.0" }, "engines": { diff --git a/n8n/TOOLCHAIN_AUDIT.md b/n8n/TOOLCHAIN_AUDIT.md index 7c57b54d..6bb90c36 100644 --- a/n8n/TOOLCHAIN_AUDIT.md +++ b/n8n/TOOLCHAIN_AUDIT.md @@ -2,7 +2,7 @@ Original toolchain audit: 2026-09-13 (supersedes 2026-09-01) -Dependency-fixture refresh: 2026-09-25. Rechecked the manifest and lockfile, +Dependency-fixture refresh: 2026-09-30. Rechecked the manifest and lockfile, peer ranges, clean install, tests, lint, build, dependency audits, and package contents. Historical CLI compatibility and Creator Portal acceptance were not revalidated. @@ -17,8 +17,8 @@ revalidated. - Linter: `eslint@9.29.0` - Formatter: `prettier@3.9.6` - Compiler: `typescript@5.9.2` -- Development host fixture: `n8n-workflow@2.39.1` -- Exact workflow peer fixture: `zod@3.25.76` +- Development host fixture: `n8n-workflow@2.28.4` +- Exact workflow peer fixture: `zod@3.25.67` - Minimum consumer Node.js: 20.19 - Locked development and publishing Node.js: 24 - Install mode: committed lockfile plus `npm ci` @@ -30,6 +30,17 @@ publishing workflow. ## Coordinated compatibility decision +The 2.39.1 fixture and available 2.41.1 stable / 2.42.1 beta releases pin +`axios@1.18.0` exactly. Seven newly reported Axios advisories require 1.20.0; +substituting a lock entry cannot satisfy that pin, and overrides remain prohibited. +Use the latest pre-Axios 2.28 patch fixture, 2.28.4, with its exact Zod peer +3.25.67. Its complete development graph audits clean. Clean installation, tests, +lint, build, runtime imports and the offline error boundary pass. All eleven +generated code, declaration and static-asset files are byte-identical to the +2.39.1 build. This changes the development fixture only: package version, +runtime peer, Node consumer floor, node source and audit policy are unchanged. +Requalify a newer fixture once its published dependency graph is patched. + Every available `@n8n/node-cli` line checked from 0.40.0 through the current stable and beta releases pulls an AI/template-only dependency branch through `@n8n/ai-node-sdk`. That branch pins `@langchain/classic@1.0.27` and @@ -84,17 +95,16 @@ The rejected dependency majors remain incoherent with this source candidate: The release-contract test pins the complete supported set so a future isolated major bump cannot look valid after changing only one manifest line. -`n8n-workflow@2.39.1` is the pinned development fixture, not a runtime -constraint. Its exact peer is satisfied by `zod@3.25.76`. The published peer +`n8n-workflow@2.28.4` is the pinned development fixture, not a runtime +constraint. Its exact peer is satisfied by `zod@3.25.67`. The published peer remains `n8n-workflow: "*"`, so host n8n versions are not narrowed. These fixtures keep clean-install build tests deterministic and are not shipped in the package tarball. -The pinned workflow fixture's development graph includes `isolated-vm@7`, -whose declared engine is Node.js 24 or newer. The published community node does -not ship that fixture, so the consumer floor remains Node.js 20.19; repository -validation and trusted publishing use Node.js 24 to satisfy the complete locked -development graph without ignoring engine metadata. +The pinned workflow fixture's development graph includes `isolated-vm@6.2.0`. +The published community node does not ship that fixture, so the consumer floor +remains Node.js 20.19; repository validation and trusted publishing continue to +use Node.js 24 without ignoring engine metadata. ## Security boundary @@ -110,9 +120,8 @@ found 0 vulnerabilities The CLI/AI/LangChain branch and `uuid@10.0.0` are absent from the lockfile. The only resolved `uuid` is patched `uuid@11.1.1` beneath the pinned workflow -fixture. The 2.39.1 development graph resolves `@n8n/utils@1.47.0` and patched -`nanoid@3.3.18`, and adds `quickjs-emscripten@0.32.0` with its `@jitl` QuickJS -WASM packages. These lock entries are development-only. `npm run audit:dev` +fixture. The vulnerable Axios dependency branch is absent from this fixture. +These lock entries are development-only. `npm run audit:dev` rejects any advisory at any severity; there is no development allowlist. The prior `brace-expansion`, `ip-address`, and `undici` findings remain resolved diff --git a/n8n/package-lock.json b/n8n/package-lock.json index 28771282..14af7cb9 100644 --- a/n8n/package-lock.json +++ b/n8n/package-lock.json @@ -15,11 +15,11 @@ "eslint-import-resolver-typescript": "4.4.5", "eslint-plugin-import-x": "4.17.1", "eslint-plugin-n8n-nodes-base": "2.0.0", - "n8n-workflow": "2.39.1", + "n8n-workflow": "2.28.4", "prettier": "3.9.6", "typescript": "5.9.2", "typescript-eslint": "8.70.0", - "zod": "3.25.76" + "zod": "3.25.67" }, "engines": { "node": ">=20.19.0" @@ -28,103 +28,6 @@ "n8n-workflow": "*" } }, - "node_modules/@apm-js-collab/code-transformer": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.18.1.tgz", - "integrity": "sha512-u1Hb6bHjWtkSpiprwVP6YaHC1DTN4RAU3zYkUDUe7WMnJwdyU1pwTL9dFKiSJB9IiLue/EQovmyx6xhU7FFtAQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/estree": "^1.0.8", - "astring": "^1.9.0", - "esquery": "^1.7.0", - "meriyah": "^6.1.4", - "semifies": "^1.0.0", - "source-map": "^0.6.0" - }, - "bin": { - "code-transformer": "cli.js" - } - }, - "node_modules/@apm-js-collab/code-transformer-bundler-plugins": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer-bundler-plugins/-/code-transformer-bundler-plugins-0.7.4.tgz", - "integrity": "sha512-nAfOeZPSUAQvJa1iFT/5oCrTm5YQhMMrfCNthNnaXHZiOQhu1KGuLoIx7HtbAi3wfwaBYLaICPIeenIaEwcXIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@apm-js-collab/code-transformer": "^0.18.1", - "es-module-lexer": "^2.1.0", - "magic-string": "^0.30.21", - "module-details-from-path": "^1.0.4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@apm-js-collab/tracing-hooks": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.13.0.tgz", - "integrity": "sha512-mTvWz9rnQwx1U3h0XPTHaX7bgfkpipLLTQyjlC2cdhQpQEuoLT0AGzoydeoq2NxfEVv6fWOOETcSbb2nptleyw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@apm-js-collab/code-transformer": "^0.18.0", - "debug": "^4.4.1", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@codemirror/autocomplete": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz", - "integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@codemirror/language": { - "version": "6.12.4", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.4.tgz", - "integrity": "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.23.0", - "@lezer/common": "^1.5.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - }, - "node_modules/@codemirror/state": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.1.tgz", - "integrity": "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@marijn/find-cluster-break": "^1.0.0" - } - }, - "node_modules/@codemirror/view": { - "version": "6.43.9", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.9.tgz", - "integrity": "sha512-sTuUzTpPMFebRhg6dawChoKKgndIwfjmJgKVxBefPElcU2NwQ6AFroupk0SFqEerQyZOGRfDNnSN8Dw/lMAsXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@codemirror/state": "^6.7.0", - "crelt": "^1.0.6", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" - } - }, "node_modules/@emnapi/core": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", @@ -227,9 +130,9 @@ "license": "MIT" }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -305,9 +208,9 @@ "license": "MIT" }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -441,105 +344,10 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@jitl/quickjs-ffi-types": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@jitl/quickjs-ffi-types/-/quickjs-ffi-types-0.32.0.tgz", - "integrity": "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jitl/quickjs-wasmfile-debug-asyncify": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@jitl/quickjs-wasmfile-debug-asyncify/-/quickjs-wasmfile-debug-asyncify-0.32.0.tgz", - "integrity": "sha512-EX8zbXwGqCgAE764M+qvkHtyXDi/FUoMBea0JnES7vCM3P7a2+EOZOjGv85wtZ2sJhI1oJ+nekmqpOODFDY+hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jitl/quickjs-ffi-types": "0.32.0" - } - }, - "node_modules/@jitl/quickjs-wasmfile-debug-sync": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@jitl/quickjs-wasmfile-debug-sync/-/quickjs-wasmfile-debug-sync-0.32.0.tgz", - "integrity": "sha512-LeYWrPGC1uNCTBWvibo3ZLJj0CSVNYUXvJpXMCmuQ5Sap2cCACc3uvGvYV4homHHBAzfw5akoTqMMS4YFRtw+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jitl/quickjs-ffi-types": "0.32.0" - } - }, - "node_modules/@jitl/quickjs-wasmfile-release-asyncify": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@jitl/quickjs-wasmfile-release-asyncify/-/quickjs-wasmfile-release-asyncify-0.32.0.tgz", - "integrity": "sha512-3oSwPfja12ICz4aIblB58cuY8JlEq5Txt8Cut4VLo+LH47QN+mzCnSgnbB03hWzg1LBcc+VyyI9UOag7a1NF+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jitl/quickjs-ffi-types": "0.32.0" - } - }, - "node_modules/@jitl/quickjs-wasmfile-release-sync": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@jitl/quickjs-wasmfile-release-sync/-/quickjs-wasmfile-release-sync-0.32.0.tgz", - "integrity": "sha512-BKNDI/TPBfGlLNGYpLrhcDGXmIk4xHm4MRAisOBnOzpXVn9HZWsfmMAc9WMBrAHjvvds6HOikKeaOBKdPdpVrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jitl/quickjs-ffi-types": "0.32.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@lezer/common": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", - "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@lezer/highlight": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", - "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.3.0" - } - }, - "node_modules/@lezer/lr": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", - "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@marijn/find-cluster-break": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.3.tgz", - "integrity": "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@n8n/constants": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@n8n/constants/-/constants-0.38.0.tgz", - "integrity": "sha512-xcAHwgKl17ZQS1RFAwjit95dr/AL53WlLqmAcUIFi+A9cTr494aD00RBO/egACTycOY1ZWTst2D1B7jlHpESkg==", - "dev": true, - "license": "SEE LICENSE IN LICENSE.md" - }, "node_modules/@n8n/errors": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@n8n/errors/-/errors-0.13.0.tgz", - "integrity": "sha512-hUZ+ePKp2BzJL40OK1+wTVefJkKLy+mjiBT6KsejkXi1HboYoqHruFqeQZivm5PHhX3CZsHa9ZJy2LGHZrpKCQ==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@n8n/errors/-/errors-0.10.0.tgz", + "integrity": "sha512-Qle4gCsx7qtDRMdie3YkjuXPuo369PP32cR9Svb+INwcjLMxNzmnbJ66jFnOmLn0ntwBccUw1IecJ6axwyTgBQ==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { @@ -563,48 +371,40 @@ } }, "node_modules/@n8n/expression-runtime": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/@n8n/expression-runtime/-/expression-runtime-0.30.0.tgz", - "integrity": "sha512-mC8yM8+eh0uuN7ZVwvERf/CVUN9hOiaAKG+QtKzfXrFaYSt5AwNXktTsmAdWsLEfH1HjBBXaMc3eMF4IaifPoA==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@n8n/expression-runtime/-/expression-runtime-0.19.1.tgz", + "integrity": "sha512-tv86UUD+uBqORBOH70BQaRRXYfatPlygvhk80a4dO2SEubXzIFQxshMb1oA7loEj9ZMeYQesUotvgN57JbPQgA==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@n8n/errors": "0.13.0", - "@n8n/tournament": "1.11.0", - "isolated-vm": "^7.0.0", + "@n8n/errors": "0.10.0", + "@n8n/tournament": "1.4.0", + "isolated-vm": "^6.1.2", "jmespath": "0.16.0", "js-base64": "3.7.8", "jssha": "3.3.1", "lodash": "4.18.1", "luxon": "3.7.2", "md5": "2.3.0", - "quickjs-emscripten": "^0.32.0", "title-case": "3.0.3", "transliteration": "2.3.5", - "zod": "3.25.76" + "zod": "3.25.67" } }, "node_modules/@n8n/tournament": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@n8n/tournament/-/tournament-1.11.0.tgz", - "integrity": "sha512-cwu7t9UTTwAPDhGRjY03dRiWeR01wavkj2yWBEN/Rs0og17SvJqfgJ4z7Wgsiha8+iwg+mZ/l+Fv4uEhC5F3FQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@n8n/tournament/-/tournament-1.4.0.tgz", + "integrity": "sha512-llDdmIj3Kdfq2vX/NpLBOUU4kLjhITtIMeJmOSTApAOeAArj1D45ey4D7CFaYcYsPBw6DgF0vxifyskPDlgekw==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "ast-types": "^0.16.1", "esprima-next": "^5.8.4", "recast": "^0.22.0" - } - }, - "node_modules/@n8n/utils": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/@n8n/utils/-/utils-1.47.0.tgz", - "integrity": "sha512-/wE1a/EEZWqFdJkeEqMp971hDvI2noDSVwQDm1AHFv5oW23h5kVTeG2SySVMWcGZYgnc5bl6Mvcrt35vaSFasg==", - "dev": true, - "license": "SEE LICENSE IN LICENSE.md", - "dependencies": { - "@n8n/constants": "0.38.0", - "nanoid": "3.3.18" + }, + "engines": { + "node": ">=20.15", + "pnpm": ">=9.5" } }, "node_modules/@napi-rs/wasm-runtime": { @@ -626,247 +426,6 @@ "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", - "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/api-logs": { - "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.220.0.tgz", - "integrity": "sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/core": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.10.0.tgz", - "integrity": "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/instrumentation": { - "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.220.0.tgz", - "integrity": "sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.220.0", - "import-in-the-middle": "^3.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.10.0.tgz", - "integrity": "sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.10.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.10.0.tgz", - "integrity": "sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.10.0", - "@opentelemetry/resources": "2.10.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.10.0.tgz", - "integrity": "sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.10.0", - "@opentelemetry/resources": "2.10.0", - "@opentelemetry/sdk-trace": "2.10.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", - "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@sentry/conventions": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@sentry/conventions/-/conventions-0.16.0.tgz", - "integrity": "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/@sentry/core": { - "version": "10.70.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.70.0.tgz", - "integrity": "sha512-ozhCTDqg89oB4XmWfAwuHshABpvT7AkRpaPnogopPfMAaI61G1t8EKCJ4W7aum8JSBonlfyjPCyW5oYZFm0KvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.16.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry/node": { - "version": "10.70.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.70.0.tgz", - "integrity": "sha512-SPOOVxmKTVIEtqvOKkQT163e/pOwucjS7OPsCHyRs8sFR4nfBNu0EThplyqnvqd5BWBMTPH6WTBQfo+QWHV+HA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "^1.9.1", - "@opentelemetry/instrumentation": "^0.220.0", - "@opentelemetry/sdk-trace-base": "^2.9.0", - "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.70.0", - "@sentry/node-core": "10.70.0", - "@sentry/opentelemetry": "10.70.0", - "@sentry/server-utils": "10.70.0", - "import-in-the-middle": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry/node-core": { - "version": "10.70.0", - "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.70.0.tgz", - "integrity": "sha512-oPOEVVNxv5WHtckx2i06Wi9FLWyvOg/1DUeX732jZ4iqT2nupINaMH4nF4f4kSvUThFnxkFSRQxwqOxgzMKhKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.70.0", - "@sentry/opentelemetry": "10.70.0", - "import-in-the-middle": "^3.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "^1.30.1 || ^2.1.0", - "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1", - "@opentelemetry/instrumentation": ">=0.57.1 <1", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@opentelemetry/core": { - "optional": true - }, - "@opentelemetry/exporter-trace-otlp-http": { - "optional": true - }, - "@opentelemetry/instrumentation": { - "optional": true - }, - "@opentelemetry/sdk-trace-base": { - "optional": true - } - } - }, - "node_modules/@sentry/opentelemetry": { - "version": "10.70.0", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.70.0.tgz", - "integrity": "sha512-UNV/2tqypcUK6FDzerAsFJn1Km/c4VZCYkUZDNbnV5S0cwAq2BYKMo4M5vovaLDBQlxA+Wk9ovbxi5wYjjl9fw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.70.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "^1.30.1 || ^2.1.0", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" - } - }, - "node_modules/@sentry/server-utils": { - "version": "10.70.0", - "resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.70.0.tgz", - "integrity": "sha512-rzegZjMFFgCp3o+N8+XU13rfSvz4B+f8rU0ijBGrQcHdMNyfsFDTu1UTm262JofmrV2u+s+D0u0vFTnqtOGkbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@apm-js-collab/code-transformer-bundler-plugins": "^0.7.3", - "@apm-js-collab/tracing-hooks": "^0.13.0", - "@sentry/conventions": "^0.16.0", - "@sentry/core": "10.70.0", - "meriyah": "^6.1.4" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@tybys/wasm-util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", @@ -1545,16 +1104,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, "node_modules/assert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", @@ -1582,16 +1131,6 @@ "node": ">=4" } }, - "node_modules/astring": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "dev": true, - "license": "MIT", - "bin": { - "astring": "bin/astring" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1615,56 +1154,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/axios": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", - "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, - "node_modules/axios/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/axios/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/axios/node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -1675,20 +1164,10 @@ "node": "18 || 20 || >=22" } }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1698,16 +1177,6 @@ "node": "20 || >=22" } }, - "node_modules/buildcheck": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", - "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", - "dev": true, - "optional": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", @@ -1819,13 +1288,6 @@ "node": "*" } }, - "node_modules/cjs-module-lexer": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.1.tgz", - "integrity": "sha512-Ca8swihM+/4yKecYHY52kgJd300hi2lADU/a1RxNTRe+RJ9jvqQlESpbz9DnG9mowez8qwXHB8qYdIUw9e+F5Q==", - "dev": true, - "license": "MIT" - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -1891,28 +1353,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cpu-features": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", - "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "dependencies": { - "buildcheck": "~0.0.6", - "nan": "^2.19.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/crelt": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz", - "integrity": "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==", - "dev": true, - "license": "MIT" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2051,13 +1491,6 @@ "node": ">= 0.4" } }, - "node_modules/es-module-lexer": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", - "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", - "dev": true, - "license": "MIT" - }, "node_modules/es-object-atoms": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", @@ -2352,9 +1785,9 @@ "license": "MIT" }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -2559,27 +1992,6 @@ "dev": true, "license": "ISC" }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -2826,21 +2238,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-in-the-middle": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.3.3.tgz", - "integrity": "sha512-AiohS3H80sXO6owEltjGX+glb7qXaDhBoJb9XcQVH4UI207xu/bDLUcadVKp7Qe576reg9yr/PXZjV5qx8gfbA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "cjs-module-lexer": "^2.2.0", - "es-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -3028,9 +2425,9 @@ "license": "ISC" }, "node_modules/isolated-vm": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/isolated-vm/-/isolated-vm-7.0.1.tgz", - "integrity": "sha512-nBCQ1hXEaYKYJ79wwfhaF44ITOJC30Hw0wPEsFeNsdD3hdeKK7FW1SGMQYBUA+qasvkqdOazI+iBuq0lcbN/aw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/isolated-vm/-/isolated-vm-6.2.0.tgz", + "integrity": "sha512-UuSlxSHWt2QuJ5WvBhzlIJx2VVZN/a44SqBbEZFKNdvuSyhOvhmyDo8SQ+njVbhnh/njoL/aW0bUTiFYlpweGQ==", "dev": true, "hasInstallScript": true, "license": "ISC", @@ -3038,7 +2435,7 @@ "node-gyp-build": "^4.8.4" }, "engines": { - "node": ">=24.0.0" + "node": ">=22.0.0" } }, "node_modules/jmespath": { @@ -3088,13 +2485,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3203,16 +2593,6 @@ "node": ">=12" } }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3235,16 +2615,6 @@ "is-buffer": "~1.1.6" } }, - "node_modules/meriyah": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/meriyah/-/meriyah-6.1.4.tgz", - "integrity": "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -3284,13 +2654,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/module-details-from-path": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", - "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", - "dev": true, - "license": "MIT" - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3299,42 +2662,34 @@ "license": "MIT" }, "node_modules/n8n-workflow": { - "version": "2.39.1", - "resolved": "https://registry.npmjs.org/n8n-workflow/-/n8n-workflow-2.39.1.tgz", - "integrity": "sha512-nN6Wctmwr8O/e1mxmjXA58PbAtbmHQf0tYFHExRW4ZvmgwpowJ/VZBxEaCia2HwtzippmdfCj8ckTQbQnibvRw==", + "version": "2.28.4", + "resolved": "https://registry.npmjs.org/n8n-workflow/-/n8n-workflow-2.28.4.tgz", + "integrity": "sha512-wNWCEvk5eBxnp5p2y/6FpMsA5t6XZVBm8HIV/u1XiUX/p91UtdBvQW0f8tTr4B79G1aeKl/l8cSroit/WFuK6A==", "dev": true, "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@codemirror/autocomplete": "6.20.0", - "@n8n/constants": "0.38.0", - "@n8n/errors": "0.13.0", - "@n8n/expression-runtime": "0.30.0", - "@n8n/tournament": "1.11.0", - "@n8n/utils": "1.47.0", - "@sentry/core": "10.70.0", - "@sentry/node": "10.70.0", + "@n8n/errors": "0.10.0", + "@n8n/expression-runtime": "0.19.1", + "@n8n/tournament": "1.4.0", "ast-types": "0.16.1", - "axios": "1.18.0", "callsites": "3.1.0", "esprima-next": "5.8.4", "form-data": "4.0.6", "jmespath": "0.16.0", "js-base64": "3.7.8", - "json-schema": "0.4.0", "jsonrepair": "3.13.2", "jssha": "3.3.1", "lodash": "4.18.1", "luxon": "3.7.2", "md5": "2.3.0", "recast": "0.22.0", - "ssh2": "1.15.0", "title-case": "3.0.3", "transliteration": "2.3.5", "uuid": "11.1.1", "xml2js": "0.6.2" }, "peerDependencies": { - "zod": "3.25.76" + "zod": "3.25.67" } }, "node_modules/n8n-workflow/node_modules/uuid": { @@ -3351,33 +2706,6 @@ "uuid": "dist/esm/bin/uuid" } }, - "node_modules/nan": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.28.0.tgz", - "integrity": "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/napi-postinstall": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", @@ -3622,33 +2950,6 @@ "node": ">=6" } }, - "node_modules/quickjs-emscripten": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/quickjs-emscripten/-/quickjs-emscripten-0.32.0.tgz", - "integrity": "sha512-So0Sqw869y/S2oE3Nuc0uT3Dhqgvsj8FSrwBdsuTosVsG8ME5/OcudU1GxsrIFdFABgy17GHnTVO9TYV/bLQcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jitl/quickjs-wasmfile-debug-asyncify": "0.32.0", - "@jitl/quickjs-wasmfile-debug-sync": "0.32.0", - "@jitl/quickjs-wasmfile-release-asyncify": "0.32.0", - "@jitl/quickjs-wasmfile-release-sync": "0.32.0", - "quickjs-emscripten-core": "0.32.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/quickjs-emscripten-core": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/quickjs-emscripten-core/-/quickjs-emscripten-core-0.32.0.tgz", - "integrity": "sha512-QFnPfjFey8EqknSrSxe1hZrf1/8z7/6s1QzGOmKo6++02r7QRRX7ZoyNaZh7JuVjWsVW87KnQrbZqnHkOAzUyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jitl/quickjs-ffi-types": "0.32.0" - } - }, "node_modules/recast": { "version": "0.22.0", "resolved": "https://registry.npmjs.org/recast/-/recast-0.22.0.tgz", @@ -3689,20 +2990,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3741,13 +3028,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, "node_modules/sax": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", @@ -3758,13 +3038,6 @@ "node": ">=11.0.0" } }, - "node_modules/semifies": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semifies/-/semifies-1.0.0.tgz", - "integrity": "sha512-xXR3KGeoxTNWPD4aBvL5NUpMTT7WMANr3EWnaS190QVkY52lqqcVRD7Q05UVbBhiWDGWMlJEUam9m7uFFGVScw==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", @@ -3841,24 +3114,6 @@ "node": ">=0.10.0" } }, - "node_modules/ssh2": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz", - "integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "asn1": "^0.2.6", - "bcrypt-pbkdf": "^1.0.2" - }, - "engines": { - "node": ">=10.16.0" - }, - "optionalDependencies": { - "cpu-features": "~0.0.9", - "nan": "^2.18.0" - } - }, "node_modules/stable-hash-x": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz", @@ -3910,13 +3165,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/style-mod": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", - "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", - "dev": true, - "license": "MIT" - }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -4012,13 +3260,6 @@ "dev": true, "license": "0BSD" }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true, - "license": "Unlicense" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4142,13 +3383,6 @@ "which-typed-array": "^1.1.2" } }, - "node_modules/w3c-keyname": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", - "dev": true, - "license": "MIT" - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4292,9 +3526,9 @@ } }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "3.25.67", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", + "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", "dev": true, "license": "MIT", "funding": { diff --git a/n8n/package.json b/n8n/package.json index d2d3b6e9..04e57256 100644 --- a/n8n/package.json +++ b/n8n/package.json @@ -53,11 +53,11 @@ "eslint-import-resolver-typescript": "4.4.5", "eslint-plugin-import-x": "4.17.1", "eslint-plugin-n8n-nodes-base": "2.0.0", - "n8n-workflow": "2.39.1", + "n8n-workflow": "2.28.4", "prettier": "3.9.6", "typescript": "5.9.2", "typescript-eslint": "8.70.0", - "zod": "3.25.76" + "zod": "3.25.67" }, "peerDependencies": { "n8n-workflow": "*" diff --git a/n8n/scripts/audit-dev-dependencies.cjs b/n8n/scripts/audit-dev-dependencies.cjs index c3a422b7..23b651a6 100644 --- a/n8n/scripts/audit-dev-dependencies.cjs +++ b/n8n/scripts/audit-dev-dependencies.cjs @@ -29,7 +29,7 @@ function main() { : ['audit', '--json', '--audit-level=low']; const result = spawnSync(command, args, { encoding: 'utf8', - shell: process.platform === 'win32', + shell: !npmCli && process.platform === 'win32', }); if (result.error) throw result.error; if (![0, 1].includes(result.status)) { diff --git a/n8n/test/release.test.cjs b/n8n/test/release.test.cjs index 8c6a9cc6..444a9355 100644 --- a/n8n/test/release.test.cjs +++ b/n8n/test/release.test.cjs @@ -19,11 +19,11 @@ test('0.1.4 release metadata is locked to the audited direct toolchain', () => { 'eslint-import-resolver-typescript': '4.4.5', 'eslint-plugin-import-x': '4.17.1', 'eslint-plugin-n8n-nodes-base': '2.0.0', - 'n8n-workflow': '2.39.1', + 'n8n-workflow': '2.28.4', prettier: '3.9.6', typescript: '5.9.2', 'typescript-eslint': '8.70.0', - zod: '3.25.76', + zod: '3.25.67', }; assert.equal(pkg.version, '0.1.4'); diff --git a/risk-fork-hosted-mcp/THIRD_PARTY_NOTICES.txt b/risk-fork-hosted-mcp/THIRD_PARTY_NOTICES.txt index a6870c9c..5fd1bd1b 100644 --- a/risk-fork-hosted-mcp/THIRD_PARTY_NOTICES.txt +++ b/risk-fork-hosted-mcp/THIRD_PARTY_NOTICES.txt @@ -548,7 +548,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ======================================================================== -fast-uri@3.1.7 +fast-uri@3.1.8 Declared license: BSD-3-Clause Notice source: risk-fork-hosted-mcp/node_modules/fast-uri/LICENSE Notice source method: standalone_license_file diff --git a/risk-fork-hosted-mcp/dist/runtime/index.mjs b/risk-fork-hosted-mcp/dist/runtime/index.mjs index 04ae0296..c87447a0 100644 --- a/risk-fork-hosted-mcp/dist/runtime/index.mjs +++ b/risk-fork-hosted-mcp/dist/runtime/index.mjs @@ -116,7 +116,7 @@ var require_package = __commonJS({ }, overrides: { "@hono/node-server": "2.0.11", - "fast-uri": "3.1.7", + "fast-uri": "3.1.8", qs: "6.16.0" }, engines: { @@ -48050,13 +48050,14 @@ var require_fast_uri = __commonJS({ if (!malformedIPLiteral) { malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP2); } - if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) { - if (uri.indexOf("%") !== -1) { - if (parsed.host !== void 0 && !malformedIPLiteral) { - const host = isIP2 ? parsed.host : normalizePercentEncoding(parsed.host, true); - parsed.host = reescapeHostDelimiters(host, isIP2); - } + if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) { + let host = isIP2 ? parsed.host : normalizePercentEncoding(parsed.host, true); + if (!isIP2) { + host = normalizePercentEncoding(host.toLowerCase()); } + parsed.host = reescapeHostDelimiters(host, isIP2); + } + if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) { if (parsed.path) { parsed.path = normalizePathEncoding(parsed.path); } @@ -74494,7 +74495,7 @@ function createE2BAuthorityFreeSourceVerifier(options = {}) { } // risk-fork-hosted-mcp/src/index.mjs -var REVIEWED_SOURCE_INTEGRITY = true ? "sha256:b376639a54275b44e3bb845c7d26aa63b7119f0ced68ebc6a7ca75e54f4a787f" : null; +var REVIEWED_SOURCE_INTEGRITY = true ? "sha256:7cf5b428998d43726f192359130d6c5d250507ab0cace2cc63fff273955b6739" : null; var HOSTED_MCP_BUNDLE_METADATA = Object.freeze({ package_name: "@agoragentic/risk-fork-hosted-mcp", package_version: "0.1.0-alpha.0", diff --git a/risk-fork-hosted-mcp/integrity-manifest.json b/risk-fork-hosted-mcp/integrity-manifest.json index ae74bf50..27745032 100644 --- a/risk-fork-hosted-mcp/integrity-manifest.json +++ b/risk-fork-hosted-mcp/integrity-manifest.json @@ -9,7 +9,7 @@ "schema": "agoragentic.risk-fork-hosted-mcp.reviewed-sources.v2", "normalization": "utf8_crlf_to_lf_lone_cr_preserved", "files": 66, - "sha256": "sha256:b376639a54275b44e3bb845c7d26aa63b7119f0ced68ebc6a7ca75e54f4a787f" + "sha256": "sha256:7cf5b428998d43726f192359130d6c5d250507ab0cace2cc63fff273955b6739" }, "reviewed_sources": [ { @@ -25,7 +25,7 @@ { "path": "mcp/package.json", "bytes": 3195, - "sha256": "sha256:bb551da95f41912a91b331e809f6195652b4d8bccd5881e94843d84b9f73c8d0" + "sha256": "sha256:f1810726d850cee5c84d2902e2ce108596e780d1cba573845f294d2cd59c85a1" }, { "path": "risk-fork/LICENSE", @@ -520,7 +520,7 @@ "path": "mcp/package.json", "source": "reviewed_source", "bytes": 3195, - "sha256": "sha256:bb551da95f41912a91b331e809f6195652b4d8bccd5881e94843d84b9f73c8d0" + "sha256": "sha256:f1810726d850cee5c84d2902e2ce108596e780d1cba573845f294d2cd59c85a1" }, { "path": "risk-fork-hosted-mcp/node_modules/@modelcontextprotocol/core/LICENSE", @@ -1191,8 +1191,8 @@ { "path": "risk-fork-hosted-mcp/node_modules/fast-uri/index.js", "source": "workspace_dependency", - "bytes": 22061, - "sha256": "sha256:e82d3e55d954483e4927f9740bc3113a24d19ef5a73c7962d8a93c499fc982ff" + "bytes": 22264, + "sha256": "sha256:62876e5094b43ba2ba90374613dab4576fd337063dad20b4b07cc5e3a7e2303b" }, { "path": "risk-fork-hosted-mcp/node_modules/fast-uri/lib/schemes.js", @@ -1210,7 +1210,7 @@ "path": "risk-fork-hosted-mcp/node_modules/fast-uri/package.json", "source": "workspace_dependency", "bytes": 1983, - "sha256": "sha256:e634f3848a2c6c7e1cdc28b119b16e072e10dada8a3ca3f175a2372d0dd83360" + "sha256": "sha256:30cad05aad3d3596513706451dbfc7e0422f9c1d00f2903ecc34580327d54bc7" }, { "path": "risk-fork-hosted-mcp/node_modules/json-schema-traverse/LICENSE", @@ -2380,7 +2380,7 @@ "third_party_notices": { "path": "THIRD_PARTY_NOTICES.txt", "bytes": 56885, - "sha256": "sha256:88c6a3301b747237128435917d181c66aa2568234f40947531a0b0de0d3477fd", + "sha256": "sha256:84761a19dbe9d442127b5dd3d83f35831a756f888589ae6fc5132b31af733855", "sources": [ { "package": "@modelcontextprotocol/core", @@ -2439,7 +2439,7 @@ }, { "package": "fast-uri", - "version": "3.1.7", + "version": "3.1.8", "declared_license": "BSD-3-Clause", "method": "standalone_license_file", "path": "risk-fork-hosted-mcp/node_modules/fast-uri/LICENSE", @@ -2628,8 +2628,8 @@ }, "artifact": { "path": "dist/runtime/index.mjs", - "bytes": 3111081, - "sha256": "sha256:181a3ecf5788ee443595d4d7192c4c9bff50829e02ea6c52e9b842a2a74d6609" + "bytes": 3111138, + "sha256": "sha256:db99484dfa89944be8edaf8e5e52c0b0ce6ca620fc5455c509d245ae2ffd3a09" }, "packaged_assets": [ { @@ -2653,7 +2653,7 @@ { "path": "THIRD_PARTY_NOTICES.txt", "bytes": 56885, - "sha256": "sha256:88c6a3301b747237128435917d181c66aa2568234f40947531a0b0de0d3477fd" + "sha256": "sha256:84761a19dbe9d442127b5dd3d83f35831a756f888589ae6fc5132b31af733855" }, { "path": "e2b-context/risk-fork/e2b-template/bin/boot-guard.mjs", diff --git a/risk-fork-hosted-mcp/package-lock.json b/risk-fork-hosted-mcp/package-lock.json index 1d61d55d..e5c7d962 100644 --- a/risk-fork-hosted-mcp/package-lock.json +++ b/risk-fork-hosted-mcp/package-lock.json @@ -1049,9 +1049,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "dev": true, "funding": [ { @@ -1252,9 +1252,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.5.0.tgz", - "integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==", + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.3.tgz", + "integrity": "sha512-A1kdq/tSb5QjvKvAMgIoEvDBIgL7qaqVP/jkvSwYYRZ9iEzvPpopxp2wQfu3SuZRHtpHNxMn8Fs0bS+gf5Xmwg==", "dev": true, "license": "MIT", "engines": { diff --git a/risk-fork-hosted-mcp/test/package-artifact.test.mjs b/risk-fork-hosted-mcp/test/package-artifact.test.mjs index 3179c584..32228a2c 100644 --- a/risk-fork-hosted-mcp/test/package-artifact.test.mjs +++ b/risk-fork-hosted-mcp/test/package-artifact.test.mjs @@ -44,6 +44,7 @@ const HOSTED_FIXTURE_COPIES = Object.freeze([ ['transaction-assurance/src', 'transaction-assurance/src'], ['risk-fork-hosted-mcp/integrity-manifest.json', 'risk-fork-hosted-mcp/integrity-manifest.json'], ['risk-fork-hosted-mcp/node_modules', 'risk-fork-hosted-mcp/node_modules'], + ['risk-fork-hosted-mcp/package-lock.json', 'risk-fork-hosted-mcp/package-lock.json'], ['risk-fork-hosted-mcp/package.json', 'risk-fork-hosted-mcp/package.json'], ['risk-fork-hosted-mcp/scripts', 'risk-fork-hosted-mcp/scripts'], ['risk-fork-hosted-mcp/src', 'risk-fork-hosted-mcp/src'], @@ -249,6 +250,34 @@ async function assertGeneratedHostedOutputsUnchanged(expectedSnapshot) { } } +async function assertHostedFastUriClosure(root) { + const hostedRoot = path.join(root, 'risk-fork-hosted-mcp'); + const lock = JSON.parse(await readFile(path.join(hostedRoot, 'package-lock.json'), 'utf8')); + const mcpPackage = JSON.parse(await readFile(path.join(root, 'mcp', 'package.json'), 'utf8')); + const advertisedVersion = mcpPackage.overrides?.['fast-uri']; + assert.equal(typeof advertisedVersion, 'string'); + + const lockRecord = lock.packages?.['node_modules/fast-uri']; + assert.equal(lockRecord?.version, advertisedVersion, 'hosted lock must satisfy the MCP fast-uri override'); + const installedPackagePath = path.join(hostedRoot, 'node_modules', 'fast-uri', 'package.json'); + const installedPackageBytes = await readFile(installedPackagePath); + const installedPackage = JSON.parse(installedPackageBytes); + assert.equal(installedPackage.version, lockRecord.version, 'installed fast-uri must satisfy the hosted lock'); + + const manifest = JSON.parse(await readFile(path.join(hostedRoot, 'integrity-manifest.json'), 'utf8')); + const notice = manifest.third_party_notices.sources.find((source) => source.package === 'fast-uri'); + assert.equal(notice?.version, installedPackage.version, 'attested fast-uri notice must match the installed package'); + const inputPath = 'risk-fork-hosted-mcp/node_modules/fast-uri/package.json'; + const input = manifest.inputs.find((record) => record.path === inputPath); + assert.ok(input, 'attested fast-uri package metadata must be an integrity input'); + assert.equal(input.bytes, installedPackageBytes.byteLength); + assert.equal(input.sha256, sha256(installedPackageBytes)); + + const bundle = (await readFile(path.join(hostedRoot, 'dist', 'runtime', 'index.mjs'))).toString('utf8'); + const advertisedEntry = `${JSON.stringify('fast-uri')}: ${JSON.stringify(advertisedVersion)}`; + assert.ok(bundle.includes(advertisedEntry), 'bundle must retain the MCP fast-uri advertisement'); +} + const committedGeneratedOutputSnapshot = await snapshotGeneratedHostedOutputs(packageRoot); test('package contract is private, exact-version, and has no mandatory runtime dependencies', async () => { @@ -539,6 +568,27 @@ test('build is deterministic and records exact source and artifact integrity', a run(process.execPath, ['scripts/verify-integrity.mjs', '--source']); }); +test('hosted lock, bundle advertisement, and attested fast-uri records stay aligned', async () => { + const fixture = await createHostedFixture('risk-fork-hosted-dependency-closure-'); + try { + run(process.execPath, ['scripts/build.mjs'], { cwd: fixture.packageRoot }); + await assertHostedFastUriClosure(fixture.repositoryRoot); + + const lockPath = path.join(fixture.packageRoot, 'package-lock.json'); + const staleLock = JSON.parse(await readFile(lockPath, 'utf8')); + staleLock.packages['node_modules/fast-uri'].version = '3.1.7'; + staleLock.packages['node_modules/fast-uri'].resolved = 'https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz'; + staleLock.packages['node_modules/fast-uri'].integrity = 'sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg=='; + await writeFile(lockPath, `${JSON.stringify(staleLock, null, 2)}\n`, 'utf8'); + await assert.rejects( + assertHostedFastUriClosure(fixture.repositoryRoot), + /hosted lock must satisfy the MCP fast-uri override/, + ); + } finally { + await cleanupTemporary(fixture.temporary); + } +}); + test('standalone license discovery preserves actual casing and fails closed on ambiguity or non-files', async () => { const entry = (name, type = 'file') => ({ name,