mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 16:01:54 +00:00
238 lines
12 KiB
JavaScript
238 lines
12 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { execFileSync, spawnSync } from 'node:child_process';
|
|
import { existsSync, mkdtempSync, readFileSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import test from 'node:test';
|
|
import { createRuntimeAdapterRegistry } from '../../src/adapters/registry.mjs';
|
|
import { inspectRuntimePluginTrust, planRuntimePluginTrust, trustRuntimePlugin } from '../../src/adapters/plugins.mjs';
|
|
import { openControlPlane } from '../../src/control-plane/service.mjs';
|
|
import { analyzeRepository } from '../../src/kernel/analyze.mjs';
|
|
import { proposeDomains } from '../../src/kernel/domains.mjs';
|
|
import { diagnoseProject } from '../../src/kernel/doctor.mjs';
|
|
import { inspectRepository } from '../../src/kernel/git.mjs';
|
|
import { installProject } from '../../src/kernel/install.mjs';
|
|
import { createWorktrees } from '../../src/kernel/worktrees.mjs';
|
|
import { planFleetUp } from '../../src/runtime/lifecycle.mjs';
|
|
|
|
const CLI = new URL('../../bin/torch.mjs', import.meta.url).pathname;
|
|
const PLUGIN_NAME = 'portable-local';
|
|
|
|
function pluginSource(name, sentinel) {
|
|
return `
|
|
const fs = require('node:fs');
|
|
fs.appendFileSync(${JSON.stringify(sentinel)}, 'loaded\\n');
|
|
exports.createTorchRuntimeAdapter = ({ env }) => ({
|
|
name: ${JSON.stringify(name)},
|
|
configuration: { executable: 'local-agent' },
|
|
capabilities: {
|
|
detect: true, configure: 'plan-only', modelSelection: true, modelRequired: true,
|
|
reasoningSelection: true, createSession: true, resumeSession: true,
|
|
perInvocationCostCeiling: { createSession: [], resumeSession: [] },
|
|
sendOrSteer: false, getStatus: false, listSessions: false, stopSession: false,
|
|
captureRuntimeId: false, installHooks: false, removeHooks: false,
|
|
},
|
|
detect: () => ({ available: true, adapter: ${JSON.stringify(name)} }),
|
|
configure: ({ repositoryRoot, areaId }) => ({
|
|
adapter: ${JSON.stringify(name)}, mutationPerformed: false,
|
|
mcp: { name: 'torch-' + areaId, command: process.execPath, args: [], cwd: repositoryRoot },
|
|
}),
|
|
createSession: ({ areaId, worktree, model, reasoning }) => ({
|
|
adapter: ${JSON.stringify(name)}, areaId, runtimeSessionId: null, requiresRuntimeIdCapture: false,
|
|
profile: { model, reasoning },
|
|
launch: { command: 'local-agent', args: [model, reasoning].filter(Boolean), cwd: worktree, mutatesRuntime: true },
|
|
}),
|
|
resumeSession: ({ areaId, runtimeSessionId, worktree, model, reasoning }) => ({
|
|
adapter: ${JSON.stringify(name)}, areaId, runtimeSessionId, profile: { model, reasoning },
|
|
launch: { command: 'local-agent', args: [model, reasoning].filter(Boolean), cwd: worktree, mutatesRuntime: true },
|
|
}),
|
|
sendOrSteer: () => ({ supported: false }), getStatus: () => ({ supported: false }),
|
|
listSessions: () => ({ supported: false }), stopSession: () => ({ supported: false }),
|
|
captureRuntimeId: () => ({ supported: false }), installHooks: () => ({ supported: false }),
|
|
removeHooks: () => ({ supported: false }),
|
|
});
|
|
`;
|
|
}
|
|
|
|
function environment(root) {
|
|
return {
|
|
...process.env,
|
|
XDG_CONFIG_HOME: join(root, 'config'),
|
|
XDG_DATA_HOME: join(root, 'data'),
|
|
};
|
|
}
|
|
|
|
function runCli(args, env, cwd) {
|
|
return spawnSync(process.execPath, [CLI, ...args], { cwd, env, encoding: 'utf8' });
|
|
}
|
|
|
|
function repository(root) {
|
|
execFileSync('git', ['init', '-b', 'main', root]);
|
|
execFileSync('git', ['-C', root, 'config', 'user.email', '[email protected]']);
|
|
execFileSync('git', ['-C', root, 'config', 'user.name', 'TORCH Test']);
|
|
writeFileSync(join(root, 'app.mjs'), 'export const application = true;\n');
|
|
execFileSync('git', ['-C', root, 'add', '.']);
|
|
execFileSync('git', ['-C', root, 'commit', '-m', 'fixture']);
|
|
return inspectRepository(root);
|
|
}
|
|
|
|
test('SCN-runtime-plugin-trust: user approval pins one module hash without executing it during review', () => {
|
|
const root = mkdtempSync(join(tmpdir(), 'torch-runtime-plugin-trust-'));
|
|
const env = environment(root);
|
|
const modulePath = join(root, 'portable-local.cjs');
|
|
const sentinel = join(root, 'plugin-loaded.txt');
|
|
writeFileSync(modulePath, pluginSource(PLUGIN_NAME, sentinel));
|
|
|
|
const preview = planRuntimePluginTrust({ name: PLUGIN_NAME, modulePath, env });
|
|
assert.equal(preview.mutationPerformed, false);
|
|
assert.equal(preview.executesPluginDuringPlan, false);
|
|
assert.equal(existsSync(sentinel), false, 'review must never evaluate plugin code');
|
|
assert.equal(existsSync(preview.trustStorePath), false);
|
|
|
|
const cliPreview = runCli([
|
|
'runtimes', 'trust', '--name', PLUGIN_NAME, '--module', modulePath, '--json',
|
|
], env, root);
|
|
assert.equal(cliPreview.status, 0, cliPreview.stderr || cliPreview.stdout);
|
|
let plannedTrust = JSON.parse(cliPreview.stdout);
|
|
assert.equal(plannedTrust.requiresApproval, true);
|
|
assert.equal(existsSync(sentinel), false);
|
|
assert.equal(existsSync(preview.trustStorePath), false, 'preview does not change user config');
|
|
|
|
writeFileSync(modulePath, `${pluginSource(PLUGIN_NAME, sentinel)}\n// changed after preview\n`);
|
|
const staleApproval = runCli([
|
|
'runtimes', 'trust', '--name', PLUGIN_NAME, '--module', modulePath,
|
|
'--yes', '--sha256', plannedTrust.sha256, '--json',
|
|
], env, root);
|
|
assert.equal(staleApproval.status, 2);
|
|
assert.equal(JSON.parse(staleApproval.stdout).error, 'RUNTIME_PLUGIN_REVIEW_STALE');
|
|
assert.equal(existsSync(preview.trustStorePath), false, 'a changed module cannot inherit prior approval');
|
|
const refreshedPreview = runCli([
|
|
'runtimes', 'trust', '--name', PLUGIN_NAME, '--module', modulePath, '--json',
|
|
], env, root);
|
|
plannedTrust = JSON.parse(refreshedPreview.stdout);
|
|
|
|
const trusted = runCli([
|
|
'runtimes', 'trust', '--name', PLUGIN_NAME, '--module', modulePath,
|
|
'--yes', '--sha256', plannedTrust.sha256, '--json',
|
|
], env, root);
|
|
assert.equal(trusted.status, 0, trusted.stderr || trusted.stdout);
|
|
assert.equal(JSON.parse(trusted.stdout).mutationPerformed, true);
|
|
assert.equal(existsSync(sentinel), false, 'trusting records code; it does not load or launch it');
|
|
const trustPath = JSON.parse(trusted.stdout).trustStorePath;
|
|
assert.equal(statSync(trustPath).mode & 0o777, 0o600);
|
|
|
|
const catalog = runCli(['runtimes', 'list', '--json'], env, root);
|
|
assert.equal(catalog.status, 0, catalog.stderr || catalog.stdout);
|
|
const list = JSON.parse(catalog.stdout);
|
|
assert.equal(list.executesPluginCode, false);
|
|
assert.equal(list.trust.plugins[0].status, 'trusted');
|
|
assert.equal(existsSync(sentinel), false, 'catalog inspection must not evaluate plugins');
|
|
|
|
const inspectionRegistry = createRuntimeAdapterRegistry({ env });
|
|
assert.equal(inspectionRegistry.has(PLUGIN_NAME), false, 'general registry inspection does not execute plugins');
|
|
assert.equal(inspectionRegistry.trustedPlugins[0].status, 'trusted');
|
|
assert.equal(existsSync(sentinel), false);
|
|
|
|
const registry = createRuntimeAdapterRegistry({ env, loadPlugins: true });
|
|
assert.equal(registry.has(PLUGIN_NAME), true);
|
|
assert.equal(registry.pluginErrors.length, 0);
|
|
assert.equal(existsSync(sentinel), true, 'only a registry load executes explicitly trusted code');
|
|
|
|
writeFileSync(modulePath, `${pluginSource(PLUGIN_NAME, sentinel)}\n// changed after approval\n`);
|
|
const changedRegistry = createRuntimeAdapterRegistry({ env });
|
|
assert.equal(changedRegistry.has(PLUGIN_NAME), false);
|
|
assert.equal(changedRegistry.pluginErrors[0].code, 'RUNTIME_PLUGIN_HASH_MISMATCH');
|
|
assert.equal(inspectRuntimePluginTrust({ env }).plugins[0].status, 'hash-mismatch');
|
|
|
|
const revokePreview = runCli(['runtimes', 'revoke', '--name', PLUGIN_NAME, '--json'], env, root);
|
|
assert.equal(revokePreview.status, 0, revokePreview.stderr || revokePreview.stdout);
|
|
assert.equal(JSON.parse(revokePreview.stdout).requiresApproval, true);
|
|
const plannedRevoke = JSON.parse(revokePreview.stdout);
|
|
const revoked = runCli([
|
|
'runtimes', 'revoke', '--name', PLUGIN_NAME, '--yes', '--sha256', plannedRevoke.sha256, '--json',
|
|
], env, root);
|
|
assert.equal(revoked.status, 0, revoked.stderr || revoked.stdout);
|
|
assert.equal(JSON.parse(revoked.stdout).disposition, 'revoked');
|
|
assert.equal(JSON.parse(revoked.stdout).sessionsStopped, false);
|
|
assert.deepEqual(inspectRuntimePluginTrust({ env }).plugins, []);
|
|
});
|
|
|
|
test('SCN-runtime-plugin-lifecycle: an explicitly trusted local adapter participates in mixed Fleet planning only', () => {
|
|
const root = mkdtempSync(join(tmpdir(), 'torch-runtime-plugin-lifecycle-'));
|
|
const env = environment(root);
|
|
const repositoryRoot = join(root, 'project');
|
|
const modulePath = join(root, 'portable-local.cjs');
|
|
writeFileSync(modulePath, pluginSource(PLUGIN_NAME, join(root, 'loaded.txt')));
|
|
const trustPlan = planRuntimePluginTrust({ name: PLUGIN_NAME, modulePath, env });
|
|
trustRuntimePlugin({
|
|
name: PLUGIN_NAME, modulePath, env, approved: true, expectedSha256: trustPlan.sha256,
|
|
});
|
|
|
|
const inspected = repository(repositoryRoot);
|
|
const proposal = proposeDomains({ repository: inspected, analysis: analyzeRepository(inspected) });
|
|
const worker = proposal.domains[0];
|
|
worker.runtime = 'claude';
|
|
worker.model = null;
|
|
worker.reasoning = null;
|
|
proposal.session_manager = { runtime: 'claude', reasoning: 'high' };
|
|
proposal.review = {
|
|
status: 'approved', reviewedAt: '2026-09-28T00:00:00Z', reviewedBy: 'fixture-owner', notes: [],
|
|
};
|
|
installProject({
|
|
repository: inspected, proposal, env, projectId: 'trusted-runtime-plugin',
|
|
runtimes: ['claude'],
|
|
});
|
|
execFileSync('git', ['-C', repositoryRoot, 'add', '.torch']);
|
|
execFileSync('git', ['-C', repositoryRoot, 'commit', '-m', 'install TORCH']);
|
|
const profileChange = runCli([
|
|
'profile', 'set', '--area', worker.id, '--runtime', PLUGIN_NAME,
|
|
'--model', 'local-provider/model-a', '--reasoning', 'high', '--yes', '--json',
|
|
], env, repositoryRoot);
|
|
assert.equal(profileChange.status, 0, profileChange.stderr || profileChange.stdout);
|
|
assert.equal(JSON.parse(profileChange.stdout).profile.runtime, PLUGIN_NAME);
|
|
const config = JSON.parse(readFileSync(join(repositoryRoot, '.torch', 'torch.yaml'), 'utf8'));
|
|
assert.deepEqual(config.runtimes[PLUGIN_NAME], { executable: 'local-agent' });
|
|
assert.equal(config.domains.find((domain) => domain.id === worker.id).runtime, PLUGIN_NAME);
|
|
execFileSync('git', ['-C', repositoryRoot, 'add', '.torch/torch.yaml']);
|
|
execFileSync('git', ['-C', repositoryRoot, 'commit', '-m', 'assign trusted local runtime']);
|
|
createWorktrees({
|
|
repository: inspectRepository(repositoryRoot),
|
|
parentOverride: join(root, 'worktrees'),
|
|
});
|
|
const control = openControlPlane({ repositoryRoot, env });
|
|
try {
|
|
const registry = createRuntimeAdapterRegistry({ env, loadPlugins: true });
|
|
const plan = planFleetUp({
|
|
repositoryRoot, controlPlane: control, adapters: registry.toMap(), fresh: true,
|
|
});
|
|
assert.equal(plan.canProceed, true, JSON.stringify(plan.blockers));
|
|
const action = plan.actions.find((entry) => entry.areaId === worker.id);
|
|
assert.equal(action.runtime, PLUGIN_NAME);
|
|
assert.deepEqual(action.profile, {
|
|
model: 'local-provider/model-a', reasoning: 'high', launchPolicy: {},
|
|
});
|
|
assert.equal(action.launch.command, 'local-agent');
|
|
assert.equal(plan.mutationPerformed, false);
|
|
assert.equal(plan.actions.every((entry) => entry.launch.mutatesRuntime), true);
|
|
assert.equal(existsSync(join(root, 'loaded.txt')), true);
|
|
} finally {
|
|
control.close();
|
|
}
|
|
|
|
unlinkSync(join(root, 'loaded.txt'));
|
|
const trustedDiagnosis = diagnoseProject({ repository: inspectRepository(repositoryRoot), env });
|
|
assert.equal(trustedDiagnosis.findings.some((finding) =>
|
|
finding.code === 'RUNTIME_ADAPTER_TRUSTED_NOT_LOADED' && finding.runtime === PLUGIN_NAME), true);
|
|
assert.equal(existsSync(join(root, 'loaded.txt')), false, 'doctor reports the user trust record without loading plugin code');
|
|
|
|
const untrustedRoot = mkdtempSync(join(tmpdir(), 'torch-runtime-plugin-untrusted-'));
|
|
const untrustedEnv = environment(untrustedRoot);
|
|
const untrustedRegistry = createRuntimeAdapterRegistry({ env: untrustedEnv });
|
|
assert.equal(untrustedRegistry.has(PLUGIN_NAME), false);
|
|
assert.equal(untrustedRegistry.pluginErrors.length, 0);
|
|
const diagnosis = diagnoseProject({ repository: inspectRepository(repositoryRoot), env: untrustedEnv });
|
|
assert.equal(diagnosis.findings.some((finding) =>
|
|
finding.code === 'RUNTIME_ADAPTER_UNKNOWN' && finding.runtime === PLUGIN_NAME), true,
|
|
'project runtime configuration cannot grant trust to a local plugin');
|
|
});
|