@sanqianx/project-knowledge 5.0.9-rc.9 → 5.0.9
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/CHANGELOG.md +129 -0
- package/README.md +11 -4
- package/_modules/knowledge-engine/README.md +5 -0
- package/_modules/knowledge-engine/package.json +1 -1
- package/_modules/knowledge-engine/src/coordinator.js +1 -1
- package/_modules/knowledge-engine/src/headless-analyzer.js +1 -0
- package/_modules/knowledge-engine/src/retrieval.js +2 -1
- package/_modules/knowledge-engine/src/runtime/knowledge-promotion.js +6 -2
- package/_modules/knowledge-engine/src/runtime/markdown-knowledge-indexer.js +7 -2
- package/_modules/knowledge-engine/src/store.js +4 -6
- package/_site/_test/bin-cli-test.js +15 -2
- package/_site/_test/mcp-server-test.js +1 -1
- package/_site/_test/module-agent-start-proxy-test.js +65 -0
- package/_site/_test/module-bridge-eventbridge-test.js +1 -1
- package/_site/_test/module-wiring-unbind-race-test.js +59 -0
- package/_site/_test/path-consistency-test.js +1 -1
- package/_site/_test/product-diagnostics-ui-test.js +5 -1
- package/_site/_test/product-progress-status-test.js +70 -0
- package/_site/_test/shared-scope-root-binding-test.js +67 -0
- package/_site/_test/test-server-cleanup-test.js +5 -1
- package/_site/_test/vector-storage-root-test.js +16 -0
- package/_site/lib/engine-connection.js +4 -1
- package/_site/lib/knowledge-tool-runtime.js +1 -1
- package/_site/lib/module-bridge.js +65 -8
- package/_site/lib/server-app.js +4 -2
- package/_site/lib/settings-store.js +20 -0
- package/module-runtime-manifest.json +39 -39
- package/package.json +3 -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 +1 -1
- package/ui/product.js +4 -2
- package/vendor-manifest.json +9 -9
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const os = require('node:os');
|
|
6
|
+
const { createEngineServer } = require('../../_modules/knowledge-engine/src/server');
|
|
7
|
+
const { SettingsStore } = require('../lib/settings-store');
|
|
8
|
+
const { ModuleBridge } = require('../lib/module-bridge');
|
|
9
|
+
|
|
10
|
+
(async () => {
|
|
11
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'kb-shared-bindings-'));
|
|
12
|
+
const oldRoot = path.join(root, '原知识根目录'), newRoot = path.join(root, '新知识根目录');
|
|
13
|
+
const settings = new SettingsStore({ dataDir: path.join(root, 'shell') });
|
|
14
|
+
await settings.initialize({ knowledge: { rootPath: oldRoot } });
|
|
15
|
+
await settings.addDomain({ id: 'domain-tools', name: '开发工具' });
|
|
16
|
+
let engine;
|
|
17
|
+
async function start() {
|
|
18
|
+
engine = createEngineServer({ dataDir: path.join(root, 'engine'), consume: false, runtimeToken: 'scope-binding-test' });
|
|
19
|
+
await engine.ready;
|
|
20
|
+
await new Promise(resolve => engine.server.listen(0, '127.0.0.1', resolve));
|
|
21
|
+
return `http://127.0.0.1:${engine.server.address().port}`;
|
|
22
|
+
}
|
|
23
|
+
const logs = [];
|
|
24
|
+
const bridge = new ModuleBridge({ settingsStore: settings, runtimeToken: 'scope-binding-test', logger: { warn: (...args) => logs.push(args) } });
|
|
25
|
+
const upstream = bridge._fetch.bind(bridge);
|
|
26
|
+
bridge._fetch = (url, init) => url.startsWith(bridge.engineUrl) ? upstream(url, init) : Promise.resolve({ ok: false });
|
|
27
|
+
try {
|
|
28
|
+
bridge.engineUrl = await start();
|
|
29
|
+
assert.equal((await bridge.syncEngineConfiguration()).scopes, true);
|
|
30
|
+
const scopes = structuredClone(engine.store.state.sharedScopes);
|
|
31
|
+
const brain = scopes.find(scope => scope.kind === 'brain');
|
|
32
|
+
const domain = scopes.find(scope => scope.kind === 'domain');
|
|
33
|
+
engine.store.addProject({ projectId: 'project-test', repoPath: path.join(root, 'code'), knowledgePath: path.join(root, 'project-knowledge'), domainId: domain.id });
|
|
34
|
+
engine.store.state.memories.push({ id: 'memory-test', brainId: brain.id, status: 'candidate', text: '保持目录绑定稳定', evidence: { projectId: 'project-test', commitSha: 'a'.repeat(40), entryId: 'README.md' } });
|
|
35
|
+
engine.store.save();
|
|
36
|
+
await engine.coordinator.memories.update('memory-test', { status: 'confirmed' });
|
|
37
|
+
fs.writeFileSync(path.join(domain.knowledgePath, 'README.md'), '# 领域经验\n\n领域知识不能随根目录设置隐式移动。\n');
|
|
38
|
+
const memoryFile = engine.coordinator.memories.file(brain);
|
|
39
|
+
const before = fs.readFileSync(memoryFile);
|
|
40
|
+
await settings.updatePatch({ knowledge: { rootPath: newRoot } });
|
|
41
|
+
assert.equal((await bridge.syncEngineConfiguration()).scopes, true, 'changing index root must not invalidate shared memory wiring');
|
|
42
|
+
assert.deepEqual(engine.store.state.sharedScopes.map(scope => scope.knowledgePath), scopes.map(scope => scope.knowledgePath));
|
|
43
|
+
assert.deepEqual(fs.readFileSync(memoryFile), before);
|
|
44
|
+
assert.equal(settings.read().brain.knowledgePath, brain.knowledgePath);
|
|
45
|
+
assert.equal(settings.read().brain.domains[0].knowledgePath, domain.knowledgePath);
|
|
46
|
+
await assert.rejects(settings.updatePatch({ brain: { knowledgePath: newRoot } }), { code: 'INVALID_ARGUMENT' });
|
|
47
|
+
await assert.rejects(settings.bindSharedKnowledgePaths([{ ...brain, knowledgePath: newRoot }]), { code: 'IMMUTABLE_FIELD' });
|
|
48
|
+
await settings.addDomain({ id: 'domain-new', name: '新领域' });
|
|
49
|
+
assert.equal((await bridge.syncEngineConfiguration()).scopes, true);
|
|
50
|
+
assert.equal(engine.store.state.sharedScopes.find(scope => scope.id === 'domain-new').knowledgePath, path.join(newRoot, '_shared', 'domain-new'));
|
|
51
|
+
await engine.close();
|
|
52
|
+
bridge.engineUrl = await start();
|
|
53
|
+
assert.equal((await bridge.syncEngineConfiguration()).scopes, true);
|
|
54
|
+
assert.deepEqual(fs.readFileSync(memoryFile), before);
|
|
55
|
+
const memory = await engine.coordinator.retrieval.get({ projectId: 'project-test', spaceId: 'brain:brain-default', entryId: 'memories/confirmed.md' });
|
|
56
|
+
assert.ok(memory.chunks.some(chunk => chunk.chunk_text.includes('保持目录绑定稳定')));
|
|
57
|
+
const shared = await engine.coordinator.retrieval.get({ projectId: 'project-test', spaceId: 'domain:domain-tools', entryId: 'README.md' });
|
|
58
|
+
assert.ok(shared.chunks.some(chunk => chunk.chunk_text.includes('不能随根目录设置隐式移动')));
|
|
59
|
+
await engine.close(); engine = null;
|
|
60
|
+
const pending = await bridge.syncEngineConfiguration();
|
|
61
|
+
assert.equal(pending.scopes, false);
|
|
62
|
+
assert.equal(pending.scopeStatus.status, 'pending');
|
|
63
|
+
assert.ok(logs.some(entry => entry[0] === 'modules.scopes.pending'));
|
|
64
|
+
assert.equal(settings.read().brain.knowledgePath, brain.knowledgePath);
|
|
65
|
+
console.log('shared-scope-root-binding-test PASS');
|
|
66
|
+
} finally { if (engine) await engine.close(); }
|
|
67
|
+
})().catch(error => { console.error(error); process.exitCode = 1; });
|
|
@@ -5,6 +5,7 @@ const path = require('node:path');
|
|
|
5
5
|
const { spawnServer } = require('./helpers/spawn-server');
|
|
6
6
|
const freePort = require('./helpers/free-port');
|
|
7
7
|
const { readEndpoint } = require('../lib/runtime-endpoint');
|
|
8
|
+
const { spawn } = require('node:child_process');
|
|
8
9
|
|
|
9
10
|
(async () => {
|
|
10
11
|
const port = await freePort();
|
|
@@ -19,7 +20,10 @@ const { readEndpoint } = require('../lib/runtime-endpoint');
|
|
|
19
20
|
assert.equal(readEndpoint(spawned.dataDir)?.pid, spawned.child.pid);
|
|
20
21
|
let output = '';
|
|
21
22
|
spawned.child.stderr.on('data', bytes => { output += bytes; });
|
|
22
|
-
|
|
23
|
+
const locker = process.platform === 'win32' ? spawn(process.execPath, ['-e', 'process.stdout.write("holding-cwd\\n");setTimeout(()=>process.exit(0),2500)'], { cwd: spawned.dataDir, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }) : null;
|
|
24
|
+
if (locker) await new Promise((resolve, reject) => { locker.once('error', reject); locker.stdout.once('data', resolve); });
|
|
25
|
+
try { await spawned.cleanup(); stopped = true; }
|
|
26
|
+
finally { if (locker?.exitCode === null) { locker.kill(); await new Promise(resolve => locker.once('close', resolve)); } }
|
|
23
27
|
assert.notEqual(spawned.child.exitCode, null, output);
|
|
24
28
|
assert.equal(spawned.child.signalCode, null, 'normal cleanup must drain the server instead of force killing it');
|
|
25
29
|
assert.equal(spawned.child.exitCode, 0, output);
|
|
@@ -3,11 +3,27 @@ const assert = require('node:assert/strict');
|
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
const { StorageLayout } = require('../lib/storage-layout');
|
|
5
5
|
const { ModuleBridge } = require('../lib/module-bridge');
|
|
6
|
+
const { KnowledgeToolRuntime } = require('../lib/knowledge-tool-runtime');
|
|
6
7
|
const root = path.resolve('D:/knowledge storage 中文');
|
|
7
8
|
const layout = new StorageLayout({ dataDir: path.resolve('D:/metadata') });
|
|
8
9
|
assert.equal(layout.getVectorRootPath({ knowledge: { rootPath: root } }), path.join(root, '.indexes'));
|
|
9
10
|
assert.equal(layout.getVectorRootPath({ knowledge: { rootPath: '' } }), path.join(layout.getDataDir(), 'vectorhub'));
|
|
10
11
|
const settingsStore = { read: () => ({ knowledge: { rootPath: root }, brain: { id: 'brain-default', domains: [] } }) };
|
|
12
|
+
let knowledgeRoot = root;
|
|
13
|
+
const tools = new KnowledgeToolRuntime({
|
|
14
|
+
layout,
|
|
15
|
+
settingsStore: { read: () => ({ knowledge: { rootPath: knowledgeRoot } }) },
|
|
16
|
+
registryStore: { listIds: () => ['project-storage'] },
|
|
17
|
+
projectStore: {
|
|
18
|
+
readConfig: () => ({ displayName: 'Storage', repoPath: path.join(root, 'repo'), knowledgePath: path.join(root, 'project') }),
|
|
19
|
+
readState: () => ({ index: { generation: 3 } }),
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
assert.equal(tools.resolveProject({ projectId: 'project-storage' }).indexPath, path.join(root, '.indexes'), 'MCP/CLI resolution must report the vector-hub location, not the retired shell database');
|
|
23
|
+
knowledgeRoot = path.join(root, 'changed root');
|
|
24
|
+
assert.equal(tools.resolveProject({ projectId: 'project-storage' }).indexPath, path.join(knowledgeRoot, '.indexes'), 'a running MCP process must reread the selected knowledge root');
|
|
25
|
+
knowledgeRoot = '';
|
|
26
|
+
assert.equal(tools.resolveProject({ projectId: 'project-storage' }).indexPath, path.join(layout.getDataDir(), 'vectorhub'));
|
|
11
27
|
const bridge = new ModuleBridge({ settingsStore, dataDir: layout.getDataDir() });
|
|
12
28
|
const spec = bridge._supervisedSpecs().find(spec => spec.name === 'vector-hub');
|
|
13
29
|
assert.equal(spec.env.VECTOR_HUB_ROOT, path.join(root, '.indexes'));
|
|
@@ -24,7 +24,10 @@ async function connectEngine(runtime) {
|
|
|
24
24
|
step = 'project-wiring';
|
|
25
25
|
for (const id of runtime.registryStore.listIds()) {
|
|
26
26
|
projectId = id;
|
|
27
|
-
|
|
27
|
+
if (!runtime.registryStore.readDisplaySnapshot(id)) continue;
|
|
28
|
+
let config;
|
|
29
|
+
try { config = runtime.projectStore.readConfig(id); }
|
|
30
|
+
catch (error) { if (!runtime.registryStore.readDisplaySnapshot(id)) continue; throw error; }
|
|
28
31
|
const modules = await bridge.registerProjectLinks({ projectId: id, name: config.displayName, workspacePath: config.repoPath, knowledgePath: config.knowledgePath });
|
|
29
32
|
if (!modules.engine.ok || !modules.eventBridge.ok) throw new Error('PROJECT_WIRING_PENDING');
|
|
30
33
|
}
|
|
@@ -163,7 +163,7 @@ class KnowledgeToolRuntime {
|
|
|
163
163
|
repoPath: path.resolve(resolvedRepoPath || config.repoPath),
|
|
164
164
|
knowledgePath: path.resolve(config.knowledgePath),
|
|
165
165
|
kbPath: path.resolve(config.knowledgePath),
|
|
166
|
-
indexPath: this.layout.
|
|
166
|
+
indexPath: this.layout.getVectorRootPath(this.settingsStore.read({ allowMissing: true })),
|
|
167
167
|
primarySpaceId: `project:${projectId}`,
|
|
168
168
|
relatedProjectIds: Array.isArray(config.relatedProjectIds) ? [...config.relatedProjectIds] : [],
|
|
169
169
|
index: { ...state.index },
|
|
@@ -31,6 +31,9 @@ const { once } = require('events');
|
|
|
31
31
|
const { DomainError } = require('./contracts');
|
|
32
32
|
|
|
33
33
|
const PROXY_TIMEOUT_MS = 10000;
|
|
34
|
+
// Native client startup includes knowledge retrieval and a local server/CLI
|
|
35
|
+
// handshake. It has a bounded setup budget, separate from ordinary reads/SSE.
|
|
36
|
+
const AGENT_START_TIMEOUT_MS = 90000;
|
|
34
37
|
// Registration drives a full embedding pass in vector-hub (remote API); a
|
|
35
38
|
// fresh knowledge folder can easily exceed the interactive proxy budget.
|
|
36
39
|
const REGISTER_TIMEOUT_MS = 60000;
|
|
@@ -90,6 +93,7 @@ class ModuleBridge {
|
|
|
90
93
|
this.runtimeToken = options.runtimeToken || crypto.randomBytes(32).toString('hex');
|
|
91
94
|
this.registryStore = options.registryStore || null;
|
|
92
95
|
this.projectStore = options.projectStore || null;
|
|
96
|
+
this.projectWiringQueue = new Map();
|
|
93
97
|
this.settingsStore = options.settingsStore || null;
|
|
94
98
|
this.dataDir = options.dataDir || null;
|
|
95
99
|
this.supervised = [];
|
|
@@ -131,7 +135,11 @@ class ModuleBridge {
|
|
|
131
135
|
// The native folder pickers hold the request open until the user closes
|
|
132
136
|
// the dialog — they need a human-scale budget, not the API budget.
|
|
133
137
|
const isPicker = /\/(system\/)?pick-folder/.test(target);
|
|
134
|
-
const
|
|
138
|
+
const targetUrl = new URL(target);
|
|
139
|
+
const pathname = targetUrl.pathname;
|
|
140
|
+
const isAgentStart = req.method === 'POST' && /^\/api\/claude-workbench\/v1\/sessions(?:\/[^/]+\/input)?\/?$/.test(pathname);
|
|
141
|
+
const timeoutMs = isPicker ? PICKER_TIMEOUT_MS : isAgentStart ? AGENT_START_TIMEOUT_MS : PROXY_TIMEOUT_MS;
|
|
142
|
+
const startedAt = Date.now();
|
|
135
143
|
let upstream;
|
|
136
144
|
const controller = new AbortController();
|
|
137
145
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -148,8 +156,13 @@ class ModuleBridge {
|
|
|
148
156
|
clearTimeout(timer);
|
|
149
157
|
res.off?.('close', disconnected);
|
|
150
158
|
if (res.destroyed) return;
|
|
159
|
+
const details = { failureKind: controller.signal.aborted ? 'UPSTREAM_TIMEOUT' : 'NETWORK_ERROR', timeoutMs };
|
|
160
|
+
this.logger.warn('module.proxy.failed', '模块请求未完成,请检查连接或客户端启动。', {
|
|
161
|
+
component: 'module-bridge', phase: 'proxy', durationMs: Date.now() - startedAt,
|
|
162
|
+
context: { ...details, method: req.method, path: pathname },
|
|
163
|
+
});
|
|
151
164
|
res.writeHead(502, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
152
|
-
res.end(JSON.stringify({ ok: false, error: 'MODULE_UNREACHABLE', target }));
|
|
165
|
+
res.end(JSON.stringify({ ok: false, error: 'MODULE_UNREACHABLE', target: targetUrl.origin + pathname, details }));
|
|
153
166
|
return;
|
|
154
167
|
}
|
|
155
168
|
const contentType = upstream.headers.get('content-type') || 'application/json; charset=utf-8';
|
|
@@ -323,7 +336,7 @@ class ModuleBridge {
|
|
|
323
336
|
let indexPath = null;
|
|
324
337
|
try { indexPath = new StorageLayout({ dataDir }).getVectorRootPath(this.settingsStore?.read()); }
|
|
325
338
|
catch { /* Corrupt settings must not switch storage or prevent credential revocation. */ }
|
|
326
|
-
const status = { scopes: false, model: false, embedding: false, indexStorage: { expectedPath: indexPath, actualPath: null, status: 'pending' } };
|
|
339
|
+
const status = { scopes: false, scopeStatus: { status: 'pending' }, model: false, embedding: false, indexStorage: { expectedPath: indexPath, actualPath: null, status: 'pending' } };
|
|
327
340
|
const headers = { 'Content-Type': 'application/json', Authorization: `Bearer ${this.runtimeToken}` };
|
|
328
341
|
try {
|
|
329
342
|
if (this.settingsStore) {
|
|
@@ -331,11 +344,28 @@ class ModuleBridge {
|
|
|
331
344
|
if (settings.knowledge.rootPath) {
|
|
332
345
|
const brain = settings.brain;
|
|
333
346
|
const root = path.join(settings.knowledge.rootPath, '_shared');
|
|
334
|
-
const
|
|
335
|
-
|
|
347
|
+
const response = await this._fetch(`${this.engineUrl}/api/snapshot`);
|
|
348
|
+
if (!response.ok) throw new DomainError('ENGINE_UNAVAILABLE', '共享知识接线暂时不可用。');
|
|
349
|
+
const prior = (await response.json()).sharedScopes || [];
|
|
350
|
+
// Root settings govern new directories and derived indexes, never move
|
|
351
|
+
// existing knowledge. Retain engine-acknowledged bindings in the spine.
|
|
352
|
+
const binding = (item, kind) => {
|
|
353
|
+
const existing = prior.find(scope => scope.id === item.id && scope.kind === kind && scope.brainId === brain.id);
|
|
354
|
+
if (existing && !path.isAbsolute(existing.knowledgePath || '')) throw new DomainError('DATA_CORRUPT', '共享目录绑定损坏。');
|
|
355
|
+
return item.knowledgePath || existing?.knowledgePath || path.join(root, item.id);
|
|
356
|
+
};
|
|
357
|
+
const scopes = [{ id: brain.id, kind: 'brain', brainId: brain.id, name: brain.name, knowledgePath: binding(brain, 'brain') }, ...brain.domains.map(d => ({ id: d.id, kind: 'domain', brainId: brain.id, domainId: d.id, name: d.name, knowledgePath: binding(d, 'domain') }))];
|
|
358
|
+
const applied = await this._fetch(`${this.engineUrl}/api/scopes`, { method: 'POST', headers, body: JSON.stringify({ scopes }) });
|
|
359
|
+
if (!applied.ok) throw new DomainError('SHARED_SCOPE_APPLY_FAILED', '共享目录接线未完成。');
|
|
360
|
+
await this.settingsStore.bindSharedKnowledgePaths?.(scopes);
|
|
361
|
+
status.scopes = true; status.scopeStatus = { status: 'ready' };
|
|
336
362
|
}
|
|
337
363
|
}
|
|
338
|
-
} catch {
|
|
364
|
+
} catch (error) {
|
|
365
|
+
status.scopeStatus = { status: 'pending', code: ['DATA_CORRUPT', 'IMMUTABLE_FIELD', 'ENGINE_UNAVAILABLE', 'SHARED_SCOPE_APPLY_FAILED'].includes(error.code) ? error.code : 'ENGINE_UNAVAILABLE' };
|
|
366
|
+
this.logger.warn('modules.scopes.pending', 'Shared knowledge wiring is pending; existing data is retained.', { context: { code: status.scopeStatus.code } });
|
|
367
|
+
// A scope failure must not prevent revoking a stale model key.
|
|
368
|
+
}
|
|
339
369
|
try {
|
|
340
370
|
let profile = null;
|
|
341
371
|
try {
|
|
@@ -369,7 +399,24 @@ class ModuleBridge {
|
|
|
369
399
|
* (retry on the next register call or health-check pass), never a hard error.
|
|
370
400
|
* KB_MODULES_REGISTER=0 disables fan-out entirely (test isolation).
|
|
371
401
|
*/
|
|
372
|
-
|
|
402
|
+
_withProjectWiring(projectId, action) {
|
|
403
|
+
const prior = this.projectWiringQueue.get(projectId) || Promise.resolve();
|
|
404
|
+
const operation = prior.catch(() => {}).then(action);
|
|
405
|
+
const settled = operation.finally(() => { if (this.projectWiringQueue.get(projectId) === settled) this.projectWiringQueue.delete(projectId); });
|
|
406
|
+
this.projectWiringQueue.set(projectId, settled);
|
|
407
|
+
return settled;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
registerProjectLinks(input) {
|
|
411
|
+
return this._withProjectWiring(input.projectId, () => {
|
|
412
|
+
// A supervisor may have captured this project before a concurrent unbind.
|
|
413
|
+
// Recheck the spine after acquiring the same lease as the removal path.
|
|
414
|
+
if (this.registryStore && !this.registryStore.readDisplaySnapshot(input.projectId)) return Object.fromEntries(['terminal', 'vectorHub', 'eventBridge', 'engine'].map(key => [key, { ok: true, status: 'removed', skipped: 'project-not-registered' }]));
|
|
415
|
+
return this._registerProjectLinks(input);
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
async _registerProjectLinks({ projectId, name, workspacePath, knowledgePath }) {
|
|
373
420
|
const result = { terminal: { ok: false }, vectorHub: { ok: false }, eventBridge: { ok: false }, engine: { ok: false } };
|
|
374
421
|
if (String(process.env.KB_MODULES_REGISTER || '') === '0') {
|
|
375
422
|
result.terminal = { ok: false, status: 'disabled' };
|
|
@@ -461,7 +508,17 @@ class ModuleBridge {
|
|
|
461
508
|
* only drops its derived index (source untouched); terminal removes the project
|
|
462
509
|
* with keepSessions=1 so session records survive; the event-bridge console only
|
|
463
510
|
* unregisters the project — journals and sealed commit documents stay on disk. */
|
|
464
|
-
|
|
511
|
+
removeProjectLinks(input) {
|
|
512
|
+
return this._withProjectWiring(input.projectId, async () => {
|
|
513
|
+
const result = await this._removeProjectLinks(input);
|
|
514
|
+
// The caller owns registry/lifecycle changes. Hold the wiring lease until
|
|
515
|
+
// its transaction settles, so delayed reconfiguration cannot resurrect it.
|
|
516
|
+
await input.onRemoved?.(result);
|
|
517
|
+
return result;
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
async _removeProjectLinks({ projectId, requireEngine = true }) {
|
|
465
522
|
const wiring = (this.projectStore ? this.projectStore.readState(projectId).modules : null) || {};
|
|
466
523
|
const result = { terminal: { ok: false }, vectorHub: { ok: false }, eventBridge: { ok: false }, engine: { ok: false } };
|
|
467
524
|
// Stop the writer first, before removing hooks/index registrations.
|
package/_site/lib/server-app.js
CHANGED
|
@@ -1057,11 +1057,13 @@ function createRequestHandler(runtime, options = {}) {
|
|
|
1057
1057
|
throw new DomainError('PROJECT_NOT_FOUND', 'Project was not found.', { status: 404 });
|
|
1058
1058
|
}
|
|
1059
1059
|
const body = await readJsonBody(req);
|
|
1060
|
-
|
|
1061
|
-
const result = await runtime.lifecycleService.deleteProject(projectId, {
|
|
1060
|
+
const remove = () => runtime.lifecycleService.deleteProject(projectId, {
|
|
1062
1061
|
deleteKnowledge: body.deleteKnowledge === true,
|
|
1063
1062
|
confirmationToken: body.confirmationToken || '',
|
|
1064
1063
|
});
|
|
1064
|
+
let result;
|
|
1065
|
+
if (runtime.registryStore.readDisplaySnapshot(projectId)) await runtime.moduleBridge.removeProjectLinks({ projectId, requireEngine: runtime.analysisOwner !== 'legacy', onRemoved: async () => { result = await remove(); } });
|
|
1066
|
+
else result = await remove();
|
|
1065
1067
|
return send(res, 200, result);
|
|
1066
1068
|
}
|
|
1067
1069
|
const gitStatusMatch = pathname.match(/^\/api\/projects\/([^/]+)\/git-status$/);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { SCHEMAS, DomainError, publicAiProfilesConfig } = require('./contracts');
|
|
2
2
|
const AtomicFile = require('./atomic-file');
|
|
3
3
|
const { StorageLayout } = require('./storage-layout');
|
|
4
|
+
const path = require('node:path');
|
|
4
5
|
|
|
5
6
|
function defaultSettings() {
|
|
6
7
|
return {
|
|
@@ -43,9 +44,12 @@ function validateSettings(settings) {
|
|
|
43
44
|
}
|
|
44
45
|
settings.brain = settings.brain || { id: 'brain-default', name: '我的智脑', domains: [] };
|
|
45
46
|
if (settings.brain.id !== 'brain-default' || typeof settings.brain.name !== 'string' || !settings.brain.name.trim() || settings.brain.name.length > 100 || !Array.isArray(settings.brain.domains)) throw new DomainError('DATA_CORRUPT', 'Brain metadata is invalid.', { status: 500 });
|
|
47
|
+
const validBinding = value => value === undefined || typeof value === 'string' && path.isAbsolute(value);
|
|
48
|
+
if (!validBinding(settings.brain.knowledgePath)) throw new DomainError('DATA_CORRUPT', 'Brain knowledge binding is invalid.', { status: 500 });
|
|
46
49
|
const ids = new Set();
|
|
47
50
|
for (const domain of settings.brain.domains) {
|
|
48
51
|
if (!domain || !/^domain-[a-z0-9-]+$/.test(domain.id) || ids.has(domain.id) || typeof domain.name !== 'string' || !domain.name.trim() || domain.name.length > 100) throw new DomainError('DATA_CORRUPT', 'Domain metadata is invalid.', { status: 500 });
|
|
52
|
+
if (!validBinding(domain.knowledgePath)) throw new DomainError('DATA_CORRUPT', 'Domain knowledge binding is invalid.', { status: 500 });
|
|
49
53
|
ids.add(domain.id);
|
|
50
54
|
}
|
|
51
55
|
const logging = settings.logging || {};
|
|
@@ -92,6 +96,7 @@ class SettingsStore {
|
|
|
92
96
|
|
|
93
97
|
async updatePatch(patch) {
|
|
94
98
|
return this.atomic.withFileLock(this.lockPath, async () => {
|
|
99
|
+
if (patch?.brain && (Object.prototype.hasOwnProperty.call(patch.brain, 'knowledgePath') || patch.brain.domains?.some?.(domain => Object.prototype.hasOwnProperty.call(domain, 'knowledgePath')))) throw new DomainError('INVALID_ARGUMENT', '共享知识目录绑定只允许受控接线写入。');
|
|
95
100
|
if (patch && patch.logging && typeof patch.logging === 'object') {
|
|
96
101
|
const unknown = Object.keys(patch.logging).find(key => key !== 'levels');
|
|
97
102
|
if (unknown) throw new DomainError('INVALID_ARGUMENT', `Logging setting is read-only or unsupported: ${unknown}.`, { details: { field: unknown } });
|
|
@@ -120,6 +125,21 @@ class SettingsStore {
|
|
|
120
125
|
});
|
|
121
126
|
}
|
|
122
127
|
|
|
128
|
+
async bindSharedKnowledgePaths(scopes) {
|
|
129
|
+
return this.atomic.withFileLock(this.lockPath, async () => {
|
|
130
|
+
const settings = this.read();
|
|
131
|
+
let changed = false;
|
|
132
|
+
for (const scope of scopes) {
|
|
133
|
+
const target = scope.kind === 'brain' && scope.id === settings.brain.id ? settings.brain : settings.brain.domains.find(domain => domain.id === scope.id);
|
|
134
|
+
if (!target || !path.isAbsolute(scope.knowledgePath || '')) throw new DomainError('INVALID_ARGUMENT', '共享知识绑定无效。');
|
|
135
|
+
if (target.knowledgePath && !this.layout.pathsEqual(target.knowledgePath, scope.knowledgePath)) throw new DomainError('IMMUTABLE_FIELD', '共享知识目录不能隐式移动。', { status: 409 });
|
|
136
|
+
if (!target.knowledgePath) { target.knowledgePath = path.resolve(scope.knowledgePath); changed = true; }
|
|
137
|
+
}
|
|
138
|
+
if (changed) { settings.updatedAt = new Date().toISOString(); validateSettings(settings); this.atomic.writeJsonAtomic(this.filePath, settings); }
|
|
139
|
+
return settings;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
123
143
|
readPublicView(options = {}) {
|
|
124
144
|
const settings = this.read(options);
|
|
125
145
|
return {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema": "project-knowledge/module-content/v1",
|
|
3
3
|
"productFiles": {
|
|
4
4
|
"ui/index.html": "5cc3f9519a0671e7d5b24a94987a96b7ee81d3ed3367e3c02cd70934f08e64c8",
|
|
5
|
-
"ui/product.js": "
|
|
5
|
+
"ui/product.js": "e7ac0fc1d1d515039f981ed8481155d112034dc4961a2e7259864720f54c83ea",
|
|
6
6
|
"ui/product.css": "70547d3f2c53b6cc5c73cb95eb8cb3faf0dc8ccee7cd6b11a89a88885de7c3f8",
|
|
7
7
|
"ui/favicon.svg": "c48be946b224112ebb0238b44263f7a61bda31e531afa895c2e9801c0ff24f32"
|
|
8
8
|
},
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
"terminal": {
|
|
11
11
|
"name": "@sanqianx/claude-ai-workbench",
|
|
12
12
|
"installName": "claude-ai-workbench",
|
|
13
|
-
"version": "0.13.2-rc.
|
|
13
|
+
"version": "0.13.2-rc.7",
|
|
14
14
|
"files": {
|
|
15
15
|
"LICENSE": "c1b9df1275e769f3dbab000d1e457a2d4b0f28eb5da6c77e48dc37eeba202ed7",
|
|
16
|
-
"README.md": "
|
|
17
|
-
"apps/agent-terminal/app.css": "
|
|
18
|
-
"apps/agent-terminal/app.js": "
|
|
19
|
-
"apps/agent-terminal/index.html": "
|
|
16
|
+
"README.md": "5a382c531372deaa63b3e014f4dbd077d41ce02efee40347973463081848cb86",
|
|
17
|
+
"apps/agent-terminal/app.css": "ede36d9fdb1f7036e3453134c0d5a86197bfc30e3ac547a24b4fc1d17847277e",
|
|
18
|
+
"apps/agent-terminal/app.js": "e23fde6529f72031b745b47d5658e62f9079678248aab95599487d1d0660b6e7",
|
|
19
|
+
"apps/agent-terminal/index.html": "cdffd5c535c81f56aba70ec5dd3bc060f59ec2abcdc8676b79c8f327126be605",
|
|
20
20
|
"apps/ai-profile-settings-host/index.html": "bc158f68954506d2b8e43c690f28dbe931eff9a02b1524855ac17e74ea4df736",
|
|
21
21
|
"apps/ai-profile-settings-host/page.css": "8b75cbf7f1ea5aaa92f2889390720ece310a8fd4b9ca38c890fa447d4323a557",
|
|
22
22
|
"apps/ai-profile-settings-host/page.js": "f42755a3ed17bd7e00919cee56875b04035aa3c5526fce889f8ba52f4278a8b8",
|
|
@@ -27,20 +27,20 @@
|
|
|
27
27
|
"apps/minimal-test-host/README.md": "fa73136f4ee3865c2d91eb97b2e8c03679aa4688e44c1e1c8ec6e27d604b0de5",
|
|
28
28
|
"docs/sdk/FILE_LAYOUT.md": "17882f96505c8161bfb66876684b9d2d3f50021a111b8b844502d3cb4e97870a",
|
|
29
29
|
"docs/sdk/INTEGRATION.md": "d4c0c80d2d2fc610205102d703a429bfaa20f9f3a48f1718f5bab02ee90138df",
|
|
30
|
-
"docs/sdk/openapi.yaml": "
|
|
30
|
+
"docs/sdk/openapi.yaml": "18581c89859580ba153ca6582691fe87daf9bc37fb9971040bd8c564a0440281",
|
|
31
31
|
"docs/sdk/README.md": "7ab591d39c36a3b84cfdf487ff567040ec453975f3ef0fba86dc822094667b61",
|
|
32
|
-
"package.json": "
|
|
32
|
+
"package.json": "f2f45324f4bff372fe99e8bcb0af04624162a2f65712f8b8f6479e5630d64de7",
|
|
33
33
|
"packages/client/index.d.ts": "fb66b7b21ac936cddcb7f74543cf4f3bb7e80c169534ec249ab8ef2356224162",
|
|
34
34
|
"packages/client/index.js": "92fe5ddd32289d729f336f669e47bd9601e562f5a4309ee4fa7f7f15b567edc2",
|
|
35
|
-
"packages/client/package.json": "
|
|
35
|
+
"packages/client/package.json": "2cc2ee4f2305ddb5fe32f0c9bc928f47257e4522f7b2063e84de92d981f7660a",
|
|
36
36
|
"packages/client/README.md": "342dff9b1dcbc85fccc9b980ed3e4e455aa71133af1d0805b8431ea2ac564ef8",
|
|
37
37
|
"packages/contracts/index.d.ts": "edc717067bfb864c6c95df0c408a799fbf90b7552519131e6dc35af3f9eb9006",
|
|
38
38
|
"packages/contracts/index.js": "565b4a88190dbb8994cd0a9309b30b00f64305d65505fff9fa360d7ce69dfb58",
|
|
39
|
-
"packages/contracts/package.json": "
|
|
39
|
+
"packages/contracts/package.json": "502ec29cf54dcbc604dc9bc6ccd7c74a553ae9b301616b37e2624c6a038601fc",
|
|
40
40
|
"packages/contracts/README.md": "44d83a3cc508cec6c5c20dfa1f2c08d5ea03e3afb2853639fdc0a9cc8eb5b9c8",
|
|
41
41
|
"packages/core/index.d.ts": "c95b24a13f8aeeb128497833cf298790563baca6036ea5d29a9d8daff53199d7",
|
|
42
42
|
"packages/core/index.js": "5579ceb1a63d3f962f5656c75f82cf60cd834fd270b4aa95e581bf38e4432448",
|
|
43
|
-
"packages/core/package.json": "
|
|
43
|
+
"packages/core/package.json": "56adcbfba15bf283e885032f3d6bab4ff3a8d3ba1ab03e84e44fde04ed2ff445",
|
|
44
44
|
"packages/core/README.md": "e222c546104a168f1038d95c6c4efb8e5603edd6145ab2cab79cab977fb710e5",
|
|
45
45
|
"packages/server/bin/agent-terminal-server.js": "043dff4d76e3fd213b666a55e581ed1e72f9e5b8de6cb798fbf42bd39ed26861",
|
|
46
46
|
"packages/server/bin/claude-workbench-server.js": "b03c730b6d86faf6974afea26c74109d59b1bbc1ecc656c1d6270258708694ac",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"packages/server/lib/agent-registry.js": "fbf4f3e96542502a41e7fdf77037d63c3b0444d26708ad9766bec90fbb6b38bb",
|
|
52
52
|
"packages/server/lib/attachment-store.js": "5c92c45c7ec5a8b0ee9c14edb42eeda5fce93d8e654718dcd75c12e3f0bfa6e7",
|
|
53
53
|
"packages/server/lib/bridges/app-server-bridge.js": "85bb644ab570a7a7e6aa26123c8e3cd2e8c11c3ff4e1270954b8e26517968f69",
|
|
54
|
-
"packages/server/lib/bridges/opencode-server-bridge.js": "
|
|
54
|
+
"packages/server/lib/bridges/opencode-server-bridge.js": "910e4216b184d35b98fb525c629670007f4eaf52f27cfe58a631764983271ff6",
|
|
55
55
|
"packages/server/lib/capture-settings-store.js": "eb18885d7bedd3e353287dd7d1cf81e815e5d65d5d82170aa8fe5506a03de8bd",
|
|
56
56
|
"packages/server/lib/cli-session-recovery.js": "e66dc0ad83af18841bf3a598bb27932af42a20cf9395c16dcbaf9b022a5201b5",
|
|
57
57
|
"packages/server/lib/context-resolver.js": "d3060bfe35fb184e521847fc0900cf6114f8ebf3120ce13ed39dfc224af54660",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"packages/server/lib/system-doctor.js": "2b0e858d69d8fb268eaed950c9883473ee9850684b532bc2db8e14674a974919",
|
|
75
75
|
"packages/server/lib/ui-preferences-store.js": "e108cb22e7cc1f3438704913d5401e01de2c0e717eb2abd3bdc4727209393819",
|
|
76
76
|
"packages/server/lib/workbench-service.js": "92b9323db8742bbdd69abd111c81cab86b225103596f6818b007fbfe2376fc6f",
|
|
77
|
-
"packages/server/package.json": "
|
|
77
|
+
"packages/server/package.json": "08195a1f8a78f8a9405f3c5c7e037f4c451c77128fd0f81e7406cada89ca8075",
|
|
78
78
|
"packages/server/README.md": "a34cfaa4f4e859008b44e2be8e1aafb56e97fe83e6129c968a8562945fa843f3",
|
|
79
79
|
"packages/ui/ai-profile-settings.d.ts": "0551b6c6eda6d18ea89a976daddf01b2161195f4874fe2d67a57cd9ecc6ce643",
|
|
80
80
|
"packages/ui/ai-profile-settings.js": "e92bbac67a1199d473e4e44b32d1cc76c5b6c79306d4fef9d9067a1bb6ec42ca",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"packages/ui/claude-terminal.js": "96bd217a4a7e4792ae3fbaaf46249ecee13b0f4e01fc6d25a1b8b22d8f8fe00c",
|
|
83
83
|
"packages/ui/index.d.ts": "e547ab39640fe7519fb367b78442dc8a2c51b2efd17b66f8b0a003b154b36ead",
|
|
84
84
|
"packages/ui/index.js": "f4394aabd395938f5c3fe587e430471d4033e83945a9b9c7e0149ad4040982c9",
|
|
85
|
-
"packages/ui/package.json": "
|
|
85
|
+
"packages/ui/package.json": "a07586cb53b6eb77c4c5af51bcc81509c753e4bbf715a7359e466cec2ba80012",
|
|
86
86
|
"packages/ui/README.md": "793c4a9c286fee23f39062a1976ca3afe9d2816dcdb55e156ba940e1ef514f89",
|
|
87
87
|
"packages/ui/theme.css": "8f03a081272a7db11b196f7a70d0fafb1f8d954778f329f1ba53d110d9d5fd98",
|
|
88
88
|
"src/assets/claude-prompts.json": "cb111f2e0a45afe8a31c251a504d1ab5375966ed61291660e61fd2efd77f6d9f",
|
|
@@ -112,9 +112,9 @@
|
|
|
112
112
|
"vectorHub": {
|
|
113
113
|
"name": "@sanqianx/vector-hub",
|
|
114
114
|
"installName": "@sanqianx/vector-hub",
|
|
115
|
-
"version": "1.0.8-rc.
|
|
115
|
+
"version": "1.0.8-rc.3",
|
|
116
116
|
"files": {
|
|
117
|
-
"README.md": "
|
|
117
|
+
"README.md": "da267117f2f3cf61bc865a6fa3a2097dcb999f7f7e50eb7add0b74494a9f982e",
|
|
118
118
|
"dist/cjs/bin.d.ts": "43e818adf60173644896298637f47b01d5819b17eda46eaa32d0c7d64724d012",
|
|
119
119
|
"dist/cjs/bin.js": "8d0317ce087d22074b4ad350d63bf39cd6d218da565496621cd0daf4245cbdc4",
|
|
120
120
|
"dist/cjs/bin.js.map": "818e872380d9c7ba045238ba65ce77111ba822b356be26327ed05936e5667e5e",
|
|
@@ -122,12 +122,12 @@
|
|
|
122
122
|
"dist/cjs/core/embeddings.js": "5e199fd6e0de174aad1ba5440dd6358d9b180d4f82d62c7d8cd1fb8a17efb2d7",
|
|
123
123
|
"dist/cjs/core/embeddings.js.map": "f1b9ec78274cdd7359a59e8a47af7c376de61587bc4b9fc78df970734169ee38",
|
|
124
124
|
"dist/cjs/core/hub.d.ts": "5173ac7396ffdce780fa0b93428a330142a1a2edf378527b6291036edc9a0d7d",
|
|
125
|
-
"dist/cjs/core/hub.js": "
|
|
126
|
-
"dist/cjs/core/hub.js.map": "
|
|
127
|
-
"dist/cjs/core/kb-sync.d.ts": "
|
|
128
|
-
"dist/cjs/core/kb-sync.js": "
|
|
129
|
-
"dist/cjs/core/kb-sync.js.map": "
|
|
130
|
-
"dist/cjs/core/types.d.ts": "
|
|
125
|
+
"dist/cjs/core/hub.js": "f90ee514c0cf9ac67d7aae728d7af2606d7404ab84436fbdc066663006cb67e2",
|
|
126
|
+
"dist/cjs/core/hub.js.map": "10d5d99d4ca8edb6af289b9da28eb63037ffa6762f382434040292d517d887fa",
|
|
127
|
+
"dist/cjs/core/kb-sync.d.ts": "8290ae4c5840dcf98769cfec1734d5ebeb102020b348da2a8382b1cad057da1b",
|
|
128
|
+
"dist/cjs/core/kb-sync.js": "b6bc7f11d3e63894fe8633151ac38c853f30d8675a15e57605bd8ff68bffd4b5",
|
|
129
|
+
"dist/cjs/core/kb-sync.js.map": "687ce74cdf668c1b99809a23c078861e66dacd0050056ff0231d69c3d6e6b85a",
|
|
130
|
+
"dist/cjs/core/types.d.ts": "b54fd90a3aaef7b90fe106857de350b2300b7eaa46a5bfb6b4959bfe09faa554",
|
|
131
131
|
"dist/cjs/core/types.js": "b0d2bc4142d0c62d43f996aaeb64f22c4889ac853f8a3765758b505d972d0149",
|
|
132
132
|
"dist/cjs/core/types.js.map": "9c8da204af4d662188e539b27e61f9ee44ecbe68da4211676ebdda14303e6f3b",
|
|
133
133
|
"dist/cjs/index.d.ts": "96aa5a8b9c843a85c7cdc00901e0e95ecae6f01655e7de2d965f96b1d19181a0",
|
|
@@ -136,12 +136,12 @@
|
|
|
136
136
|
"dist/cjs/server/folder-picker.d.ts": "b9c59dbc834fff668b1894f516450c2edc985c7bfac31edf9b7b4e766474b32b",
|
|
137
137
|
"dist/cjs/server/folder-picker.js": "053121152a210c899e8593d60670a2a510b435c3b479300c572aa4bc8443e58e",
|
|
138
138
|
"dist/cjs/server/folder-picker.js.map": "dce1ad1b03c8a6a4fba7b3cacec5b65e55c1dfd1886c5e70df384efd1a5c68a6",
|
|
139
|
-
"dist/cjs/server/manager.d.ts": "
|
|
140
|
-
"dist/cjs/server/manager.js": "
|
|
141
|
-
"dist/cjs/server/manager.js.map": "
|
|
139
|
+
"dist/cjs/server/manager.d.ts": "06c0e5c05ad47b3ab0a826303703d1b38d68e45f3686e687d832470dac56a05b",
|
|
140
|
+
"dist/cjs/server/manager.js": "947aba093ae79b294a67d8ebd10ceedcc5ff92a3298dfffbc09681f66eed4448",
|
|
141
|
+
"dist/cjs/server/manager.js.map": "d5236999f576019189c8464c4a9aaf1776902da2c5c09d962b557210cc3d8427",
|
|
142
142
|
"dist/cjs/server/server.d.ts": "13288ace9aa6ec016a2b06dc036868324ac6c23c7e73771e95d120be778f238a",
|
|
143
|
-
"dist/cjs/server/server.js": "
|
|
144
|
-
"dist/cjs/server/server.js.map": "
|
|
143
|
+
"dist/cjs/server/server.js": "18cb8c4ada3a7b4c8fa20a2bdfcee2d7bff440ab1d0caabf7c6eaed2b5178bed",
|
|
144
|
+
"dist/cjs/server/server.js.map": "e7a4689fdaedcea9644c0c1a3065cce71017af9aa13be6cb4a90889eae0e7885",
|
|
145
145
|
"dist/esm/bin.d.ts": "43e818adf60173644896298637f47b01d5819b17eda46eaa32d0c7d64724d012",
|
|
146
146
|
"dist/esm/bin.js": "69f6059ce2336eb7f0d9503f86c83da787aaf74a8bfd804c694ba5b2e1728be0",
|
|
147
147
|
"dist/esm/bin.js.map": "fc174c2b5c66c20ff88184ddeb00577406185945ccadb0bddbb2014909339e9e",
|
|
@@ -149,12 +149,12 @@
|
|
|
149
149
|
"dist/esm/core/embeddings.js": "b19e0cf42ca03e50644e39e39da4c003b9e273213c4532bd443c77085d2db521",
|
|
150
150
|
"dist/esm/core/embeddings.js.map": "043aa6822e6b4a692be025d89efb5d7d44f4b6b39ce36bcfad2e64dde45589bb",
|
|
151
151
|
"dist/esm/core/hub.d.ts": "5173ac7396ffdce780fa0b93428a330142a1a2edf378527b6291036edc9a0d7d",
|
|
152
|
-
"dist/esm/core/hub.js": "
|
|
153
|
-
"dist/esm/core/hub.js.map": "
|
|
154
|
-
"dist/esm/core/kb-sync.d.ts": "
|
|
155
|
-
"dist/esm/core/kb-sync.js": "
|
|
156
|
-
"dist/esm/core/kb-sync.js.map": "
|
|
157
|
-
"dist/esm/core/types.d.ts": "
|
|
152
|
+
"dist/esm/core/hub.js": "ea14e891ce2b1861b2a2a1cc9acf0d4249fab2b3354694cfbe519a26f4199453",
|
|
153
|
+
"dist/esm/core/hub.js.map": "a02798e02a9b4a4c41e7496c23d12c12bee25b3ed6adccb0fde91a5b757f70a6",
|
|
154
|
+
"dist/esm/core/kb-sync.d.ts": "8290ae4c5840dcf98769cfec1734d5ebeb102020b348da2a8382b1cad057da1b",
|
|
155
|
+
"dist/esm/core/kb-sync.js": "cbdfab9fecbdd2d6996744ad0bf88f446012d5616f57fdc60f5a8e8d8c462c5f",
|
|
156
|
+
"dist/esm/core/kb-sync.js.map": "e14d56e04a08642f6fb917310cf296ab2cf05c6a2fc6a2cab8ab929f50300843",
|
|
157
|
+
"dist/esm/core/types.d.ts": "b54fd90a3aaef7b90fe106857de350b2300b7eaa46a5bfb6b4959bfe09faa554",
|
|
158
158
|
"dist/esm/core/types.js": "01ae2a5b120382f9a648ced7ee8507493a134f216d100fc61600c6c9738235d2",
|
|
159
159
|
"dist/esm/core/types.js.map": "9c8da204af4d662188e539b27e61f9ee44ecbe68da4211676ebdda14303e6f3b",
|
|
160
160
|
"dist/esm/index.d.ts": "96aa5a8b9c843a85c7cdc00901e0e95ecae6f01655e7de2d965f96b1d19181a0",
|
|
@@ -164,13 +164,13 @@
|
|
|
164
164
|
"dist/esm/server/folder-picker.d.ts": "b9c59dbc834fff668b1894f516450c2edc985c7bfac31edf9b7b4e766474b32b",
|
|
165
165
|
"dist/esm/server/folder-picker.js": "6c8247e431b7181c45fb169b29e5825a0b3589f44e1625b3a5a291d7b8358042",
|
|
166
166
|
"dist/esm/server/folder-picker.js.map": "f70723213f032a0f10f538289a176a50fc8f02c200b0194479ffc139b5fbb13b",
|
|
167
|
-
"dist/esm/server/manager.d.ts": "
|
|
168
|
-
"dist/esm/server/manager.js": "
|
|
169
|
-
"dist/esm/server/manager.js.map": "
|
|
167
|
+
"dist/esm/server/manager.d.ts": "06c0e5c05ad47b3ab0a826303703d1b38d68e45f3686e687d832470dac56a05b",
|
|
168
|
+
"dist/esm/server/manager.js": "96e1262505a02299e3d6ae5dc1c7ba3a3f69c694f57d89d97e3e4c163b40523e",
|
|
169
|
+
"dist/esm/server/manager.js.map": "10bb43dec7cdf07d1e6260552f40ca347a6eb491ba8bbe81b6ce2a41157a9a87",
|
|
170
170
|
"dist/esm/server/server.d.ts": "13288ace9aa6ec016a2b06dc036868324ac6c23c7e73771e95d120be778f238a",
|
|
171
|
-
"dist/esm/server/server.js": "
|
|
172
|
-
"dist/esm/server/server.js.map": "
|
|
173
|
-
"package.json": "
|
|
171
|
+
"dist/esm/server/server.js": "fcf74d2d6ddc19be2ceab561efc904a2b703507901a6a35c3646c4032cac56d6",
|
|
172
|
+
"dist/esm/server/server.js.map": "6c19f311146c60a1ccea89ac223a16ca44e690ac5ac4d8408cd19edc7066f83e",
|
|
173
|
+
"package.json": "5010182529b9f1d4624ff4abd02b8eb48dbbeb4ddf92ef4bc45dd6b332aed100",
|
|
174
174
|
"ui/index.html": "7301a595d625dadcd424069e7bb978004bfb6118eddef7a223bd25449ca6e0eb",
|
|
175
175
|
"ui/vendor/marked.min.js": "c68075672d976e4777390560baa112194855bd4404b13647da4855aae1f9360c"
|
|
176
176
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanqianx/project-knowledge",
|
|
3
|
-
"version": "5.0.9
|
|
3
|
+
"version": "5.0.9",
|
|
4
4
|
"description": "Knowledge base manager with Git integration, AI-driven analysis, and bilingual (zh-CN/en-US) knowledge output",
|
|
5
5
|
"main": "_site/server.js",
|
|
6
6
|
"scripts": {
|
|
@@ -81,10 +81,10 @@
|
|
|
81
81
|
"@lancedb/lancedb": "0.31.0",
|
|
82
82
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
83
83
|
"@sanqianx/ai-coding-event-bridge-console": "0.7.2",
|
|
84
|
-
"@sanqianx/vector-hub": "1.0.8-rc.
|
|
84
|
+
"@sanqianx/vector-hub": "1.0.8-rc.3",
|
|
85
85
|
"buffer": "^6.0.3",
|
|
86
86
|
"cheerio": "^1.2.0",
|
|
87
|
-
"claude-ai-workbench": "npm:@sanqianx/claude-ai-workbench@0.13.2-rc.
|
|
87
|
+
"claude-ai-workbench": "npm:@sanqianx/claude-ai-workbench@0.13.2-rc.7",
|
|
88
88
|
"cross-spawn": "7.0.6",
|
|
89
89
|
"dotenv": "^17.4.2",
|
|
90
90
|
"gpt-tokenizer": "^3.4.0",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "project-knowledge",
|
|
4
|
-
"version": "5.0.9
|
|
4
|
+
"version": "5.0.9",
|
|
5
5
|
"description": "Read durable project decisions and implementation history from Project Knowledge.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "SanQianX",
|
package/ui/product.js
CHANGED
|
@@ -160,6 +160,7 @@
|
|
|
160
160
|
if (epoch !== registryEpoch) return;
|
|
161
161
|
state.brain = brain.brain; state.root = brain.knowledgeRoot; state.projects = projects.projects; state.health = health.modules; state.ownership = health.ownership;
|
|
162
162
|
state.indexStorage = { path: brain.indexStoragePath, ...brain.configuration?.indexStorage };
|
|
163
|
+
state.scopeStatus = brain.configuration?.scopeStatus;
|
|
163
164
|
if (!activeProject()) { state.projectId = state.projects[0]?.projectId || ''; state.epoch++; Object.keys(frames).forEach(frameContext); }
|
|
164
165
|
renderProjectOptions(); renderSystem(); connectProgress(); await refreshSnapshot(); setView(state.view);
|
|
165
166
|
} catch (error) { toast(error.message); }
|
|
@@ -177,6 +178,7 @@
|
|
|
177
178
|
$('systemTitle').textContent = pending ? '知识引擎连接待恢复' : down.length ? '部分服务需要处理' : '系统连接正常';
|
|
178
179
|
$('systemDetail').textContent = pending ? `${state.ownership.message || '服务恢复后自动重试。'}${state.ownership.error ? ` · ${state.ownership.error.step} (${state.ownership.error.code})` : ''}` : down.length ? down.map(k => modules[k]).join('、') : '本机知识服务已连接';
|
|
179
180
|
if (storagePending && !pending && !down.length) { $('systemDot').className = 'dot issue'; $('systemTitle').textContent = '索引目录接线待恢复'; $('systemDetail').textContent = `目标目录:${state.indexStorage.expectedPath}。请在系统状态中修复接线。`; }
|
|
181
|
+
if (state.root && state.scopeStatus?.status === 'pending' && !pending && !down.length) { $('systemDot').className = 'dot issue'; $('systemTitle').textContent = '共享知识接线待恢复'; $('systemDetail').textContent = '已有知识与长期记忆保留,请在系统状态中修复接线。'; }
|
|
180
182
|
$('moduleHealth').innerHTML = Object.entries(modules).map(([key, name]) => `<div class="row"><div><b>${esc(name)}</b><small>${esc(state.health[key]?.url || '')}</small></div>${badge(state.health[key]?.up ? '在线' : '离线', state.health[key]?.up ? 'up' : 'issue')}</div>`).join('');
|
|
181
183
|
if (!navigator.onLine) {
|
|
182
184
|
$('systemDot').className = 'dot issue'; $('systemTitle').textContent = '网页连接中断';
|
|
@@ -385,7 +387,7 @@
|
|
|
385
387
|
on('embeddingForm', 'submit', async event => { event.preventDefault(); await saveEmbedding(); });
|
|
386
388
|
on('disableEmbedding', 'click', () => saveEmbedding(false));
|
|
387
389
|
on('embeddingDialog', 'close', () => { $('embeddingSecret').value = ''; });
|
|
388
|
-
on('saveBrainSettings', 'click', async () => { await api('/api/brain', { method: 'PATCH', body: { name: $('brainName').value } }); const saved = await api('/api/settings', { method: 'PATCH', body: { knowledge: { rootPath: $('knowledgeRoot').value } } }); toast(saved.configuration?.indexStorage?.status === 'ready' ? '设置已保存,索引目录已生效;已有知识未移动。' : '设置已保存,索引目录接线待恢复;已有知识保留。'); await refreshRegistry(true); if (resumeImport) closeSystem(); });
|
|
390
|
+
on('saveBrainSettings', 'click', async () => { await api('/api/brain', { method: 'PATCH', body: { name: $('brainName').value } }); const saved = await api('/api/settings', { method: 'PATCH', body: { knowledge: { rootPath: $('knowledgeRoot').value } } }); toast(saved.configuration?.scopeStatus?.status === 'pending' ? '设置已保存,共享知识接线待恢复;已有知识保留。' : saved.configuration?.indexStorage?.status === 'ready' ? '设置已保存,索引目录已生效;已有知识未移动。' : '设置已保存,索引目录接线待恢复;已有知识保留。'); await refreshRegistry(true); if (resumeImport) closeSystem(); });
|
|
389
391
|
on('setupImportRoot', 'click', async () => { $('importDialog').close(); resumeImport = true; await openSystem(); $('knowledgeRoot').focus(); });
|
|
390
392
|
on('pickRoot', 'click', () => pick('knowledgeRoot')); on('pickCode', 'click', () => pick('importCode')); on('pickKnowledge', 'click', () => pick('importKnowledge'));
|
|
391
393
|
on('openEngine', 'click', () => { closeSystem(); initializeFrame('engine'); $('engineDialog').showModal(); post('engine', { type: 'kb:visibility', visible: true }); });
|
|
@@ -449,7 +451,7 @@
|
|
|
449
451
|
renderSystem(); render();
|
|
450
452
|
});
|
|
451
453
|
window.addEventListener('online', () => {
|
|
452
|
-
$('activityConnection').textContent = '正在恢复快照'; refreshRegistry();
|
|
454
|
+
$('activityConnection').textContent = state.engineAvailable && state.stream?.readyState === EventSource.OPEN ? '已连接实时更新' : '正在恢复快照'; refreshRegistry();
|
|
453
455
|
if ($('logsDialog').open) loadLogs().catch(error => toast(error.message));
|
|
454
456
|
});
|
|
455
457
|
window.addEventListener('beforeunload', () => { state.stream?.close(); logStream?.close(); });
|