@sanqianx/project-knowledge 5.0.8 → 5.0.9-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/CHANGELOG.md +25 -0
- package/_modules/knowledge-engine/README.md +2 -0
- package/_modules/knowledge-engine/package.json +1 -1
- package/_modules/knowledge-engine/src/coordinator.js +68 -36
- package/_modules/knowledge-engine/src/diagnostics.js +1 -1
- package/_modules/knowledge-engine/src/index-request.js +49 -0
- package/_modules/knowledge-engine/src/runtime/conversation-store.js +1 -1
- package/_modules/knowledge-engine/src/server.js +1 -1
- package/_modules/knowledge-engine/ui/app.js +1 -1
- package/_site/_test/candidate-release-contract-test.js +14 -0
- package/_site/_test/conversation-store-test.js +2 -0
- package/_site/_test/fixtures/make-git-repos.js +1 -9
- package/_site/_test/index-error-display-contract-test.js +10 -0
- package/_site/_test/product-brand-contract-test.js +25 -0
- package/_site/_test/release-version-sync-test.js +7 -1
- package/_site/_test/sessions-stream-test.js +3 -2
- package/_site/_test/simple-import-test.js +3 -2
- package/_site/_test/team-knowledge-store-test.js +3 -2
- package/_site/lib/conversation-store.js +1 -1
- package/_site/scripts/pack-module-candidates.js +1 -1
- package/_site/scripts/sync-release-version.js +12 -10
- package/_site/scripts/verify-module-runtime.js +7 -2
- package/_site/scripts/verify-product-runtime.js +1 -0
- package/module-runtime-manifest.json +28 -27
- package/package.json +6 -6
- 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/favicon.svg +5 -38
- package/ui/index.html +2 -2
- package/ui/product.css +1 -1
- package/ui/product.js +1 -1
- package/vendor-manifest.json +9 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.0.9-rc.2] - 2026-10-11
|
|
4
|
+
|
|
5
|
+
- Pin the verified Workbench 0.13.2-rc.1 and vector-hub 1.0.8-rc.1 candidates.
|
|
6
|
+
Normalize known official MiniMax embedding route aliases at runtime without
|
|
7
|
+
changing existing v5 configuration bytes or private gateway paths.
|
|
8
|
+
- Queue same-project index writes across embedding-model rebuilds. Failed writes
|
|
9
|
+
do not poison retries, and unrelated projects remain independent.
|
|
10
|
+
- Restore safe relative Markdown document navigation and verify the standalone
|
|
11
|
+
retrieval UI at 1920x1080. Tests never kill unrelated port owners.
|
|
12
|
+
- Isolate regression-suite ports and tolerate Windows fixture file release.
|
|
13
|
+
Candidate publication is not full product acceptance; latest remains unchanged.
|
|
14
|
+
|
|
15
|
+
## [5.0.9-rc.1] - 2026-10-11
|
|
16
|
+
|
|
17
|
+
- Retain classified, bounded and redacted index errors in run details, activity
|
|
18
|
+
and local log/v2 exports. Index-only retries preserve promoted files and do not
|
|
19
|
+
invoke the analysis model again.
|
|
20
|
+
- Accept ZCode conversation evidence, isolate per-project journal failures and
|
|
21
|
+
report the exact blocked sequence without dropping evidence or flooding logs.
|
|
22
|
+
- Synchronize production asset hashes, including the shared favicon. Permit
|
|
23
|
+
exact rc module pins but reject tags, ranges, links and source overrides.
|
|
24
|
+
- Prepare a web-only candidate; latest is not promoted by this release. Existing
|
|
25
|
+
v5 data and third-party integrations are preserved; system acceptance remains
|
|
26
|
+
a separate gate from publication.
|
|
27
|
+
|
|
3
28
|
## [5.0.8] - 2026-10-10
|
|
4
29
|
|
|
5
30
|
- Repair managed Codex notify chains after scoped npm package or Bridge-home
|
|
@@ -67,6 +67,7 @@ node src/bin.js serve [--port 5790] [--host 127.0.0.1] [--data DIR]
|
|
|
67
67
|
## 真实运行与恢复边界
|
|
68
68
|
|
|
69
69
|
- journal 游标按来源隔离;证据投影及可恢复任务先落盘,再 ACK。不等待模型才能消费下一条事件。
|
|
70
|
+
- 会话来源接受 bridge 的四客户端契约(Claude Code、Codex、OpenCode、ZCode)。单个 journal 的非法证据不被跳过或 ACK,但不阻塞其他项目;失败位置和错误码写入日志,健康接口提供 `journal.failures`,重复轮询不刷屏,恢复后续接原游标。
|
|
70
71
|
- 不同项目最多并发 2 个分析,同项目按提交顺序执行。未晋升的失败提交会阻挡本项目后续提交,不阻挡其他项目。
|
|
71
72
|
- 无头分析在隔离进程中运行,只读冻结证据,只写本次 staging。产物必须通过路径、内容 hash 和依据校验,只有晋升路径写最终项目知识。
|
|
72
73
|
- 归档对话和检索内容只作证据,不能覆盖后台维护指令。SDK 返回成功文字但未生成 manifest 时,记录 `ANALYSIS_OUTPUT_MISSING` 并保留证据供重试,不误报晋升完成。
|
|
@@ -74,6 +75,7 @@ node src/bin.js serve [--port 5790] [--host 127.0.0.1] [--data DIR]
|
|
|
74
75
|
- 具体失败原因、失败步骤和提交依据进入运行详情及 `log/v2` 诊断。受外壳守护时通过 IPC 交给外壳现有日志系统;独立运行保存在 `<data>/logs/`,不写模型凭据和原始证据。升级不会重新运行已失败提交,用户可显式重试失败阶段。
|
|
75
76
|
- 数据目录有单实例租约,目标知识目录有写入锁。关闭或超时等待子进程实际退出,再释放写入锁。
|
|
76
77
|
- 晋升与索引分别记录。索引失败保留已晋升知识并显示待修复,不重新调用模型。
|
|
78
|
+
- 索引错误保留兼容的 `INDEX_FAILED` 及可选 `error.details`(类别、HTTP 状态、上游代码/脱敏原因、请求标识、耗时和尝试次数);项目和共享索引均写入 `log/v2`。响应仅解析有界 JSON,不记录原始 body 或认证头。只重试索引保留原分析运行、晋升时间和知识字节。
|
|
77
79
|
- 项目、领域、智脑检索使用包含范围。向量只提供排序,显示内容必须来自当前真实文件;索引不可用时降级为文件检索。
|
|
78
80
|
- 候选必须有已晋升文件中的引用依据,未确认候选不能进入任务。确认记录原子持久化后写共享记忆文件,派生索引异步更新,索引失败不会撤销有效记忆。
|
|
79
81
|
- 旧格式不读取、不自动迁移。损坏的正式状态不覆盖、不清空。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanqianx/ai-coding-knowledge-engine",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3-rc.1",
|
|
4
4
|
"description": "Knowledge engine module for project-knowledge: commit-boundary triggered knowledge analysis/generation (trigger plane) and retrieval for development assistants (retrieval plane).",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "src/server.js",
|
|
@@ -15,6 +15,7 @@ const { MemoryService } = require('./memories');
|
|
|
15
15
|
const { execGit } = require('./runtime/git-runner');
|
|
16
16
|
const { createDiagnostics } = require('./diagnostics');
|
|
17
17
|
const { finalizeAnalysisOutput } = require('./analysis-output');
|
|
18
|
+
const { requestIndex } = require('./index-request');
|
|
18
19
|
|
|
19
20
|
class EngineCoordinator extends EventEmitter {
|
|
20
21
|
constructor(store, options = {}) {
|
|
@@ -28,6 +29,7 @@ class EngineCoordinator extends EventEmitter {
|
|
|
28
29
|
this.concurrency = Math.max(1, Math.min(2, Number(options.concurrency) || 2));
|
|
29
30
|
this.active = new Map();
|
|
30
31
|
this.jobs = new Set();
|
|
32
|
+
this.journalFailures = new Map();
|
|
31
33
|
this.initializeHook = options.initializeHook;
|
|
32
34
|
this.profiles = new Map();
|
|
33
35
|
const diagnostics = createDiagnostics({ dataDir: store.dataDir, secrets: () => [...this.profiles.values()].flatMap(profile => [profile.apiKey, profile.authToken]) });
|
|
@@ -123,34 +125,58 @@ class EngineCoordinator extends EventEmitter {
|
|
|
123
125
|
this.draining = true;
|
|
124
126
|
try {
|
|
125
127
|
await this.bridge.registerConsumer('knowledge-engine', { journals: ['*'] });
|
|
126
|
-
|
|
128
|
+
const journals = await this.bridge.listJournals();
|
|
129
|
+
this.journalFailures.delete('connection');
|
|
130
|
+
for (const journal of journals) {
|
|
127
131
|
if (this.closed) break;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|| ((journal.journalId === 'global' || !p.journalId) && event.repoIdentity?.workspaceRoot && this.layout.pathsEqual(p.repoPath, event.repoIdentity.workspaceRoot))));
|
|
135
|
-
if (p) {
|
|
136
|
-
if (event.schema === 'ai-coding-event/v1' && ['user_prompt', 'assistant_response'].includes(event.eventType)) await this.conversations.appendBridgeEvent(p.projectId, { ...event, journalId: journal.journalId });
|
|
137
|
-
if (event.schema === 'git-commit-boundary/v1' && /^[a-f0-9]{7,64}$/i.test(event.commitSha || '')) {
|
|
138
|
-
if (!fs.existsSync(this.layout.getProjectCommitBoundaryPath(p.projectId, event.commitSha))) this.conversations.writeBoundary(p.projectId, { ...event, journalId: journal.journalId, parentShas: event.parents || [], bridgeCursorAtCommit: event.sequence, journalSequence: event.sequence, operationId: event.operationId || `bridge-${journal.journalId}-${event.sequence}`, committedAt: event.committedAt || event.capturedAt });
|
|
139
|
-
const baseline = p.runtime.lastAnalyzedCommit || p.trackingStartCommit;
|
|
140
|
-
const completed = this.reconciler.processingLedger.read(p.projectId, event.commitSha);
|
|
141
|
-
const historical = baseline && (baseline === event.commitSha || (await execGit(p.repoPath, ['merge-base', '--is-ancestor', event.commitSha, baseline])).ok);
|
|
142
|
-
if (!completed && !historical && this.store.getProject(p.projectId)?.enabled) this.enqueue({ projectId: p.projectId, commitSha: event.commitSha, trigger: 'git-hook', journalId: journal.journalId, journalSequence: event.sequence });
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
through = event.sequence;
|
|
132
|
+
try {
|
|
133
|
+
await this.consumeJournal(journal);
|
|
134
|
+
if (this.journalFailures.delete(journal.journalId)) await this.diagnostics.info('knowledge.journal.recovered', '项目事件消费已恢复。', { phase: 'consuming', context: { journalId: journal.journalId } });
|
|
135
|
+
} catch (error) {
|
|
136
|
+
// Never ACK invalid evidence; a project fault must not starve other journals.
|
|
137
|
+
await this.recordJournalFailure(journal.journalId, error);
|
|
146
138
|
}
|
|
147
|
-
// All evidence projections and queued jobs are durable before ACK.
|
|
148
|
-
if (through > cursor) this.store.setJournalCursor(journal.journalId, through);
|
|
149
|
-
await this.bridge.ackConsumerCursor('knowledge-engine', through, { journalId: journal.journalId });
|
|
150
139
|
}
|
|
151
|
-
this.
|
|
152
|
-
} catch { this.
|
|
153
|
-
finally {
|
|
140
|
+
for (const id of this.journalFailures.keys()) if (!journals.some(j => j.journalId === id)) this.journalFailures.delete(id);
|
|
141
|
+
} catch (error) { await this.recordJournalFailure('connection', error); }
|
|
142
|
+
finally {
|
|
143
|
+
this.journalError = this.journalFailures.size ? '事件消费异常,未消费证据与游标已保留,请查看诊断日志。' : null;
|
|
144
|
+
this.draining = false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async recordJournalFailure(journalId, error) {
|
|
148
|
+
const context = error.journalContext || {};
|
|
149
|
+
const failure = { journalId, sequence: context.sequence ?? null, projectId: context.projectId || null, code: error.code || 'JOURNAL_FAILED', message: this.safeMessage(error.message) };
|
|
150
|
+
if (JSON.stringify(this.journalFailures.get(journalId)) !== JSON.stringify(failure)) {
|
|
151
|
+
this.journalFailures.set(journalId, failure);
|
|
152
|
+
await this.diagnostics.error('knowledge.journal.failed', '事件消费失败,已保留未确认的证据与游标。', { projectId: failure.projectId, phase: 'consuming', error, context: { ...failure, source: context.source || null } });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async consumeJournal(journal) {
|
|
156
|
+
const cursor = this.store.state.journalCursors[journal.journalId] || 0;
|
|
157
|
+
const events = await this.bridge.readEvents({ journalId: journal.journalId, fromSequence: cursor + 1, limit: 100 });
|
|
158
|
+
let through = cursor;
|
|
159
|
+
for (const event of events) {
|
|
160
|
+
if (this.closed) break;
|
|
161
|
+
const p = this.store.listProjects().find(p => p.enabled !== false && ((journal.journalId !== 'global' && p.journalId === journal.journalId)
|
|
162
|
+
|| ((journal.journalId === 'global' || !p.journalId) && event.repoIdentity?.workspaceRoot && this.layout.pathsEqual(p.repoPath, event.repoIdentity.workspaceRoot))));
|
|
163
|
+
try {
|
|
164
|
+
if (p) {
|
|
165
|
+
if (event.schema === 'ai-coding-event/v1' && ['user_prompt', 'assistant_response'].includes(event.eventType)) await this.conversations.appendBridgeEvent(p.projectId, { ...event, journalId: journal.journalId });
|
|
166
|
+
if (event.schema === 'git-commit-boundary/v1' && /^[a-f0-9]{7,64}$/i.test(event.commitSha || '')) {
|
|
167
|
+
if (!fs.existsSync(this.layout.getProjectCommitBoundaryPath(p.projectId, event.commitSha))) this.conversations.writeBoundary(p.projectId, { ...event, journalId: journal.journalId, parentShas: event.parents || [], bridgeCursorAtCommit: event.sequence, journalSequence: event.sequence, operationId: event.operationId || `bridge-${journal.journalId}-${event.sequence}`, committedAt: event.committedAt || event.capturedAt });
|
|
168
|
+
const baseline = p.runtime.lastAnalyzedCommit || p.trackingStartCommit;
|
|
169
|
+
const completed = this.reconciler.processingLedger.read(p.projectId, event.commitSha);
|
|
170
|
+
const historical = baseline && (baseline === event.commitSha || (await execGit(p.repoPath, ['merge-base', '--is-ancestor', event.commitSha, baseline])).ok);
|
|
171
|
+
if (!completed && !historical && this.store.getProject(p.projectId)?.enabled) this.enqueue({ projectId: p.projectId, commitSha: event.commitSha, trigger: 'git-hook', journalId: journal.journalId, journalSequence: event.sequence });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
} catch (error) { error.journalContext = { projectId: p?.projectId, sequence: event.sequence, source: event.source }; throw error; }
|
|
175
|
+
through = event.sequence;
|
|
176
|
+
}
|
|
177
|
+
// All evidence projections and queued jobs are durable before ACK.
|
|
178
|
+
if (through > cursor) this.store.setJournalCursor(journal.journalId, through);
|
|
179
|
+
await this.bridge.ackConsumerCursor('knowledge-engine', through, { journalId: journal.journalId });
|
|
154
180
|
}
|
|
155
181
|
pump() {
|
|
156
182
|
if (this.closed || !this.executionEnabled) return;
|
|
@@ -198,16 +224,19 @@ class EngineCoordinator extends EventEmitter {
|
|
|
198
224
|
}
|
|
199
225
|
async index(run) {
|
|
200
226
|
const project = this.store.getProject(run.projectId);
|
|
227
|
+
const attempt = (run.indexAttempts || 0) + 1;
|
|
228
|
+
const context = { projectId: run.projectId, runId: run.runId, operationId: run.runId, commitSha: run.commitSha, phase: 'indexing' };
|
|
201
229
|
try {
|
|
202
|
-
this.store.updateRun(run.runId, { phase: 'indexing', indexState: 'running' });
|
|
203
|
-
|
|
204
|
-
const payload = await response.json();
|
|
205
|
-
if (!response.ok || payload.ok !== true) throw new Error('INDEX_FAILED');
|
|
230
|
+
this.store.updateRun(run.runId, { phase: 'indexing', indexState: 'running', indexAttempts: attempt });
|
|
231
|
+
await requestIndex({ baseUrl: this.vectorUrl, route: '/api/sync', body: { project: project.vectorProjectId || project.projectId, sourceDir: project.knowledgePath }, attempt, sanitize: this.safeMessage });
|
|
206
232
|
await this.store.updateRuntime(run.projectId, state => { state.index.dirty = false; state.index.status = 'ready'; });
|
|
207
233
|
this.store.updateRun(run.runId, { state: 'succeeded', phase: 'ready', indexState: 'succeeded', error: null, finishedAt: new Date().toISOString() });
|
|
234
|
+
await this.diagnostics.info('knowledge.index.completed', '知识向量索引已更新。', { ...context, context: { attempt } });
|
|
208
235
|
this.notify(run, '知识已更新并可检索');
|
|
209
|
-
} catch {
|
|
210
|
-
|
|
236
|
+
} catch (error) {
|
|
237
|
+
const details = error.details || { failureKind: 'LOCAL_ERROR', upstreamCode: error.code || 'UNKNOWN', upstreamMessage: this.safeMessage(error.message), attempt };
|
|
238
|
+
await this.diagnostics.error('knowledge.index.failed', '知识已更新,向量索引失败。', { ...context, error, context: details });
|
|
239
|
+
this.store.updateRun(run.runId, { state: 'index-pending', phase: 'index-pending', indexState: 'failed', finishedAt: new Date().toISOString(), error: { code: 'INDEX_FAILED', message: '知识已更新,索引待修复。', retryable: true, details } });
|
|
211
240
|
this.notify(run, '知识已更新,索引待修复');
|
|
212
241
|
}
|
|
213
242
|
}
|
|
@@ -220,6 +249,7 @@ class EngineCoordinator extends EventEmitter {
|
|
|
220
249
|
const job = this.sharedIndexQueue.catch(() => {}).then(async () => {
|
|
221
250
|
if (this.closed || !this.store.state.sharedScopes.some(s => s.id === scope.id)) return;
|
|
222
251
|
let index;
|
|
252
|
+
const attempt = (this.store.state.sharedScopes.find(s => s.id === scope.id)?.index?.attempts || 0) + 1;
|
|
223
253
|
try {
|
|
224
254
|
const files = require('./runtime/markdown-knowledge-indexer').listMarkdownFiles(scope.knowledgePath);
|
|
225
255
|
const digest = crypto.createHash('sha256');
|
|
@@ -235,11 +265,13 @@ class EngineCoordinator extends EventEmitter {
|
|
|
235
265
|
const payload = await health.json();
|
|
236
266
|
if (health.ok && payload.projects?.some(p => p.name === `scope-${scope.kind}-${scope.id}` && p.hasIndex && this.layout.pathsEqual(p.sourceDir || '', scope.knowledgePath))) return;
|
|
237
267
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
268
|
+
await requestIndex({ baseUrl: this.vectorUrl, route: '/api/import', body: { projectName: `scope-${scope.kind}-${scope.id}`, sourceDir: scope.knowledgePath, watch: false }, attempt, sanitize: this.safeMessage });
|
|
269
|
+
index = { status: 'ready', fingerprint, attempts: attempt, updatedAt: new Date().toISOString() };
|
|
270
|
+
} catch (error) {
|
|
271
|
+
const details = error.details || { failureKind: 'LOCAL_ERROR', upstreamCode: error.code || 'UNKNOWN', upstreamMessage: this.safeMessage(error.message), attempt };
|
|
272
|
+
index = { status: 'pending', message: '共享知识已保存,索引待恢复。', attempts: attempt, error: { code: 'INDEX_FAILED', details } };
|
|
273
|
+
await this.diagnostics.error('knowledge.shared_index.failed', index.message, { phase: 'indexing', operationId: `scope-index-${scope.id}-${attempt}`, error, context: { scopeId: scope.id, ...details } });
|
|
274
|
+
}
|
|
243
275
|
// Configuration can replace projection objects while the import awaits.
|
|
244
276
|
const current = this.store.state.sharedScopes.find(s => s.id === scope.id);
|
|
245
277
|
if (!current) return;
|
|
@@ -10,7 +10,7 @@ function createDiagnostics({ dataDir, component = 'knowledge-engine', secrets =
|
|
|
10
10
|
let text = String(value || '');
|
|
11
11
|
let values = []; try { values = secrets() || []; } catch {}
|
|
12
12
|
for (const secret of values) if (typeof secret === 'string' && secret.length >= 4) text = text.split(secret).join('[REDACTED]');
|
|
13
|
-
return text.replace(/(https?:\/\/)[^\s/@]+:[^\s/@]+@/gi, '$1[REDACTED]@').replace(/\b(Bearer|Basic)\s+[^\s]+/gi, '$1 [REDACTED]').replace(/((?:api[_-]?key|token|password|secret)
|
|
13
|
+
return text.replace(/(https?:\/\/)[^\s/@]+:[^\s/@]+@/gi, '$1[REDACTED]@').replace(/\b(Bearer|Basic)\s+[^\s]+/gi, '$1 [REDACTED]').replace(/(["']?(?:api[_-]?key|auth[_-]?token|token|password|secret)["']?\s*[=:]\s*)(?:"[^"]*"|'[^']*'|[^\s,;&]+)/gi, '$1[REDACTED]').replace(/\bsk[-_][A-Za-z0-9_-]{8,}/g, '[REDACTED]').slice(0, 8000);
|
|
14
14
|
};
|
|
15
15
|
const logger = { sanitize, close: async () => {} };
|
|
16
16
|
for (const level of ['debug', 'info', 'warn', 'error']) logger[level] = async (event, message, input = {}) => {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
async function requestIndex({ baseUrl, route, body, attempt = 1, timeoutMs = 120000, sanitize, fetchImpl = fetch }) {
|
|
4
|
+
const started = Date.now();
|
|
5
|
+
const clean = value => sanitize(String(value || '')).slice(0, 2048);
|
|
6
|
+
let httpStatus = null, requestId = null;
|
|
7
|
+
function failure(failureKind, upstreamCode, upstreamMessage) {
|
|
8
|
+
const details = { failureKind, httpStatus, upstreamCode: clean(upstreamCode).slice(0, 120), upstreamMessage: clean(upstreamMessage), requestId, durationMs: Math.max(0, Date.now() - started), attempt };
|
|
9
|
+
return Object.assign(new Error(`向量索引请求失败:${details.upstreamMessage}`), { code: 'INDEX_FAILED', phase: 'indexing', retryable: true, details });
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
const response = await fetchImpl(new URL(route, baseUrl), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: AbortSignal.timeout(timeoutMs) });
|
|
13
|
+
httpStatus = response.status;
|
|
14
|
+
requestId = clean(response.headers.get('x-request-id') || response.headers.get('request-id')).slice(0, 120) || null;
|
|
15
|
+
// Error responses can contain credentials or arbitrarily large HTML. Read
|
|
16
|
+
// a bounded JSON envelope, never persist a raw body or response headers.
|
|
17
|
+
const reader = response.body?.getReader();
|
|
18
|
+
let text = '', size = 0;
|
|
19
|
+
if (reader) {
|
|
20
|
+
const decoder = new TextDecoder();
|
|
21
|
+
try {
|
|
22
|
+
for (;;) {
|
|
23
|
+
const chunk = await reader.read();
|
|
24
|
+
if (chunk.done) break;
|
|
25
|
+
size += chunk.value.byteLength;
|
|
26
|
+
if (size > 16384) throw failure('INVALID_RESPONSE', 'RESPONSE_TOO_LARGE', '索引服务响应超过安全大小限制。');
|
|
27
|
+
text += decoder.decode(chunk.value, { stream: true });
|
|
28
|
+
}
|
|
29
|
+
text += decoder.decode();
|
|
30
|
+
} finally { await reader.cancel().catch(() => {}); }
|
|
31
|
+
}
|
|
32
|
+
let payload;
|
|
33
|
+
try { payload = JSON.parse(text); }
|
|
34
|
+
catch { throw failure('INVALID_RESPONSE', 'INVALID_JSON', `索引服务返回非 JSON 响应(HTTP ${httpStatus})。`); }
|
|
35
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) throw failure('INVALID_RESPONSE', 'INVALID_ENVELOPE', '索引服务响应不符合协议。');
|
|
36
|
+
if (!response.ok || payload.ok === false || payload.error) {
|
|
37
|
+
const upstream = payload.error;
|
|
38
|
+
const message = typeof upstream === 'string' ? upstream : upstream?.message || payload.message || `索引服务返回 HTTP ${httpStatus}。`;
|
|
39
|
+
throw failure(response.ok ? 'UPSTREAM_ERROR' : 'HTTP_ERROR', upstream?.code || payload.code || `HTTP_${httpStatus}`, message);
|
|
40
|
+
}
|
|
41
|
+
if (payload.ok !== true) throw failure('INVALID_RESPONSE', 'INVALID_ENVELOPE', '索引服务响应缺少成功标识。');
|
|
42
|
+
return payload;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error.details && error.code === 'INDEX_FAILED') throw error;
|
|
45
|
+
const timeout = ['TimeoutError', 'AbortError'].includes(error.name);
|
|
46
|
+
throw failure(timeout ? 'TIMEOUT' : 'NETWORK_ERROR', error.cause?.code || error.code || (timeout ? 'INDEX_TIMEOUT' : 'INDEX_CONNECTION_FAILED'), error.message || (timeout ? '索引服务请求超时。' : '无法连接索引服务。'));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
module.exports = { requestIndex };
|
|
@@ -3,7 +3,7 @@ const crypto = require('crypto');
|
|
|
3
3
|
const { SCHEMAS, DomainError, validateProjectId } = require('./contracts');
|
|
4
4
|
const AtomicFile = require('./atomic-file');
|
|
5
5
|
|
|
6
|
-
const SOURCES = new Set(['claude-code', 'codex', 'opencode']);
|
|
6
|
+
const SOURCES = new Set(['claude-code', 'codex', 'opencode', 'zcode']);
|
|
7
7
|
const ROLES = new Set(['user', 'assistant', null]);
|
|
8
8
|
const SNAPSHOT_STATUSES = new Set(['available', 'no-new-user-prompt', 'unavailable', 'ambiguous']);
|
|
9
9
|
const BINDING_KINDS = new Set(['direct', 'shared-spanning', 'explicit']);
|
|
@@ -100,7 +100,7 @@ function createEngineServer(options = {}) {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
async function api(method, pathname, search, body, store, coordinator, ctx) {
|
|
103
|
-
if (method === 'GET' && pathname === '/api/health') return { name: 'knowledge-engine', version: VERSION, dataSchemaVersion: store.state.schemaVersion, debug: ctx.debug, executionEnabled: coordinator.executionEnabled, uptimeSec: Math.floor((Date.now() - ctx.startedAt) / 1000), dataDir: ctx.dataDir, counts: { projects: store.state.projects.length, runs: store.state.runs.length, running: coordinator.active.size }, journal: { cursor: store.state.cursor, cursors: store.state.journalCursors, consumer: 'knowledge-engine', enabled: coordinator.consume, error: coordinator.journalError || null }, concurrency: coordinator.concurrency };
|
|
103
|
+
if (method === 'GET' && pathname === '/api/health') return { name: 'knowledge-engine', version: VERSION, dataSchemaVersion: store.state.schemaVersion, debug: ctx.debug, executionEnabled: coordinator.executionEnabled, uptimeSec: Math.floor((Date.now() - ctx.startedAt) / 1000), dataDir: ctx.dataDir, counts: { projects: store.state.projects.length, runs: store.state.runs.length, running: coordinator.active.size }, journal: { cursor: store.state.cursor, cursors: store.state.journalCursors, consumer: 'knowledge-engine', enabled: coordinator.consume, error: coordinator.journalError || null, failures: [...coordinator.journalFailures.values()] }, concurrency: coordinator.concurrency };
|
|
104
104
|
if (method === 'GET' && pathname === '/api/projects') return { projects: store.listProjects() };
|
|
105
105
|
if (method === 'GET' && pathname === '/api/memories') return { memories: coordinator.memories.list(Object.fromEntries(search)) };
|
|
106
106
|
const memoryMatch = pathname.match(/^\/api\/memories\/([^/]+)$/);
|
|
@@ -197,7 +197,7 @@ async function showRunDetail(runId) {
|
|
|
197
197
|
$('#runDetail').classList.remove('hidden');
|
|
198
198
|
$('#runDetailId').textContent = run.runId;
|
|
199
199
|
$('#runTimeline').innerHTML = runTimeline(run);
|
|
200
|
-
$('#runMeta').innerHTML = `<div><dt>状态</dt><dd>${stateBadge(run.state)}</dd></div><div><dt>项目 / commit</dt><dd>${escapeHtml(run.projectId)} · <span class="mono">${shortSha(run.commitSha)}</span></dd></div><div><dt>触发入口</dt><dd class="mono">${escapeHtml(run.trigger)}</dd></div><div><dt>错误</dt><dd>${run.error ? escapeHtml(run.error.message) : '–'}</dd></div>${run.error?.retryable ? '<div><dt>恢复</dt><dd><button class="btn" id="retryRun">重试失败阶段</button></dd></div>' : ''}`;
|
|
200
|
+
$('#runMeta').innerHTML = `<div><dt>状态</dt><dd>${stateBadge(run.state)}</dd></div><div><dt>项目 / commit</dt><dd>${escapeHtml(run.projectId)} · <span class="mono">${shortSha(run.commitSha)}</span></dd></div><div><dt>触发入口</dt><dd class="mono">${escapeHtml(run.trigger)}</dd></div><div><dt>错误</dt><dd>${run.error ? escapeHtml(run.error.message) : '–'}</dd></div>${run.error?.details ? `<div><dt>索引原因</dt><dd>${escapeHtml(run.error.details.upstreamMessage)}<br>${escapeHtml(run.error.details.failureKind)} · HTTP ${escapeHtml(run.error.details.httpStatus ?? '–')} · ${escapeHtml(run.error.details.upstreamCode)}<br>请求 ${escapeHtml(run.error.details.requestId || '–')} · 尝试 ${escapeHtml(run.error.details.attempt)}</dd></div>` : ''}${run.error?.retryable ? '<div><dt>恢复</dt><dd><button class="btn" id="retryRun">重试失败阶段</button></dd></div>' : ''}`;
|
|
201
201
|
$('#retryRun')?.addEventListener('click', async () => { await api(`/api/runs/${encodeURIComponent(runId)}/retry`, { method: 'POST' }); loadRuns(); });
|
|
202
202
|
$('#runStaging').textContent = run.staging ? JSON.stringify(run.staging, null, 2) : '该运行没有 staging 产物。';
|
|
203
203
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { isPinnedModuleVersion } = require('../scripts/verify-module-runtime');
|
|
6
|
+
for (const name of ['claude-ai-workbench', '@sanqianx/vector-hub', '@sanqianx/ai-coding-event-bridge-console']) {
|
|
7
|
+
for (const version of ['1.2.3', '1.2.3-rc.1', '1.2.3-rc.12']) assert.ok(isPinnedModuleVersion(name, version));
|
|
8
|
+
for (const version of ['latest', 'next', '^1.2.3', '~1.2.3', '>=1.2.3', 'file:../module', 'link:module', 'workspace:*', '1.2.3-beta.1', '1.2.3-rc', undefined]) assert.equal(isPinnedModuleVersion(name, version), false, String(version));
|
|
9
|
+
}
|
|
10
|
+
assert.ok(isPinnedModuleVersion('claude-ai-workbench', 'npm:@sanqianx/claude-ai-workbench@0.13.2-rc.1'));
|
|
11
|
+
assert.equal(isPinnedModuleVersion('@sanqianx/vector-hub', 'npm:@sanqianx/claude-ai-workbench@0.13.2-rc.1'), false);
|
|
12
|
+
const manifest = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../module-runtime-manifest.json')));
|
|
13
|
+
for (const file of ['ui/index.html', 'ui/product.js', 'ui/product.css', 'ui/favicon.svg']) assert.match(manifest.productFiles[file], /^[a-f0-9]{64}$/);
|
|
14
|
+
console.log('candidate-release-contract-test PASS');
|
|
@@ -45,6 +45,8 @@ const { ConversationStore, sha256 } = require('../lib/conversation-store');
|
|
|
45
45
|
assert.strictEqual(store.readEvents(projectId).length, 1);
|
|
46
46
|
assert.strictEqual(store.readEvents(projectId)[0].content, privatePrompt, 'full prompt belongs in the private business store');
|
|
47
47
|
assert.strictEqual(store.readEvents(projectId)[0].contentHash, sha256(privatePrompt));
|
|
48
|
+
await store.appendBridgeEvent(projectId, { ...store.readEvents(projectId)[0], eventId: 'event-zcode-1', sequence: 12, source: 'zcode' });
|
|
49
|
+
assert.strictEqual(store.readEvents(projectId).find(event => event.eventId === 'event-zcode-1').source, 'zcode');
|
|
48
50
|
assert(!JSON.stringify(logRecords).includes(privatePrompt), 'conversation body must never enter structured logs');
|
|
49
51
|
assert(JSON.stringify(logRecords).includes(sha256(privatePrompt)), 'logs may retain body hash for correlation');
|
|
50
52
|
|
|
@@ -49,15 +49,7 @@ function makeRepo({ kind = 'one-commit' } = {}) {
|
|
|
49
49
|
const commits = [];
|
|
50
50
|
const cleanup = () => {
|
|
51
51
|
if (path.dirname(repoPath) !== path.resolve(os.tmpdir()) || !path.basename(repoPath).startsWith(`kb-fixture-${kind}-${process.pid}-`)) throw new Error('Unsafe fixture cleanup');
|
|
52
|
-
|
|
53
|
-
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
54
|
-
const file = path.join(directory, entry.name);
|
|
55
|
-
if (entry.isDirectory() && !entry.isSymbolicLink()) remove(file);
|
|
56
|
-
else { try { fs.unlinkSync(file); } catch (error) { if (error.code !== 'EPERM') throw error; fs.chmodSync(file, 0o600); fs.unlinkSync(file); } }
|
|
57
|
-
}
|
|
58
|
-
fs.rmdirSync(directory);
|
|
59
|
-
}
|
|
60
|
-
if (fs.existsSync(repoPath)) remove(repoPath);
|
|
52
|
+
if (fs.existsSync(repoPath)) fs.rmSync(repoPath, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
61
53
|
};
|
|
62
54
|
|
|
63
55
|
if (kind === 'not-git') {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const source = fs.readFileSync(path.resolve(__dirname, '../../ui/product.js'), 'utf8');
|
|
6
|
+
for (const field of ['upstreamMessage', 'failureKind', 'upstreamCode', 'requestId', 'attempt', 'httpStatus', 'durationMs']) {
|
|
7
|
+
assert.ok(source.includes(`esc(r.error.details.${field}`), `index detail must be escaped: ${field}`);
|
|
8
|
+
}
|
|
9
|
+
assert.ok(source.includes("['failed', 'index-pending'].includes(r.state)"));
|
|
10
|
+
console.log('index-error-display-contract-test PASS (real rendering/export covered by engine test:web:faults)');
|
|
@@ -0,0 +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');
|
|
@@ -90,8 +90,14 @@ try {
|
|
|
90
90
|
]);
|
|
91
91
|
const scripts = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8')).scripts;
|
|
92
92
|
assert.strictEqual(scripts.preversion, 'npm run release:verify');
|
|
93
|
-
assert.strictEqual(scripts.version, 'node _site/scripts/sync-release-version.js --stage');
|
|
93
|
+
assert.strictEqual(scripts.version, 'node _site/scripts/sync-release-version.js --stage --web-only');
|
|
94
94
|
assert.strictEqual(scripts.postversion, 'npm run release:verify');
|
|
95
|
+
const desktopBytes = FILES.filter(file => file.startsWith('desktop/')).map(file => fs.readFileSync(path.join(TEMP, file)));
|
|
96
|
+
value.version = '9.8.8-rc.1'; fs.writeFileSync(rootPackage, JSON.stringify(value, null, 2) + '\n');
|
|
97
|
+
assert.strictEqual(run(['--web-only']).status, 0);
|
|
98
|
+
assert.strictEqual(run(['--check', '--web-only']).status, 0);
|
|
99
|
+
assert.notStrictEqual(run(['--check']).status, 0, 'desktop still needs explicit synchronization for desktop releases');
|
|
100
|
+
FILES.filter(file => file.startsWith('desktop/')).forEach((file, i) => assert.deepStrictEqual(fs.readFileSync(path.join(TEMP, file)), desktopBytes[i]));
|
|
95
101
|
console.log('release-version-sync-test PASS');
|
|
96
102
|
} finally {
|
|
97
103
|
fs.rmSync(TEMP, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
@@ -5,10 +5,10 @@ const os = require('os');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { spawnSync } = require('child_process');
|
|
7
7
|
const { spawnServer } = require('./helpers/spawn-server');
|
|
8
|
+
const freePort = require('./helpers/free-port');
|
|
8
9
|
|
|
9
10
|
const ROOT = path.resolve(__dirname, '..', '..');
|
|
10
|
-
|
|
11
|
-
const BASE = `http://127.0.0.1:${PORT}`;
|
|
11
|
+
let PORT, BASE;
|
|
12
12
|
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-sse-v2-'));
|
|
13
13
|
const repoPath = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-sse-repo-'));
|
|
14
14
|
|
|
@@ -60,6 +60,7 @@ async function waitFor(predicate) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
(async () => {
|
|
63
|
+
PORT = await freePort(); BASE = `http://127.0.0.1:${PORT}`;
|
|
63
64
|
git(['init', '--initial-branch=main']);
|
|
64
65
|
git(['config', 'user.email', 'sse@example.test']);
|
|
65
66
|
git(['config', 'user.name', 'SSE Test']);
|
|
@@ -4,10 +4,10 @@ const os = require('os');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { spawnSync } = require('child_process');
|
|
6
6
|
const { spawnServer } = require('./helpers/spawn-server');
|
|
7
|
+
const freePort = require('./helpers/free-port');
|
|
7
8
|
|
|
8
9
|
const ROOT = path.resolve(__dirname, '..', '..');
|
|
9
|
-
|
|
10
|
-
const BASE = `http://127.0.0.1:${PORT}`;
|
|
10
|
+
let PORT, BASE;
|
|
11
11
|
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-import-v2-'));
|
|
12
12
|
const repoPath = fs.mkdtempSync(path.join(os.tmpdir(), 'pkb-import-repo-'));
|
|
13
13
|
const knowledgeRoot = path.join(dataDir, 'knowledge');
|
|
@@ -36,6 +36,7 @@ async function waitForServer() {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
(async () => {
|
|
39
|
+
PORT = await freePort(); BASE = `http://127.0.0.1:${PORT}`;
|
|
39
40
|
git(['init', '--initial-branch=main']);
|
|
40
41
|
git(['config', 'user.email', 'import@example.test']);
|
|
41
42
|
git(['config', 'user.name', 'Import Test']);
|
|
@@ -4,11 +4,11 @@ const os = require('os');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { execFileSync } = require('child_process');
|
|
6
6
|
const { spawnServer } = require('./helpers/spawn-server');
|
|
7
|
+
const freePort = require('./helpers/free-port');
|
|
7
8
|
const githubTeamStore = require('../lib/github-team-store');
|
|
8
9
|
|
|
9
10
|
const ROOT = path.resolve(__dirname, '..', '..');
|
|
10
|
-
|
|
11
|
-
const BASE_URL = `http://127.0.0.1:${PORT}`;
|
|
11
|
+
let PORT, BASE_URL;
|
|
12
12
|
const TEMP = fs.mkdtempSync(path.join(os.tmpdir(), `kb-team-v2-${process.pid}-`));
|
|
13
13
|
const DATA_DIR = path.join(TEMP, 'data');
|
|
14
14
|
const SOURCE_ONE = path.join(TEMP, 'source-one');
|
|
@@ -51,6 +51,7 @@ async function json(method, route, body) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
(async () => {
|
|
54
|
+
PORT = process.env.KB_TEAM_KNOWLEDGE_PORT ? Number(process.env.KB_TEAM_KNOWLEDGE_PORT) : await freePort(); BASE_URL = `http://127.0.0.1:${PORT}`;
|
|
54
55
|
let spawned;
|
|
55
56
|
try {
|
|
56
57
|
initializeRepo(SOURCE_ONE, 'Source one');
|
|
@@ -4,7 +4,7 @@ const { SCHEMAS, DomainError, validateProjectId } = require('./contracts');
|
|
|
4
4
|
const AtomicFile = require('./atomic-file');
|
|
5
5
|
const { StorageLayout } = require('./storage-layout');
|
|
6
6
|
|
|
7
|
-
const SOURCES = new Set(['claude-code', 'codex', 'opencode']);
|
|
7
|
+
const SOURCES = new Set(['claude-code', 'codex', 'opencode', 'zcode']);
|
|
8
8
|
const ROLES = new Set(['user', 'assistant', null]);
|
|
9
9
|
const SNAPSHOT_STATUSES = new Set(['available', 'no-new-user-prompt', 'unavailable', 'ambiguous']);
|
|
10
10
|
const BINDING_KINDS = new Set(['direct', 'shared-spanning', 'explicit']);
|
|
@@ -53,7 +53,7 @@ function pack(sources) {
|
|
|
53
53
|
process.stdout.write(`Packed ${key} ${pkg.version}: ${result.files.length} files\n`);
|
|
54
54
|
}
|
|
55
55
|
const manifestPath = path.join(output, 'module-candidates.json'); AtomicFile.writeJsonAtomic(manifestPath, manifest);
|
|
56
|
-
const productFiles = Object.fromEntries(['ui/index.html', 'ui/product.js', 'ui/product.css'].map(file => [file, hash(fs.readFileSync(path.join(root, file)))]));
|
|
56
|
+
const productFiles = Object.fromEntries(['ui/index.html', 'ui/product.js', 'ui/product.css', 'ui/favicon.svg'].map(file => [file, hash(fs.readFileSync(path.join(root, file)))]));
|
|
57
57
|
AtomicFile.writeJsonAtomic(path.join(root, 'module-runtime-manifest.json'), { schema: 'project-knowledge/module-content/v1', productFiles, modules: Object.fromEntries(Object.entries(manifest.modules).map(([key, module]) => [key, { name: module.name, installName: module.installName, version: module.version, files: module.files }])) });
|
|
58
58
|
process.stdout.write(`MODULE_CANDIDATES ${manifestPath}\n`);
|
|
59
59
|
return manifestPath;
|
|
@@ -20,7 +20,7 @@ function packageVersion(rootDir) {
|
|
|
20
20
|
return version;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function releaseVersionTargets(rootDir, version) {
|
|
23
|
+
function releaseVersionTargets(rootDir, version, { webOnly = false } = {}) {
|
|
24
24
|
return [
|
|
25
25
|
{
|
|
26
26
|
file: 'package-lock.json',
|
|
@@ -67,13 +67,13 @@ function releaseVersionTargets(rootDir, version) {
|
|
|
67
67
|
args[index] = `@sanqianx/project-knowledge@${version}`;
|
|
68
68
|
},
|
|
69
69
|
},
|
|
70
|
-
].map(target => ({ ...target, path: path.join(rootDir, target.file) }));
|
|
70
|
+
].filter(target => !webOnly || !target.file.startsWith('desktop/')).map(target => ({ ...target, path: path.join(rootDir, target.file) }));
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
function inspectReleaseVersion(rootDir) {
|
|
73
|
+
function inspectReleaseVersion(rootDir, options) {
|
|
74
74
|
const version = packageVersion(rootDir);
|
|
75
75
|
const drift = [];
|
|
76
|
-
for (const target of releaseVersionTargets(rootDir, version)) {
|
|
76
|
+
for (const target of releaseVersionTargets(rootDir, version, options)) {
|
|
77
77
|
const before = fs.readFileSync(target.path, 'utf8');
|
|
78
78
|
const value = JSON.parse(before);
|
|
79
79
|
target.update(value);
|
|
@@ -83,10 +83,10 @@ function inspectReleaseVersion(rootDir) {
|
|
|
83
83
|
return { version, drift };
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
function syncReleaseVersion(rootDir) {
|
|
86
|
+
function syncReleaseVersion(rootDir, options) {
|
|
87
87
|
const version = packageVersion(rootDir);
|
|
88
88
|
const changed = [];
|
|
89
|
-
for (const target of releaseVersionTargets(rootDir, version)) {
|
|
89
|
+
for (const target of releaseVersionTargets(rootDir, version, options)) {
|
|
90
90
|
const before = fs.readFileSync(target.path, 'utf8');
|
|
91
91
|
const value = JSON.parse(before);
|
|
92
92
|
target.update(value);
|
|
@@ -114,21 +114,23 @@ function parseArgs(argv) {
|
|
|
114
114
|
let check = false;
|
|
115
115
|
let tag = null;
|
|
116
116
|
let stage = false;
|
|
117
|
+
let webOnly = false;
|
|
117
118
|
for (let index = 0; index < argv.length; index += 1) {
|
|
118
119
|
const arg = argv[index];
|
|
119
120
|
if (arg === '--check') check = true;
|
|
120
121
|
else if (arg === '--stage') stage = true;
|
|
122
|
+
else if (arg === '--web-only') webOnly = true;
|
|
121
123
|
else if (arg === '--root') rootDir = path.resolve(argv[++index] || '');
|
|
122
124
|
else if (arg === '--tag') tag = String(argv[++index] || '').trim();
|
|
123
125
|
else throw new Error(`Unknown argument: ${arg}`);
|
|
124
126
|
}
|
|
125
|
-
return { rootDir, check, tag, stage };
|
|
127
|
+
return { rootDir, check, tag, stage, webOnly };
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
function main(argv = process.argv.slice(2)) {
|
|
129
|
-
const { rootDir, check, tag, stage } = parseArgs(argv);
|
|
131
|
+
const { rootDir, check, tag, stage, webOnly } = parseArgs(argv);
|
|
130
132
|
if (check) {
|
|
131
|
-
const result = inspectReleaseVersion(rootDir);
|
|
133
|
+
const result = inspectReleaseVersion(rootDir, { webOnly });
|
|
132
134
|
if (result.drift.length) {
|
|
133
135
|
throw new Error(`Release version ${result.version} is out of sync: ${result.drift.join(', ')}`);
|
|
134
136
|
}
|
|
@@ -138,7 +140,7 @@ function main(argv = process.argv.slice(2)) {
|
|
|
138
140
|
console.log(`release version ${result.version} is synchronized`);
|
|
139
141
|
return result;
|
|
140
142
|
}
|
|
141
|
-
const result = syncReleaseVersion(rootDir);
|
|
143
|
+
const result = syncReleaseVersion(rootDir, { webOnly });
|
|
142
144
|
if (stage) stageReleaseVersionFiles(rootDir, result.changed);
|
|
143
145
|
console.log(result.changed.length
|
|
144
146
|
? `${stage ? 'synchronized and staged' : 'synchronized'} release version ${result.version}: ${result.changed.join(', ')}`
|
|
@@ -5,6 +5,10 @@ const crypto = require('crypto');
|
|
|
5
5
|
const { spawnSync } = require('child_process');
|
|
6
6
|
const root = path.resolve(__dirname, '../..');
|
|
7
7
|
const workingTree = process.argv.includes('--working-tree');
|
|
8
|
+
function isPinnedModuleVersion(name, value) {
|
|
9
|
+
const version = name === 'claude-ai-workbench' ? String(value || '').replace(/^npm:@sanqianx\/claude-ai-workbench@/, '') : value;
|
|
10
|
+
return /^\d+\.\d+\.\d+(?:-rc\.\d+)?$/.test(version || '');
|
|
11
|
+
}
|
|
8
12
|
function verify() {
|
|
9
13
|
const manifest = JSON.parse(fs.readFileSync(path.join(root, 'vendor-manifest.json'), 'utf8'));
|
|
10
14
|
if (manifest.schema !== 'project-knowledge/vendor-runtime/v1') throw new Error('Invalid runtime manifest');
|
|
@@ -12,7 +16,7 @@ function verify() {
|
|
|
12
16
|
if (!engine || engine.root !== '_modules/knowledge-engine') throw new Error('Missing engine runtime');
|
|
13
17
|
const packageInfo = JSON.parse(fs.readFileSync(path.join(root, 'package.json')));
|
|
14
18
|
for (const dependency of ['claude-ai-workbench', '@sanqianx/vector-hub', '@sanqianx/ai-coding-event-bridge-console']) {
|
|
15
|
-
if (
|
|
19
|
+
if (!isPinnedModuleVersion(dependency, packageInfo.dependencies[dependency])) throw new Error(`Module dependency must be pinned: ${dependency}`);
|
|
16
20
|
}
|
|
17
21
|
const required = ['package.json', 'src/bin.js', 'src/server.js', 'src/coordinator.js', 'src/analysis-worker.js', 'ui/index.html', 'ui/app.js', 'ui/app.css'];
|
|
18
22
|
if (required.some(file => !engine.files[file])) throw new Error('Incomplete runtime manifest');
|
|
@@ -42,4 +46,5 @@ function verify() {
|
|
|
42
46
|
if (enginePackage.version !== engine.version || Object.keys(enginePackage.dependencies || {}).length || Object.keys(enginePackage.devDependencies || {}).length) throw new Error('Engine version/dependency contract failed');
|
|
43
47
|
process.stdout.write(workingTree ? 'Runtime content verified; uncommitted working tree is not release approval.\n' : 'Vendored runtime release gate passed.\n');
|
|
44
48
|
}
|
|
45
|
-
try { verify(); } catch (error) { process.stderr.write(`${error.message}\n`); process.exitCode = 1; }
|
|
49
|
+
if (require.main === module) { try { verify(); } catch (error) { process.stderr.write(`${error.message}\n`); process.exitCode = 1; } }
|
|
50
|
+
module.exports = { isPinnedModuleVersion };
|
|
@@ -7,6 +7,7 @@ const { safeRelative } = require('./pack-module-candidates');
|
|
|
7
7
|
function verifyProductRuntime(coreRoot, resolveFrom = path.join(coreRoot, 'package.json')) {
|
|
8
8
|
const manifest = JSON.parse(fs.readFileSync(path.join(coreRoot, 'module-runtime-manifest.json')));
|
|
9
9
|
if (manifest.schema !== 'project-knowledge/module-content/v1' || Object.keys(manifest.modules).length !== 3) throw new Error('Incomplete module content manifest');
|
|
10
|
+
for (const file of ['ui/index.html', 'ui/product.js', 'ui/product.css', 'ui/favicon.svg']) if (!manifest.productFiles?.[file]) throw new Error(`Product asset missing from content manifest: ${file}`);
|
|
10
11
|
const resolve = createRequire(resolveFrom);
|
|
11
12
|
for (const [relative, expected] of Object.entries(manifest.productFiles || {})) {
|
|
12
13
|
if (!safeRelative(relative) || crypto.createHash('sha256').update(fs.readFileSync(path.join(coreRoot, relative))).digest('hex') !== expected) throw new Error(`Product UI content mismatch: ${relative}`);
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "project-knowledge/module-content/v1",
|
|
3
3
|
"productFiles": {
|
|
4
|
-
"ui/index.html": "
|
|
5
|
-
"ui/product.js": "
|
|
6
|
-
"ui/product.css": "
|
|
4
|
+
"ui/index.html": "db9cc5624132d336ee6b31ba37c93572b89e382f68e4b6331b4b886be8253bb5",
|
|
5
|
+
"ui/product.js": "50d26a9aa3a95889804f8ca771dada9e1ea89d6956851e58dadf119a75842b9c",
|
|
6
|
+
"ui/product.css": "3f89daeb52c5f1ed8a79150c9a84200187474a013fd97c6d7c8799d850e5b0b7",
|
|
7
|
+
"ui/favicon.svg": "c48be946b224112ebb0238b44263f7a61bda31e531afa895c2e9801c0ff24f32"
|
|
7
8
|
},
|
|
8
9
|
"modules": {
|
|
9
10
|
"terminal": {
|
|
10
11
|
"name": "@sanqianx/claude-ai-workbench",
|
|
11
12
|
"installName": "claude-ai-workbench",
|
|
12
|
-
"version": "0.13.1",
|
|
13
|
+
"version": "0.13.2-rc.1",
|
|
13
14
|
"files": {
|
|
14
15
|
"LICENSE": "c1b9df1275e769f3dbab000d1e457a2d4b0f28eb5da6c77e48dc37eeba202ed7",
|
|
15
|
-
"README.md": "
|
|
16
|
+
"README.md": "7efdf88b6eabdd3280fe69b6b78fce4ad9c1b5373ed5fc59e2a3db39d3f774d0",
|
|
16
17
|
"apps/agent-terminal/app.css": "39cc65ac65bcb390f4aa21d9b783182ca229557f6bec15090f69598120161917",
|
|
17
18
|
"apps/agent-terminal/app.js": "2fedcc79f9515ebae89d1c7dc996d732aa1d0946038eaaa045438121bf27f551",
|
|
18
19
|
"apps/agent-terminal/index.html": "6a52ba210696c8aed99e2786e21d2771340070d51583199dd74c8da41edf0257",
|
|
@@ -26,20 +27,20 @@
|
|
|
26
27
|
"apps/minimal-test-host/README.md": "fa73136f4ee3865c2d91eb97b2e8c03679aa4688e44c1e1c8ec6e27d604b0de5",
|
|
27
28
|
"docs/sdk/FILE_LAYOUT.md": "17882f96505c8161bfb66876684b9d2d3f50021a111b8b844502d3cb4e97870a",
|
|
28
29
|
"docs/sdk/INTEGRATION.md": "d4c0c80d2d2fc610205102d703a429bfaa20f9f3a48f1718f5bab02ee90138df",
|
|
29
|
-
"docs/sdk/openapi.yaml": "
|
|
30
|
+
"docs/sdk/openapi.yaml": "c14dd8a1989a6fbb748285d8b488f553e5673d52ff2439c124c435f65a57b570",
|
|
30
31
|
"docs/sdk/README.md": "7ab591d39c36a3b84cfdf487ff567040ec453975f3ef0fba86dc822094667b61",
|
|
31
|
-
"package.json": "
|
|
32
|
+
"package.json": "21b1d94149a71e9678e4a50c3146d292c2672f206ef621ae919850b67660804b",
|
|
32
33
|
"packages/client/index.d.ts": "fb66b7b21ac936cddcb7f74543cf4f3bb7e80c169534ec249ab8ef2356224162",
|
|
33
34
|
"packages/client/index.js": "92fe5ddd32289d729f336f669e47bd9601e562f5a4309ee4fa7f7f15b567edc2",
|
|
34
|
-
"packages/client/package.json": "
|
|
35
|
+
"packages/client/package.json": "5b5ea236e6a0769fd71b32759e0d87a33b84f4211ad298fe60452e5e8cb8bc3d",
|
|
35
36
|
"packages/client/README.md": "342dff9b1dcbc85fccc9b980ed3e4e455aa71133af1d0805b8431ea2ac564ef8",
|
|
36
37
|
"packages/contracts/index.d.ts": "edc717067bfb864c6c95df0c408a799fbf90b7552519131e6dc35af3f9eb9006",
|
|
37
38
|
"packages/contracts/index.js": "565b4a88190dbb8994cd0a9309b30b00f64305d65505fff9fa360d7ce69dfb58",
|
|
38
|
-
"packages/contracts/package.json": "
|
|
39
|
+
"packages/contracts/package.json": "95887ef01fa0e1ccc4f8e22089bfabe8252566e38cbfb8aef0882cfe8fe22e61",
|
|
39
40
|
"packages/contracts/README.md": "44d83a3cc508cec6c5c20dfa1f2c08d5ea03e3afb2853639fdc0a9cc8eb5b9c8",
|
|
40
41
|
"packages/core/index.d.ts": "c95b24a13f8aeeb128497833cf298790563baca6036ea5d29a9d8daff53199d7",
|
|
41
42
|
"packages/core/index.js": "5579ceb1a63d3f962f5656c75f82cf60cd834fd270b4aa95e581bf38e4432448",
|
|
42
|
-
"packages/core/package.json": "
|
|
43
|
+
"packages/core/package.json": "d21084880b3d52e371d208c071ec94a3df456ea483055acc4a65b1aa7ec2fb9a",
|
|
43
44
|
"packages/core/README.md": "e222c546104a168f1038d95c6c4efb8e5603edd6145ab2cab79cab977fb710e5",
|
|
44
45
|
"packages/server/bin/agent-terminal-server.js": "b01297ab245a2b994bf42043b7469bd6e71d8f76a4bb99d4ed651e71844c152f",
|
|
45
46
|
"packages/server/bin/claude-workbench-server.js": "a8c46ed7e71afcc10e00a104ed6f2d0ac05346a743b33c3a5bd11b1314d80f40",
|
|
@@ -65,13 +66,13 @@
|
|
|
65
66
|
"packages/server/lib/json-file.js": "c922e10116655cd519a5365aaac5468266f6d8a76e41db33eaa51f94b79dae13",
|
|
66
67
|
"packages/server/lib/knowledge-context.js": "82023a94472fcb6fd6a74a53be43091473dbef11e6ac94ff9482c8a7fbc5c286",
|
|
67
68
|
"packages/server/lib/model-detection.js": "fa52213200c1f1a1f00e463ada57c415bf1bd4e2aedfebd04de50750509d06cf",
|
|
68
|
-
"packages/server/lib/profile-store.js": "
|
|
69
|
+
"packages/server/lib/profile-store.js": "70a9ff6aa31fe438ba8431f3e461f72e5d977f3510f2c42d39e9133e2f3491a8",
|
|
69
70
|
"packages/server/lib/project-store.js": "5225c78f3216340f01b99356496c8e2c2d9cfc6247954a3f00b273dd2cc5b692",
|
|
70
71
|
"packages/server/lib/session-archive-store.js": "d3335b832a6e9fb9792991056f6596a997a4959efa6e62b60fb60133fb724196",
|
|
71
72
|
"packages/server/lib/system-doctor.js": "2b0e858d69d8fb268eaed950c9883473ee9850684b532bc2db8e14674a974919",
|
|
72
73
|
"packages/server/lib/ui-preferences-store.js": "e108cb22e7cc1f3438704913d5401e01de2c0e717eb2abd3bdc4727209393819",
|
|
73
74
|
"packages/server/lib/workbench-service.js": "b5caac09665b21a118a0cd826adfa3f3006e68989182e4d80bf3a4554b951250",
|
|
74
|
-
"packages/server/package.json": "
|
|
75
|
+
"packages/server/package.json": "77ded7b6683df90e731a0f0204ea56698a19f2a138959bee18355ba987eb04c2",
|
|
75
76
|
"packages/server/README.md": "a34cfaa4f4e859008b44e2be8e1aafb56e97fe83e6129c968a8562945fa843f3",
|
|
76
77
|
"packages/ui/ai-profile-settings.d.ts": "0551b6c6eda6d18ea89a976daddf01b2161195f4874fe2d67a57cd9ecc6ce643",
|
|
77
78
|
"packages/ui/ai-profile-settings.js": "e92bbac67a1199d473e4e44b32d1cc76c5b6c79306d4fef9d9067a1bb6ec42ca",
|
|
@@ -79,7 +80,7 @@
|
|
|
79
80
|
"packages/ui/claude-terminal.js": "96bd217a4a7e4792ae3fbaaf46249ecee13b0f4e01fc6d25a1b8b22d8f8fe00c",
|
|
80
81
|
"packages/ui/index.d.ts": "e547ab39640fe7519fb367b78442dc8a2c51b2efd17b66f8b0a003b154b36ead",
|
|
81
82
|
"packages/ui/index.js": "f4394aabd395938f5c3fe587e430471d4033e83945a9b9c7e0149ad4040982c9",
|
|
82
|
-
"packages/ui/package.json": "
|
|
83
|
+
"packages/ui/package.json": "47418b48345efd2099b5b0861eed4a04db8cfe613efc95cec4aa181c373af72c",
|
|
83
84
|
"packages/ui/README.md": "793c4a9c286fee23f39062a1976ca3afe9d2816dcdb55e156ba940e1ef514f89",
|
|
84
85
|
"packages/ui/theme.css": "8f03a081272a7db11b196f7a70d0fafb1f8d954778f329f1ba53d110d9d5fd98",
|
|
85
86
|
"src/assets/claude-prompts.json": "cb111f2e0a45afe8a31c251a504d1ab5375966ed61291660e61fd2efd77f6d9f",
|
|
@@ -109,9 +110,9 @@
|
|
|
109
110
|
"vectorHub": {
|
|
110
111
|
"name": "@sanqianx/vector-hub",
|
|
111
112
|
"installName": "@sanqianx/vector-hub",
|
|
112
|
-
"version": "1.0.
|
|
113
|
+
"version": "1.0.8-rc.1",
|
|
113
114
|
"files": {
|
|
114
|
-
"README.md": "
|
|
115
|
+
"README.md": "801e0d2f258bc6d386b7ab44e1d8157c9f48d9c979b84c813773c34a9ee358c1",
|
|
115
116
|
"dist/cjs/bin.d.ts": "43e818adf60173644896298637f47b01d5819b17eda46eaa32d0c7d64724d012",
|
|
116
117
|
"dist/cjs/bin.js": "8d0317ce087d22074b4ad350d63bf39cd6d218da565496621cd0daf4245cbdc4",
|
|
117
118
|
"dist/cjs/bin.js.map": "818e872380d9c7ba045238ba65ce77111ba822b356be26327ed05936e5667e5e",
|
|
@@ -133,12 +134,12 @@
|
|
|
133
134
|
"dist/cjs/server/folder-picker.d.ts": "b9c59dbc834fff668b1894f516450c2edc985c7bfac31edf9b7b4e766474b32b",
|
|
134
135
|
"dist/cjs/server/folder-picker.js": "053121152a210c899e8593d60670a2a510b435c3b479300c572aa4bc8443e58e",
|
|
135
136
|
"dist/cjs/server/folder-picker.js.map": "dce1ad1b03c8a6a4fba7b3cacec5b65e55c1dfd1886c5e70df384efd1a5c68a6",
|
|
136
|
-
"dist/cjs/server/manager.d.ts": "
|
|
137
|
-
"dist/cjs/server/manager.js": "
|
|
138
|
-
"dist/cjs/server/manager.js.map": "
|
|
137
|
+
"dist/cjs/server/manager.d.ts": "8aeb6b7d6e5f8ef94b65d0c078a3333b39e03546b2fc3cf8c0c0e144db4cff01",
|
|
138
|
+
"dist/cjs/server/manager.js": "1faed06b5efd6bea357ebf86e09af13791277fa07258eacc602af2945ae42845",
|
|
139
|
+
"dist/cjs/server/manager.js.map": "0d42dc88ad03bc723971a4967fb16b4fbf734ba06845e10ff10d85cf98c7b130",
|
|
139
140
|
"dist/cjs/server/server.d.ts": "13288ace9aa6ec016a2b06dc036868324ac6c23c7e73771e95d120be778f238a",
|
|
140
|
-
"dist/cjs/server/server.js": "
|
|
141
|
-
"dist/cjs/server/server.js.map": "
|
|
141
|
+
"dist/cjs/server/server.js": "8b42771d2d1c4f18f51f1d7c305edd702bf86b009ea14e5f02d133a46de3c141",
|
|
142
|
+
"dist/cjs/server/server.js.map": "701f115fb78fcca13723568fc7514a290acff9dc3fe63c9104a35bdb7120459e",
|
|
142
143
|
"dist/esm/bin.d.ts": "43e818adf60173644896298637f47b01d5819b17eda46eaa32d0c7d64724d012",
|
|
143
144
|
"dist/esm/bin.js": "69f6059ce2336eb7f0d9503f86c83da787aaf74a8bfd804c694ba5b2e1728be0",
|
|
144
145
|
"dist/esm/bin.js.map": "fc174c2b5c66c20ff88184ddeb00577406185945ccadb0bddbb2014909339e9e",
|
|
@@ -161,14 +162,14 @@
|
|
|
161
162
|
"dist/esm/server/folder-picker.d.ts": "b9c59dbc834fff668b1894f516450c2edc985c7bfac31edf9b7b4e766474b32b",
|
|
162
163
|
"dist/esm/server/folder-picker.js": "6c8247e431b7181c45fb169b29e5825a0b3589f44e1625b3a5a291d7b8358042",
|
|
163
164
|
"dist/esm/server/folder-picker.js.map": "f70723213f032a0f10f538289a176a50fc8f02c200b0194479ffc139b5fbb13b",
|
|
164
|
-
"dist/esm/server/manager.d.ts": "
|
|
165
|
-
"dist/esm/server/manager.js": "
|
|
166
|
-
"dist/esm/server/manager.js.map": "
|
|
165
|
+
"dist/esm/server/manager.d.ts": "8aeb6b7d6e5f8ef94b65d0c078a3333b39e03546b2fc3cf8c0c0e144db4cff01",
|
|
166
|
+
"dist/esm/server/manager.js": "4f9c7a4cb12c7ac6d13f43ce37a8e2984bf1b4229feddb412f08ace12ca6859b",
|
|
167
|
+
"dist/esm/server/manager.js.map": "286cadd292ff7225a5faaa0946fb3b127e0ede488f1cae9f33aac2c7ec8f7726",
|
|
167
168
|
"dist/esm/server/server.d.ts": "13288ace9aa6ec016a2b06dc036868324ac6c23c7e73771e95d120be778f238a",
|
|
168
|
-
"dist/esm/server/server.js": "
|
|
169
|
-
"dist/esm/server/server.js.map": "
|
|
170
|
-
"package.json": "
|
|
171
|
-
"ui/index.html": "
|
|
169
|
+
"dist/esm/server/server.js": "357f53c83ded73318016256fbb752e78b90f3a94aecfca71607de8326cad9ebf",
|
|
170
|
+
"dist/esm/server/server.js.map": "97d1cbd3a4fff6ab0cb7994df6c5b73daea03a0dd9c8a1841cec5bb7be82e66c",
|
|
171
|
+
"package.json": "acb511c809a53643d2ee501a58bb08c74fc85edbac0ecbc35d8f3e7c549389d3",
|
|
172
|
+
"ui/index.html": "7301a595d625dadcd424069e7bb978004bfb6118eddef7a223bd25449ca6e0eb",
|
|
172
173
|
"ui/vendor/marked.min.js": "c68075672d976e4777390560baa112194855bd4404b13647da4855aae1f9360c"
|
|
173
174
|
}
|
|
174
175
|
},
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanqianx/project-knowledge",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.9-rc.2",
|
|
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": {
|
|
7
7
|
"start": "node _site/server.js",
|
|
8
8
|
"test": "node _site/_test/run-all-tests.js",
|
|
9
|
-
"release:sync": "node _site/scripts/sync-release-version.js",
|
|
9
|
+
"release:sync": "node _site/scripts/sync-release-version.js --web-only",
|
|
10
10
|
"vendor:modules": "node _site/scripts/vendor-modules.js",
|
|
11
11
|
"runtime:verify": "node _site/scripts/verify-module-runtime.js --working-tree",
|
|
12
|
-
"release:verify": "node _site/scripts/sync-release-version.js --check && node _site/scripts/verify-module-runtime.js && node _site/scripts/verify-product-runtime.js && node _site/_test/integration-adapters-test.js",
|
|
12
|
+
"release:verify": "node _site/scripts/sync-release-version.js --check --web-only && node _site/scripts/verify-module-runtime.js && node _site/scripts/verify-product-runtime.js && node _site/_test/integration-adapters-test.js",
|
|
13
13
|
"preversion": "npm run release:verify",
|
|
14
|
-
"version": "node _site/scripts/sync-release-version.js --stage",
|
|
14
|
+
"version": "node _site/scripts/sync-release-version.js --stage --web-only",
|
|
15
15
|
"postversion": "npm run release:verify"
|
|
16
16
|
},
|
|
17
17
|
"bin": {
|
|
@@ -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.
|
|
84
|
+
"@sanqianx/vector-hub": "1.0.8-rc.1",
|
|
85
85
|
"buffer": "^6.0.3",
|
|
86
86
|
"cheerio": "^1.2.0",
|
|
87
|
-
"claude-ai-workbench": "npm:@sanqianx/claude-ai-workbench@0.13.1",
|
|
87
|
+
"claude-ai-workbench": "npm:@sanqianx/claude-ai-workbench@0.13.2-rc.1",
|
|
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.
|
|
4
|
+
"version": "5.0.9-rc.2",
|
|
5
5
|
"description": "Read durable project decisions and implementation history from Project Knowledge.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "SanQianX",
|
package/ui/favicon.svg
CHANGED
|
@@ -1,38 +1,5 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
<stop offset="1" stop-color="#06111f"/>
|
|
7
|
-
</linearGradient>
|
|
8
|
-
<linearGradient id="core" x1="21" y1="16" x2="45" y2="44" gradientUnits="userSpaceOnUse">
|
|
9
|
-
<stop offset="0" stop-color="#dff8ff"/>
|
|
10
|
-
<stop offset=".48" stop-color="#7dd3fc"/>
|
|
11
|
-
<stop offset="1" stop-color="#2f7d64"/>
|
|
12
|
-
</linearGradient>
|
|
13
|
-
<linearGradient id="book" x1="18" y1="35" x2="48" y2="54" gradientUnits="userSpaceOnUse">
|
|
14
|
-
<stop offset="0" stop-color="#f8fafc"/>
|
|
15
|
-
<stop offset="1" stop-color="#b8c6d8"/>
|
|
16
|
-
</linearGradient>
|
|
17
|
-
<filter id="glow" x="-40%" y="-40%" width="180%" height="180%">
|
|
18
|
-
<feGaussianBlur stdDeviation="1.8" result="blur"/>
|
|
19
|
-
<feMerge>
|
|
20
|
-
<feMergeNode in="blur"/>
|
|
21
|
-
<feMergeNode in="SourceGraphic"/>
|
|
22
|
-
</feMerge>
|
|
23
|
-
</filter>
|
|
24
|
-
</defs>
|
|
25
|
-
<rect x="4" y="4" width="56" height="56" rx="15" fill="url(#bg)"/>
|
|
26
|
-
<path d="M16 42c-6-9-3-22 7-28 9-6 21-4 28 5" fill="none" stroke="#38bdf8" stroke-width="3.5" stroke-linecap="round" opacity=".82"/>
|
|
27
|
-
<path d="M48 18h-8l5 6" fill="none" stroke="#38bdf8" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" opacity=".82"/>
|
|
28
|
-
<path d="M50 25c4 10-1 23-12 27-9 4-20 1-25-8" fill="none" stroke="#75b798" stroke-width="3.5" stroke-linecap="round" opacity=".86"/>
|
|
29
|
-
<path d="M13 44h8l-5-6" fill="none" stroke="#75b798" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" opacity=".86"/>
|
|
30
|
-
<path d="M32 13 45 21v15l-13 8-13-8V21z" fill="url(#core)" filter="url(#glow)"/>
|
|
31
|
-
<path d="M32 18v21M23 24l9-6 9 6M23 33l9 6 9-6" fill="none" stroke="#0f1724" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" opacity=".9"/>
|
|
32
|
-
<circle cx="16" cy="20" r="4" fill="#c5a15d"/>
|
|
33
|
-
<circle cx="49" cy="43" r="4" fill="#38bdf8"/>
|
|
34
|
-
<path d="M20 42h25l5 5-6 6H19l-5-5z" fill="url(#book)"/>
|
|
35
|
-
<path d="M20 42 14 48l25 1 6-7" fill="#e2e8f0"/>
|
|
36
|
-
<path d="M19 53h25l6-6" fill="none" stroke="#64748b" stroke-width="2.7" stroke-linecap="round" stroke-linejoin="round"/>
|
|
37
|
-
<path d="M24 47h14" stroke="#4f6f8f" stroke-width="3" stroke-linecap="round"/>
|
|
38
|
-
</svg>
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Project Knowledge">
|
|
2
|
+
<title>Project Knowledge · 折页 K</title>
|
|
3
|
+
<rect width="64" height="64" rx="16" fill="#516f89"/>
|
|
4
|
+
<path d="M16 15h8v13l14-13h10L30 32l18 17H38L24 36v13h-8Z" fill="#fff"/>
|
|
5
|
+
</svg>
|
package/ui/index.html
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
<html lang="zh-CN" data-theme="light">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="light dark">
|
|
5
|
-
<title>Project Knowledge · 我的智脑</title><link rel="icon" href="/favicon.svg"><link rel="stylesheet" href="/product.css">
|
|
5
|
+
<title>Project Knowledge · 我的智脑</title><link rel="icon" type="image/svg+xml" href="/favicon.svg?v=folded-k-1"><link rel="stylesheet" href="/product.css">
|
|
6
6
|
</head>
|
|
7
7
|
<body>
|
|
8
8
|
<div class="app">
|
|
9
9
|
<aside class="sidebar">
|
|
10
|
-
<div class="brand"><
|
|
10
|
+
<div class="brand"><img class="brand-mark" src="/favicon.svg?v=folded-k-1" width="32" height="32" alt=""><div><strong>Project Knowledge</strong><small>个人工程智脑</small></div></div>
|
|
11
11
|
<div id="brainLabel" class="brain-label">我的智脑</div>
|
|
12
12
|
<nav aria-label="主要导航">
|
|
13
13
|
<button data-view="home"><svg viewBox="0 0 24 24"><path d="m3 11 9-7 9 7M5 10v10h14V10"/></svg>智脑首页</button>
|
package/ui/product.css
CHANGED
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
.knowledge-context{overflow-x:hidden}.reference{overflow-wrap:anywhere}.reference small{min-width:0;max-width:100%}
|
|
6
6
|
html[data-theme=dark]{--page:#171a1f;--surface:#20252d;--surface2:#252b34;--surface3:#2c333e;--line:#39424f;--text:#e6ebf0;--muted:#a0aaba;--faint:#788393;--nav:#1b2028;--accent:#8aa8cf;--accent-soft:#293748;--good:#81b29a;--bad:#d88b8b;--bad-soft:#39292d;color-scheme:dark}
|
|
7
7
|
*{box-sizing:border-box}[hidden]{display:none!important}html,body{height:100%}body{margin:0;overflow:hidden;background:var(--page);color:var(--text);font:13px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI","Microsoft YaHei",sans-serif}button,input,select,textarea{font:inherit;color:inherit}button{cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:34px;padding:6px 12px;border:1px solid var(--line);border-radius:8px;background:var(--surface)}button:hover{background:var(--surface3)}button:disabled{opacity:.5;cursor:not-allowed}button.primary{background:var(--accent);color:#fff;border-color:var(--accent)}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--accent);outline-offset:3px}svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;flex:none}h1,h2,h3,p{margin:0}h1{font-size:18px;font-weight:650}h2{font-size:16px;font-weight:600}h3{font-size:13px;font-weight:600}small,.muted{color:var(--muted)}small{font-size:11px}p{color:var(--muted);margin-top:8px}input,select,textarea{background:var(--surface);border:1px solid var(--line);border-radius:8px;padding:8px 12px;min-width:0}label{display:block;font-size:12px;margin:16px 0;color:var(--muted)}label>input,label>select,label>textarea{display:block;width:100%;margin-top:8px;color:var(--text)}pre{font:12px/1.8 Consolas,"Microsoft YaHei",monospace;white-space:pre-wrap;overflow-wrap:anywhere;color:var(--text)}
|
|
8
|
-
.app{height:100%;display:grid;grid-template-columns:232px minmax(0,1fr)}.sidebar{display:flex;flex-direction:column;border-right:1px solid var(--line);background:var(--nav);min-height:0}.brand{display:flex;align-items:center;gap:8px;padding:24px 20px 16px}.brand-mark{width:
|
|
8
|
+
.app{height:100%;display:grid;grid-template-columns:232px minmax(0,1fr)}.sidebar{display:flex;flex-direction:column;border-right:1px solid var(--line);background:var(--nav);min-height:0}.brand{display:flex;align-items:center;gap:8px;padding:24px 20px 16px}.brand-mark{width:32px;height:32px;display:block;flex:none;object-fit:contain}.brand strong{font-size:14px}.brand small{display:block}.brain-label{padding:0 20px 16px;color:var(--muted);overflow-wrap:anywhere}.sidebar nav{display:grid;gap:8px;padding:8px 12px}.sidebar nav button{justify-content:flex-start;min-height:40px;text-align:left;border-color:transparent;background:transparent;color:var(--muted)}.sidebar nav button.active{border-color:var(--line);background:var(--surface);color:var(--text)}.count{margin-left:auto;background:var(--surface3);padding:0 6px;border-radius:4px}.system-button{margin: auto 12px 16px;justify-content:flex-start;min-height:56px;background:transparent;border-color:transparent;padding:8px}.system-button>span:nth-child(2){flex:1;text-align:left}.system-button b,.system-button small{display:block;font-size:11px}.dot{width:7px;height:7px;border-radius:50%;background:var(--faint);flex:none}.dot.up{background:var(--good)}.dot.issue{background:var(--bad)}main{min-width:0;min-height:0;display:flex;flex-direction:column}.topbar{height:72px;flex:none;display:flex;align-items:center;gap:16px;padding:0 24px;background:var(--surface);border-bottom:1px solid var(--line)}.spacer{flex:1}.topbar select{width:240px}.icon-button{padding:8px;min-width:34px}.content{position:relative;flex:1;min-height:0}.view{display:none;height:100%;overflow:auto;padding:32px}.view.active{display:block}.view.module-view{padding:0;overflow:hidden}.module-view.active{display:flex;flex-direction:column}.frame-container{position:relative;flex:1;min-width:0;min-height:0;overflow:hidden;background:var(--surface)}iframe{display:block;width:100%;height:100%;border:0;background:var(--surface)}.work-layout{display:grid;grid-template-columns:minmax(0,1fr) 288px;height:100%;min-height:0}.knowledge-context{background:var(--surface2);border-left:1px solid var(--line);overflow:auto;padding:24px 16px}.knowledge-context header{margin-bottom:24px}.knowledge-context h2{font-size:13px}.module-toolbar{flex:none;min-height:56px;display:flex;justify-content:space-between;align-items:center;gap:16px;padding:8px 24px;border-bottom:1px solid var(--line);background:var(--surface)}.segmented{display:flex;gap:4px}.segmented button{background:transparent;border-color:transparent;color:var(--muted)}.segmented button.active{background:var(--surface);border-color:var(--line);color:var(--text)}
|
|
9
9
|
.welcome{display:flex;align-items:center;justify-content:space-between;gap:24px;margin-bottom:32px}.welcome h2{font-size:24px;margin-top:8px}.actions{display:flex;gap:8px;align-items:center}.grid2{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(320px,1fr);gap:24px}.stack{display:grid;gap:24px}.panel{border:1px solid var(--line);background:var(--surface);border-radius:8px;padding:24px;min-width:0}.panel h2{margin-bottom:16px}.section-heading{display:flex;align-items:center;justify-content:space-between;gap:24px;margin-bottom:24px}.section-heading p{font-size:12px}.row{display:flex;align-items:center;gap:16px;padding:16px 0;border-bottom:1px solid var(--line)}.row:last-child{border-bottom:0}.row>div{flex:1;min-width:0}.row b{font-weight:600}.row small{display:block;margin-top:4px;overflow-wrap:anywhere}.row time{color:var(--muted);font-size:11px;flex:none}.status{font-size:11px;border:1px solid var(--line);border-radius:4px;padding:2px 8px;white-space:nowrap;color:var(--muted)}.status.up{color:var(--good)}.status.issue{color:var(--bad);background:var(--bad-soft)}.empty{padding:64px 24px;text-align:center;color:var(--muted);background:var(--surface);border-radius:8px;line-height:1.9}.empty button{display:block;margin:16px auto 0}.small-empty{padding:16px 0;text-align:left;background:transparent;font-size:12px}.hierarchy{display:grid;grid-template-columns:296px minmax(0,1fr);gap:24px}.tree h3{margin:24px 0 8px;color:var(--muted);font-size:11px}.tree button{width:100%;justify-content:flex-start;text-align:left;background:transparent;border-color:transparent;margin-top:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree button.active{background:var(--accent-soft);color:var(--accent)}.detail-path{padding:12px 16px;background:var(--surface2);border:1px solid var(--line);border-radius:8px;margin:8px 0 24px;font:12px/1.8 Consolas,"Microsoft YaHei",monospace;overflow-wrap:anywhere}.detail-label{color:var(--muted);font-size:11px}.memory-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:24px;margin-top:24px}.memory-card{padding:24px;border:1px solid var(--line);border-radius:8px;background:var(--surface)}.memory-card p{color:var(--text);margin:0 0 16px;white-space:pre-wrap}.memory-card .actions{margin-top:16px}.reference{display:block;text-align:left;width:100%;background:transparent;border:0;border-bottom:1px solid var(--line);border-radius:0;padding:16px 0}.reference b,.reference small{display:block;text-align:left}.reference p{font-size:12px;text-align:left}.activity-updates{flex:1;overflow:auto;padding:24px 32px}.run{margin-bottom:16px;background:var(--surface);border:1px solid var(--line);border-radius:8px}.run summary{padding:16px 24px;cursor:pointer;display:flex;align-items:center;gap:16px}.run summary strong{flex:1}.run>div{padding:0 24px 24px}.run .error{color:var(--bad)}.operation{padding:8px 0;color:var(--muted)}
|
|
10
10
|
.drawer-backdrop{position:fixed;inset:0;background:rgba(10,18,25,.25);z-index:20}.drawer{position:fixed;right:0;top:0;height:100%;width:448px;background:var(--surface);border-left:1px solid var(--line);z-index:21;display:flex;flex-direction:column}.drawer>header{height:72px;display:flex;align-items:center;justify-content:space-between;padding:24px;border-bottom:1px solid var(--line)}.drawer-content{overflow:auto;flex:1;padding:24px}.drawer section{padding-bottom:24px;margin-bottom:24px;border-bottom:1px solid var(--line)}.drawer select{width:100%;margin:16px 0}.drawer .actions{flex-wrap:wrap}.drawer section:last-child{border-bottom:0}.input-with-action{display:flex;gap:8px;margin-top:8px}.input-with-action input{flex:1}.input-with-action button{flex:none}.checkbox{display:flex;align-items:center;gap:8px}.checkbox input{width:auto;margin:0}dialog{width:560px;max-width:90vw;max-height:90vh;overflow:auto;background:var(--surface);color:var(--text);border:1px solid var(--line);border-radius:12px;padding:24px}dialog::backdrop{background:rgba(10,18,25,.35)}.dialog-heading{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}.dialog-heading>button{border:0;background:transparent;font-size:20px}.dialog-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:24px}.feedback{padding:12px 16px;background:var(--surface2);border:1px solid var(--line);border-radius:8px;color:var(--muted);white-space:pre-wrap}.feedback.issue{color:var(--bad)}.reference-dialog{width:880px}.reference-dialog pre{margin-top:24px;max-height:60vh;overflow:auto}.engine-dialog{width:calc(100vw - 96px);height:calc(100vh - 96px);padding:0;overflow:hidden}.engine-dialog .dialog-heading{height:64px;padding:16px 24px;border-bottom:1px solid var(--line);margin:0}.engine-dialog iframe{height:calc(100% - 64px)}.toast{position:fixed;bottom:24px;right:24px;z-index:50;background:var(--surface);padding:12px 16px;border:1px solid var(--line);border-radius:8px;max-width:480px;color:var(--text)}
|
package/ui/product.js
CHANGED
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
$('activityUpdates').hidden = state.activityTab !== 'updates'; $('activitySessions').hidden = state.activityTab !== 'sessions';
|
|
114
114
|
const runs = state.snapshot.runs.filter(r => r.projectId === state.projectId);
|
|
115
115
|
const open = new Set(all('.run[open]').map(d => d.dataset.runId));
|
|
116
|
-
$('activityUpdates').innerHTML = state.engineAvailable ? runs.map(r => `<details class="run" data-run-id="${esc(r.runId)}" ${open.has(r.runId) ? 'open' : ''}><summary><time>${time(r.startedAt)}</time><strong>${esc(projectName(r.projectId))} · ${esc(r.commitSha.slice(0, 8))}</strong>${badge(label(r), ['failed', 'index-pending'].includes(r.state) ? 'issue' : r.state === 'succeeded' ? 'up' : '')}</summary><div><p>提交:${esc(r.commitSha)}<br>阶段:${esc(r.phase)} · 入口:${esc(r.trigger)}<br>知识晋升:${time(r.promotedAt)} · 索引:${esc(r.indexState)}</p>${r.error ? `<p class="error">${esc(r.error.message)} (${esc(r.error.code)})</p>` : ''}${(r.staging?.operations || []).map(op => `<div class="operation">${esc(op.operation)} · ${esc(op.path)}<br>${esc(op.reason)}</div>`).join('')}${['failed', 'index-pending'].includes(r.state) ? `<button data-retry-run="${esc(r.runId)}">重试失败阶段</button>` : ''}</div></details>`).join('') || '<div class="empty">当前项目还没有知识维护记录。</div>' : '<div class="empty">知识引擎离线。记录仍保留在本机,连接恢复后自动读取。</div>';
|
|
116
|
+
$('activityUpdates').innerHTML = state.engineAvailable ? runs.map(r => `<details class="run" data-run-id="${esc(r.runId)}" ${open.has(r.runId) ? 'open' : ''}><summary><time>${time(r.startedAt)}</time><strong>${esc(projectName(r.projectId))} · ${esc(r.commitSha.slice(0, 8))}</strong>${badge(label(r), ['failed', 'index-pending'].includes(r.state) ? 'issue' : r.state === 'succeeded' ? 'up' : '')}</summary><div><p>提交:${esc(r.commitSha)}<br>阶段:${esc(r.phase)} · 入口:${esc(r.trigger)}<br>知识晋升:${time(r.promotedAt)} · 索引:${esc(r.indexState)}</p>${r.error ? `<p class="error">${esc(r.error.message)} (${esc(r.error.code)})</p>${r.error.details ? `<p>失败原因:${esc(r.error.details.upstreamMessage)}<br>类别:${esc(r.error.details.failureKind)} · HTTP:${esc(r.error.details.httpStatus ?? '—')} · 上游代码:${esc(r.error.details.upstreamCode)}<br>请求标识:${esc(r.error.details.requestId || '—')} · 尝试:${esc(r.error.details.attempt)} · 耗时:${esc(r.error.details.durationMs ?? '—')} ms</p>` : ''}` : ''}${(r.staging?.operations || []).map(op => `<div class="operation">${esc(op.operation)} · ${esc(op.path)}<br>${esc(op.reason)}</div>`).join('')}${['failed', 'index-pending'].includes(r.state) ? `<button data-retry-run="${esc(r.runId)}">重试失败阶段</button>` : ''}</div></details>`).join('') || '<div class="empty">当前项目还没有知识维护记录。</div>' : '<div class="empty">知识引擎离线。记录仍保留在本机,连接恢复后自动读取。</div>';
|
|
117
117
|
}
|
|
118
118
|
function renderContext() {
|
|
119
119
|
const saved = state.contexts.get(state.projectId);
|
package/vendor-manifest.json
CHANGED
|
@@ -3,19 +3,20 @@
|
|
|
3
3
|
"modules": {
|
|
4
4
|
"knowledge-engine": {
|
|
5
5
|
"name": "@sanqianx/ai-coding-knowledge-engine",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.3-rc.1",
|
|
7
7
|
"root": "_modules/knowledge-engine",
|
|
8
8
|
"files": {
|
|
9
|
-
"README.md": "
|
|
10
|
-
"package.json": "
|
|
9
|
+
"README.md": "899de4400e5baf2a981fbfeb1b6adef0b64a0c022a0dbf8d11706d58b0c6b277",
|
|
10
|
+
"package.json": "a90e49be24b25b526c2325da9a2bf897de3b449b1cfbdb595427fd080484cc9c",
|
|
11
11
|
"src/analysis-contract.js": "fee063967d32eefc955cf0729f7dc8cb31bd690b85e22471c9059494a599c77a",
|
|
12
12
|
"src/analysis-output.js": "95a3c19dcc9ab85d1dba7fee5b9aec80f9c000dfba89da3fd0be25a81e2fb680",
|
|
13
13
|
"src/analysis-worker.js": "a105cabe6e1573b2d986b1e7a5aaf615e2fb53524156fc8e4e2c0a71f2c97e55",
|
|
14
14
|
"src/bin.js": "22208907431d0f3a971a669c7e5eb44b3b7af9bbb36fd7ba17c3b90c4288bd70",
|
|
15
15
|
"src/bridge-client.js": "ae0dba21e4286694bcd9e064ca36564ec997969ae1eb1ce4f8a6665e447cbffe",
|
|
16
|
-
"src/coordinator.js": "
|
|
17
|
-
"src/diagnostics.js": "
|
|
16
|
+
"src/coordinator.js": "1df096060faa83245c49b65a861f32cdaeeb7a190b94abf2ad6477151d7ca2ff",
|
|
17
|
+
"src/diagnostics.js": "f7b516ace61a1e9faaa6876930b9e2e258499a25857d1203f4e53d28bfd0fa1a",
|
|
18
18
|
"src/headless-analyzer.js": "cfe54b392b2aff6e2c5157481dfeb5ccebc4ce83d1d74aa44f28cf2eb923b013",
|
|
19
|
+
"src/index-request.js": "78f26b872299005499fff4f2d1f9e8f986784f2ff36a5cf2be2b1e7b7c7d9378",
|
|
19
20
|
"src/memories.js": "0a8667e6daba99fa5022393951a28e96cdadf98af85b6af9a6be96c7f4e896fc",
|
|
20
21
|
"src/retrieval.js": "b3ae3f701a007e3d3ff98811bb80db3505e2fcc87f94aaa9e36057edc7eb71c2",
|
|
21
22
|
"src/runtime/README.md": "9be676eca76a7857b34d7357194e6f0ee733c22e61d962d53984715dbec7f673",
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
"src/runtime/content-hash.js": "201ee7dcc054310f14097a00058cf9b28fa913367eef616e0fdd8facbf3c6c41",
|
|
29
30
|
"src/runtime/contracts.js": "567fbace85ad0534fb0119f5c0e57d73acc0b76cc799af93d778c5ecbade53df",
|
|
30
31
|
"src/runtime/conversation-exclusions.js": "ef8545034b862d384fb460140490bb531b58d94992f5e2222ebaaa079b44fa63",
|
|
31
|
-
"src/runtime/conversation-store.js": "
|
|
32
|
+
"src/runtime/conversation-store.js": "644318f9c04376a3317ced240dd94afa99a79631f0ecc394c5aa298ae2bc0474",
|
|
32
33
|
"src/runtime/evidence-bundle.js": "33d4614849d5e1d69aba8b75c3a43c18b7b6f94e5c78654c82ef631331cc94cc",
|
|
33
34
|
"src/runtime/git-runner.js": "b74fe197bd4dcf3e70635ef7b1f2615edf965c1d9a3ffe5406aabd41b76d74a9",
|
|
34
35
|
"src/runtime/knowledge-promotion.js": "c24aba505caf11cdc8faba763beffc65956c1330ef28ed5fbd6ae16eac640fa6",
|
|
@@ -36,10 +37,10 @@
|
|
|
36
37
|
"src/runtime/layout.js": "c48fa1db62e5df1edefe13218e470c5ceea4ef2781293512c2c18e5c2ba0c7e9",
|
|
37
38
|
"src/runtime/markdown-knowledge-indexer.js": "44a76a9adefad07e78f77b1106b10d6c1cc7873d76362df4c546892b21f3939c",
|
|
38
39
|
"src/runtime/scanner.js": "d99fa8d74a6fbbaae75ca49f62127e14cf9e48b1404e4b4d597ef39df5441410",
|
|
39
|
-
"src/server.js": "
|
|
40
|
+
"src/server.js": "7deff466d28b94ec8975cd87ebd55c59d0aa6b49a8dfa447b552b1d32aa9c4b7",
|
|
40
41
|
"src/store.js": "71f0417c2c1e42b89429e3dee1adff7b2c09e86682cb3f4843ce6d9e5aafd7ec",
|
|
41
42
|
"ui/app.css": "7ad370c269e60a7b5ec907d81a607b2d16bd07b2d7c22a81f102dab51eeb2889",
|
|
42
|
-
"ui/app.js": "
|
|
43
|
+
"ui/app.js": "33a2ba38f0dafbbb2e30a1eb3fc93476bfcaf528e124e1a9b2edd2fb69221e9e",
|
|
43
44
|
"ui/index.html": "ce3565cfefd4838b11d37a91a906ef26ca5a1834844fe3f7c897b56851a3c418"
|
|
44
45
|
}
|
|
45
46
|
}
|