carthagent 1.0.7 → 1.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -83
- package/bin/ctg.mjs +14 -732
- package/docs/beta-rewards-e2e-plan.md +48 -0
- package/docs/beta-rewards-e2e-results.md +84 -0
- package/docs/glyph-review/host-chrome-glyph-compatibility-v4.md +83 -0
- package/docs/glyph-review/test-isolation-v1.md +26 -0
- package/docs/glyph-review/typecheck-base.log +1 -0
- package/docs/glyph-review/typecheck-v4.log +1 -0
- package/docs/glyph-review/unicode-inventory-v4.csv +402 -0
- package/docs/help-dispatch-review.md +69 -0
- package/docs/launch-readiness-live-results.md +65 -0
- package/docs/legacy-cli.md +150 -0
- package/docs/live-theme-plugin-api.md +18 -0
- package/docs/native-cli.md +102 -0
- package/docs/native-compatibility.md +16 -0
- package/docs/native-daily-quests.md +132 -0
- package/docs/native-glyph-pty.md +59 -0
- package/docs/native-manual-testing.md +336 -0
- package/docs/native-slash-acceptance-handoff.md +49 -0
- package/docs/native-terminal-paired-failure-2026-10-17.md +40 -0
- package/docs/native-terminal-performance.md +310 -0
- package/docs/native-terminal-semantics-evidence/README.md +25 -0
- package/docs/native-terminal-semantics-evidence/controlled-host-model-selftest.json +16 -0
- package/docs/native-terminal-semantics-evidence/current-acceptance-status.json +59 -0
- package/docs/native-terminal-semantics-evidence/final-binary-focused-85bb44a.json +154 -0
- package/docs/native-terminal-semantics-evidence/final-binary-full-incomplete-85bb44a.json +160 -0
- package/docs/native-terminal-semantics-evidence/final-binary-layout-85bb44a.json +149 -0
- package/docs/native-terminal-semantics-evidence/final-binary-layout-interactive-85bb44a.json +159 -0
- package/docs/native-terminal-semantics-evidence/final-binary-model-picker-escape-unchanged-85bb44a.json +44 -0
- package/docs/native-terminal-semantics-evidence/final-binary-model-picker-exact-identity-85bb44a.json +47 -0
- package/docs/native-terminal-semantics-evidence/final-binary-model-picker-unchanged-model-incomplete-85bb44a.json +58 -0
- package/docs/native-terminal-semantics-evidence/final-binary-model-selftest.json +16 -0
- package/docs/native-terminal-semantics-evidence/final-binary-picker-cancel-incomplete-85bb44a.json +62 -0
- package/docs/native-terminal-semantics-evidence/focused-clipboard-title-stale-binary.json +161 -0
- package/docs/native-terminal-semantics-evidence/layout-stale-binary-observation-not-accepted.json +153 -0
- package/docs/native-terminal-semantics-evidence/picker-latest-incomplete-attempt.json +62 -0
- package/docs/native-terminal-semantics-evidence/picker-previous-probe-pass.json +61 -0
- package/docs/native-terminal-semantics.md +89 -0
- package/docs/native-upgrade.md +175 -0
- package/docs/opencode-composed-source-review.md +89 -0
- package/docs/opencode-migration.md +225 -9
- package/docs/opencode-owned-runtime.md +241 -0
- package/docs/opencode-runtime.md +12 -0
- package/docs/opencode-theme-provider-proof/README.md +55 -0
- package/docs/opencode-theme-provider-proof/live-theme-api-base.patch +122 -0
- package/docs/opencode-theme-provider-proof/plugin-theme-provider.patch +107 -0
- package/docs/opencode-theme-provider-proof/session-ui-base.patch +1217 -0
- package/docs/owned-host-controls-proof.md +39 -0
- package/docs/owned-runtime-release-manifest.md +42 -0
- package/docs/owned-visual-clear-proof.md +40 -0
- package/docs/patches/opencode-v2.0.32-glyph-spinner-test-isolation-v1.patch +50 -0
- package/docs/patches/opencode-v2.0.32-glyphs.patch +3670 -0
- package/docs/patches/opencode-v2.0.32-help-dispatch.patch +156 -0
- package/docs/patches/opencode-v2.0.32-live-theme-api.patch +122 -0
- package/docs/patches/opencode-v2.0.32-session-ui-combined-v1.patch +5272 -0
- package/docs/patches/opencode-v2.0.32-session-ui-combined-v2.patch +6052 -0
- package/docs/patches/opencode-v2.0.32-session-ui-combined-v3.patch +8657 -0
- package/docs/patches/opencode-v2.0.32-session-ui-combined-v4.patch +9928 -0
- package/docs/patches/opencode-v2.0.32-session-ui-combined-v5.patch +13965 -0
- package/docs/patches/opencode-v2.0.32-session-ui.patch +5137 -0
- package/docs/release-notes-beta.1.md +60 -0
- package/docs/release-notes-native-draft.md +73 -0
- package/docs/release-notes-preview.10.md +121 -0
- package/docs/release-notes-preview.11.md +76 -0
- package/docs/release-notes-preview.12.md +55 -0
- package/docs/release-notes-preview.13.md +68 -0
- package/docs/release-notes-preview.9.md +130 -0
- package/docs/source-preview.md +120 -0
- package/evaluate.mjs +28 -1
- package/lib/budget.mjs +1 -1
- package/lib/codegraph.mjs +1 -1
- package/lib/delivery-review.mjs +19 -6
- package/lib/delivery.mjs +3 -2
- package/lib/environment.mjs +8 -0
- package/lib/guidance.mjs +1 -1
- package/lib/opencode/cli-runtime.mjs +1171 -0
- package/lib/opencode/cli.mjs +252 -0
- package/lib/opencode/credentials.mjs +63 -8
- package/lib/opencode/demo.mjs +53 -0
- package/lib/opencode/evaluation-budget-plugin/index.ts +3 -0
- package/lib/opencode/evaluation.mjs +32 -12
- package/lib/opencode/headless.mjs +22 -5
- package/lib/opencode/provider-policy.mjs +23 -0
- package/lib/opencode/review.mjs +37 -16
- package/lib/opencode/runtime-artifact.mjs +335 -0
- package/lib/opencode/runtime-github-transport.mjs +121 -0
- package/lib/opencode/runtime-release.json +36 -0
- package/lib/opencode/runtime.mjs +299 -20
- package/lib/opencode/service.mjs +77 -35
- package/lib/opencode/session-options.mjs +6 -2
- package/lib/opencode/update.mjs +222 -0
- package/lib/opencode/workspace.mjs +1 -1
- package/lib/planning-advice.mjs +2 -2
- package/lib/providers/experiential.mjs +2 -2
- package/lib/tui/framing.mjs +1 -80
- package/lib/update.mjs +10 -1
- package/package.json +33 -7
- package/plugins/carthagent/README.md +23 -0
- package/plugins/carthagent/src/bootstrap.ts +1 -1
- package/plugins/carthagent/src/daily-evidence.mjs +57 -0
- package/plugins/carthagent/src/daily-quests-rpc.ts +50 -0
- package/plugins/carthagent/src/daily-source-rpc.ts +52 -0
- package/plugins/carthagent/src/index.ts +71 -24
- package/plugins/carthagent/src/rpc.ts +51 -6
- package/plugins/carthagent/src/server/codegraph.ts +12 -5
- package/plugins/carthagent/src/server/daily-quests-rpc.ts +277 -0
- package/plugins/carthagent/src/server/daily-source-rpc.ts +243 -0
- package/plugins/carthagent/src/server/delivery.ts +227 -112
- package/plugins/carthagent/src/server/framing.mjs +80 -0
- package/plugins/carthagent/src/server/policy.ts +1 -1
- package/plugins/carthagent/src/server/providers.ts +59 -31
- package/plugins/carthagent/src/server/session-controls.ts +3 -1
- package/plugins/carthagent/src/server/ship-agents.ts +77 -0
- package/plugins/carthagent/src/server/ship-context.mjs +60 -0
- package/plugins/carthagent/src/server/ship-guidance.ts +41 -0
- package/plugins/carthagent/src/server/ship-rpc.ts +69 -0
- package/plugins/carthagent/src/server/voyages-rpc.ts +280 -0
- package/plugins/carthagent/src/testing.ts +5 -0
- package/plugins/carthagent/src/tui.tsx +496 -0
- package/plugins/carthagent/src/ui/client.ts +127 -0
- package/plugins/carthagent/src/ui/commands.ts +70 -0
- package/plugins/carthagent/src/ui/daily-editor-focus.mjs +52 -0
- package/plugins/carthagent/src/ui/daily-editor.mjs +4 -0
- package/plugins/carthagent/src/ui/daily-quests-events.mjs +18 -0
- package/plugins/carthagent/src/ui/daily-quests-state.mjs +218 -0
- package/plugins/carthagent/src/ui/daily-quests.tsx +235 -0
- package/plugins/carthagent/src/ui/host-state.ts +42 -0
- package/plugins/carthagent/src/ui/model-command.ts +32 -0
- package/plugins/carthagent/src/ui/native-navigation.ts +23 -0
- package/plugins/carthagent/src/ui/native-review.d.mts +33 -0
- package/plugins/carthagent/src/ui/native-review.mjs +159 -0
- package/plugins/carthagent/src/ui/resume-session.mjs +34 -0
- package/plugins/carthagent/src/ui/select-mouse.ts +17 -0
- package/plugins/carthagent/src/ui/session-preferences.d.mts +7 -0
- package/plugins/carthagent/src/ui/session-preferences.mjs +14 -0
- package/plugins/carthagent/src/ui/theme-provision.d.mts +3 -0
- package/plugins/carthagent/src/ui/theme-provision.mjs +134 -0
- package/plugins/carthagent/src/ui/voyages-navigation-state.mjs +56 -0
- package/plugins/carthagent/src/ui/voyages-navigation.tsx +131 -0
- package/plugins/carthagent/src/ui/voyages-state.mjs +165 -0
- package/plugins/carthagent/src/ui/voyages.tsx +131 -0
- package/plugins/carthagent/src/ui/workspace-live.ts +61 -0
- package/plugins/carthagent/src/ui/workspace.tsx +379 -0
- package/plugins/carthagent/src/voyages-rpc.ts +64 -0
- package/plugins/carthagent/src/workspace-rpc.ts +36 -0
- package/plugins/carthagent/tests/commands.test.ts +107 -0
- package/plugins/carthagent/tests/session-preferences.test.mjs +41 -0
- package/plugins/carthagent/tests/voyages-navigation.test.tsx +269 -0
- package/plugins/carthagent/themes/carthage.json +67 -0
- package/plugins/carthagent/tui.ts +26 -1
- package/script/ctg/build-owned-runtime.sh +166 -0
- package/tests/budget-engine.test.mjs +4 -3
- package/tests/cli.test.mjs +52 -96
- package/tests/daily-evidence.test.mjs +62 -0
- package/tests/delivery-review.test.mjs +57 -2
- package/tests/delivery.test.mjs +22 -1
- package/tests/fixtures/native-discovery-plugin/server.ts +15 -0
- package/tests/generate-owned-runtime-release.test.mjs +354 -0
- package/tests/installed-runtime-contract.test.mjs +43 -0
- package/tests/native-cli-host-smoke.mjs +457 -0
- package/tests/native-daily-editor-focus.test.mjs +213 -0
- package/tests/native-daily-editor.test.mjs +43 -0
- package/tests/native-daily-quests-layout.test.mjs +55 -0
- package/tests/native-daily-quests-rpc.test.mjs +456 -0
- package/tests/native-daily-quests-ui.test.mjs +778 -0
- package/tests/native-daily-source.test.mjs +525 -0
- package/tests/native-demo-ui.test.mjs +48 -0
- package/tests/native-evaluation-admission.test.mjs +24 -0
- package/tests/native-fixture-browser-cleanup.test.mjs +65 -0
- package/tests/native-fixture-runtime.test.mjs +111 -0
- package/tests/native-fixture-stop-diagnostics.test.mjs +67 -0
- package/tests/native-glyph-pty.test.mjs +107 -0
- package/tests/native-manual-projects.test.mjs +170 -0
- package/tests/native-model-command.test.mjs +67 -0
- package/tests/native-owned-runtime-env.test.mjs +140 -0
- package/tests/native-paired-guards.test.mjs +464 -0
- package/tests/native-provider-policy.test.mjs +31 -0
- package/tests/native-pty-invocation.test.mjs +114 -0
- package/tests/native-pty-runtime.test.mjs +45 -0
- package/tests/native-resume-session.test.mjs +46 -0
- package/tests/native-review-consumer.test.mjs +286 -0
- package/tests/native-runtime-identity.test.mjs +214 -0
- package/tests/native-select-mouse.test.mjs +23 -0
- package/tests/native-ship-guidance.test.mjs +16 -0
- package/tests/native-ship-parity.test.mjs +526 -0
- package/tests/native-ship-plan.test.mjs +85 -0
- package/tests/native-ship-rpc.test.mjs +71 -0
- package/tests/native-ui-commands.test.mjs +6 -0
- package/tests/native-voyages-layout.test.mjs +28 -0
- package/tests/native-voyages-navigation.test.mjs +169 -0
- package/tests/native-voyages-rpc.test.mjs +397 -0
- package/tests/native-voyages-ui.test.mjs +307 -0
- package/tests/opencode-atomic-write.test.mjs +55 -0
- package/tests/opencode-cli.test.mjs +1312 -0
- package/tests/opencode-codegraph-plugin.test.mjs +47 -0
- package/tests/opencode-delivery.test.mjs +16 -0
- package/tests/opencode-providers.test.mjs +245 -18
- package/tests/opencode-runtime.test.mjs +597 -8
- package/tests/opencode-session-controls.test.mjs +15 -0
- package/tests/opencode-ship-rpc.test.mjs +71 -0
- package/tests/opencode-update.test.mjs +475 -0
- package/tests/opencode-workspace.test.mjs +51 -2
- package/tests/owned-runtime-publish-workflow.test.mjs +477 -0
- package/tests/release-workflow.test.mjs +46 -0
- package/tests/runtime-artifact.test.mjs +269 -0
- package/tests/runtime-development-trust.test.mjs +231 -0
- package/tests/runtime-github-transport.test.mjs +132 -0
- package/tests/runtime-token-env.test.mjs +23 -0
- package/tests/server.test.mjs +3 -1
- package/tests/ship-telemetry.test.mjs +1 -1
- package/tests/source-preview.test.mjs +253 -0
- package/tests/test_native_terminal_probe.py +76 -0
- package/tests/test_native_terminal_semantics.py +232 -0
- package/tests/update.test.mjs +42 -5
- package/tests/verify-draft-runtime-gate.test.mjs +280 -0
- package/tests/verify-owned-runtime-release.test.mjs +371 -0
- package/tests/workspace-live.test.mjs +127 -0
- package/tools/benchmark/diagnose-request.mjs +59 -36
- package/tools/benchmark/eval-common.mjs +3 -3
- package/tools/benchmark/evaluators.test.mjs +4 -4
- package/tools/benchmark/guard.test.mjs +35 -2
- package/tools/benchmark/guard.ts +20 -7
- package/tools/benchmark/planning-startup.test.mjs +30 -34
- package/tools/benchmark/runner.mjs +12 -7
- package/tools/benchmark/runner.test.mjs +23 -7
- package/tools/benchmark/upstream-error.test.mjs +16 -2
- package/tools/generate-owned-runtime-release.mjs +217 -0
- package/tools/installed-runtime-contract.mjs +16 -0
- package/tools/manual-runtime-manifest.mjs +39 -0
- package/tools/native-fixture-browser-cleanup.mjs +59 -0
- package/tools/native-fixture-stop-diagnostics.mjs +43 -0
- package/tools/native-fixture.mjs +199 -40
- package/tools/native-glyph-helpers.py +127 -0
- package/tools/native-glyph-pty.py +144 -0
- package/tools/native-manual-projects.mjs +259 -0
- package/tools/native-manual.mjs +338 -0
- package/tools/native-model-picker-acceptance.py +306 -0
- package/tools/native-owned-runtime-env.sh +47 -0
- package/tools/native-paired-guards.mjs +282 -0
- package/tools/native-paired-manifest.mjs +72 -0
- package/tools/native-paired-orchestration.sh +51 -0
- package/tools/native-plugin-readiness.mjs +33 -0
- package/tools/native-plugin-readiness.test.mjs +53 -0
- package/tools/native-pty-invocation.mjs +26 -0
- package/tools/native-pty-runtime.mjs +83 -0
- package/tools/native-pty-smoke.py +147 -24
- package/tools/native-public-repo-shell-result.mjs +13 -0
- package/tools/native-public-repo-shell-result.test.mjs +28 -0
- package/tools/native-runtime-identity.mjs +201 -0
- package/tools/native-slash-acceptance-env.py +38 -0
- package/tools/native-terminal-semantics.py +876 -0
- package/tools/owned-shell-observer/index.ts +16 -0
- package/tools/owned-shell-observer/package.json +1 -0
- package/tools/owned-visual-clear-env.py +48 -0
- package/tools/owned-visual-clear-model.py +21 -0
- package/tools/pinned-owned-runtime-inputs.mjs +46 -0
- package/tools/prepare-native-manual.mjs +122 -0
- package/tools/run-native-terminal-paired.sh +403 -0
- package/tools/runtime-token-env.mjs +8 -0
- package/tools/seed-native-switch-fixture.mjs +79 -0
- package/tools/test-native-controls.mjs +3 -1
- package/tools/test-native-daily-quests.mjs +65 -0
- package/tools/test-native-default-cli.mjs +106 -0
- package/tools/test-native-delivery-host.mjs +102 -20
- package/tools/test-native-evaluation.mjs +7 -3
- package/tools/test-native-glyph-pty.mjs +94 -0
- package/tools/test-native-host.mjs +87 -63
- package/tools/test-native-outcomes.mjs +3 -1
- package/tools/test-native-package.mjs +206 -38
- package/tools/test-native-performance.mjs +26 -0
- package/tools/test-native-product.mjs +90 -0
- package/tools/test-native-pty.mjs +34 -9
- package/tools/test-native-public-repo.mjs +224 -0
- package/tools/test-native-refresh-reentry.mjs +106 -0
- package/tools/test-native-report.mjs +27 -20
- package/tools/test-native-review.mjs +75 -0
- package/tools/test-native-runtime.mjs +120 -0
- package/tools/test-native-ship-models.mjs +176 -0
- package/tools/test-native-ship-walkthrough.mjs +96 -0
- package/tools/test-native-slash-commands.mjs +125 -0
- package/tools/test-native-slash-commands.py +242 -0
- package/tools/test-native-swe-host.mjs +224 -55
- package/tools/test-native-tasks.mjs +77 -0
- package/tools/test-native-terminal-performance.py +887 -0
- package/tools/test-native-terminal-readiness.sh +85 -0
- package/tools/test-native-terminal-semantics-installed.mjs +38 -0
- package/tools/test-native-visual-clear-installed.mjs +48 -0
- package/tools/test-owned-host-controls.mjs +238 -0
- package/tools/test-owned-opencode-runtime.mjs +35 -0
- package/tools/test-owned-visual-clear-env.py +120 -0
- package/tools/test-owned-visual-clear.py +373 -0
- package/tools/verify-draft-runtime-gate.mjs +138 -0
- package/tools/verify-owned-runtime-release.mjs +114 -0
- package/tools/verify-release-version.mjs +21 -1
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { mkdir, mkdtemp, readdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { gzipSync } from 'node:zlib';
|
|
7
|
+
import { after, before, test } from 'node:test';
|
|
8
|
+
import { generateOwnedRuntimeRelease, REQUIRED_RUNTIME_TARGETS } from '../tools/generate-owned-runtime-release.mjs';
|
|
9
|
+
|
|
10
|
+
const sourceRevision = '0123456789abcdef0123456789abcdef01234567';
|
|
11
|
+
const patchBytes = Buffer.from('fixture pinned recipe patch');
|
|
12
|
+
const patchSha256 = sha256(patchBytes);
|
|
13
|
+
const lockSha256 = 'b'.repeat(64);
|
|
14
|
+
const releaseVersion = '9.8.7';
|
|
15
|
+
const compatibilityVersion = '2.0.32';
|
|
16
|
+
const runtimeVersion = '0.0.0-ctg.2.0.32';
|
|
17
|
+
let temp;
|
|
18
|
+
let root;
|
|
19
|
+
let assetsDirectory;
|
|
20
|
+
|
|
21
|
+
before(async () => {
|
|
22
|
+
temp = await mkdtemp(path.join(os.tmpdir(), 'ctg-owned-runtime-manifest-test-'));
|
|
23
|
+
root = path.join(temp, 'repo');
|
|
24
|
+
assetsDirectory = path.join(temp, 'assets');
|
|
25
|
+
await mkdir(path.join(root, 'script/ctg'), { recursive: true });
|
|
26
|
+
await mkdir(path.join(root, 'docs/patches'), { recursive: true });
|
|
27
|
+
await mkdir(assetsDirectory, { recursive: true });
|
|
28
|
+
await writeFile(path.join(root, 'package.json'), JSON.stringify({ name: 'fixture', version: releaseVersion }));
|
|
29
|
+
await writeFile(path.join(root, 'script/ctg/build-owned-runtime.sh'), [
|
|
30
|
+
`EXPECTED_SOURCE="${sourceRevision}"`,
|
|
31
|
+
`EXPECTED_LOCK="${lockSha256}"`,
|
|
32
|
+
`EXPECTED_PATCH="${patchSha256}"`,
|
|
33
|
+
'patch_file="$ROOT/docs/patches/opencode-v2.0.32-session-ui-combined-v1.patch"',
|
|
34
|
+
`OPENCODE_VERSION=${runtimeVersion} OPENCODE_CHANNEL=ctg.${compatibilityVersion}`,
|
|
35
|
+
'bun run script/build.ts',
|
|
36
|
+
].join('\n'));
|
|
37
|
+
await writeFile(path.join(root, 'docs/patches/opencode-v2.0.32-session-ui-combined-v1.patch'), patchBytes);
|
|
38
|
+
await writeFile(path.join(root, 'docs/patches/opencode-v2.0.32-session-ui.patch'), Buffer.from('preserved base patch'));
|
|
39
|
+
await createAllFixtures();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
after(async () => rm(temp, { recursive: true, force: true }));
|
|
43
|
+
|
|
44
|
+
function sha256(bytes) { return createHash('sha256').update(bytes).digest('hex'); }
|
|
45
|
+
function octal(value, width) { return Buffer.from(`${value.toString(8).padStart(width - 1, '0')}\0`); }
|
|
46
|
+
function tarEntry(name, data, type = '0', mode = 0o644) {
|
|
47
|
+
const header = Buffer.alloc(512);
|
|
48
|
+
header.write(name, 0, 100);
|
|
49
|
+
octal(mode, 8).copy(header, 100);
|
|
50
|
+
octal(0, 8).copy(header, 108); octal(0, 8).copy(header, 116);
|
|
51
|
+
octal(data.length, 12).copy(header, 124); octal(0, 12).copy(header, 136);
|
|
52
|
+
header.fill(32, 148, 156); header[156] = type.charCodeAt(0);
|
|
53
|
+
header.write('ustar\0', 257, 6); header.write('00', 263, 2);
|
|
54
|
+
let checksum = 0; for (const byte of header) checksum += byte;
|
|
55
|
+
octal(checksum, 8).copy(header, 148);
|
|
56
|
+
return Buffer.concat([header, data, Buffer.alloc((512 - (data.length % 512)) % 512)]);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function createAllFixtures() {
|
|
60
|
+
for (const [key, [platform, arch, archiveName, executablePath]] of Object.entries(REQUIRED_RUNTIME_TARGETS)) {
|
|
61
|
+
const binary = Buffer.from(`fixture executable for ${key}\n`);
|
|
62
|
+
const inner = Buffer.from(JSON.stringify({
|
|
63
|
+
owner: 'Carthagent', sourceRepository: 'https://github.com/anomalyco/opencode',
|
|
64
|
+
sourceRevision, lockSha256, patchSha256, compatibilityVersion, runtimeVersion,
|
|
65
|
+
channel: `ctg.${compatibilityVersion}`, platform: `${platform}-${arch}`, webUI: false,
|
|
66
|
+
}));
|
|
67
|
+
const packageJson = Buffer.from(JSON.stringify({
|
|
68
|
+
name: `@opencode/cli-${key}`, version: runtimeVersion, os: [platform === 'windows' ? 'win32' : platform], cpu: [arch],
|
|
69
|
+
}));
|
|
70
|
+
const license = Buffer.from('MIT License\n');
|
|
71
|
+
const sums = Buffer.from(`${sha256(binary)} ${executablePath}\n`);
|
|
72
|
+
const parentDirectories = executablePath.split('/').slice(0, -1).reduce((all, _part, index, parts) => {
|
|
73
|
+
const current = parts.slice(0, index + 1).join('/');
|
|
74
|
+
all.push(tarEntry(`${current}/`, Buffer.alloc(0), '5'));
|
|
75
|
+
return all;
|
|
76
|
+
}, []);
|
|
77
|
+
const archive = gzipSync(Buffer.concat([
|
|
78
|
+
tarEntry('./', Buffer.alloc(0), '5'),
|
|
79
|
+
...parentDirectories,
|
|
80
|
+
tarEntry(executablePath, binary, '0', 0o755),
|
|
81
|
+
tarEntry(`cli-${key}/package.json`, packageJson),
|
|
82
|
+
tarEntry('LICENSE', license),
|
|
83
|
+
tarEntry('SHA256SUMS', sums),
|
|
84
|
+
tarEntry('ctg-runtime.json', inner),
|
|
85
|
+
Buffer.alloc(1024),
|
|
86
|
+
]));
|
|
87
|
+
await writeFile(path.join(assetsDirectory, archiveName), archive);
|
|
88
|
+
await writeFile(path.join(assetsDirectory, `${archiveName}.build.json`), JSON.stringify({
|
|
89
|
+
version: 1, target: key, platform, arch, sourceRevision, lockSha256, patchSha256,
|
|
90
|
+
compatibilityVersion, runtimeVersion, executableSha256: sha256(binary),
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
test('generates exact five-platform manifest only after production USTAR acquisition verifies bytes and provenance', async () => {
|
|
96
|
+
await rm(path.join(temp, 'runtime-release.json.candidate'), { force: true });
|
|
97
|
+
const manifest = await generateOwnedRuntimeRelease({ releaseVersion, assetsDirectory, repoRoot: root });
|
|
98
|
+
assert.deepEqual(JSON.parse(await readFile(path.join(temp, 'runtime-release.json.candidate'), 'utf8')), manifest);
|
|
99
|
+
await assert.rejects(
|
|
100
|
+
generateOwnedRuntimeRelease({ releaseVersion, assetsDirectory, repoRoot: root }),
|
|
101
|
+
/EEXIST/,
|
|
102
|
+
'a second run must not silently replace a reviewed candidate',
|
|
103
|
+
);
|
|
104
|
+
assert.deepEqual(Object.keys(manifest), [
|
|
105
|
+
'version', 'releaseVersion', 'compatibilityVersion', 'runtimeVersion', 'sourceRevision', 'patchSha256', 'lockSha256', 'assets',
|
|
106
|
+
]);
|
|
107
|
+
assert.equal(manifest.version, 1);
|
|
108
|
+
assert.equal(manifest.releaseVersion, releaseVersion);
|
|
109
|
+
assert.equal(manifest.compatibilityVersion, compatibilityVersion);
|
|
110
|
+
assert.equal(manifest.runtimeVersion, runtimeVersion);
|
|
111
|
+
assert.equal(manifest.sourceRevision, sourceRevision);
|
|
112
|
+
assert.equal(manifest.patchSha256, patchSha256);
|
|
113
|
+
assert.equal(manifest.lockSha256, lockSha256);
|
|
114
|
+
assert.deepEqual(Object.keys(manifest.assets).sort(), Object.keys(REQUIRED_RUNTIME_TARGETS).sort());
|
|
115
|
+
for (const [key, [, , name]] of Object.entries(REQUIRED_RUNTIME_TARGETS)) {
|
|
116
|
+
assert.deepEqual(Object.keys(manifest.assets[key]).sort(), ['archiveSha256', 'executableSha256', 'name']);
|
|
117
|
+
assert.equal(manifest.assets[key].name, name);
|
|
118
|
+
assert.match(manifest.assets[key].archiveSha256, /^[a-f0-9]{64}$/);
|
|
119
|
+
assert.match(manifest.assets[key].executableSha256, /^[a-f0-9]{64}$/);
|
|
120
|
+
assert.equal(manifest.assets[key].archiveSha256, sha256(await readFile(path.join(assetsDirectory, name))));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('rejects missing, extra, malformed, and wrong-target build outputs', async (t) => {
|
|
125
|
+
await t.test('missing platform output', async () => {
|
|
126
|
+
const missing = path.join(temp, 'missing');
|
|
127
|
+
await mkdir(missing);
|
|
128
|
+
for (const name of await readdir(assetsDirectory)) {
|
|
129
|
+
if (name.startsWith('opencode-linux-x64')) continue;
|
|
130
|
+
await writeFile(path.join(missing, name), await readFile(path.join(assetsDirectory, name)));
|
|
131
|
+
}
|
|
132
|
+
await assert.rejects(generateOwnedRuntimeRelease({ releaseVersion, assetsDirectory: missing, repoRoot: root }), /contain exactly/);
|
|
133
|
+
});
|
|
134
|
+
await t.test('extra asset', async () => {
|
|
135
|
+
const extra = path.join(temp, 'extra');
|
|
136
|
+
await mkdir(extra);
|
|
137
|
+
for (const name of await readdir(assetsDirectory)) {
|
|
138
|
+
await writeFile(path.join(extra, name), await readFile(path.join(assetsDirectory, name)));
|
|
139
|
+
}
|
|
140
|
+
await writeFile(path.join(extra, 'unexpected.tar.gz'), 'extra');
|
|
141
|
+
await assert.rejects(generateOwnedRuntimeRelease({ releaseVersion, assetsDirectory: extra, repoRoot: root }), /contain exactly/);
|
|
142
|
+
});
|
|
143
|
+
await t.test('record with changed target provenance', async () => {
|
|
144
|
+
const recordPath = path.join(assetsDirectory, 'opencode-linux-x64.tar.gz.build.json');
|
|
145
|
+
const original = await readFile(recordPath);
|
|
146
|
+
const changed = JSON.parse(original);
|
|
147
|
+
changed.sourceRevision = 'f'.repeat(40);
|
|
148
|
+
await writeFile(recordPath, JSON.stringify(changed));
|
|
149
|
+
try {
|
|
150
|
+
await assert.rejects(generateOwnedRuntimeRelease({ releaseVersion, assetsDirectory, repoRoot: root }), /Build record for linux-x64/);
|
|
151
|
+
} finally { await writeFile(recordPath, original); }
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('rejects record executable digest that does not describe archived executable bytes', async () => {
|
|
156
|
+
const recordPath = path.join(assetsDirectory, 'opencode-windows-x64.tar.gz.build.json');
|
|
157
|
+
const original = await readFile(recordPath);
|
|
158
|
+
const changed = JSON.parse(original);
|
|
159
|
+
changed.executableSha256 = 'e'.repeat(64);
|
|
160
|
+
await writeFile(recordPath, JSON.stringify(changed));
|
|
161
|
+
try {
|
|
162
|
+
await assert.rejects(generateOwnedRuntimeRelease({ releaseVersion, assetsDirectory, repoRoot: root }), /SHA256SUMS executable hash does not match pinned release metadata/);
|
|
163
|
+
} finally { await writeFile(recordPath, original); }
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('rejects a safe but unexpected archive member while retaining the actual build output files', async () => {
|
|
167
|
+
const archiveName = 'opencode-linux-x64.tar.gz';
|
|
168
|
+
const archivePath = path.join(assetsDirectory, archiveName);
|
|
169
|
+
const original = await readFile(archivePath);
|
|
170
|
+
const key = 'linux-x64';
|
|
171
|
+
const executablePath = `cli-${key}/bin/opencode`;
|
|
172
|
+
const binary = Buffer.from(`fixture executable for ${key}\n`);
|
|
173
|
+
const packageJson = Buffer.from(JSON.stringify({
|
|
174
|
+
name: '@opencode/cli-linux-x64', version: runtimeVersion, os: ['linux'], cpu: ['x64'],
|
|
175
|
+
}));
|
|
176
|
+
const inner = Buffer.from(JSON.stringify({
|
|
177
|
+
owner: 'Carthagent', sourceRepository: 'https://github.com/anomalyco/opencode',
|
|
178
|
+
sourceRevision, lockSha256, patchSha256, compatibilityVersion, runtimeVersion,
|
|
179
|
+
channel: `ctg.${compatibilityVersion}`, platform: 'linux-x64', webUI: false,
|
|
180
|
+
}));
|
|
181
|
+
const makeEntry = (extra = []) => gzipSync(Buffer.concat([
|
|
182
|
+
tarEntry('./', Buffer.alloc(0), '5'),
|
|
183
|
+
tarEntry('./cli-linux-x64/', Buffer.alloc(0), '5'),
|
|
184
|
+
tarEntry('./cli-linux-x64/bin/', Buffer.alloc(0), '5'),
|
|
185
|
+
tarEntry(executablePath, binary, '0', 0o755),
|
|
186
|
+
tarEntry('cli-linux-x64/package.json', packageJson),
|
|
187
|
+
tarEntry('LICENSE', Buffer.from('MIT License\n')),
|
|
188
|
+
tarEntry('SHA256SUMS', Buffer.from(`${sha256(binary)} ${executablePath}\n`)),
|
|
189
|
+
tarEntry('ctg-runtime.json', inner),
|
|
190
|
+
...extra,
|
|
191
|
+
Buffer.alloc(1024),
|
|
192
|
+
]));
|
|
193
|
+
await writeFile(archivePath, makeEntry([tarEntry('extra/payload', Buffer.from('unexpected'))]));
|
|
194
|
+
try {
|
|
195
|
+
await assert.rejects(
|
|
196
|
+
generateOwnedRuntimeRelease({ releaseVersion, assetsDirectory, repoRoot: root }),
|
|
197
|
+
/unexpected directory: extra/,
|
|
198
|
+
);
|
|
199
|
+
} finally { await writeFile(archivePath, original); }
|
|
200
|
+
// Restored valid input includes the genuine runtime LICENSE and target package metadata.
|
|
201
|
+
const manifest = await generateOwnedRuntimeRelease({
|
|
202
|
+
releaseVersion, assetsDirectory, repoRoot: root,
|
|
203
|
+
outputPath: path.join(temp, 'allowed-members.candidate'),
|
|
204
|
+
});
|
|
205
|
+
assert.equal(manifest.assets['linux-x64'].name, archiveName);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test('matches pinned OpenCode build.ts Windows package metadata (win32 OS, windows target name)', async () => {
|
|
209
|
+
const archivePath = path.join(assetsDirectory, 'opencode-windows-x64.tar.gz');
|
|
210
|
+
const original = await readFile(archivePath);
|
|
211
|
+
const expectedExecutablePath = 'cli-windows-x64/bin/opencode.exe';
|
|
212
|
+
const binary = Buffer.from('fixture executable for windows-x64\n');
|
|
213
|
+
const entries = [
|
|
214
|
+
tarEntry('./', Buffer.alloc(0), '5'),
|
|
215
|
+
tarEntry('./cli-windows-x64/', Buffer.alloc(0), '5'),
|
|
216
|
+
tarEntry('./cli-windows-x64/bin/', Buffer.alloc(0), '5'),
|
|
217
|
+
tarEntry(expectedExecutablePath, binary, '0', 0o755),
|
|
218
|
+
tarEntry('cli-windows-x64/package.json', Buffer.from(JSON.stringify({
|
|
219
|
+
name: '@opencode/cli-windows-x64', version: runtimeVersion, os: ['win32'], cpu: ['x64'],
|
|
220
|
+
}))),
|
|
221
|
+
tarEntry('LICENSE', Buffer.from('MIT License\n')),
|
|
222
|
+
tarEntry('SHA256SUMS', Buffer.from(`${sha256(binary)} ${expectedExecutablePath}\n`)),
|
|
223
|
+
tarEntry('ctg-runtime.json', Buffer.from(JSON.stringify({
|
|
224
|
+
owner: 'Carthagent', sourceRepository: 'https://github.com/anomalyco/opencode',
|
|
225
|
+
sourceRevision, lockSha256, patchSha256, compatibilityVersion, runtimeVersion,
|
|
226
|
+
channel: `ctg.${compatibilityVersion}`, platform: 'windows-x64', webUI: false,
|
|
227
|
+
}))),
|
|
228
|
+
Buffer.alloc(1024),
|
|
229
|
+
];
|
|
230
|
+
await writeFile(archivePath, gzipSync(Buffer.concat(entries)));
|
|
231
|
+
try {
|
|
232
|
+
const manifest = await generateOwnedRuntimeRelease({
|
|
233
|
+
releaseVersion, assetsDirectory, repoRoot: root,
|
|
234
|
+
outputPath: path.join(temp, 'windows-metadata.candidate'),
|
|
235
|
+
});
|
|
236
|
+
assert.equal(manifest.assets['windows-x64'].executableSha256, sha256(binary));
|
|
237
|
+
} finally { await writeFile(archivePath, original); }
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('rejects npm Windows OS metadata that does not match pinned build.ts', async () => {
|
|
241
|
+
const archivePath = path.join(assetsDirectory, 'opencode-windows-x64.tar.gz');
|
|
242
|
+
const original = await readFile(archivePath);
|
|
243
|
+
const expectedExecutablePath = 'cli-windows-x64/bin/opencode.exe';
|
|
244
|
+
const binary = Buffer.from('fixture executable for windows-x64\n');
|
|
245
|
+
await writeFile(archivePath, gzipSync(Buffer.concat([
|
|
246
|
+
tarEntry('./', Buffer.alloc(0), '5'),
|
|
247
|
+
tarEntry('./cli-windows-x64/', Buffer.alloc(0), '5'),
|
|
248
|
+
tarEntry('./cli-windows-x64/bin/', Buffer.alloc(0), '5'),
|
|
249
|
+
tarEntry(expectedExecutablePath, binary, '0', 0o755),
|
|
250
|
+
tarEntry('cli-windows-x64/package.json', Buffer.from(JSON.stringify({
|
|
251
|
+
name: '@opencode/cli-windows-x64', version: runtimeVersion, os: ['windows'], cpu: ['x64'],
|
|
252
|
+
}))),
|
|
253
|
+
tarEntry('LICENSE', Buffer.from('MIT License\n')),
|
|
254
|
+
tarEntry('SHA256SUMS', Buffer.from(`${sha256(binary)} ${expectedExecutablePath}\n`)),
|
|
255
|
+
tarEntry('ctg-runtime.json', Buffer.from(JSON.stringify({
|
|
256
|
+
owner: 'Carthagent', sourceRepository: 'https://github.com/anomalyco/opencode',
|
|
257
|
+
sourceRevision, lockSha256, patchSha256, compatibilityVersion, runtimeVersion,
|
|
258
|
+
channel: `ctg.${compatibilityVersion}`, platform: 'windows-x64', webUI: false,
|
|
259
|
+
}))),
|
|
260
|
+
Buffer.alloc(1024),
|
|
261
|
+
])));
|
|
262
|
+
try {
|
|
263
|
+
await assert.rejects(
|
|
264
|
+
generateOwnedRuntimeRelease({
|
|
265
|
+
releaseVersion, assetsDirectory, repoRoot: root,
|
|
266
|
+
outputPath: path.join(temp, 'windows-wrong-os.candidate'),
|
|
267
|
+
}),
|
|
268
|
+
/package metadata does not match windows-x64/,
|
|
269
|
+
);
|
|
270
|
+
} finally { await writeFile(archivePath, original); }
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test('rejects a noncanonical release version and a recipe/patch mismatch', async (t) => {
|
|
274
|
+
await t.test('v-prefixed release version', async () => {
|
|
275
|
+
await assert.rejects(generateOwnedRuntimeRelease({ releaseVersion: 'v9.8.7', assetsDirectory, repoRoot: root }), /without a v prefix/);
|
|
276
|
+
});
|
|
277
|
+
await t.test('patch bytes changed after recipe pin', async () => {
|
|
278
|
+
const patchPath = path.join(root, 'docs/patches/opencode-v2.0.32-session-ui-combined-v1.patch');
|
|
279
|
+
const original = await readFile(patchPath);
|
|
280
|
+
await writeFile(patchPath, Buffer.concat([original, Buffer.from('changed')]));
|
|
281
|
+
try {
|
|
282
|
+
await assert.rejects(generateOwnedRuntimeRelease({ releaseVersion, assetsDirectory, repoRoot: root }), /patch bytes do not match/);
|
|
283
|
+
} finally { await writeFile(patchPath, original); }
|
|
284
|
+
});
|
|
285
|
+
await t.test('missing recipe-selected combined patch fails rather than falling back to preserved base patch', async () => {
|
|
286
|
+
const selectedPath = path.join(root, 'docs/patches/opencode-v2.0.32-session-ui-combined-v1.patch');
|
|
287
|
+
const original = await readFile(selectedPath);
|
|
288
|
+
await rm(selectedPath);
|
|
289
|
+
try {
|
|
290
|
+
await assert.rejects(generateOwnedRuntimeRelease({
|
|
291
|
+
releaseVersion, assetsDirectory, repoRoot: root,
|
|
292
|
+
outputPath: path.join(temp, 'missing-selected-patch.candidate'),
|
|
293
|
+
}), /ENOENT/);
|
|
294
|
+
} finally { await writeFile(selectedPath, original); }
|
|
295
|
+
});
|
|
296
|
+
await t.test('legacy recipe patch literal remains supported', async () => {
|
|
297
|
+
const recipePath = path.join(root, 'script/ctg/build-owned-runtime.sh');
|
|
298
|
+
const original = await readFile(recipePath, 'utf8');
|
|
299
|
+
await writeFile(recipePath, original.replace(
|
|
300
|
+
'opencode-v2.0.32-session-ui-combined-v1.patch',
|
|
301
|
+
'opencode-v2.0.32-session-ui.patch',
|
|
302
|
+
));
|
|
303
|
+
// Keep the legacy selected patch authoritative and digest-pinned.
|
|
304
|
+
const legacyPatchPath = path.join(root, 'docs/patches/opencode-v2.0.32-session-ui.patch');
|
|
305
|
+
const legacyOriginal = await readFile(legacyPatchPath);
|
|
306
|
+
await writeFile(legacyPatchPath, patchBytes);
|
|
307
|
+
try {
|
|
308
|
+
const manifest = await generateOwnedRuntimeRelease({
|
|
309
|
+
releaseVersion, assetsDirectory, repoRoot: root,
|
|
310
|
+
outputPath: path.join(temp, 'legacy-recipe.candidate'),
|
|
311
|
+
});
|
|
312
|
+
assert.equal(manifest.patchSha256, patchSha256);
|
|
313
|
+
} finally {
|
|
314
|
+
await writeFile(recipePath, original);
|
|
315
|
+
await writeFile(legacyPatchPath, legacyOriginal);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
await t.test('rejects noncanonical or escaping patch paths without evaluating recipe text', async () => {
|
|
319
|
+
const recipePath = path.join(root, 'script/ctg/build-owned-runtime.sh');
|
|
320
|
+
const original = await readFile(recipePath, 'utf8');
|
|
321
|
+
await writeFile(recipePath, original.replace(
|
|
322
|
+
'patch_file="$ROOT/docs/patches/opencode-v2.0.32-session-ui-combined-v1.patch"',
|
|
323
|
+
'patch_file="$ROOT/docs/patches/../patches/opencode-v2.0.32-session-ui.patch"',
|
|
324
|
+
));
|
|
325
|
+
try {
|
|
326
|
+
await assert.rejects(generateOwnedRuntimeRelease({
|
|
327
|
+
releaseVersion, assetsDirectory, repoRoot: root,
|
|
328
|
+
outputPath: path.join(temp, 'escaping-patch.candidate'),
|
|
329
|
+
}), /canonical docs\/patches-relative path/);
|
|
330
|
+
} finally { await writeFile(recipePath, original); }
|
|
331
|
+
});
|
|
332
|
+
await t.test('rejects a second nonliteral patch_file assignment', async () => {
|
|
333
|
+
const recipePath = path.join(root, 'script/ctg/build-owned-runtime.sh');
|
|
334
|
+
const original = await readFile(recipePath, 'utf8');
|
|
335
|
+
await writeFile(recipePath, `${original}\npatch_file="${'${PATCH_FILE}'}"\n`);
|
|
336
|
+
try {
|
|
337
|
+
await assert.rejects(generateOwnedRuntimeRelease({
|
|
338
|
+
releaseVersion, assetsDirectory, repoRoot: root,
|
|
339
|
+
outputPath: path.join(temp, 'multiple-patch-assignments.candidate'),
|
|
340
|
+
}), /exactly one literal ROOT-relative patch_file/);
|
|
341
|
+
} finally { await writeFile(recipePath, original); }
|
|
342
|
+
});
|
|
343
|
+
await t.test('rejects a recipe with no selected patch instead of choosing a repository patch', async () => {
|
|
344
|
+
const recipePath = path.join(root, 'script/ctg/build-owned-runtime.sh');
|
|
345
|
+
const original = await readFile(recipePath, 'utf8');
|
|
346
|
+
await writeFile(recipePath, original.replace(/^patch_file=.*\n/m, ''));
|
|
347
|
+
try {
|
|
348
|
+
await assert.rejects(generateOwnedRuntimeRelease({
|
|
349
|
+
releaseVersion, assetsDirectory, repoRoot: root,
|
|
350
|
+
outputPath: path.join(temp, 'missing-patch-selection.candidate'),
|
|
351
|
+
}), /exactly one literal ROOT-relative patch_file/);
|
|
352
|
+
} finally { await writeFile(recipePath, original); }
|
|
353
|
+
});
|
|
354
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { assertTerminalSemanticsProof, resolveInstalledRuntime } from '../tools/installed-runtime-contract.mjs';
|
|
4
|
+
|
|
5
|
+
test('installed resolution passes the identical options object through identity reporting', async () => {
|
|
6
|
+
const manifestPath = '/installed/lib/opencode/runtime-release.json';
|
|
7
|
+
let resolverOptions;
|
|
8
|
+
let identityOptions;
|
|
9
|
+
const binary = '/isolated/cache/cli-linux-x64/bin/opencode';
|
|
10
|
+
const result = await resolveInstalledRuntime(async options => {
|
|
11
|
+
resolverOptions = options;
|
|
12
|
+
options.resolvedMarker = true;
|
|
13
|
+
return binary;
|
|
14
|
+
}, async (_binary, options) => {
|
|
15
|
+
identityOptions = options;
|
|
16
|
+
assert.equal(options.resolvedMarker, true);
|
|
17
|
+
return { kind: 'ctg-owned', binarySha256: 'a'.repeat(64) };
|
|
18
|
+
}, manifestPath);
|
|
19
|
+
|
|
20
|
+
assert.equal(resolverOptions, identityOptions);
|
|
21
|
+
assert.equal(result.options, resolverOptions);
|
|
22
|
+
assert.equal(result.binary, binary);
|
|
23
|
+
assert.equal(result.identity.kind, 'ctg-owned');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('terminal semantics contract accepts only exact owned controlled-emulator evidence', () => {
|
|
27
|
+
const binary = '/isolated/cache/cli-linux-x64/bin/opencode';
|
|
28
|
+
const identity = { binarySha256: 'b'.repeat(64) };
|
|
29
|
+
const manifest = { runtimeVersion: '0.0.0-ctg.test' };
|
|
30
|
+
const proof = {
|
|
31
|
+
status: 'PASS', binaryResolvedPath: binary, binarySha256: identity.binarySha256,
|
|
32
|
+
binaryVersion: `opencode v${manifest.runtimeVersion}`,
|
|
33
|
+
physicalOSClipboardVerified: false, controlledHostClipboardModel: true,
|
|
34
|
+
};
|
|
35
|
+
assert.doesNotThrow(() => assertTerminalSemanticsProof(proof, { binary, identity, manifest }));
|
|
36
|
+
for (const [key, value] of [
|
|
37
|
+
['status', 'INCOMPLETE'], ['binaryResolvedPath', '/other/opencode'],
|
|
38
|
+
['binarySha256', 'c'.repeat(64)], ['binaryVersion', 'opencode v2.0.32'],
|
|
39
|
+
['physicalOSClipboardVerified', true], ['controlledHostClipboardModel', false],
|
|
40
|
+
]) {
|
|
41
|
+
assert.throws(() => assertTerminalSemanticsProof({ ...proof, [key]: value }, { binary, identity, manifest }), key);
|
|
42
|
+
}
|
|
43
|
+
});
|