diff --git a/agent-harness.lock.json b/agent-harness.lock.json index 01be3fa5ded..abe5f0a0da4 100644 --- a/agent-harness.lock.json +++ b/agent-harness.lock.json @@ -2,6 +2,5 @@ "repository": "n8n-io/cat-bot", "releaseTag": "harness-v0.3.0", "version": "0.3.0", - "assetName": "n8n-opencode-harness-0.3.0.tgz", - "sha256": "e1ff2d17291b7f9fb16c7238d23af3d6dc59fc0fe60639ef9e7d1035f21f0fe9" + "assetName": "n8n-opencode-harness-0.3.0.tgz" } diff --git a/scripts/agent-harness.mjs b/scripts/agent-harness.mjs index 122472ef845..6b1129640ad 100644 --- a/scripts/agent-harness.mjs +++ b/scripts/agent-harness.mjs @@ -1,6 +1,5 @@ #!/usr/bin/env node import { execFileSync } from 'node:child_process'; -import { createHash } from 'node:crypto'; import { existsSync, lstatSync, @@ -25,8 +24,7 @@ export function validateLock(lock) { lock?.repository !== REPOSITORY || !/^[0-9]+\.[0-9]+\.[0-9]+$/.test(lock.version) || lock.releaseTag !== `harness-v${lock.version}` || - lock.assetName !== `n8n-opencode-harness-${lock.version}.tgz` || - !/^[0-9a-f]{64}$/.test(lock.sha256) + lock.assetName !== `n8n-opencode-harness-${lock.version}.tgz` ) { throw new Error('The agent harness lock is invalid.'); } @@ -41,10 +39,6 @@ export function readLock(lockPath = join(REPO_ROOT, 'agent-harness.lock.json')) } } -export function sha256File(path) { - return createHash('sha256').update(readFileSync(path)).digest('hex'); -} - export function downloadReleaseAsset(lock, destination, { run = execFileSync } = {}) { try { run( @@ -99,7 +93,7 @@ export function installAgentHarness({ const pluginPath = join(bundlePath, 'plugins', PLUGIN_FILE); mkdirSync(cacheRoot, { recursive: true }); - // The version cache is write-once and trusted after its checksum succeeds. + // The version cache is write-once after a successful installation. const cacheHit = existsSync(pluginPath); if (!cacheHit) { const stagingRoot = mkdtempSync(join(cacheRoot, '.install-')); @@ -108,9 +102,6 @@ export function installAgentHarness({ try { mkdirSync(stagedVersion); download(lock, stagedVersion); - if (sha256File(archivePath) !== lock.sha256) { - throw new Error('Agent harness checksum mismatch.'); - } // Cat-bot validates archive contents before publishing these pinned bytes. extract(archivePath, stagedVersion); if (!existsSync(join(stagedVersion, BUNDLE_ROOT, 'plugins', PLUGIN_FILE))) { diff --git a/scripts/agent-harness.test.mjs b/scripts/agent-harness.test.mjs index a08d5534cab..44ca98f3ddc 100644 --- a/scripts/agent-harness.test.mjs +++ b/scripts/agent-harness.test.mjs @@ -15,12 +15,7 @@ import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; import { afterEach, test } from 'node:test'; -import { - downloadReleaseAsset, - installAgentHarness, - sha256File, - validateLock, -} from './agent-harness.mjs'; +import { downloadReleaseAsset, installAgentHarness, validateLock } from './agent-harness.mjs'; const temporaryDirectories = []; @@ -46,14 +41,13 @@ function makeAsset(root, version = '1.2.3') { return asset; } -function writeLock(root, asset, overrides = {}) { +function writeLock(root, overrides = {}) { const version = '1.2.3'; const lock = { repository: 'n8n-io/cat-bot', releaseTag: `harness-v${version}`, version, assetName: `n8n-opencode-harness-${version}.tgz`, - sha256: sha256File(asset), ...overrides, }; const lockPath = join(root, 'agent-harness.lock.json'); @@ -78,7 +72,6 @@ test('validates the pinned release values', () => { releaseTag: 'harness-v1.2.3', version: '1.2.3', assetName: 'n8n-opencode-harness-1.2.3.tgz', - sha256: 'a'.repeat(64), }; assert.equal(validateLock(lock), lock); for (const invalid of [ @@ -86,18 +79,17 @@ test('validates the pinned release values', () => { { releaseTag: 'latest' }, { version: '../outside' }, { assetName: 'other.tgz' }, - { sha256: 'invalid' }, ]) { assert.throws(() => validateLock({ ...lock, ...invalid }), /lock is invalid/); } }); -test('installs the verified release and activates its plugin', () => { +test('installs the pinned release and activates its plugin', () => { const root = fixtureDirectory(); const asset = makeAsset(root); const result = installAgentHarness({ ...installerPaths(root), - lockPath: writeLock(root, asset), + lockPath: writeLock(root), download: copyAsset(asset), }); @@ -115,7 +107,7 @@ test('reuses an installed release without downloading it again', () => { const asset = makeAsset(root); const options = { ...installerPaths(root), - lockPath: writeLock(root, asset), + lockPath: writeLock(root), download: copyAsset(asset), }; installAgentHarness(options); @@ -127,7 +119,7 @@ test('reuses an installed release without downloading it again', () => { assert.equal(result.cacheHit, true); }); -test('keeps the active plugin when verification fails', () => { +test('keeps the active plugin when installation fails', () => { const root = fixtureDirectory(); const asset = makeAsset(root); const paths = installerPaths(root); @@ -140,10 +132,13 @@ test('keeps the active plugin when verification fails', () => { () => installAgentHarness({ ...paths, - lockPath: writeLock(root, asset, { sha256: '0'.repeat(64) }), + lockPath: writeLock(root), download: copyAsset(asset), + extract: () => { + throw new Error('extraction failed'); + }, }), - /checksum mismatch/, + /extraction failed/, ); assert.equal(readlinkSync(paths.pluginLink), previous); }); @@ -159,7 +154,7 @@ test('does not overwrite a user plugin file', () => { () => installAgentHarness({ ...paths, - lockPath: writeLock(root, asset), + lockPath: writeLock(root), download: copyAsset(asset), }), /non-symlink path/,