@sanqianx/project-knowledge 5.0.16-rc.1 → 5.0.16-rc.2
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/.claude-plugin/marketplace.json +1 -1
- package/README.md +5 -0
- package/_modules/knowledge-engine/README.md +2 -2
- package/_modules/knowledge-engine/package.json +1 -1
- package/_modules/knowledge-engine/src/retrieval.js +119 -24
- package/_modules/knowledge-engine/src/runtime/knowledge-promotion.js +80 -24
- package/_modules/knowledge-engine/src/runtime/knowledge-retrieval-service.js +8 -6
- package/_modules/knowledge-engine/src/runtime/markdown-knowledge-indexer.js +105 -64
- package/_site/_test/agent-capture-mcp-ui-test.js +67 -0
- package/_site/_test/agent-capture-ui-test.js +2 -1
- package/_site/_test/integration-adapters-test.js +7 -1
- package/_site/_test/integration-runtime-binding-a2-test.js +566 -0
- package/_site/_test/integration-runtime-binding-a3-test.js +227 -0
- package/_site/_test/integration-runtime-binding-test.js +473 -0
- package/_site/_test/native-context-integrations-test.js +1 -1
- package/_site/_test/release-version-sync-test.js +12 -1
- package/_site/lib/integration-installer.js +325 -8
- package/_site/lib/module-bridge.js +6 -2
- package/_site/scripts/sync-product-content.js +1 -1
- package/_site/scripts/sync-release-version.js +9 -5
- package/_site/scripts/verify-product-runtime.js +1 -1
- package/docs/leader-glm-workbench-playbook.md +87 -0
- package/docs/release-5.0.16-acceptance.md +53 -0
- package/module-runtime-manifest.json +29 -28
- package/package.json +5 -3
- package/plugins/project-knowledge/.claude-plugin/plugin.json +1 -1
- package/plugins/project-knowledge/.codex-plugin/plugin.json +1 -1
- package/plugins/project-knowledge/.mcp.json +7 -6
- package/plugins/project-knowledge/scripts/mcp-launcher.cjs +78 -0
- package/ui/product.js +3 -1
- package/vendor-manifest.json +8 -8
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const os = require('node:os');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const http = require('node:http');
|
|
7
|
+
const { createRequire } = require('node:module');
|
|
8
|
+
const coreRoot = path.resolve(process.env.KB_CAPTURE_TEST_CORE_ROOT || path.join(__dirname, '../..'));
|
|
9
|
+
const fromCore = createRequire(path.join(coreRoot, 'package.json'));
|
|
10
|
+
const { createRuntime, initializeRuntime, createRequestHandler } = fromCore('./_site/lib/server-app');
|
|
11
|
+
const { BridgeAdapter } = fromCore('./_site/lib/bridge-adapter');
|
|
12
|
+
const { ModuleBridge } = fromCore('./_site/lib/module-bridge');
|
|
13
|
+
const { findChrome } = require('./helpers/find-chrome');
|
|
14
|
+
const { launchCdpBrowser, waitFor } = require('./helpers/cdp-browser');
|
|
15
|
+
|
|
16
|
+
const HOSTILE_DETAIL = '<img src=x onerror="window.__kbXssProbe=1"> INJECT-STATUS-MARKER sk-abcdef123456';
|
|
17
|
+
|
|
18
|
+
function clientFixture(client, label) {
|
|
19
|
+
const mcpCapable = client === 'claude' || client === 'codex';
|
|
20
|
+
return {
|
|
21
|
+
client, label, available: false, configFound: false,
|
|
22
|
+
knowledgeIntegration: { ok: true, state: 'installed' },
|
|
23
|
+
automaticContext: { supported: client !== 'opencode', configured: false, state: 'check-failed', detail: HOSTILE_DETAIL },
|
|
24
|
+
developmentCapture: { ok: true, state: 'installed', installed: true, detail: HOSTILE_DETAIL },
|
|
25
|
+
mcpStartup: mcpCapable ? { state: 'failed', checkedAt: new Date().toISOString(), scope: 'client:' + client, code: 'PROBE_TIMEOUT', expectedVersion: '9.9.9-test', detail: HOSTILE_DETAIL } : undefined,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function main() {
|
|
30
|
+
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'kb-agent-mcp-ui-'));
|
|
31
|
+
const dataDir = path.join(temp, 'data');
|
|
32
|
+
const homeDir = path.join(temp, 'home');
|
|
33
|
+
const eventsHome = path.join(dataDir, 'events');
|
|
34
|
+
const offline = http.createServer((req, res) => { res.writeHead(503, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: false, error: { message: 'offline stub' } })); });
|
|
35
|
+
await new Promise(resolve => offline.listen(0, '127.0.0.1', resolve));
|
|
36
|
+
const offlineUrl = 'http://127.0.0.1:' + offline.address().port;
|
|
37
|
+
const runtime = createRuntime({ dataDir, analysisOwner: 'engine', bridgeAdapter: new BridgeAdapter({ dataDir, bridgeHomeDir: eventsHome }), moduleBridge: new ModuleBridge({ terminalUrl: offlineUrl, engineUrl: offlineUrl, eventBridgeUrl: offlineUrl, vectorHubUrl: offlineUrl }) });
|
|
38
|
+
await initializeRuntime(runtime);
|
|
39
|
+
const server = http.createServer(createRequestHandler(runtime, { integrationOptions: { homeDir, env: {}, commandResolver: () => '' } }));
|
|
40
|
+
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
|
41
|
+
const base = 'http://127.0.0.1:' + server.address().port;
|
|
42
|
+
const payload = JSON.stringify({ bridge: { ok: true }, scan: { state: 'ready' }, clients: [clientFixture('claude', 'Claude Code'), clientFixture('codex', 'Codex'), clientFixture('zcode', 'ZCode'), clientFixture('opencode', 'OpenCode')] });
|
|
43
|
+
let browser;
|
|
44
|
+
try {
|
|
45
|
+
browser = await launchCdpBrowser({ chrome: findChrome(), profileDir: path.join(temp, 'browser'), url: base, width: 1440, height: 900 });
|
|
46
|
+
const run = expression => browser.evaluate(expression);
|
|
47
|
+
const click = selector => run('document.querySelector(' + JSON.stringify(selector) + ').click()');
|
|
48
|
+
await run('(() => { const original = window.fetch; window.__originalFetch = original; window.fetch = (input, init) => { const url = String(input && input.url || input); if (url.includes(\'/api/integrations/status\')) { return Promise.resolve({ ok: true, json: async () => (' + payload + ') }); } return original(input, init); }; })()');
|
|
49
|
+
await click('#openSystem');
|
|
50
|
+
await waitFor(() => run("document.querySelectorAll('#agentCaptureList .agent-capture-row').length === 4"), 'capture rows render');
|
|
51
|
+
assert(await run("document.getElementById('agentCaptureList').textContent.includes('PROBE_TIMEOUT')"), 'the MCP startup failure code must be rendered for MCP-capable clients');
|
|
52
|
+
const claudeRowHtml = await run("document.querySelector('#agentCaptureList .agent-capture-row').innerHTML");
|
|
53
|
+
assert(claudeRowHtml.includes('<img'), 'hostile mcpStartup detail must be HTML-escaped, got: ' + claudeRowHtml.slice(0, 300));
|
|
54
|
+
assert(!claudeRowHtml.includes('<img'), 'no raw hostile markup may reach the DOM');
|
|
55
|
+
assert.equal(await run('window.__kbXssProbe'), undefined, 'the hostile onerror must never execute');
|
|
56
|
+
const smalls = await run("[...document.querySelectorAll('#agentCaptureList small')].map(el => el.textContent).join('|')");
|
|
57
|
+
assert(smalls.split('|').some(text => text.startsWith('MCP')), 'the MCP diagnostic is its own line item, got: ' + smalls);
|
|
58
|
+
assert.equal(browser.exceptions.length, 0, JSON.stringify(browser.exceptions));
|
|
59
|
+
console.log('agent-capture-mcp-ui-test PASS (MCP line rendered separately; hostile detail escaped; no XSS execution)');
|
|
60
|
+
} finally {
|
|
61
|
+
await browser?.close(); server.closeAllConnections(); await new Promise(resolve => server.close(resolve));
|
|
62
|
+
offline.closeAllConnections(); await new Promise(resolve => offline.close(resolve)); await runtime.logger.close();
|
|
63
|
+
assert.equal(path.dirname(temp), path.resolve(os.tmpdir())); assert.ok(path.basename(temp).startsWith('kb-agent-mcp-ui-'));
|
|
64
|
+
fs.rmSync(temp, { recursive: true, force: true, maxRetries: 12, retryDelay: 200 });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
main().catch(error => { console.error(error.stack); process.exitCode = 1; });
|
|
@@ -62,6 +62,7 @@ async function main() {
|
|
|
62
62
|
await waitFor(() => run("document.querySelector('[data-agent-client=zcode][data-agent-capture=uninstall]') !== null"), 'theme retains capture status');
|
|
63
63
|
await browser.screenshot(path.join(artifacts, '1920-dark.png'));
|
|
64
64
|
fs.writeFileSync(configFile, '{invalid JSON');
|
|
65
|
+
await waitFor(() => run("(() => { const b = document.querySelector('[data-agent-client=zcode][data-agent-capture=install]'); return b !== null && !b.disabled; })()"), 'capture row settled before invalid config');
|
|
65
66
|
await click('[data-agent-client=zcode][data-agent-capture=install]');
|
|
66
67
|
await waitFor(() => run("document.getElementById('agentCaptureFeedback').textContent.includes('操作 ID')"), 'partial failure rendered');
|
|
67
68
|
assert(await run("document.getElementById('agentCaptureFeedback').classList.contains('issue')"));
|
|
@@ -69,7 +70,7 @@ async function main() {
|
|
|
69
70
|
assert(logs.length > 0 && logs[0].error.message.includes('valid JSON'));
|
|
70
71
|
fs.writeFileSync(configFile, JSON.stringify(config));
|
|
71
72
|
await click('#refreshAgentCapture');
|
|
72
|
-
await waitFor(() => run("document.querySelector('[data-agent-client=zcode][data-agent-capture=uninstall]') !== null"), 'manual status retry');
|
|
73
|
+
await waitFor(() => run("(() => { const b = document.querySelector('[data-agent-client=zcode][data-agent-capture=uninstall]'); return b !== null && !b.disabled; })()"), 'manual status retry');
|
|
73
74
|
await click('[data-agent-client=zcode][data-agent-capture=uninstall]');
|
|
74
75
|
await waitFor(() => run("!document.querySelector('[data-agent-client=zcode]').disabled && document.querySelector('[data-agent-client=zcode][data-agent-capture=uninstall]') === null"), 'removed capture status');
|
|
75
76
|
assert.equal(JSON.parse(fs.readFileSync(configFile)).provider.name, 'retain');
|
|
@@ -37,7 +37,8 @@ async function main() {
|
|
|
37
37
|
assert(claudeMarketplace.plugins[0].version === packageInfo.version, 'Claude marketplace version should match the npm package');
|
|
38
38
|
assert(codexMarketplace.name === 'project-knowledge', 'Codex marketplace should have a stable public name');
|
|
39
39
|
assert(codexMarketplace.plugins[0].source.path === './plugins/project-knowledge', 'Codex marketplace should point at the shared plugin');
|
|
40
|
-
|
|
40
|
+
const mcpServer = mcpManifest.mcpServers['project-knowledge'];
|
|
41
|
+
assert(mcpServer.command === 'node' && mcpServer.args[0] === '-e' && mcpServer.cwd === undefined && String(mcpServer.args[1] || '').includes('mcp-launcher.cjs'), 'plugin MCP should launch the bundled local launcher instead of npx');
|
|
41
42
|
assert(!skill.includes('[TODO:'), 'bundled Skill should not contain scaffold placeholders');
|
|
42
43
|
assert(skill.includes('project_knowledge_resolve'), 'bundled Skill should teach the resolve-first workflow');
|
|
43
44
|
assert(skill.includes('project_knowledge_record_requirement'), 'bundled Skill should document the explicit supplemental adapter');
|
|
@@ -98,6 +99,7 @@ async function main() {
|
|
|
98
99
|
const claudeManager = new IntegrationManager({
|
|
99
100
|
rootDir: ROOT,
|
|
100
101
|
homeDir: claudeHome,
|
|
102
|
+
env: {},
|
|
101
103
|
commandResolver: name => name,
|
|
102
104
|
runner: (command, args) => {
|
|
103
105
|
claudeCalls.push({ command, args });
|
|
@@ -117,6 +119,8 @@ async function main() {
|
|
|
117
119
|
fs.mkdirSync(codexCache, { recursive: true }); fs.cpSync(pluginRoot, codexCache, { recursive: true });
|
|
118
120
|
const codexManager = new IntegrationManager({
|
|
119
121
|
rootDir: ROOT,
|
|
122
|
+
homeDir: path.join(TEMP, 'codex-home'),
|
|
123
|
+
env: {},
|
|
120
124
|
commandResolver: name => name,
|
|
121
125
|
runner: (command, args) => {
|
|
122
126
|
codexCalls.push({ command, args });
|
|
@@ -218,6 +222,8 @@ async function main() {
|
|
|
218
222
|
const integrationLogs = [];
|
|
219
223
|
const failingManager = new IntegrationManager({
|
|
220
224
|
rootDir: ROOT,
|
|
225
|
+
homeDir: path.join(TEMP, 'failing-home'),
|
|
226
|
+
env: {},
|
|
221
227
|
commandResolver: name => name,
|
|
222
228
|
runner: () => ({ status: 9, stdout: '', stderr: 'failure includes sk-do-not-log' }),
|
|
223
229
|
logger: {
|