apt-mcp-agent-setup 3.0.8 → 3.2.5
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/bin/cli.js +1 -1
- package/bundle/NOTICES.md +0 -0
- package/bundle/core.enc +0 -0
- package/bundle/mcp-rules.enc +0 -0
- package/bundle/skills-aso.enc +0 -0
- package/bundle/skills-ba.enc +0 -0
- package/bundle/skills-base.enc +0 -0
- package/bundle/skills-be.enc +0 -0
- package/bundle/skills-design.enc +0 -0
- package/bundle/skills-fe.enc +0 -0
- package/bundle/skills-mobile.enc +0 -0
- package/bundle/skills-pm.enc +0 -0
- package/integrity-manifest.json +65 -53
- package/package.json +1 -1
- package/release-check.js +250 -33
- package/src/backends/browser-tools-0.0.82.json +375 -0
- package/src/backends/browser-tools.js +1 -0
- package/src/backends/index-command.js +1 -1
- package/src/backends/index-store.js +1 -1
- package/src/backends/profiles.js +1 -1
- package/src/core/aso-import.js +1 -0
- package/src/core/catalog.js +1 -1
- package/src/core/codex-host-metadata.js +1 -0
- package/src/core/context-metrics.js +1 -1
- package/src/core/doctor.js +1 -1
- package/src/core/execution.js +1 -1
- package/src/core/hook-bridge.js +1 -1
- package/src/core/host-adapters.js +1 -1
- package/src/core/memory-store.js +1 -1
- package/src/core/model-inventory.js +1 -0
- package/src/core/model-routing.js +1 -1
- package/src/core/npm-cli.js +1 -0
- package/src/core/owned-lock.js +1 -1
- package/src/core/presentations.js +1 -0
- package/src/core/quality.js +1 -0
- package/src/core/session.js +1 -1
- package/src/core/skill-names.js +1 -1
- package/src/core/workspace-path.js +1 -1
- package/src/core/xlsx-reader.py +62 -0
- package/src/installer/bounded-command.js +1 -1
- package/src/installer/global-setup.js +1 -1
- package/src/installer/host-hooks.js +1 -1
- package/src/installer/managed-config.js +1 -1
- package/src/installer/platform-config.js +1 -1
- package/src/installer/prerequisites.js +1 -1
- package/src/installer/presentations-requirements.txt +14 -0
- package/src/installer/presentations.js +1 -0
- package/src/installer/project-setup.js +1 -1
- package/src/installer/runtime-lock.js +1 -1
- package/src/installer/runtime-store.js +1 -1
- package/src/installer/setup-wizard.js +1 -1
- package/src/license/crypto.js +1 -1
- package/src/license/fingerprint.js +1 -1
- package/src/license/terms.js +1 -1
- package/src/license/verify.js +1 -1
- package/src/presets/index.js +1 -1
- package/src/proxy/backends.js +1 -1
- package/src/proxy/browser-sessions.js +1 -0
- package/src/proxy/core-tools.js +1 -1
- package/src/proxy/pipeline.js +1 -1
- package/src/proxy/router.config.js +1 -1
- package/src/proxy/router.js +1 -1
- package/src/proxy/server.js +1 -1
- package/src/templates/apt-runtime.md +2 -2
- package/src/templates/mcp-tools.md +14 -21
package/release-check.js
CHANGED
|
@@ -2,26 +2,42 @@
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const crypto = require('crypto');
|
|
5
|
-
const { spawn, execFileSync } = require('child_process');
|
|
5
|
+
const { spawn, spawnSync, execFileSync } = require('child_process');
|
|
6
6
|
const assert = require('assert/strict');
|
|
7
7
|
|
|
8
8
|
// Standalone: this file is also shipped as the signed dist prepublish gate.
|
|
9
|
-
async function handshake(installed, workspace, fixture, realBackends = false) {
|
|
10
|
-
const child = spawn(process.execPath, ['--require', fixture,
|
|
9
|
+
async function handshake(installed, workspace, fixture, realBackends = false, launcherArgs = [installed.launcherPath, '--path', workspace]) {
|
|
10
|
+
const child = spawn(process.execPath, ['--require', fixture, ...launcherArgs], {
|
|
11
11
|
windowsHide: true,
|
|
12
12
|
env: { ...process.env, APT_TEST_RELEASE: installed.path, APT_GATE_REAL: realBackends ? '1' : '0', MCP_SKIP_LICENSE_CHECK: '1', MCP_SKIP_INTEGRITY_CHECK: '0' },
|
|
13
13
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
14
14
|
});
|
|
15
15
|
let buffer = '', stderr = '', initialized = false, listed = false;
|
|
16
|
+
const checkBootstrappedWorkspace = message => {
|
|
17
|
+
const content = message.result?.content?.find(item => item.type === 'text');
|
|
18
|
+
assert.ok(content, 'Missing bootstrap result');
|
|
19
|
+
const actual = JSON.parse(content.text).workspace;
|
|
20
|
+
const identity = value => process.platform === 'win32' ? path.resolve(value).toLowerCase() : path.resolve(value);
|
|
21
|
+
assert.equal(identity(actual), identity(fs.realpathSync(workspace)), 'Launcher ignored its workspace binding');
|
|
22
|
+
};
|
|
16
23
|
const checks = realBackends ? [
|
|
17
|
-
['session_bootstrap', {sessionId:'package-gate'
|
|
24
|
+
['session_bootstrap', {sessionId:'package-gate'}, checkBootstrappedWorkspace],
|
|
25
|
+
['model_list', {sessionId:'package-gate'}, /sources/],
|
|
18
26
|
['code_search', {query:'callee'}, /callee/],
|
|
19
27
|
['find_callers', {symbol:'callee'}, /caller/],
|
|
20
28
|
['find_callees', {symbol:'caller'}, /callee/],
|
|
21
29
|
['explore_code', {query:'callee'}, /callee/],
|
|
22
30
|
['list_code_files', {pattern:'sample.py'}, /sample\.py/],
|
|
23
31
|
['impact_analysis', {changed_files:['sample.py'],repo_root:workspace}, /sample\.py/]
|
|
24
|
-
] : [];
|
|
32
|
+
] : [['session_bootstrap', {sessionId:'package-gate'}, checkBootstrappedWorkspace]];
|
|
33
|
+
checks.push(['route_request', {sessionId:'package-gate',requestId:'package-readonly-route',
|
|
34
|
+
prompt:'Read-only inspection of Node.js backend sample.js. This is not a web or mobile application. Do not edit source.',
|
|
35
|
+
stack:'nodejs',target:'sample.js'}, message => {
|
|
36
|
+
const result = JSON.parse(message.result.content.find(item => item.type === 'text').text);
|
|
37
|
+
assert.equal(result.pipeline, 'read-only', 'Negated edit instruction was routed as a change');
|
|
38
|
+
assert.equal(result.authorizesEdits, false);
|
|
39
|
+
assert.notEqual(result.workflow, 'implement');
|
|
40
|
+
}]);
|
|
25
41
|
let completed = 0;
|
|
26
42
|
const send = message => child.stdin.write(JSON.stringify({ jsonrpc: '2.0', ...message }) + '\n');
|
|
27
43
|
const next = () => {
|
|
@@ -49,14 +65,16 @@ async function handshake(installed, workspace, fixture, realBackends = false) {
|
|
|
49
65
|
send({ id: 2, method: 'tools/list', params: {} });
|
|
50
66
|
} else if (message.id === 2) {
|
|
51
67
|
assert.ok(initialized);
|
|
52
|
-
for (const name of ['session_bootstrap', 'route_request', 'pipeline_status']) {
|
|
68
|
+
for (const name of ['session_bootstrap', 'route_request', 'model_list', 'context_load', 'pipeline_status']) {
|
|
53
69
|
assert.ok(message.result?.tools?.some(tool => tool.name === name), `Missing ${name}: ${JSON.stringify(message)}`);
|
|
54
70
|
}
|
|
55
71
|
listed = true;
|
|
56
72
|
next();
|
|
57
73
|
} else if (message.id === 3 + completed && completed < checks.length) {
|
|
58
|
-
assert.ok(!message.error && !message.result?.isError, JSON.stringify(message));
|
|
59
|
-
|
|
74
|
+
assert.ok(!message.error && !message.result?.isError, `${JSON.stringify(message)}\n${stderr}`);
|
|
75
|
+
const check = checks[completed][2];
|
|
76
|
+
if (typeof check === 'function') check(message);
|
|
77
|
+
else assert.match(JSON.stringify(message.result), check);
|
|
60
78
|
completed++;
|
|
61
79
|
next();
|
|
62
80
|
}
|
|
@@ -70,17 +88,114 @@ async function handshake(installed, workspace, fixture, realBackends = false) {
|
|
|
70
88
|
});
|
|
71
89
|
send({ id: 1, method: 'initialize', params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'apt-package-gate', version: '1' } } });
|
|
72
90
|
});
|
|
73
|
-
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function verifyPackagedQualityGate(installed, temp) {
|
|
94
|
+
const { assessReport } = require(path.join(installed.path, 'src/core/quality.js'));
|
|
95
|
+
const flutterStart = { type: 'testStart', test: { id: 1, name: 'native behavior' } };
|
|
96
|
+
const flutterDone = { type: 'testDone', testID: 1, result: 'success', skipped: false, hidden: false };
|
|
97
|
+
const completed = { type: 'done', success: true };
|
|
98
|
+
const flutterResult = events => assessReport('flutter-machine', events.map(event => JSON.stringify(event)).join('\n'),
|
|
99
|
+
{ format: 'flutter-machine', testIds: ['native behavior'] }).status;
|
|
100
|
+
assert.equal(flutterResult([flutterStart, flutterDone, completed]), 'passed');
|
|
101
|
+
assert.equal(flutterResult([flutterStart, { ...flutterDone, skipped: true }, completed]), 'unverified');
|
|
102
|
+
assert.equal(flutterResult([flutterStart, { ...flutterDone, hidden: true }, completed]), 'unverified');
|
|
103
|
+
assert.equal(flutterResult([flutterStart, flutterDone]), 'unverified');
|
|
104
|
+
assert.notEqual(flutterResult([flutterStart, flutterDone, { ...completed, success: false }]), 'passed');
|
|
105
|
+
const { PipelineManager } = require(path.join(installed.path, 'src/proxy/pipeline.js'));
|
|
106
|
+
const workspace = path.join(temp, 'quality workspace');
|
|
107
|
+
fs.mkdirSync(workspace);
|
|
108
|
+
fs.writeFileSync(path.join(workspace, 'code.js'), 'exports.add = (a, b) => a + b;\n');
|
|
109
|
+
fs.writeFileSync(path.join(workspace, 'check.test.js'),
|
|
110
|
+
"const { test } = require('node:test'); const assert = require('node:assert/strict'); test('adds', () => assert.equal(require('./code').add(2, 3), 5));\n");
|
|
111
|
+
fs.writeFileSync(path.join(workspace, 'failing.test.js'),
|
|
112
|
+
"const { test } = require('node:test'); const assert = require('node:assert/strict'); test('adds', () => assert.equal(require('./code').add(2, 3), 6));\n");
|
|
113
|
+
fs.writeFileSync(path.join(workspace, 'package.json'), JSON.stringify({ scripts: {
|
|
114
|
+
test: 'node --test --test-reporter=tap check.test.js'
|
|
115
|
+
} }));
|
|
116
|
+
for (const skill of ['review', 'testing-patterns', 'tdd-loop', 'code-review-checklist']) {
|
|
117
|
+
const folder = path.join(workspace, '.agents', 'skills', skill);
|
|
118
|
+
fs.mkdirSync(folder, { recursive: true });
|
|
119
|
+
fs.writeFileSync(path.join(folder, 'SKILL.md'), skill);
|
|
120
|
+
}
|
|
121
|
+
const manager = new PipelineManager(workspace);
|
|
122
|
+
manager.env = { ...process.env }; delete manager.env.NODE_TEST_CONTEXT;
|
|
123
|
+
const plan = { criteria: [{ id: 'adds', expectedOutcome: 'add(2,3) is 5', source: 'user-request', checkIds: ['add-test'] }],
|
|
124
|
+
checks: [{ id: 'add-test', criterionIds: ['adds'], format: 'node-tap', testIds: ['adds'], required: true }],
|
|
125
|
+
reviews: [{ id: 'technical', required: true }] };
|
|
126
|
+
const start = options => manager.start({ task: options.task, pipeline: 'hotfix', sessionId: 'package-quality',
|
|
127
|
+
acceptanceCriteria: ['add(2,3) is 5'], workspace: { mode: 'current', confirmed: true }, ...options });
|
|
128
|
+
const routed = start({ task: 'web-quality', productImpact: true, productTarget: 'web' });
|
|
129
|
+
assert.equal(routed.success, true, JSON.stringify(routed));
|
|
130
|
+
const routedState = manager.readRun('web-quality', routed.runId);
|
|
131
|
+
assert.deepEqual(routedState.quality.plan.platforms, [{ id: 'web', checkIds: ['platform-web'] }]);
|
|
132
|
+
assert.ok(routedState.quality.plan.reviews.some(item => item.id === 'product'));
|
|
133
|
+
assert.equal(start({ task: 'web-bypass', qualityPlan: plan, productImpact: true, productTarget: 'web' }).code, 'INVALID_QUALITY_PLAN');
|
|
134
|
+
const lowered = start({ task: 'web-lowered', qualityPlan: plan, productImpact: false });
|
|
135
|
+
assert.equal(lowered.code, 'ACCEPTANCE_ALREADY_ACTIVE');
|
|
136
|
+
assert.equal(lowered.task, 'web-quality');
|
|
137
|
+
assert.equal(manager.reset({ task: 'web-quality', runId: routed.runId, confirm: true }).success, true);
|
|
138
|
+
const started = start({ task: 'functional-quality', qualityPlan: plan });
|
|
139
|
+
assert.equal(started.success, true, JSON.stringify(started));
|
|
140
|
+
const base = { task: 'functional-quality', runId: started.runId, sessionId: 'package-quality' };
|
|
141
|
+
let serial = 0;
|
|
142
|
+
const next = () => manager.next({ ...base, requestId: `package-${++serial}` });
|
|
143
|
+
const checkpoint = (attempt, extra = {}) => manager.checkpoint({ ...base, attemptId: attempt.attemptId,
|
|
144
|
+
actorId: attempt.actorId, step: attempt.step, status: 'completed', ...extra });
|
|
145
|
+
assert.equal(checkpoint(next()).success, true);
|
|
146
|
+
const writer = next();
|
|
147
|
+
const unrelated = manager.recordVerification({ ...base, attemptId: writer.attemptId, actorId: writer.actorId,
|
|
148
|
+
command: [process.execPath, '-e', 'process.exit(0)'], checkId: 'add-test' });
|
|
149
|
+
assert.equal(unrelated.evidence.quality.status, 'unverified');
|
|
150
|
+
assert.equal(checkpoint(writer, { evidenceIds: [unrelated.evidence.id] }).code, 'QUALITY_CHECK_REQUIRED');
|
|
151
|
+
const command = [process.execPath, '--test', '--test-reporter=tap', 'check.test.js'];
|
|
152
|
+
const failed = manager.recordVerification({ ...base, attemptId: writer.attemptId, actorId: writer.actorId,
|
|
153
|
+
command: [process.execPath, '--test', '--test-reporter=tap', 'failing.test.js'], checkId: 'add-test', reportFormat: 'node-tap' });
|
|
154
|
+
assert.equal(failed.evidence.quality.status, 'failed');
|
|
155
|
+
const passed = manager.recordVerification({ ...base, attemptId: writer.attemptId, actorId: writer.actorId,
|
|
156
|
+
command: ['npm', 'test'], checkId: 'add-test', reportFormat: 'node-tap' });
|
|
157
|
+
assert.equal(passed.evidence.quality.status, 'passed');
|
|
158
|
+
assert.deepEqual(passed.evidence.command, ['npm', 'test']);
|
|
159
|
+
if (process.platform === 'win32') assert.match(passed.evidence.executedCommand[1], /npm-cli\.js$/);
|
|
160
|
+
assert.equal(checkpoint(writer, { evidenceIds: [passed.evidence.id] }).success, true);
|
|
161
|
+
const reviewer = next();
|
|
162
|
+
const legacy = manager.checkpoint({ ...base, attemptId: reviewer.attemptId, actorId: reviewer.actorId,
|
|
163
|
+
action: 'review', review: { stage: 'quality', verdict: 'pass', reviewedScope: ['code.js'], findings: [] } });
|
|
164
|
+
assert.equal(legacy.code, 'REVIEW_STAGE_NOT_REQUIRED');
|
|
165
|
+
assert.deepEqual(legacy.requiredStages, ['technical']);
|
|
166
|
+
const review = verdict => manager.checkpoint({ ...base, attemptId: reviewer.attemptId, actorId: reviewer.actorId,
|
|
167
|
+
action: 'review', review: { stage: 'technical', verdict, reviewedScope: ['code.js'],
|
|
168
|
+
findings: verdict === 'fail' ? [{ id: 'regression', required: true, severity: 'high', message: 'Review finding remains open' }] : [] } });
|
|
169
|
+
const optional = manager.checkpoint({ ...base, attemptId: reviewer.attemptId, actorId: reviewer.actorId,
|
|
170
|
+
action: 'review', review: { stage: 'technical', verdict: 'pass', reviewedScope: ['code.js'],
|
|
171
|
+
findings: [{ id: 'regression', required: false, severity: 'low', message: 'Optional observation before escalation' }] } });
|
|
172
|
+
assert.equal(optional.success, true);
|
|
173
|
+
const defaultSeverity = manager.checkpoint({ ...base, attemptId: reviewer.attemptId, actorId: reviewer.actorId,
|
|
174
|
+
action: 'review', review: { stage: 'technical', verdict: 'pass', reviewedScope: ['code.js'],
|
|
175
|
+
findings: [{ id: 'regression', required: false, message: 'Implicit normal severity before escalation' }] } });
|
|
176
|
+
assert.equal(defaultSeverity.success, true);
|
|
177
|
+
assert.equal(manager.readRun(base.task, base.runId).quality.findings.regression.severity, 'normal');
|
|
178
|
+
const rejected = review('fail');
|
|
179
|
+
assert.equal(rejected.success, true, JSON.stringify({ reviewer, rejected }));
|
|
180
|
+
const escalated = manager.readRun(base.task, base.runId).quality.findings.regression;
|
|
181
|
+
assert.equal(escalated.required, true, 'A later required finding must promote the existing stable ID');
|
|
182
|
+
assert.equal(escalated.severity, 'high');
|
|
183
|
+
assert.equal(checkpoint(reviewer, { evidenceIds: [rejected.evidence.id] }).code, 'REVIEW_REQUIRED');
|
|
184
|
+
const omitted = review('pass');
|
|
185
|
+
assert.equal(omitted.success, true);
|
|
186
|
+
assert.equal(checkpoint(reviewer, { evidenceIds: [omitted.evidence.id] }).code, 'OPEN_FINDINGS');
|
|
187
|
+
}
|
|
74
188
|
|
|
75
189
|
async function main() {
|
|
76
190
|
const args = process.argv.slice(2);
|
|
77
191
|
const value = name => { const i = args.indexOf(name); return i < 0 ? null : args[i + 1]; };
|
|
78
|
-
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'apt-package-gate-'));
|
|
79
|
-
const root = path.resolve(__dirname, '..');
|
|
80
|
-
const keep = args.includes('--keep');
|
|
81
|
-
try {
|
|
82
|
-
let dist = value('--dist'), publicKey;
|
|
83
|
-
|
|
192
|
+
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'apt-package-gate-'));
|
|
193
|
+
const root = path.resolve(__dirname, '..');
|
|
194
|
+
const keep = args.includes('--keep');
|
|
195
|
+
try {
|
|
196
|
+
let dist = value('--dist'), publicKey;
|
|
197
|
+
const archive = value('--package') ? path.resolve(value('--package')) : null;
|
|
198
|
+
if (!dist && !archive) {
|
|
84
199
|
const build = path.join(temp, 'build'); fs.mkdirSync(build);
|
|
85
200
|
for (const item of ['src', 'bin', 'scripts', '.agents', 'docs', 'test', 'package.json', 'package-lock.json', 'README.md', 'LICENSE', '.npmignore']) {
|
|
86
201
|
fs.cpSync(path.join(root, item), path.join(build, item), { recursive: true });
|
|
@@ -97,21 +212,118 @@ async function main() {
|
|
|
97
212
|
finally { fs.closeSync(log); }
|
|
98
213
|
dist = path.join(build, 'dist');
|
|
99
214
|
execFileSync(process.execPath, ['-r', './test/setup.js', '--test', 'test/installer/runtime-store.test.js', 'test/proxy/transport-reliability.test.js'], { cwd: build, windowsHide: true, stdio: 'inherit' });
|
|
100
|
-
} else {
|
|
101
|
-
dist = path.resolve(dist);
|
|
102
|
-
publicKey = value('--public-key') ? fs.readFileSync(value('--public-key'), 'utf8') : require(path.join(dist, 'src/license/verify.js')).PRODUCTION_PUBLIC_KEY;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
215
|
+
} else if (dist) {
|
|
216
|
+
dist = path.resolve(dist);
|
|
217
|
+
publicKey = value('--public-key') ? fs.readFileSync(value('--public-key'), 'utf8') : require(path.join(dist, 'src/license/verify.js')).PRODUCTION_PUBLIC_KEY;
|
|
218
|
+
} else {
|
|
219
|
+
assert.ok(fs.existsSync(archive), `Package archive is missing: ${archive}`);
|
|
220
|
+
publicKey = value('--public-key') ? fs.readFileSync(value('--public-key'), 'utf8') : null;
|
|
221
|
+
}
|
|
222
|
+
let packed;
|
|
223
|
+
if (archive) packed = [{ filename: path.basename(archive) }];
|
|
224
|
+
else {
|
|
225
|
+
const npm = require(path.join(dist, 'src/core/npm-cli.js'))();
|
|
226
|
+
packed = JSON.parse(execFileSync(process.execPath, [npm, 'pack', '--ignore-scripts', '--json', '--pack-destination', temp], { cwd: dist, windowsHide: true, encoding: 'utf8' }));
|
|
227
|
+
}
|
|
228
|
+
execFileSync('tar', ['-xzf', archive || path.join(temp, packed[0].filename), '-C', temp], { windowsHide: true });
|
|
229
|
+
const pkg = path.join(temp, 'package');
|
|
230
|
+
if (!publicKey) publicKey = require(path.join(pkg, 'src/license/verify.js')).PRODUCTION_PUBLIC_KEY;
|
|
231
|
+
const installer = require(path.join(pkg, 'src/installer/runtime-store.js'));
|
|
232
|
+
const hostConfig = require(path.join(pkg, 'src/installer/platform-config.js'));
|
|
233
|
+
const runtimeRoot = path.join(temp, 'runtime tiếng Việt');
|
|
234
|
+
const installed = installer.installRuntime(pkg, { root: runtimeRoot, publicKey });
|
|
235
|
+
verifyPackagedQualityGate(installed, temp);
|
|
236
|
+
const globalConfig = path.join(temp, 'antigravity-global.json');
|
|
237
|
+
const workspaceA = path.join(temp, 'workspace A');
|
|
238
|
+
const workspaceB = path.join(temp, 'workspace B');
|
|
239
|
+
fs.mkdirSync(workspaceA); fs.mkdirSync(workspaceB);
|
|
240
|
+
hostConfig.deployProjectConfig('claude', workspaceA, installed.launcherPath);
|
|
241
|
+
const claudeServer = JSON.parse(fs.readFileSync(path.join(workspaceA, '.mcp.json'), 'utf8')).mcpServers['apt-mcp-agent'];
|
|
242
|
+
assert.equal(claudeServer.command, 'node');
|
|
243
|
+
assert.deepEqual(claudeServer.args, [installed.launcherPath.replace(/\\/g, '/'), '--path', workspaceA.replace(/\\/g, '/')]);
|
|
244
|
+
const { buildHookConfig } = require(path.join(pkg, 'src/core/host-adapters.js'));
|
|
245
|
+
const claudeHooks = buildHookConfig('claude-code', path.join(pkg, 'src/core/hook-bridge.js'), workspaceA).config.hooks;
|
|
246
|
+
for (const event of ['SessionStart', 'PreToolUse']) {
|
|
247
|
+
const handler = claudeHooks[event][0].hooks[0];
|
|
248
|
+
assert.equal(handler.command, 'node');
|
|
249
|
+
assert.deepEqual(handler.args.slice(0, 1), ['-e']);
|
|
250
|
+
const input = { session_id: 'claude-package-gate', cwd: workspaceA, permission_mode: 'default',
|
|
251
|
+
...(event === 'PreToolUse' ? { tool_name: 'Read', tool_input: { file_path: path.join(workspaceA, 'sample.js') } } : {}) };
|
|
252
|
+
const result = spawnSync(handler.command, handler.args, { input: JSON.stringify(input), encoding: 'utf8', timeout: 10000, windowsHide: true });
|
|
253
|
+
assert.equal(result.status, 0, `${event}: ${result.stderr}`);
|
|
254
|
+
assert.ok(JSON.parse(result.stdout));
|
|
255
|
+
}
|
|
256
|
+
const { SessionRuntime } = require(path.join(pkg, 'src/core/session.js'));
|
|
257
|
+
const claudeSession = new SessionRuntime(workspaceA).status({ sessionId: 'claude-package-gate' });
|
|
258
|
+
assert.deepEqual(claudeSession.hookObservations['claude-code'].events.sort(), ['PreToolUse', 'SessionStart']);
|
|
259
|
+
fs.writeFileSync(globalConfig, JSON.stringify({ mcpServers: { custom: { command: 'custom' } } }));
|
|
260
|
+
for (const workspacePath of [workspaceA, workspaceB, workspaceA]) {
|
|
261
|
+
hostConfig.deployProjectConfig('antigravity', workspacePath, installed.launcherPath);
|
|
262
|
+
hostConfig.configureIDE('antigravity', installed.launcherPath, { configPathOverride: globalConfig, projectPath: workspacePath });
|
|
263
|
+
}
|
|
264
|
+
const { workspaceServerAlias } = require(path.join(pkg, 'src/core/workspace-path.js'));
|
|
265
|
+
const aliases = [workspaceA, workspaceB].map(workspaceServerAlias);
|
|
266
|
+
const globalServers = JSON.parse(fs.readFileSync(globalConfig, 'utf8')).mcpServers;
|
|
267
|
+
assert.notEqual(aliases[0], aliases[1]);
|
|
268
|
+
assert.equal(globalServers.custom.command, 'custom');
|
|
269
|
+
const packageHook = require(path.join(pkg, 'src/core/hook-bridge.js'));
|
|
270
|
+
const packageLicense = require(path.join(pkg, 'src/license/verify.js'));
|
|
271
|
+
const savedPublicKey = packageLicense.PRODUCTION_PUBLIC_KEY;
|
|
272
|
+
packageLicense.PRODUCTION_PUBLIC_KEY = publicKey;
|
|
273
|
+
try {
|
|
274
|
+
const signedCli = path.join(installed.path, 'bin/cli.js').replace(/\\/g, '/');
|
|
275
|
+
assert.equal(packageHook.isWriteTool('run_command', { command: `node "${signedCli}" doctor --project .` }, workspaceA), false);
|
|
276
|
+
assert.equal(packageHook.isWriteTool('run_command', { command: `node "${signedCli}" doctor --project . 2>&1` }, workspaceA), false);
|
|
277
|
+
assert.equal(packageHook.isWriteTool('run_command', { command: `node "${signedCli}" doctor --project . 2>&1 > sample.js` }, workspaceA), true);
|
|
278
|
+
assert.equal(packageHook.isWriteTool('run_command', { command: `node "${signedCli}" doctor --project . 2>&1; echo unsafe` }, workspaceA), true);
|
|
279
|
+
assert.equal(packageHook.isWriteTool('run_command', { command: 'npm test 2>&1' }, workspaceA), false);
|
|
280
|
+
assert.equal(packageHook.isWriteTool('run_command', { command: 'git diff 2>&1' }, workspaceA), false);
|
|
281
|
+
assert.equal(packageHook.isWriteTool('run_command', { command: 'npm test 2>&1 > sample.js' }, workspaceA), true);
|
|
282
|
+
assert.equal(packageHook.isWriteTool('run_command', { command: `node "${signedCli}" update --project .` }, workspaceA), true);
|
|
283
|
+
assert.equal(packageHook.isWriteTool('run_command', { command: `node "${signedCli}" doctor --project .; echo unsafe` }, workspaceA), true);
|
|
284
|
+
} finally { packageLicense.PRODUCTION_PUBLIC_KEY = savedPublicKey; }
|
|
285
|
+
for (const [index, workspacePath] of [workspaceA, workspaceB].entries()) {
|
|
286
|
+
assert.deepEqual(globalServers[aliases[index]].args, [installed.launcherPath.replace(/\\/g, '/'), '--path', workspacePath.replace(/\\/g, '/')]);
|
|
287
|
+
const localServers = JSON.parse(fs.readFileSync(path.join(workspacePath, '.agents/mcp_config.json'), 'utf8')).mcpServers;
|
|
288
|
+
assert.ok(localServers[aliases[index]]);
|
|
289
|
+
assert.equal(localServers['apt-mcp-agent'], undefined);
|
|
290
|
+
}
|
|
291
|
+
const metadataWorkspace = path.join(temp, 'codex metadata workspace');
|
|
292
|
+
fs.mkdirSync(metadataWorkspace);
|
|
293
|
+
const metadataScript = path.join(temp, 'codex-metadata-fixture.js');
|
|
294
|
+
const metadataSession = '01a0e102-b189-7b70-bde8-f93a55ac6594';
|
|
295
|
+
const metadataAttempt = 'd5ac530c-8201-423e-b7c9-f2203132ee71';
|
|
296
|
+
const metadataName = `apt_${metadataAttempt.replace(/-/g, '')}`;
|
|
297
|
+
const metadataHome = path.join(temp, 'codex-home');
|
|
298
|
+
const metadataSessions = path.join(metadataHome, 'sessions', '2026', '09', '27');
|
|
299
|
+
fs.mkdirSync(metadataSessions, { recursive: true });
|
|
300
|
+
const metadataChildId = '01a0e103-b7d1-7650-a63b-fe2290282b4e';
|
|
301
|
+
const metadataRollout = path.join(metadataSessions, `rollout-2026-09-27T10-58-32-${metadataChildId}.jsonl`);
|
|
302
|
+
const metadataNow = Math.floor(Date.now() / 1000);
|
|
303
|
+
fs.writeFileSync(metadataRollout, JSON.stringify({ type: 'turn_context', timestamp: new Date().toISOString(),
|
|
304
|
+
payload: { model: 'gpt-6-luna', effort: 'low' } }) + '\n');
|
|
305
|
+
const metadataThread = { id: metadataChildId, model: 'stale-thread-model', reasoningEffort: 'high', path: metadataRollout,
|
|
306
|
+
parentThreadId: metadataSession, cwd: metadataWorkspace, createdAt: metadataNow,
|
|
307
|
+
source: { subAgent: { thread_spawn: { parent_thread_id: metadataSession,
|
|
308
|
+
agent_path: `/root/${metadataName}` } } } };
|
|
309
|
+
fs.writeFileSync(metadataScript, `const readline = require('readline');
|
|
310
|
+
readline.createInterface({ input: process.stdin }).on('line', line => {
|
|
311
|
+
const message = JSON.parse(line);
|
|
312
|
+
if (message.method === 'initialize') process.stdout.write(JSON.stringify({ id: message.id, result: {} }) + '\\n');
|
|
313
|
+
if (message.method === 'thread/list') process.stdout.write(JSON.stringify({ id: message.id,
|
|
314
|
+
result: { data: [${JSON.stringify(metadataThread)}], nextCursor: null } }) + '\\n');
|
|
315
|
+
if (message.method === 'thread/turns/list') process.stdout.write(JSON.stringify({ id: message.id,
|
|
316
|
+
result: { data: [{ status: 'completed', startedAt: ${metadataNow}, completedAt: ${metadataNow + 2} }], nextCursor: null } }) + '\\n');
|
|
317
|
+
});`);
|
|
318
|
+
const metadata = require(path.join(installed.path, 'src/core/codex-host-metadata.js'));
|
|
319
|
+
assert.deepEqual(await metadata.verifyWorker({ workspace: metadataWorkspace, sessionId: metadataSession,
|
|
320
|
+
attemptId: metadataAttempt, nativeTaskName: metadataName, reservedAt: new Date().toISOString() },
|
|
321
|
+
{ command: process.execPath, args: [metadataScript], timeoutMs: 3000, codexHome: metadataHome }),
|
|
322
|
+
{ hostWorkerId: metadataChildId, model: 'gpt-6-luna', reasoningEffort: 'low', source: 'codex-app-server+rollout' });
|
|
111
323
|
fs.writeFileSync(path.join(runtimeRoot, 'updates.json'), JSON.stringify({ checkedAt: Date.now() }));
|
|
112
|
-
const workspace = path.join(temp, 'workspace thử nghiệm'); fs.mkdirSync(workspace);
|
|
324
|
+
const workspace = path.join(temp, 'workspace thử nghiệm'); fs.mkdirSync(path.join(workspace, '.agents'), { recursive: true });
|
|
113
325
|
const fixture = path.join(temp, 'backend-fixture.cjs');
|
|
114
|
-
fs.writeFileSync(fixture, `const path = require('path'), Module = require('module');
|
|
326
|
+
fs.writeFileSync(fixture, `const path = require('path'), Module = require('module');
|
|
115
327
|
const root = process.env.APT_TEST_RELEASE, load = Module._load;
|
|
116
328
|
Module._load = function(id, parent, isMain) {
|
|
117
329
|
const result = load.apply(this, arguments);
|
|
@@ -119,12 +331,16 @@ async function main() {
|
|
|
119
331
|
if (file === path.join(root, 'src/proxy/backends.js')) {
|
|
120
332
|
const original = result.spawnBackends;
|
|
121
333
|
result.spawnBackends = workspace => process.env.APT_GATE_REAL === '1'
|
|
122
|
-
? Object.fromEntries(Object.entries(original(workspace)).filter(([name]) => ['codegraph','code-review-graph'].includes(name))) : {};
|
|
334
|
+
? Object.fromEntries(Object.entries(original(workspace)).filter(([name]) => ['codegraph','code-review-graph','server-memory'].includes(name))) : {};
|
|
123
335
|
}
|
|
124
336
|
if (file === path.join(root, 'src/proxy/server.js')) result.ProxyServer.prototype.checkLatestVersion = async () => {};
|
|
125
337
|
return result;
|
|
126
|
-
};`);
|
|
127
|
-
|
|
338
|
+
};`);
|
|
339
|
+
for (const [index, workspacePath] of [workspaceA, workspaceB].entries()) {
|
|
340
|
+
await handshake(installed, workspacePath, fixture, false, globalServers[aliases[index]].args);
|
|
341
|
+
}
|
|
342
|
+
await handshake(installed, workspaceA, fixture, false, claudeServer.args);
|
|
343
|
+
await handshake(installed, workspace, fixture);
|
|
128
344
|
const good = fs.readFileSync(installed.launcherPath);
|
|
129
345
|
for (const old of ['_0x5e52();', "require('./server.js');"]) {
|
|
130
346
|
fs.writeFileSync(installed.launcherPath, old);
|
|
@@ -155,7 +371,8 @@ async function main() {
|
|
|
155
371
|
assert.throws(() => process.kill(pid, 0), {code:'ESRCH'});
|
|
156
372
|
fs.utimesSync(nativeLock, new Date(0), new Date(0));
|
|
157
373
|
}
|
|
158
|
-
|
|
374
|
+
const reviewCache = path.join(realWorkspace, '.agents/.data/graph-2.3.9');
|
|
375
|
+
if (cycle === 1) fs.writeFileSync(path.join(reviewCache, 'graph.db'), 'package recovery fixture');
|
|
159
376
|
execFileSync(process.execPath, [indexCli, realWorkspace], { windowsHide:true, stdio:'inherit', timeout:120000 });
|
|
160
377
|
const report = JSON.parse(fs.readFileSync(reportFile, 'utf8'));
|
|
161
378
|
assert.equal(report.status, 'built');
|
|
@@ -168,7 +385,7 @@ async function main() {
|
|
|
168
385
|
}
|
|
169
386
|
await handshake(installed, realWorkspace, fixture, true);
|
|
170
387
|
}
|
|
171
|
-
console.log(`Package gate passed: ${packed[0].filename}; fresh install + two repairs, initialize/tools/list/EOF, signature verification enabled.`);
|
|
388
|
+
console.log(`Package gate passed: ${packed[0].filename}; quality policy, fresh install + two repairs, initialize/tools/list/EOF, signature verification enabled.`);
|
|
172
389
|
} finally {
|
|
173
390
|
if (keep) console.log(`Validation artifacts: ${temp}`);
|
|
174
391
|
else fs.rmSync(temp, { recursive: true, force: true });
|