@sanqianx/project-knowledge 5.0.18 → 5.0.20-rc.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/.agents/plugins/marketplace.json +3 -3
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +14 -0
- package/LICENSE +201 -201
- package/NOTICE +5 -5
- package/README.md +349 -349
- package/_modules/knowledge-engine/README.md +4 -0
- package/_modules/knowledge-engine/src/analysis-contract.js +4 -3
- package/_modules/knowledge-engine/src/analysis-failures.js +39 -0
- package/_modules/knowledge-engine/src/analysis-worker.js +6 -2
- package/_modules/knowledge-engine/src/coordinator.js +8 -0
- package/_modules/knowledge-engine/src/headless-analyzer.js +2 -1
- package/_modules/knowledge-engine/src/memories.js +10 -5
- package/_modules/knowledge-engine/src/memory-eligibility.js +74 -0
- package/_modules/knowledge-engine/src/retrieval-ranking.js +19 -1
- package/_modules/knowledge-engine/src/runtime/commit-conversation-binder.js +1 -0
- package/_modules/knowledge-engine/src/runtime/commit-prompt.js +12 -9
- package/_modules/knowledge-engine/src/runtime/conversation-store.js +2 -0
- package/_modules/knowledge-engine/src/server.js +1 -1
- package/_site/_test/agent-capture-mcp-ui-test.js +67 -67
- package/_site/_test/ai-profile-test.js +75 -75
- package/_site/_test/ai-vendor-presets-test.js +91 -91
- package/_site/_test/automatic-context-test.js +70 -70
- package/_site/_test/bin-cli-test.js +230 -230
- package/_site/_test/bridge-adapter-test.js +95 -95
- package/_site/_test/bridge-consumer-service-test.js +310 -310
- package/_site/_test/candidate-bin-content-test.js +35 -0
- package/_site/_test/candidate-product-assets-test.js +13 -0
- package/_site/_test/candidate-release-contract-test.js +14 -14
- package/_site/_test/capture-hook-dedup-test.js +158 -158
- package/_site/_test/claude-executable-discovery-test.js +28 -28
- package/_site/_test/claude-session-lifecycle-sweep-test.js +252 -252
- package/_site/_test/cli-instance-management-test.js +62 -62
- package/_site/_test/client-capture-restart-e2e-test.js +189 -189
- package/_site/_test/codex-conversation-projection-test.js +60 -60
- package/_site/_test/codex-marketplace-scoped-path-test.js +186 -186
- package/_site/_test/codex-notify-capture-e2e-test.js +101 -101
- package/_site/_test/codex-notify-fanout-test.js +134 -134
- package/_site/_test/codex-notify-upgrade-test.js +253 -253
- package/_site/_test/commit-boundary-freeze-test.js +147 -147
- package/_site/_test/commit-conversation-binding-test.js +4 -4
- package/_site/_test/configuration-generation-test.js +22 -22
- package/_site/_test/context-pack-test.js +200 -200
- package/_site/_test/conversation-api-test.js +10 -10
- package/_site/_test/conversation-origin-test.js +15 -0
- package/_site/_test/cross-repo-isolation-e2e-test.js +187 -187
- package/_site/_test/embedding-config-test.js +32 -32
- package/_site/_test/fixtures/fake-browser-exit.js +10 -10
- package/_site/_test/fixtures/fake-browser-hang.js +10 -10
- package/_site/_test/fixtures/integration-busy-worker.cjs +6 -6
- package/_site/_test/fixtures/make-git-repos.js +92 -92
- package/_site/_test/fixtures/retrieval-gold-queries.json +32 -0
- package/_site/_test/fresh-hook-reimport-test.js +53 -53
- package/_site/_test/fresh-product-data-test.js +56 -56
- package/_site/_test/git-path-compatibility-test.js +80 -80
- package/_site/_test/git-validation-test.js +81 -81
- package/_site/_test/hook-worktree-test.js +42 -42
- package/_site/_test/index-builder-test.js +41 -41
- package/_site/_test/index-error-display-contract-test.js +10 -10
- package/_site/_test/integration-adapters-test.js +67 -67
- package/_site/_test/integration-capture-setup-test.js +118 -118
- package/_site/_test/integration-runtime-binding-a2-test.js +566 -566
- package/_site/_test/integration-runtime-binding-a3-test.js +227 -227
- package/_site/_test/integration-runtime-binding-test.js +473 -473
- package/_site/_test/integration-status-scan-test.js +29 -29
- package/_site/_test/kb-framework-test.js +57 -57
- package/_site/_test/kbpath-follow-test.js +63 -63
- package/_site/_test/knowledge-db-test.js +144 -144
- package/_site/_test/knowledge-engine-client-test.js +109 -109
- package/_site/_test/knowledge-maintenance-test.js +40 -40
- package/_site/_test/knowledge-panel-ui-contract-test.js +112 -0
- package/_site/_test/knowledge-scopes-test.js +37 -37
- package/_site/_test/knowledge-storage-location-test.js +28 -28
- package/_site/_test/launcher-readiness-test.js +80 -80
- package/_site/_test/legacy-claude-session-alias-test.js +106 -106
- package/_site/_test/legacy-routes-removed-test.js +47 -47
- package/_site/_test/legacy-workbench-exclusion-test.js +92 -92
- package/_site/_test/live-gateway-test.js +48 -48
- package/_site/_test/local-marketplace-test.js +16 -16
- package/_site/_test/log-redaction-test.js +58 -58
- package/_site/_test/markdown-maintenance-test.js +11 -11
- package/_site/_test/model-context-windows-test.js +89 -89
- package/_site/_test/module-agent-start-proxy-test.js +65 -65
- package/_site/_test/module-artifact-integrity-test.js +27 -27
- package/_site/_test/module-boundary-test.js +30 -30
- package/_site/_test/module-bridge-eventbridge-test.js +202 -202
- package/_site/_test/module-dynamic-port-test.js +118 -118
- package/_site/_test/module-index-proxy-test.js +33 -0
- package/_site/_test/module-model-configuration-test.js +44 -44
- package/_site/_test/module-process-lifecycle-test.js +35 -35
- package/_site/_test/module-stream-proxy-test.js +34 -34
- package/_site/_test/module-terminal-restart-test.js +63 -0
- package/_site/_test/module-wiring-unbind-race-test.js +59 -59
- package/_site/_test/native-context-integrations-test.js +86 -86
- package/_site/_test/orphaned-claim-recovery-test.js +80 -80
- package/_site/_test/package-boundary-test.js +43 -42
- package/_site/_test/package-startup-test.js +56 -56
- package/_site/_test/packaged-ui-smoke-test.js +88 -88
- package/_site/_test/pr-consumer-contract-test.js +55 -55
- package/_site/_test/product-brand-contract-test.js +25 -25
- package/_site/_test/product-diagnostics-ui-test.js +93 -93
- package/_site/_test/product-import-ui-test.js +104 -104
- package/_site/_test/product-progress-status-test.js +12 -1
- package/_site/_test/project-remove-running-guard-test.js +79 -79
- package/_site/_test/project-store-test.js +90 -90
- package/_site/_test/registry-events-test.js +38 -0
- package/_site/_test/release-version-sync-test.js +115 -115
- package/_site/_test/requirement-binding-test.js +113 -113
- package/_site/_test/runtime-endpoint-test.js +64 -64
- package/_site/_test/scanner-test.js +83 -83
- package/_site/_test/server-security-test.js +96 -96
- package/_site/_test/sessions-stream-test.js +93 -93
- package/_site/_test/shared-scope-root-binding-test.js +67 -67
- package/_site/_test/simple-import-test.js +98 -98
- package/_site/_test/startup-analysis-disabled-test.js +6 -6
- package/_site/_test/team-knowledge-store-sparse-checkout-test.js +196 -196
- package/_site/_test/team-knowledge-store-test.js +132 -132
- package/_site/_test/test-server-cleanup-test.js +35 -35
- package/_site/_test/test-server-module-isolation-test.js +37 -37
- package/_site/_test/tracking-start-test.js +35 -35
- package/_site/_test/workbench-context-receipts-test.js +22 -0
- package/_site/_test/workspace-ui-contract-test.js +31 -31
- package/_site/_test/zcode-capture-integration-test.js +108 -108
- package/_site/lib/ai-vendor-presets.js +83 -83
- package/_site/lib/ai-workspace.js +3 -3
- package/_site/lib/atomic-file.js +204 -204
- package/_site/lib/bridge-adapter.js +260 -260
- package/_site/lib/capture-hook-config.js +108 -108
- package/_site/lib/claude-cli-runner.js +89 -89
- package/_site/lib/cli-instances.js +43 -43
- package/_site/lib/codex-context-status.js +17 -17
- package/_site/lib/codex-notify-config.js +193 -193
- package/_site/lib/commit-conversation-binder.js +13 -12
- package/_site/lib/commit-reconciler.js +38 -38
- package/_site/lib/context-pack-builder.js +299 -299
- package/_site/lib/conversation-exclusions.js +204 -204
- package/_site/lib/conversation-query-service.js +17 -17
- package/_site/lib/conversation-store.js +2 -0
- package/_site/lib/data-dir.js +13 -13
- package/_site/lib/embedding-service.js +85 -85
- package/_site/lib/engine-connection.js +93 -90
- package/_site/lib/engine-knowledge-client.js +72 -72
- package/_site/lib/folder-picker-output.js +3 -3
- package/_site/lib/git-path.js +14 -14
- package/_site/lib/github-team-store.js +3 -3
- package/_site/lib/index-builder.js +157 -157
- package/_site/lib/integration-status-scan.js +99 -99
- package/_site/lib/integration-status-worker.js +14 -14
- package/_site/lib/kb-framework.js +15 -15
- package/_site/lib/knowledge-db.js +363 -363
- package/_site/lib/knowledge-schema.js +121 -121
- package/_site/lib/live-gateway.js +78 -76
- package/_site/lib/llm-client.js +12 -12
- package/_site/lib/model-context-windows.js +82 -82
- package/_site/lib/module-bridge.js +874 -865
- package/_site/lib/module-port-resolver.js +57 -57
- package/_site/lib/post-commit-automation.js +54 -54
- package/_site/lib/product-data.js +35 -35
- package/_site/lib/project-lifecycle-service.js +14 -14
- package/_site/lib/project-registry-store.js +129 -129
- package/_site/lib/prompt-registry.js +148 -148
- package/_site/lib/registry-events.js +16 -0
- package/_site/lib/requirement-adapters.js +10 -10
- package/_site/lib/requirement-binder.js +112 -112
- package/_site/lib/runtime-control.js +89 -89
- package/_site/lib/runtime-endpoint.js +5 -5
- package/_site/lib/server-app.js +10 -1
- package/_site/lib/zcode-context-integration.js +60 -60
- package/_site/scripts/codex-notify-fanout.cjs +95 -95
- package/_site/scripts/install-module-candidates.js +69 -69
- package/_site/scripts/pack-module-candidates.js +89 -72
- package/_site/scripts/sync-product-content.js +28 -28
- package/_site/scripts/sync-release-version.js +164 -164
- package/_site/scripts/vendor-modules.js +49 -49
- package/_site/scripts/verify-module-runtime.js +50 -50
- package/_site/scripts/verify-product-runtime.js +36 -36
- package/_site/scripts/verify-retrieval-gold.js +83 -0
- package/_site/server.js +15 -15
- package/_site/start.bat +26 -26
- package/_site/stop.bat +11 -11
- package/bin/project-knowledge-integrations.js +254 -254
- package/bin/project-knowledge-kb.js +67 -67
- package/bin/project-knowledge-mcp.js +8 -8
- package/bin/project-knowledge.js +170 -170
- package/claude-prompts.json +21 -21
- package/docs/README.zh-CN.md +205 -205
- package/docs/assets/architecture.svg +181 -181
- package/docs/assets/logo.svg +49 -49
- package/docs/fresh-v5-start.md +42 -42
- package/docs/leader-glm-workbench-playbook.md +122 -119
- package/docs/project-registry-schema.md +11 -11
- package/docs/release-5.0.16-acceptance.md +176 -176
- package/docs/release-5.0.17-acceptance.md +117 -117
- package/docs/release-5.0.19-acceptance.md +39 -0
- package/docs/team-knowledge-mode-a-plan.md +73 -73
- package/module-runtime-manifest.json +126 -104
- package/package.json +110 -108
- 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 +1 -1
- package/plugins/project-knowledge/opencode/project-knowledge.md +3 -3
- package/plugins/project-knowledge/skills/project-knowledge/SKILL.md +28 -28
- package/templates/change.md +30 -30
- package/templates/project-readme.md +18 -18
- package/ui/index.html +1 -1
- package/ui/knowledge-panel.html +118 -0
- package/ui/product.css +8 -0
- package/ui/product.js +175 -8
- package/vendor-manifest.json +55 -53
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
const assert = require('assert');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { spawn } = require('child_process');
|
|
6
|
-
|
|
7
|
-
const ROOT = path.resolve(__dirname, '..', '..');
|
|
8
|
-
const BIN = path.join(ROOT, 'bin', 'project-knowledge.js');
|
|
9
|
-
const PORT = 7825;
|
|
10
|
-
const BASE = `http://127.0.0.1:${PORT}`;
|
|
11
|
-
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-package-v2-'));
|
|
12
|
-
|
|
13
|
-
async function waitForServer() {
|
|
14
|
-
for (let attempt = 0; attempt < 150; attempt += 1) {
|
|
15
|
-
try { if ((await fetch(`${BASE}/api/health`)).ok) return; } catch {}
|
|
16
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
17
|
-
}
|
|
18
|
-
throw new Error('packaged CLI server did not start');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
(async () => {
|
|
22
|
-
const child = spawn(process.execPath, [BIN, '--fg', '--port', String(PORT), '--host', '127.0.0.1', '--no-open'], {
|
|
23
|
-
cwd: ROOT,
|
|
24
|
-
env: { ...process.env, KB_DATA_DIR: dataDir, KB_SKIP_MIGRATION: '1' },
|
|
25
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
26
|
-
windowsHide: true,
|
|
27
|
-
});
|
|
28
|
-
let output = '';
|
|
29
|
-
child.stdout.on('data', chunk => { output += chunk; });
|
|
30
|
-
child.stderr.on('data', chunk => { output += chunk; });
|
|
31
|
-
try {
|
|
32
|
-
await waitForServer();
|
|
33
|
-
const stateResponse = await fetch(`${BASE}/api/state`);
|
|
34
|
-
const state = await stateResponse.json();
|
|
35
|
-
assert.strictEqual(state.schema, 'server-state/v2');
|
|
36
|
-
assert.deepStrictEqual(state.projects, []);
|
|
37
|
-
|
|
38
|
-
const registry = JSON.parse(fs.readFileSync(path.join(dataDir, 'projects.json'), 'utf8'));
|
|
39
|
-
assert.strictEqual(registry.schema, 'project-registry/v2');
|
|
40
|
-
assert.deepStrictEqual(registry.projectOrder, []);
|
|
41
|
-
const endpoint = JSON.parse(fs.readFileSync(path.join(dataDir, 'runtime-endpoint.json'), 'utf8'));
|
|
42
|
-
assert.strictEqual(endpoint.schema, 'project-knowledge/runtime-endpoint/v1');
|
|
43
|
-
assert.strictEqual(endpoint.port, PORT);
|
|
44
|
-
|
|
45
|
-
const removed = await fetch(`${BASE}/api/projects/example/hook-install`, { method: 'POST' });
|
|
46
|
-
assert.strictEqual(removed.status, 404);
|
|
47
|
-
console.log('package-startup-test PASS');
|
|
48
|
-
} catch (error) {
|
|
49
|
-
console.error(output);
|
|
50
|
-
throw error;
|
|
51
|
-
} finally {
|
|
52
|
-
child.kill('SIGTERM');
|
|
53
|
-
await new Promise(resolve => child.once('exit', resolve));
|
|
54
|
-
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
55
|
-
}
|
|
56
|
-
})().catch(error => { console.error(error); process.exitCode = 1; });
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const ROOT = path.resolve(__dirname, '..', '..');
|
|
8
|
+
const BIN = path.join(ROOT, 'bin', 'project-knowledge.js');
|
|
9
|
+
const PORT = 7825;
|
|
10
|
+
const BASE = `http://127.0.0.1:${PORT}`;
|
|
11
|
+
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-package-v2-'));
|
|
12
|
+
|
|
13
|
+
async function waitForServer() {
|
|
14
|
+
for (let attempt = 0; attempt < 150; attempt += 1) {
|
|
15
|
+
try { if ((await fetch(`${BASE}/api/health`)).ok) return; } catch {}
|
|
16
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
17
|
+
}
|
|
18
|
+
throw new Error('packaged CLI server did not start');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
(async () => {
|
|
22
|
+
const child = spawn(process.execPath, [BIN, '--fg', '--port', String(PORT), '--host', '127.0.0.1', '--no-open'], {
|
|
23
|
+
cwd: ROOT,
|
|
24
|
+
env: { ...process.env, KB_DATA_DIR: dataDir, KB_SKIP_MIGRATION: '1' },
|
|
25
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
26
|
+
windowsHide: true,
|
|
27
|
+
});
|
|
28
|
+
let output = '';
|
|
29
|
+
child.stdout.on('data', chunk => { output += chunk; });
|
|
30
|
+
child.stderr.on('data', chunk => { output += chunk; });
|
|
31
|
+
try {
|
|
32
|
+
await waitForServer();
|
|
33
|
+
const stateResponse = await fetch(`${BASE}/api/state`);
|
|
34
|
+
const state = await stateResponse.json();
|
|
35
|
+
assert.strictEqual(state.schema, 'server-state/v2');
|
|
36
|
+
assert.deepStrictEqual(state.projects, []);
|
|
37
|
+
|
|
38
|
+
const registry = JSON.parse(fs.readFileSync(path.join(dataDir, 'projects.json'), 'utf8'));
|
|
39
|
+
assert.strictEqual(registry.schema, 'project-registry/v2');
|
|
40
|
+
assert.deepStrictEqual(registry.projectOrder, []);
|
|
41
|
+
const endpoint = JSON.parse(fs.readFileSync(path.join(dataDir, 'runtime-endpoint.json'), 'utf8'));
|
|
42
|
+
assert.strictEqual(endpoint.schema, 'project-knowledge/runtime-endpoint/v1');
|
|
43
|
+
assert.strictEqual(endpoint.port, PORT);
|
|
44
|
+
|
|
45
|
+
const removed = await fetch(`${BASE}/api/projects/example/hook-install`, { method: 'POST' });
|
|
46
|
+
assert.strictEqual(removed.status, 404);
|
|
47
|
+
console.log('package-startup-test PASS');
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error(output);
|
|
50
|
+
throw error;
|
|
51
|
+
} finally {
|
|
52
|
+
child.kill('SIGTERM');
|
|
53
|
+
await new Promise(resolve => child.once('exit', resolve));
|
|
54
|
+
fs.rmSync(dataDir, { recursive: true, force: true });
|
|
55
|
+
}
|
|
56
|
+
})().catch(error => { console.error(error); process.exitCode = 1; });
|
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
const assert = require('assert');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { spawnSync } = require('child_process');
|
|
6
|
-
const { spawnServer } = require('./helpers/spawn-server');
|
|
7
|
-
const { findChrome } = require('./helpers/find-chrome');
|
|
8
|
-
const { launchCdpBrowser, waitFor } = require('./helpers/cdp-browser');
|
|
9
|
-
|
|
10
|
-
const ROOT = path.resolve(__dirname, '..', '..');
|
|
11
|
-
const PORT = 8250 + (process.pid % 500);
|
|
12
|
-
const BASE = `http://127.0.0.1:${PORT}`;
|
|
13
|
-
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), `pk-packaged-ui-${process.pid}-`));
|
|
14
|
-
const packDir = path.join(tempRoot, 'pack');
|
|
15
|
-
const installDir = path.join(tempRoot, 'install');
|
|
16
|
-
const profileDir = path.join(tempRoot, 'chrome-profile');
|
|
17
|
-
fs.mkdirSync(packDir, { recursive: true });
|
|
18
|
-
fs.mkdirSync(installDir, { recursive: true });
|
|
19
|
-
|
|
20
|
-
function npmCliPath() {
|
|
21
|
-
return process.env.npm_execpath
|
|
22
|
-
|| path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function buildAndInstallPackage() {
|
|
26
|
-
const packed = spawnSync(process.execPath, [npmCliPath(), 'pack', '--json', '--pack-destination', packDir], {
|
|
27
|
-
cwd: ROOT,
|
|
28
|
-
encoding: 'utf8',
|
|
29
|
-
windowsHide: true,
|
|
30
|
-
});
|
|
31
|
-
assert.strictEqual(packed.status, 0, packed.stderr || packed.stdout);
|
|
32
|
-
const report = JSON.parse(packed.stdout);
|
|
33
|
-
const tarball = path.join(packDir, report[0].filename);
|
|
34
|
-
assert(fs.existsSync(tarball), `npm pack did not create ${tarball}`);
|
|
35
|
-
|
|
36
|
-
const installed = spawnSync(process.execPath, [npmCliPath(), 'install', '--ignore-scripts', '--no-audit', '--no-fund', '--prefix', installDir, tarball], {
|
|
37
|
-
encoding: 'utf8',
|
|
38
|
-
windowsHide: true,
|
|
39
|
-
});
|
|
40
|
-
assert.strictEqual(installed.status, 0, installed.stderr || installed.stdout);
|
|
41
|
-
// Resolve the install path from the package's actual name (moved to
|
|
42
|
-
// @sanqianx/project-knowledge in 4.7.0; prior releases used the unscoped
|
|
43
|
-
// 'project-knowledge' which would live at node_modules/project-knowledge).
|
|
44
|
-
const pkg = require(path.join(ROOT, 'package.json'));
|
|
45
|
-
const installPath = pkg.name.startsWith('@')
|
|
46
|
-
? path.join(installDir, 'node_modules', pkg.name.split('/')[0], pkg.name.split('/')[1])
|
|
47
|
-
: path.join(installDir, 'node_modules', pkg.name);
|
|
48
|
-
return installPath;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
(async () => {
|
|
52
|
-
let server;
|
|
53
|
-
let browser;
|
|
54
|
-
try {
|
|
55
|
-
const packagedRoot = buildAndInstallPackage();
|
|
56
|
-
for (const file of ['index.html', 'product.js', 'product.css']) assert(fs.existsSync(path.join(packagedRoot, 'ui', file)), `packed UI must include ${file}`);
|
|
57
|
-
for (const file of ['app.js', 'app.css', 'i18n.js']) assert(!fs.existsSync(path.join(packagedRoot, 'ui', file)), `obsolete shell resource must not be shipped: ${file}`);
|
|
58
|
-
|
|
59
|
-
server = spawnServer({ root: packagedRoot, port: PORT, dataDir: path.join(tempRoot, 'data'), tag: 'packaged-ui', analysisOwner: 'engine', stdio: 'ignore' });
|
|
60
|
-
await waitFor(async () => {
|
|
61
|
-
try { return (await fetch(`${BASE}/api/health`)).ok ? { ok: true } : null; }
|
|
62
|
-
catch { return null; }
|
|
63
|
-
}, 'packaged server', 20000);
|
|
64
|
-
|
|
65
|
-
const productResponse = await fetch(`${BASE}/product.js`);
|
|
66
|
-
assert.strictEqual(productResponse.status, 200, 'packaged server must serve /product.js');
|
|
67
|
-
assert.match(String(productResponse.headers.get('content-type') || ''), /javascript/i);
|
|
68
|
-
|
|
69
|
-
browser = await launchCdpBrowser({ chrome: findChrome(), profileDir, url: `${BASE}/`, width: 1920, height: 1080 });
|
|
70
|
-
await browser.setViewport(1920, 1080);
|
|
71
|
-
await waitFor(async () => {
|
|
72
|
-
const ready = await browser.evaluate("document.getElementById('homeContent').textContent.includes('第一个项目')");
|
|
73
|
-
return ready ? { ok: true } : null;
|
|
74
|
-
}, 'packaged UI bootstrap', 20000);
|
|
75
|
-
|
|
76
|
-
assert.strictEqual(await browser.evaluate("document.getElementById('projectSelect').disabled"), true);
|
|
77
|
-
assert.strictEqual(await browser.evaluate("document.querySelectorAll('nav [data-view]').length"), 6);
|
|
78
|
-
assert.strictEqual(browser.exceptions.length, 0, `packaged UI runtime exceptions: ${JSON.stringify(browser.exceptions)}`);
|
|
79
|
-
console.log('packaged-ui-smoke-test PASS');
|
|
80
|
-
} finally {
|
|
81
|
-
if (browser) await browser.close();
|
|
82
|
-
if (server) await server.cleanup();
|
|
83
|
-
try { fs.rmSync(tempRoot, { recursive: true, force: true }); } catch {}
|
|
84
|
-
}
|
|
85
|
-
})().catch(error => {
|
|
86
|
-
console.error(error.stack || error.message);
|
|
87
|
-
process.exitCode = 1;
|
|
88
|
-
});
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
6
|
+
const { spawnServer } = require('./helpers/spawn-server');
|
|
7
|
+
const { findChrome } = require('./helpers/find-chrome');
|
|
8
|
+
const { launchCdpBrowser, waitFor } = require('./helpers/cdp-browser');
|
|
9
|
+
|
|
10
|
+
const ROOT = path.resolve(__dirname, '..', '..');
|
|
11
|
+
const PORT = 8250 + (process.pid % 500);
|
|
12
|
+
const BASE = `http://127.0.0.1:${PORT}`;
|
|
13
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), `pk-packaged-ui-${process.pid}-`));
|
|
14
|
+
const packDir = path.join(tempRoot, 'pack');
|
|
15
|
+
const installDir = path.join(tempRoot, 'install');
|
|
16
|
+
const profileDir = path.join(tempRoot, 'chrome-profile');
|
|
17
|
+
fs.mkdirSync(packDir, { recursive: true });
|
|
18
|
+
fs.mkdirSync(installDir, { recursive: true });
|
|
19
|
+
|
|
20
|
+
function npmCliPath() {
|
|
21
|
+
return process.env.npm_execpath
|
|
22
|
+
|| path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function buildAndInstallPackage() {
|
|
26
|
+
const packed = spawnSync(process.execPath, [npmCliPath(), 'pack', '--json', '--pack-destination', packDir], {
|
|
27
|
+
cwd: ROOT,
|
|
28
|
+
encoding: 'utf8',
|
|
29
|
+
windowsHide: true,
|
|
30
|
+
});
|
|
31
|
+
assert.strictEqual(packed.status, 0, packed.stderr || packed.stdout);
|
|
32
|
+
const report = JSON.parse(packed.stdout);
|
|
33
|
+
const tarball = path.join(packDir, report[0].filename);
|
|
34
|
+
assert(fs.existsSync(tarball), `npm pack did not create ${tarball}`);
|
|
35
|
+
|
|
36
|
+
const installed = spawnSync(process.execPath, [npmCliPath(), 'install', '--ignore-scripts', '--no-audit', '--no-fund', '--prefix', installDir, tarball], {
|
|
37
|
+
encoding: 'utf8',
|
|
38
|
+
windowsHide: true,
|
|
39
|
+
});
|
|
40
|
+
assert.strictEqual(installed.status, 0, installed.stderr || installed.stdout);
|
|
41
|
+
// Resolve the install path from the package's actual name (moved to
|
|
42
|
+
// @sanqianx/project-knowledge in 4.7.0; prior releases used the unscoped
|
|
43
|
+
// 'project-knowledge' which would live at node_modules/project-knowledge).
|
|
44
|
+
const pkg = require(path.join(ROOT, 'package.json'));
|
|
45
|
+
const installPath = pkg.name.startsWith('@')
|
|
46
|
+
? path.join(installDir, 'node_modules', pkg.name.split('/')[0], pkg.name.split('/')[1])
|
|
47
|
+
: path.join(installDir, 'node_modules', pkg.name);
|
|
48
|
+
return installPath;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
(async () => {
|
|
52
|
+
let server;
|
|
53
|
+
let browser;
|
|
54
|
+
try {
|
|
55
|
+
const packagedRoot = buildAndInstallPackage();
|
|
56
|
+
for (const file of ['index.html', 'knowledge-panel.html', 'product.js', 'product.css']) assert(fs.existsSync(path.join(packagedRoot, 'ui', file)), `packed UI must include ${file}`);
|
|
57
|
+
for (const file of ['app.js', 'app.css', 'i18n.js']) assert(!fs.existsSync(path.join(packagedRoot, 'ui', file)), `obsolete shell resource must not be shipped: ${file}`);
|
|
58
|
+
|
|
59
|
+
server = spawnServer({ root: packagedRoot, port: PORT, dataDir: path.join(tempRoot, 'data'), tag: 'packaged-ui', analysisOwner: 'engine', stdio: 'ignore' });
|
|
60
|
+
await waitFor(async () => {
|
|
61
|
+
try { return (await fetch(`${BASE}/api/health`)).ok ? { ok: true } : null; }
|
|
62
|
+
catch { return null; }
|
|
63
|
+
}, 'packaged server', 20000);
|
|
64
|
+
|
|
65
|
+
const productResponse = await fetch(`${BASE}/product.js`);
|
|
66
|
+
assert.strictEqual(productResponse.status, 200, 'packaged server must serve /product.js');
|
|
67
|
+
assert.match(String(productResponse.headers.get('content-type') || ''), /javascript/i);
|
|
68
|
+
|
|
69
|
+
browser = await launchCdpBrowser({ chrome: findChrome(), profileDir, url: `${BASE}/`, width: 1920, height: 1080 });
|
|
70
|
+
await browser.setViewport(1920, 1080);
|
|
71
|
+
await waitFor(async () => {
|
|
72
|
+
const ready = await browser.evaluate("document.getElementById('homeContent').textContent.includes('第一个项目')");
|
|
73
|
+
return ready ? { ok: true } : null;
|
|
74
|
+
}, 'packaged UI bootstrap', 20000);
|
|
75
|
+
|
|
76
|
+
assert.strictEqual(await browser.evaluate("document.getElementById('projectSelect').disabled"), true);
|
|
77
|
+
assert.strictEqual(await browser.evaluate("document.querySelectorAll('nav [data-view]').length"), 6);
|
|
78
|
+
assert.strictEqual(browser.exceptions.length, 0, `packaged UI runtime exceptions: ${JSON.stringify(browser.exceptions)}`);
|
|
79
|
+
console.log('packaged-ui-smoke-test PASS');
|
|
80
|
+
} finally {
|
|
81
|
+
if (browser) await browser.close();
|
|
82
|
+
if (server) await server.cleanup();
|
|
83
|
+
try { fs.rmSync(tempRoot, { recursive: true, force: true }); } catch {}
|
|
84
|
+
}
|
|
85
|
+
})().catch(error => {
|
|
86
|
+
console.error(error.stack || error.message);
|
|
87
|
+
process.exitCode = 1;
|
|
88
|
+
});
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
const assert = require('assert');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { StorageLayout } = require('../lib/storage-layout');
|
|
6
|
-
const { SettingsStore } = require('../lib/settings-store');
|
|
7
|
-
const { ProjectRegistryStore } = require('../lib/project-registry-store');
|
|
8
|
-
const { ProjectStore } = require('../lib/project-store');
|
|
9
|
-
const { KnowledgeToolRuntime } = require('../lib/knowledge-tool-runtime');
|
|
10
|
-
const { validateKb, buildPrContextPack } = require('../lib/kb-validator');
|
|
11
|
-
const { regenerateIndexes } = require('../lib/index-builder');
|
|
12
|
-
|
|
13
|
-
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-pr-v2-'));
|
|
14
|
-
const knowledgePath = path.join(temp, 'knowledge');
|
|
15
|
-
|
|
16
|
-
function writeKnowledge() {
|
|
17
|
-
fs.mkdirSync(path.join(knowledgePath, 'modules'), { recursive: true });
|
|
18
|
-
fs.mkdirSync(path.join(knowledgePath, 'changes'), { recursive: true });
|
|
19
|
-
fs.writeFileSync(path.join(knowledgePath, 'README.md'), '# Test KB\n');
|
|
20
|
-
fs.writeFileSync(path.join(knowledgePath, 'GOAL.md'), '# Goal\n');
|
|
21
|
-
fs.writeFileSync(path.join(knowledgePath, 'ARCHITECTURE.md'), '# Architecture\n');
|
|
22
|
-
fs.writeFileSync(path.join(knowledgePath, 'modules', 'api.md'), '---\ntags: [api]\nsourcePaths: [src/api.js]\n---\n# API\n');
|
|
23
|
-
fs.writeFileSync(path.join(knowledgePath, 'changes', 'api-change.md'), '---\ntags: [api]\naffectedModules: [api]\ndevelopmentIntent: Add API memory.\n---\n# API change\n\n## Development Intent\nAdd API memory.\n\n## Implementation Result\nDone.\n\n## Evidence\n- src/api.js\n');
|
|
24
|
-
regenerateIndexes(knowledgePath);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
(async () => {
|
|
28
|
-
try {
|
|
29
|
-
writeKnowledge();
|
|
30
|
-
const validation = validateKb(knowledgePath);
|
|
31
|
-
assert(validation.ok, JSON.stringify(validation.errors));
|
|
32
|
-
const pack = buildPrContextPack(knowledgePath);
|
|
33
|
-
assert(pack.ok);
|
|
34
|
-
assert.strictEqual(pack.pack.schema, 'pr-context-pack/v1');
|
|
35
|
-
assert(pack.pack.trustedKnowledge.some(item => item.path === 'modules/api.md'));
|
|
36
|
-
assert(!JSON.stringify(pack.pack).includes('_ai/'));
|
|
37
|
-
|
|
38
|
-
const layout = new StorageLayout({ dataDir: path.join(temp, 'data') });
|
|
39
|
-
const settingsStore = new SettingsStore({ layout });
|
|
40
|
-
const registryStore = new ProjectRegistryStore({ layout });
|
|
41
|
-
const projectStore = new ProjectStore({ layout });
|
|
42
|
-
await settingsStore.initialize({ knowledge: { rootPath: path.join(temp, 'future') } });
|
|
43
|
-
await registryStore.initialize();
|
|
44
|
-
await projectStore.create('project-consumer', { displayName: 'Consumer', storageName: 'consumer', repoPath: temp, knowledgePath }, { index: { dirty: true, generation: 1 } });
|
|
45
|
-
await registryStore.add('project-consumer', { displayNameSnapshot: 'Consumer' });
|
|
46
|
-
const runtime = new KnowledgeToolRuntime({ layout, settingsStore, registryStore, projectStore, cwd: temp });
|
|
47
|
-
const entry = await runtime.get({ projectId: 'project-consumer', entry: 'modules/api.md' });
|
|
48
|
-
assert.match(entry.chunks[0].chunk_text, /# API/);
|
|
49
|
-
await assert.rejects(runtime.get({ projectId: 'project-consumer', entry: '../settings.json' }), error => error.code === 'PATH_OUTSIDE_ROOT');
|
|
50
|
-
await runtime.close();
|
|
51
|
-
console.log('pr-consumer-contract-test PASS');
|
|
52
|
-
} finally {
|
|
53
|
-
fs.rmSync(temp, { recursive: true, force: true });
|
|
54
|
-
}
|
|
55
|
-
})().catch(error => { console.error(error); process.exitCode = 1; });
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { StorageLayout } = require('../lib/storage-layout');
|
|
6
|
+
const { SettingsStore } = require('../lib/settings-store');
|
|
7
|
+
const { ProjectRegistryStore } = require('../lib/project-registry-store');
|
|
8
|
+
const { ProjectStore } = require('../lib/project-store');
|
|
9
|
+
const { KnowledgeToolRuntime } = require('../lib/knowledge-tool-runtime');
|
|
10
|
+
const { validateKb, buildPrContextPack } = require('../lib/kb-validator');
|
|
11
|
+
const { regenerateIndexes } = require('../lib/index-builder');
|
|
12
|
+
|
|
13
|
+
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-pr-v2-'));
|
|
14
|
+
const knowledgePath = path.join(temp, 'knowledge');
|
|
15
|
+
|
|
16
|
+
function writeKnowledge() {
|
|
17
|
+
fs.mkdirSync(path.join(knowledgePath, 'modules'), { recursive: true });
|
|
18
|
+
fs.mkdirSync(path.join(knowledgePath, 'changes'), { recursive: true });
|
|
19
|
+
fs.writeFileSync(path.join(knowledgePath, 'README.md'), '# Test KB\n');
|
|
20
|
+
fs.writeFileSync(path.join(knowledgePath, 'GOAL.md'), '# Goal\n');
|
|
21
|
+
fs.writeFileSync(path.join(knowledgePath, 'ARCHITECTURE.md'), '# Architecture\n');
|
|
22
|
+
fs.writeFileSync(path.join(knowledgePath, 'modules', 'api.md'), '---\ntags: [api]\nsourcePaths: [src/api.js]\n---\n# API\n');
|
|
23
|
+
fs.writeFileSync(path.join(knowledgePath, 'changes', 'api-change.md'), '---\ntags: [api]\naffectedModules: [api]\ndevelopmentIntent: Add API memory.\n---\n# API change\n\n## Development Intent\nAdd API memory.\n\n## Implementation Result\nDone.\n\n## Evidence\n- src/api.js\n');
|
|
24
|
+
regenerateIndexes(knowledgePath);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
(async () => {
|
|
28
|
+
try {
|
|
29
|
+
writeKnowledge();
|
|
30
|
+
const validation = validateKb(knowledgePath);
|
|
31
|
+
assert(validation.ok, JSON.stringify(validation.errors));
|
|
32
|
+
const pack = buildPrContextPack(knowledgePath);
|
|
33
|
+
assert(pack.ok);
|
|
34
|
+
assert.strictEqual(pack.pack.schema, 'pr-context-pack/v1');
|
|
35
|
+
assert(pack.pack.trustedKnowledge.some(item => item.path === 'modules/api.md'));
|
|
36
|
+
assert(!JSON.stringify(pack.pack).includes('_ai/'));
|
|
37
|
+
|
|
38
|
+
const layout = new StorageLayout({ dataDir: path.join(temp, 'data') });
|
|
39
|
+
const settingsStore = new SettingsStore({ layout });
|
|
40
|
+
const registryStore = new ProjectRegistryStore({ layout });
|
|
41
|
+
const projectStore = new ProjectStore({ layout });
|
|
42
|
+
await settingsStore.initialize({ knowledge: { rootPath: path.join(temp, 'future') } });
|
|
43
|
+
await registryStore.initialize();
|
|
44
|
+
await projectStore.create('project-consumer', { displayName: 'Consumer', storageName: 'consumer', repoPath: temp, knowledgePath }, { index: { dirty: true, generation: 1 } });
|
|
45
|
+
await registryStore.add('project-consumer', { displayNameSnapshot: 'Consumer' });
|
|
46
|
+
const runtime = new KnowledgeToolRuntime({ layout, settingsStore, registryStore, projectStore, cwd: temp });
|
|
47
|
+
const entry = await runtime.get({ projectId: 'project-consumer', entry: 'modules/api.md' });
|
|
48
|
+
assert.match(entry.chunks[0].chunk_text, /# API/);
|
|
49
|
+
await assert.rejects(runtime.get({ projectId: 'project-consumer', entry: '../settings.json' }), error => error.code === 'PATH_OUTSIDE_ROOT');
|
|
50
|
+
await runtime.close();
|
|
51
|
+
console.log('pr-consumer-contract-test PASS');
|
|
52
|
+
} finally {
|
|
53
|
+
fs.rmSync(temp, { recursive: true, force: true });
|
|
54
|
+
}
|
|
55
|
+
})().catch(error => { console.error(error); process.exitCode = 1; });
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const assert = require('node:assert/strict');
|
|
4
|
-
const fs = require('node:fs');
|
|
5
|
-
const path = require('node:path');
|
|
6
|
-
|
|
7
|
-
const ui = path.resolve(__dirname, '../../ui');
|
|
8
|
-
const html = fs.readFileSync(path.join(ui, 'index.html'), 'utf8');
|
|
9
|
-
const css = fs.readFileSync(path.join(ui, 'product.css'), 'utf8');
|
|
10
|
-
const mark = fs.readFileSync(path.join(ui, 'favicon.svg'), 'utf8');
|
|
11
|
-
const favicon = html.match(/<link\b(?=[^>]*\brel="icon")[^>]*\bhref="([^"]+)"/);
|
|
12
|
-
const sidebar = html.match(/<img\b(?=[^>]*\bclass="brand-mark")[^>]*\bsrc="([^"]+)"/);
|
|
13
|
-
|
|
14
|
-
assert(favicon, 'Product needs an SVG favicon.');
|
|
15
|
-
assert(sidebar, 'Sidebar must use the product mark, not a second drawn icon.');
|
|
16
|
-
assert.equal(sidebar[1], favicon[1], 'Favicon and sidebar must share the exact asset URL.');
|
|
17
|
-
assert.equal(favicon[1].split('?')[0], '/favicon.svg');
|
|
18
|
-
assert.match(favicon[1], /\?v=/, 'Brand changes need a new browser cache identity.');
|
|
19
|
-
assert.match(mark, /viewBox="0 0 64 64"/);
|
|
20
|
-
assert.match(mark, /fill="#516f89"/);
|
|
21
|
-
assert.equal((mark.match(/<path\b/g) || []).length, 1, 'Keep one geometric silhouette.');
|
|
22
|
-
assert.doesNotMatch(mark, /<(?:script|image|text|filter|linearGradient|radialGradient)\b/i);
|
|
23
|
-
assert.doesNotMatch(html, /<span class="brand-mark">知<\/span>/);
|
|
24
|
-
assert.match(css, /\.brand-mark\{[^}]*width:32px;[^}]*height:32px;[^}]*flex:none/);
|
|
25
|
-
console.log('PASS product brand: shared SVG, cache identity, fixed-size layout, no decorative dependencies');
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
|
|
7
|
+
const ui = path.resolve(__dirname, '../../ui');
|
|
8
|
+
const html = fs.readFileSync(path.join(ui, 'index.html'), 'utf8');
|
|
9
|
+
const css = fs.readFileSync(path.join(ui, 'product.css'), 'utf8');
|
|
10
|
+
const mark = fs.readFileSync(path.join(ui, 'favicon.svg'), 'utf8');
|
|
11
|
+
const favicon = html.match(/<link\b(?=[^>]*\brel="icon")[^>]*\bhref="([^"]+)"/);
|
|
12
|
+
const sidebar = html.match(/<img\b(?=[^>]*\bclass="brand-mark")[^>]*\bsrc="([^"]+)"/);
|
|
13
|
+
|
|
14
|
+
assert(favicon, 'Product needs an SVG favicon.');
|
|
15
|
+
assert(sidebar, 'Sidebar must use the product mark, not a second drawn icon.');
|
|
16
|
+
assert.equal(sidebar[1], favicon[1], 'Favicon and sidebar must share the exact asset URL.');
|
|
17
|
+
assert.equal(favicon[1].split('?')[0], '/favicon.svg');
|
|
18
|
+
assert.match(favicon[1], /\?v=/, 'Brand changes need a new browser cache identity.');
|
|
19
|
+
assert.match(mark, /viewBox="0 0 64 64"/);
|
|
20
|
+
assert.match(mark, /fill="#516f89"/);
|
|
21
|
+
assert.equal((mark.match(/<path\b/g) || []).length, 1, 'Keep one geometric silhouette.');
|
|
22
|
+
assert.doesNotMatch(mark, /<(?:script|image|text|filter|linearGradient|radialGradient)\b/i);
|
|
23
|
+
assert.doesNotMatch(html, /<span class="brand-mark">知<\/span>/);
|
|
24
|
+
assert.match(css, /\.brand-mark\{[^}]*width:32px;[^}]*height:32px;[^}]*flex:none/);
|
|
25
|
+
console.log('PASS product brand: shared SVG, cache identity, fixed-size layout, no decorative dependencies');
|