@numa-tech/numa 1.25.0 → 1.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/business-issuer.js +3 -1
- package/dist/business-issuer.js.map +1 -1
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/kb/catalog.js +33 -2
- package/dist/kb/catalog.js.map +1 -1
- package/dist/kb/chat-stream.d.ts +5 -0
- package/dist/kb/chat-stream.js +61 -0
- package/dist/kb/chat-stream.js.map +1 -0
- package/dist/kb/client.d.ts +75 -6
- package/dist/kb/client.js +104 -7
- package/dist/kb/client.js.map +1 -1
- package/dist/kb/commands.js +92 -7
- package/dist/kb/commands.js.map +1 -1
- package/dist/kb/config.d.ts +14 -2
- package/dist/kb/config.js +21 -7
- package/dist/kb/config.js.map +1 -1
- package/dist/kb/management-commands.d.ts +7 -0
- package/dist/kb/management-commands.js +86 -0
- package/dist/kb/management-commands.js.map +1 -0
- package/dist/kb/management-schemas.d.ts +106 -0
- package/dist/kb/management-schemas.js +33 -0
- package/dist/kb/management-schemas.js.map +1 -0
- package/dist/kb/transfers.d.ts +4 -0
- package/dist/kb/transfers.js +11 -4
- package/dist/kb/transfers.js.map +1 -1
- package/dist/kb/upload-decisions.d.ts +28 -0
- package/dist/kb/upload-decisions.js +23 -0
- package/dist/kb/upload-decisions.js.map +1 -0
- package/dist/kb/upload-state.d.ts +1 -0
- package/dist/kb/upload-state.js +1 -0
- package/dist/kb/upload-state.js.map +1 -1
- package/dist/kb/web-login.d.ts +10 -0
- package/dist/kb/web-login.js +25 -0
- package/dist/kb/web-login.js.map +1 -0
- package/dist/kb/web-page.d.ts +3 -0
- package/dist/kb/web-page.js +114 -0
- package/dist/kb/web-page.js.map +1 -0
- package/dist/kb/web-queue.d.ts +61 -0
- package/dist/kb/web-queue.js +101 -0
- package/dist/kb/web-queue.js.map +1 -0
- package/dist/kb/web.d.ts +26 -0
- package/dist/kb/web.js +234 -0
- package/dist/kb/web.js.map +1 -0
- package/dist/local-web/context-menu.d.ts +2 -0
- package/dist/local-web/context-menu.js +53 -0
- package/dist/local-web/context-menu.js.map +1 -0
- package/dist/local-web/file-workspace-style.d.ts +2 -0
- package/dist/local-web/file-workspace-style.js +58 -0
- package/dist/local-web/file-workspace-style.js.map +1 -0
- package/dist/local-web/file-workspace.d.ts +2 -0
- package/dist/local-web/file-workspace.js +39 -0
- package/dist/local-web/file-workspace.js.map +1 -0
- package/dist/local-web/security.d.ts +6 -0
- package/dist/local-web/security.js +51 -0
- package/dist/local-web/security.js.map +1 -0
- package/dist/login-recovery.js +7 -2
- package/dist/login-recovery.js.map +1 -1
- package/dist/oss/transfer-queue.d.ts +7 -51
- package/dist/oss/transfer-queue.js +15 -308
- package/dist/oss/transfer-queue.js.map +1 -1
- package/dist/oss/web-page.js +21 -103
- package/dist/oss/web-page.js.map +1 -1
- package/dist/oss/web.js +4 -45
- package/dist/oss/web.js.map +1 -1
- package/dist/platform-profile.d.ts +25 -1
- package/dist/platform-profile.js +44 -1
- package/dist/platform-profile.js.map +1 -1
- package/dist/transfers/persistent-queue.d.ts +65 -0
- package/dist/transfers/persistent-queue.js +312 -0
- package/dist/transfers/persistent-queue.js.map +1 -0
- package/docs/design/kb-capability-upgrade.md +36 -0
- package/docs/design/kb-web-workspace.md +97 -0
- package/docs/kb-access.md +72 -0
- package/package.json +9 -7
- package/scripts/export-kb-contract.py +25 -6
- package/src/kb/openapi-contract.json +2297 -566
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { constants } from 'node:fs';
|
|
3
|
+
import { lstat, mkdir, open, readFile, rename, rm, chmod } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { OssError } from '../oss/errors.js';
|
|
6
|
+
export class PersistentTransferQueue {
|
|
7
|
+
options;
|
|
8
|
+
jobs = [];
|
|
9
|
+
active = new Map();
|
|
10
|
+
actions = new Map();
|
|
11
|
+
saving = Promise.resolve();
|
|
12
|
+
closing = false;
|
|
13
|
+
closed;
|
|
14
|
+
lock;
|
|
15
|
+
worker;
|
|
16
|
+
maximum;
|
|
17
|
+
storageError = false;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.options = options;
|
|
20
|
+
this.worker = options.worker;
|
|
21
|
+
this.maximum = options.maxConcurrent ?? 3;
|
|
22
|
+
if (!Number.isInteger(this.maximum) || this.maximum < 1 || this.maximum > 10)
|
|
23
|
+
throw new OssError('Invalid concurrency', 'oss_input_invalid', 400);
|
|
24
|
+
}
|
|
25
|
+
async initialize() {
|
|
26
|
+
const directory = this.options.directory;
|
|
27
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
28
|
+
const info = await lstat(directory);
|
|
29
|
+
if (!info.isDirectory() || info.isSymbolicLink())
|
|
30
|
+
throw new OssError('任务目录无效。', 'oss_transfer_storage_invalid');
|
|
31
|
+
await chmod(directory, 0o700);
|
|
32
|
+
const path = join(directory, 'queue.lock');
|
|
33
|
+
try {
|
|
34
|
+
this.lock = await open(path, 'wx', 0o600);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error.code !== 'EEXIST')
|
|
38
|
+
throw error;
|
|
39
|
+
// Serialize stale-PID recovery: comparing an inode before unlink is not
|
|
40
|
+
// sufficient when two new servers start at the same time.
|
|
41
|
+
const recoveryPath = join(directory, 'queue.recovery.lock');
|
|
42
|
+
const recovery = await open(recoveryPath, 'wx', 0o600).catch(() => { throw new OssError('任务队列正在恢复,或上次恢复被中断;请检查本机任务目录。', 'oss_transfer_queue_busy', 409); });
|
|
43
|
+
try {
|
|
44
|
+
const existing = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
45
|
+
let identity;
|
|
46
|
+
try {
|
|
47
|
+
identity = await existing.stat();
|
|
48
|
+
if (!identity.isFile() || identity.size > 100)
|
|
49
|
+
throw new Error('invalid lock');
|
|
50
|
+
const pid = Number((await existing.readFile('utf8')).trim());
|
|
51
|
+
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
52
|
+
throw new OssError('任务目录锁无效,请检查本机任务目录。', 'oss_transfer_storage_invalid');
|
|
53
|
+
let alive = true;
|
|
54
|
+
try {
|
|
55
|
+
process.kill(pid, 0);
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
if (e.code === 'ESRCH')
|
|
59
|
+
alive = false;
|
|
60
|
+
}
|
|
61
|
+
if (alive)
|
|
62
|
+
throw new OssError(`另一个文件传输服务(PID ${pid})正在使用此账号的任务队列。`, 'oss_transfer_queue_busy', 409);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
await existing.close();
|
|
66
|
+
}
|
|
67
|
+
const current = await lstat(path);
|
|
68
|
+
if (current.dev !== identity.dev || current.ino !== identity.ino)
|
|
69
|
+
throw new OssError('任务队列已被占用。', 'oss_transfer_queue_busy', 409);
|
|
70
|
+
await rm(path);
|
|
71
|
+
this.lock = await open(path, 'wx', 0o600);
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
await recovery.close();
|
|
75
|
+
await rm(recoveryPath, { force: true });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
await this.lock.writeFile(String(process.pid));
|
|
80
|
+
await this.lock.sync();
|
|
81
|
+
const statePath = join(directory, 'tasks.json');
|
|
82
|
+
const info = await lstat(statePath).catch(e => { if (e.code === 'ENOENT')
|
|
83
|
+
return undefined; throw e; });
|
|
84
|
+
if (info) {
|
|
85
|
+
if (!info.isFile() || info.isSymbolicLink() || info.size > 16 * 1024 * 1024)
|
|
86
|
+
throw new Error('invalid state file');
|
|
87
|
+
const state = this.options.stateSchema.parse(JSON.parse(await readFile(statePath, 'utf8')));
|
|
88
|
+
const seen = new Set();
|
|
89
|
+
for (const job of state.jobs) {
|
|
90
|
+
this.options.validate(job);
|
|
91
|
+
if (seen.has(job.id))
|
|
92
|
+
throw new Error('duplicate job');
|
|
93
|
+
seen.add(job.id);
|
|
94
|
+
if (['queued', 'running', 'pausing', 'cancelling'].includes(job.status))
|
|
95
|
+
job.status = 'paused';
|
|
96
|
+
}
|
|
97
|
+
this.jobs = state.jobs;
|
|
98
|
+
}
|
|
99
|
+
await this.save();
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
await this.unlock();
|
|
103
|
+
if (error instanceof OssError)
|
|
104
|
+
throw error;
|
|
105
|
+
throw new OssError('本机任务记录无法读取或写入;请检查任务目录。', 'oss_transfer_storage_invalid');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
list() {
|
|
109
|
+
return this.jobs.map(job => {
|
|
110
|
+
const active = this.active.get(job.id);
|
|
111
|
+
const bytesPerSecond = active && job.status === 'running' ? Math.max(0, (job.transferred - active.initial) / Math.max(1, (Date.now() - active.started) / 1000)) : 0;
|
|
112
|
+
return { ...this.options.view(job), bytesPerSecond };
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
async enqueue(additions) {
|
|
116
|
+
if (this.closing || this.storageError)
|
|
117
|
+
throw new OssError('任务队列不可用。', 'oss_transfer_storage_invalid');
|
|
118
|
+
const pending = this.jobs.filter(j => !['completed', 'cancelled'].includes(j.status));
|
|
119
|
+
if (!additions.length || pending.length + additions.length > 10000)
|
|
120
|
+
throw new OssError('未完成任务数量已达上限。', 'oss_transfer_limit', 400);
|
|
121
|
+
for (const job of additions) {
|
|
122
|
+
this.options.validate(job);
|
|
123
|
+
if (pending.some(other => this.options.conflicts(other, job)))
|
|
124
|
+
throw new OssError('同一路径已有未完成任务。', 'oss_transfer_duplicate', 409);
|
|
125
|
+
pending.push(job);
|
|
126
|
+
}
|
|
127
|
+
let excess = this.jobs.length + additions.length - 10000;
|
|
128
|
+
this.jobs = this.jobs.filter(job => { if (excess > 0 && ['completed', 'cancelled'].includes(job.status)) {
|
|
129
|
+
excess--;
|
|
130
|
+
return false;
|
|
131
|
+
} return true; });
|
|
132
|
+
this.jobs.push(...additions);
|
|
133
|
+
await this.save();
|
|
134
|
+
this.schedule();
|
|
135
|
+
const ids = new Set(additions.map(j => j.id));
|
|
136
|
+
return this.list().filter(j => ids.has(j.id));
|
|
137
|
+
}
|
|
138
|
+
context(job, controller) {
|
|
139
|
+
return this.options.context(job, { signal: controller.signal, save: () => this.save(), progress: bytes => {
|
|
140
|
+
if (Number.isFinite(bytes)) {
|
|
141
|
+
job.transferred = Math.max(0, Math.min(job.size, bytes));
|
|
142
|
+
job.updatedAt = new Date().toISOString();
|
|
143
|
+
}
|
|
144
|
+
} });
|
|
145
|
+
}
|
|
146
|
+
schedule() {
|
|
147
|
+
if (this.closing || this.storageError)
|
|
148
|
+
return;
|
|
149
|
+
for (const job of this.jobs) {
|
|
150
|
+
if (this.active.size >= this.maximum)
|
|
151
|
+
break;
|
|
152
|
+
if (job.status !== 'queued')
|
|
153
|
+
continue;
|
|
154
|
+
const controller = new AbortController();
|
|
155
|
+
job.status = 'running';
|
|
156
|
+
delete job.error;
|
|
157
|
+
const active = { controller, done: Promise.resolve(), started: Date.now(), initial: job.transferred };
|
|
158
|
+
this.active.set(job.id, active);
|
|
159
|
+
active.done = (async () => {
|
|
160
|
+
try {
|
|
161
|
+
await this.save();
|
|
162
|
+
await this.worker.run(job, this.context(job, controller));
|
|
163
|
+
// A committed object/file cannot be undone by a late pause or cancellation.
|
|
164
|
+
job.status = 'completed';
|
|
165
|
+
job.transferred = job.size;
|
|
166
|
+
this.options.committed?.(job);
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
if (controller.signal.aborted)
|
|
170
|
+
job.status = 'paused';
|
|
171
|
+
else {
|
|
172
|
+
job.status = 'failed';
|
|
173
|
+
job.error = this.options.safeError(error);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
job.updatedAt = new Date().toISOString();
|
|
178
|
+
try {
|
|
179
|
+
await this.save();
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
this.storageError = true;
|
|
183
|
+
}
|
|
184
|
+
this.active.delete(job.id);
|
|
185
|
+
this.schedule();
|
|
186
|
+
}
|
|
187
|
+
})();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
action(id, action) {
|
|
191
|
+
const previous = this.actions.get(id) ?? Promise.resolve();
|
|
192
|
+
const next = previous.catch(() => { }).then(() => this.performAction(id, action));
|
|
193
|
+
this.actions.set(id, next);
|
|
194
|
+
void next.finally(() => { if (this.actions.get(id) === next)
|
|
195
|
+
this.actions.delete(id); }).catch(() => { });
|
|
196
|
+
return next;
|
|
197
|
+
}
|
|
198
|
+
async performAction(id, action) {
|
|
199
|
+
if (this.closing)
|
|
200
|
+
throw new OssError('任务服务正在关闭。', 'oss_transfer_storage_invalid');
|
|
201
|
+
const job = this.jobs.find(j => j.id === id);
|
|
202
|
+
if (!job)
|
|
203
|
+
throw new OssError('任务不存在。', 'oss_input_invalid', 404);
|
|
204
|
+
if (['completed', 'cancelled'].includes(job.status))
|
|
205
|
+
return;
|
|
206
|
+
if (action === 'resume') {
|
|
207
|
+
if (!['paused', 'failed'].includes(job.status))
|
|
208
|
+
return;
|
|
209
|
+
if (this.storageError)
|
|
210
|
+
throw new OssError('任务记录写入失败。', 'oss_transfer_storage_invalid');
|
|
211
|
+
job.status = 'queued';
|
|
212
|
+
delete job.error;
|
|
213
|
+
await this.save();
|
|
214
|
+
this.schedule();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const active = this.active.get(id);
|
|
218
|
+
job.status = action === 'pause' ? 'pausing' : 'cancelling';
|
|
219
|
+
active?.controller.abort();
|
|
220
|
+
if (active)
|
|
221
|
+
await active.done;
|
|
222
|
+
if (job.status === 'completed')
|
|
223
|
+
return;
|
|
224
|
+
if (action === 'cancel') {
|
|
225
|
+
try {
|
|
226
|
+
await this.worker.cancel(job, this.context(job, new AbortController()));
|
|
227
|
+
if (job.status !== 'completed')
|
|
228
|
+
job.status = 'cancelled';
|
|
229
|
+
this.options.committed?.(job);
|
|
230
|
+
delete job.error;
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
job.status = 'failed';
|
|
234
|
+
job.error = this.options.safeError(error);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else
|
|
238
|
+
job.status = 'paused';
|
|
239
|
+
job.updatedAt = new Date().toISOString();
|
|
240
|
+
await this.save();
|
|
241
|
+
}
|
|
242
|
+
save() {
|
|
243
|
+
const write = this.saving.catch(() => { }).then(async () => {
|
|
244
|
+
const data = JSON.stringify(this.options.stateSchema.parse({ version: 1, jobs: this.jobs }));
|
|
245
|
+
if (Buffer.byteLength(data) > 16 * 1024 * 1024)
|
|
246
|
+
throw new Error('state too large');
|
|
247
|
+
const temporary = join(this.options.directory, `.tasks-${randomUUID()}.tmp`);
|
|
248
|
+
const file = await open(temporary, 'wx', 0o600);
|
|
249
|
+
try {
|
|
250
|
+
await file.writeFile(data);
|
|
251
|
+
await file.sync();
|
|
252
|
+
await file.close();
|
|
253
|
+
await rename(temporary, join(this.options.directory, 'tasks.json'));
|
|
254
|
+
// Persist the rename as well as the JSON bytes before acknowledging a checkpoint.
|
|
255
|
+
let parent;
|
|
256
|
+
try {
|
|
257
|
+
parent = await open(this.options.directory, constants.O_RDONLY);
|
|
258
|
+
await parent.sync();
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
if (process.platform !== 'win32' || !['EPERM', 'EISDIR', 'EINVAL', 'ENOTSUP'].includes(error.code ?? ''))
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
finally {
|
|
265
|
+
await parent?.close();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
await file.close().catch(() => { });
|
|
270
|
+
await rm(temporary, { force: true }).catch(() => { });
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
this.saving = write;
|
|
274
|
+
return write;
|
|
275
|
+
}
|
|
276
|
+
close() {
|
|
277
|
+
if (this.closed)
|
|
278
|
+
return this.closed;
|
|
279
|
+
this.closing = true;
|
|
280
|
+
this.closed = (async () => {
|
|
281
|
+
try {
|
|
282
|
+
for (const job of this.jobs)
|
|
283
|
+
if (job.status === 'queued')
|
|
284
|
+
job.status = 'paused';
|
|
285
|
+
for (const active of this.active.values())
|
|
286
|
+
active.controller.abort();
|
|
287
|
+
await Promise.allSettled([...this.active.values()].map(a => a.done));
|
|
288
|
+
await Promise.allSettled([...this.actions.values()]);
|
|
289
|
+
await this.save();
|
|
290
|
+
}
|
|
291
|
+
finally {
|
|
292
|
+
await this.unlock();
|
|
293
|
+
}
|
|
294
|
+
})();
|
|
295
|
+
return this.closed;
|
|
296
|
+
}
|
|
297
|
+
async unlock() {
|
|
298
|
+
if (!this.lock)
|
|
299
|
+
return;
|
|
300
|
+
const lock = this.lock;
|
|
301
|
+
this.lock = undefined;
|
|
302
|
+
try {
|
|
303
|
+
const owned = await lock.stat(), current = await lstat(join(this.options.directory, 'queue.lock'));
|
|
304
|
+
if (owned.ino === current.ino && owned.dev === current.dev)
|
|
305
|
+
await rm(join(this.options.directory, 'queue.lock'));
|
|
306
|
+
}
|
|
307
|
+
finally {
|
|
308
|
+
await lock.close();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
//# sourceMappingURL=persistent-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistent-queue.js","sourceRoot":"","sources":["../../src/transfers/persistent-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# KB 核心能力到 CLI / Skill 的接入
|
|
2
|
+
|
|
3
|
+
目标:核对 KB 系统现有主要能力,补齐 CLI 和统一 `numa-kb` Skill,验证后发布。
|
|
4
|
+
依据:KB `backend/external/agent_routes.py`、`backend/routes/knowledge_base.py`、
|
|
5
|
+
`backend/knowledge_base/{schemas,agent_search,search}.py`,以及前端调用契约。
|
|
6
|
+
|
|
7
|
+
## 能力与交付范围
|
|
8
|
+
|
|
9
|
+
| 能力 | 后端现状 | CLI / Skill 工作 |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| 多库检索 | Agent search 已接收 collection_ids,最多 20 个;统一召回及重排序 | 单库兼容;多库、全部可见库、文档限定和业务范围;结果保留来源与部分失败信息 |
|
|
12
|
+
| 租户隔离 | /auth/tenants + X-KB-Tenant | 列表、显式 --tenant;环境与租户分开,缓存身份不能串用 |
|
|
13
|
+
| 知识库管理 | 列表、详情、创建、更新、删除、转移所有者、成员角色 | CLI 接入现有接口;显式目标与破坏性确认;后端决定权限 |
|
|
14
|
+
| 文档阅读 | 列表、详情/正文、分页切片与附件、原件下载 | 限定分页与可选正文,不默认拉取全量内容;下载继续复用票据传输 |
|
|
15
|
+
| 文档生命周期 | 更新元数据、删除、重建索引 | 接入并清楚说明真实影响;不把文件操作与 KB 内容操作混同 |
|
|
16
|
+
| 版本治理 | 历史、变更历史、带 expected_current_version_id 的切换 | 明确目标版本、预期当前版本、原因和确认;冲突不自动重试 |
|
|
17
|
+
| 上传与入库 | 上传预检、直传、重复文件/新版本、异步任务 | 补齐预检及显式版本决策,复用现有传输及持久恢复记录 |
|
|
18
|
+
| 任务管理 | batch 列表、进度、取消、重试、ingest-jobs | 补齐发现和取消;保留已有 Agent 任务查询兼容性 |
|
|
19
|
+
| 冲突治理 | conflict-reviews 列表与 resolve | 列出冲突,显式 action 与 scope,不替用户猜测业务决策 |
|
|
20
|
+
| RAG 问答 | 多库 SSE + 会话列表、创建、读取、删除 | 核对流协议后提供 CLI 问答/会话入口;Skill 能区分检索证据与生成回答 |
|
|
21
|
+
| 文件存储工作区 | 文件夹、OSS 对象、imports、references | 与知识库文档实体不同,已有 OSS / KB Web 负责字节传输;梳理关联,避免复制存储系统 |
|
|
22
|
+
| 缓存与运维 | cache stats/invalidate | 属于管理能力,不改变检索业务语义;记录现有支持范围 |
|
|
23
|
+
|
|
24
|
+
## 验证与发布
|
|
25
|
+
|
|
26
|
+
- 从真实后端路由导出 OpenAPI 契约;新 CLI 请求需经过该契约验证。
|
|
27
|
+
- 检索测试必须验证多库为一次服务端调用,而不是在客户端混合不同评分。
|
|
28
|
+
- 验证 401/403、跨租户、缺失或歧义范围、超过 20 个库、部分失败、JSON 200 error。
|
|
29
|
+
- 读写都复用 KbClient 的身份、固定服务地址、响应脱敏与有界读取;写入未知不自动重试。
|
|
30
|
+
- 覆盖分页、版本 CAS、删除确认、异步任务状态、冲突决策及上传恢复。
|
|
31
|
+
- 更新 CLI 命令目录、帮助、文档和一个 numa-kb Skill;不保留多个发布包。
|
|
32
|
+
- CLI 按仓库 candidate → 验收 → latest 流程锁步发布。Skill 精确版本上传、扫描、
|
|
33
|
+
发布申请及回读;独立审核限制如仍存在需如实报告。
|
|
34
|
+
- 后端如有改动,通过 Numa/Jenkins 按确认环境部署;记录构建、平台账本和运行态。
|
|
35
|
+
|
|
36
|
+
本文件是工作范围与验收清单,不是已完成声明。
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# KB 文档工作台:复用 OSS Web
|
|
2
|
+
|
|
3
|
+
## 业务映射
|
|
4
|
+
|
|
5
|
+
| 原 OSS 交互 | KB 交互 | 权威接口 |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| Grant 选择 | 租户选择 | GET /api/v1/auth/tenants |
|
|
8
|
+
| Bucket/存储范围 | 知识库选择 | GET /api/v1/external/agent/collections |
|
|
9
|
+
| 对象列表 | 文档列表、状态、版本 | GET /api/v1/kb/documents |
|
|
10
|
+
| 原件下载 | KB 授权下载 | GET /api/v1/external/agent/documents/{id}/download-ticket |
|
|
11
|
+
| 对象删除 | 文档删除 | DELETE /api/v1/kb/documents/{id} |
|
|
12
|
+
|
|
13
|
+
仅复用交互,不建立 Grant=租户 或 Bucket=知识库 的物理一对一关系。沿用现有 bucket/prefix,所有访问与删除继续由 KB 检查权限并维护文档、文件与索引。
|
|
14
|
+
|
|
15
|
+
## 实现
|
|
16
|
+
|
|
17
|
+
新增 `numa kb web [--port PORT] [--no-open]`,遵守原有 Profile 服务路由和根级 `--issuer`。顶部选择租户、左侧选择知识库、中间分页显示文档、右侧查看详情。传输与入库任务位于独立任务面板。沿用 OSS Web 样式、原生选择器、回环地址保护和后台传输队列,不复制 OSS 的队列状态机。
|
|
18
|
+
|
|
19
|
+
- `src/transfers/persistent-queue.ts`:从 OSS 提取持久化、并发调度、暂停/恢复/取消、进程锁与原子保存;OSS 原有磁盘格式及分片 worker 保持兼容。
|
|
20
|
+
- `src/oss/transfer-queue.ts`:继续负责 Grant/STS、对象路径校验、分片检查点和作用域校验。
|
|
21
|
+
- `src/kb/web-queue.ts`:KB 队列适配器,调用现有 `uploadToKb`/`downloadFromKb`,与 CLI 使用相同 KB 传输逻辑。
|
|
22
|
+
- `src/local-web/context-menu.ts`:OSS 与 KB 共用菜单渲染、屏幕边界定位、键盘导航、外部点击关闭和焦点恢复;业务适配器分别提供可执行操作。KB 接入详情、原件下载和确认删除,未接入 OSS 对象移动、复制、重命名。文件列表的行组件和任务卡片由公共模块渲染;筛选、分页、业务详情和权限判断保留在各自适配器。
|
|
23
|
+
- `src/local-web/file-workspace.ts` / `file-workspace-style.ts`:OSS 与 KB 共用文件行、传输任务卡片及工作台样式;刷新更新现有任务卡片,保留焦点和按钮状态。
|
|
24
|
+
- `src/local-web/security.ts`:OSS 与 KB 共用 Host、Origin、本机会话认证、CSP、JSON 大小和字段校验。
|
|
25
|
+
|
|
26
|
+
本机工作台不接受浏览器指定任意本地路径;文件与目录必须通过已有原生选择器选取。界面只接收业务数据和任务摘要,Keycloak Token、OSS 凭据与签名地址均留在本地 CLI 进程。
|
|
27
|
+
|
|
28
|
+
## 租户与任务绑定
|
|
29
|
+
|
|
30
|
+
Profile 的平台 tenant 继续用于身份配置;KB 的业务 tenant 通过 `X-KB-Tenant` 独立传递。切换知识库不改变已有任务的 tenant/collection。队列目录按服务绑定和当前用户身份隔离;恢复任务重新校验所记录租户的成员资格。身份变化时拒绝继续工作,需要重启本地服务。
|
|
31
|
+
|
|
32
|
+
## 上传和入库
|
|
33
|
+
|
|
34
|
+
文件/目录扫描后写入任务队列 → 通过 KB 创建上传会话 → 并行上传 OSS → KB complete 校验并提交解析任务。
|
|
35
|
+
|
|
36
|
+
界面将“传输完成”和“入库完成”分开展示。关闭浏览器不停止本地上传;停止 CLI 会保存状态并暂停传输;已提交的服务端解析不被取消。停止传输仅清理仍在上传的 KB 会话,不删除文档、不撤销已提交的入库任务。
|
|
37
|
+
|
|
38
|
+
**当前 KB 恢复粒度仍是文件级**:已上传文件跳过,未完成文件重新上传。OSS Web 的分片 worker 依赖 Grant/STS,而 KB 当前只签发单次 PUT;本实现不伪造 STS,不把文件恢复标作分片续传。完整分片复用需要先增加 KB 会话的初始化、分片授权、完成与取消契约,再接到同一队列,无需另做 UI。
|
|
39
|
+
|
|
40
|
+
同名文件/版本冲突继续由 KB 拒绝或提示处理;本地工作台不自动覆盖、不绕过现有版本确认。
|
|
41
|
+
|
|
42
|
+
## 验收边界
|
|
43
|
+
|
|
44
|
+
目录选择拆为逐文件任务,统一限制最多 3 个文件传输;沿用 KB 文档的平面命名和版本规则。
|
|
45
|
+
|
|
46
|
+
测试覆盖租户隔离、身份改变、路径注入、Host/Origin/会话认证、确认删除、持久化暂停与重启、重复任务和后台入库状态,并运行已有 KB/OSS 回归。
|
|
47
|
+
|
|
48
|
+
本次实现与验证保持本地,不执行真实文档上传/删除,也不修改线上 bucket、Grant、租户或知识库。公开 Numa 1.25.0 尚不包含此新增命令。
|
|
49
|
+
|
|
50
|
+
## 环境选择
|
|
51
|
+
|
|
52
|
+
页面选择顺序为环境 → 租户 → 知识库。环境 ID 是 Profile 下发的服务路由键;`namespace` 仅作展示,不能用于拼接地址或决定身份。`envId` 可记录平台环境 ID。环境决定完整的 baseUrl、resource、authority、audience、roleClientId、apiVersion;authority 引用同一 Profile 的认证配置。
|
|
53
|
+
|
|
54
|
+
使用已有 `services.kb.configuration` JSON 扩展 `environments` 与 `defaultEnvironment`,复用平台已有服务配置存储和发布流程,无需增加另一套环境配置文件或数据库表。发布者必须同时维护对应 authority;客户端在使用前验证引用、完整字段和地址。默认环境的路由必须与顶层 KB 服务字段一致,旧 CLI 继续使用顶层字段。
|
|
55
|
+
|
|
56
|
+
- 未发布环境列表:显示“当前默认配置”,保持旧配置行为。
|
|
57
|
+
- 只有环境列表、无显式默认项:保留现有默认服务,并提供其他环境选项。
|
|
58
|
+
- `kb --environment <code> ...`:所有 KB 命令选择该环境。
|
|
59
|
+
- `login/auth status/auth logout --service kb --environment <code>`:使用相同环境身份与缓存。
|
|
60
|
+
- `--environment` 与 `--issuer` 互斥;环境选择不能再独立换 issuer。
|
|
61
|
+
- 每个 HTTP 请求携带环境 ID,服务端只接受启动时 Profile 中的环境;不接受浏览器传入 endpoint。
|
|
62
|
+
- 环境分别创建队列,并绑定端点与用户;切换页面不重定向或取消原任务。登录身份变化则阻止该环境继续处理。
|
|
63
|
+
- 多个环境可以共享 issuer/client/audience,复用同一认证缓存;业务端点和任务目录仍分开。
|
|
64
|
+
- 新环境未登录时使用 `numa login --service kb --environment <code>`,不复用另一个环境的不匹配 Token。
|
|
65
|
+
- 删除确认保存原始环境和租户,切换页面后仍指向确认时的对象;弹窗展示环境和租户。
|
|
66
|
+
|
|
67
|
+
示例(仅说明结构,地址和 authority 必须来自平台实际绑定):
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"defaultEnvironment": "stg",
|
|
72
|
+
"environments": {
|
|
73
|
+
"stg": {
|
|
74
|
+
"name": "预发布环境",
|
|
75
|
+
"namespace": "kb-stg",
|
|
76
|
+
"envId": 2,
|
|
77
|
+
"baseUrl": "https://gateway-stg.example/kb-management",
|
|
78
|
+
"resource": "https://gateway-stg.example/kb-management",
|
|
79
|
+
"authority": "kb-stg-login",
|
|
80
|
+
"audience": "kb-management",
|
|
81
|
+
"roleClientId": "kb-management",
|
|
82
|
+
"apiVersion": "v1"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
本地测试使用两套隔离模拟端点验证路由、Token、租户选择和删除目标;未修改线上 Profile,真实环境选项仍以平台已发布内容为准。
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
## 工作台登录恢复
|
|
92
|
+
|
|
93
|
+
工作台先启动受本地会话认证保护的页面;缺失登录不阻止启动。初次进入或选择环境后,401 / `kb_auth_required` 可由页面发起同源 POST `/api/login?environment=...`。服务端按已发布环境的配置调用共享 SDK 的 `startBrowserLogin` 和 `completeBrowserLogin`,保留 PKCE、state、回调校验和相同缓存键,不另行实现 OAuth。
|
|
94
|
+
|
|
95
|
+
回调地址只由本地服务生成,返回当前工作台及环境,页面等待服务端完成 Token 交换和身份读取后再载入租户。一个本地服务同一时间只允许一个交互登录,重复请求复用进行中的任务。只读轮询和后台队列不触发登录;不重放写请求。关闭服务会取消等待并关闭回调监听。403 和认证连接失败不触发登录。已存在队列的用户身份不能随重新登录改变,换账号需重启并使用独立队列。
|
|
96
|
+
|
|
97
|
+
返回页面的本地会话标识仅在 URL fragment 中,OIDC Token 始终留在 CLI 缓存;浏览器业务接口不返回 Token、授权码或凭据。
|
package/docs/kb-access.md
CHANGED
|
@@ -90,3 +90,75 @@ npm run root:typecheck
|
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
去掉 `--check` 可在审查服务端变化后更新快照。导出不连接部署环境,不加载真实业务数据。受控本地 HTTP 测试覆盖协议和文件行为,不能代替目标环境的真实网关、Keycloak 和 OSS 联调(旧 Key 模式另行验证)。
|
|
93
|
+
## 本地文档工作台(待发布)
|
|
94
|
+
|
|
95
|
+
新增 `numa kb web`:选择租户与知识库,查询文档、上传文件或目录、下载原件、确认删除,并查看传输及后台入库进度。使用与 `kb upload` 相同的 Profile/JWT 身份。`--no-open` 不自动打开浏览器,`--port` 指定回环端口。
|
|
96
|
+
|
|
97
|
+
在本分支完成依赖安装与 `npm run root:build` 后,本地运行:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
node dist/cli.js kb web
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
选择目录会递归生成逐文件任务,最多同时传输 3 个文件。目录结构不映射为知识库目录,同名与版本冲突沿用 KB 规则。
|
|
104
|
+
|
|
105
|
+
关闭页面后上传继续;停止 CLI 后未完成的传输暂停,已提交的入库任务继续执行。重启同一身份的工作台后可继续任务。KB 目前按文件恢复,不支持字节级分片续传。此功能尚未包含在公开 1.25.0 包中;实现边界见 [设计](design/kb-web-workspace.md)。
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
平台下发多环境配置后,可在工作台选择环境,也可通过 CLI 指定:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
numa login --service kb --environment <环境代码>
|
|
112
|
+
numa kb --environment <环境代码> web
|
|
113
|
+
numa kb --environment <环境代码> collections list
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
环境代码来自 Profile,不能填写任意网关地址;未下发列表时沿用当前默认配置。切换环境会一起切换 KB 地址、issuer、Client 和 audience,原环境的后台传输继续绑定原目标。
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
`kb web` 在所选环境未登录或会话失效时,会通过现有 PKCE 登录流程自动打开该环境的 Keycloak,登录成功后返回同一个 KB 工作台和环境。也可点击“登录当前环境”重新发起登录。有效缓存会直接复用;403 权限不足和认证服务网络错误不自动登录。取消或失败后需手动重试,不持续弹窗。
|
|
120
|
+
|
|
121
|
+
`--no-open` 不自动打开工作台或发起登录;手动打开工作台后仍可点击登录按钮。机器身份不提供浏览器登录。后台传输不会自动弹出登录窗口,认证失效时保留失败任务,登录后手动恢复;不会自动重放删除等写操作。
|
|
122
|
+
|
|
123
|
+
## 1.26 核心能力升级
|
|
124
|
+
|
|
125
|
+
跨库检索直接使用服务端统一召回/排序,一次选择 1–20 个库:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npx -y @numa-tech/numa@1.26.0 kb --environment ENV --tenant TENANT search "部署规范" --collection-ids KB_A KB_B --json
|
|
129
|
+
npx -y @numa-tech/numa@1.26.0 kb search "部署规范" --all-collections --json
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
单库、多库、全部可见库三个范围互斥。全部指当前租户内可见的库,超过 20 个明确拒绝;
|
|
133
|
+
不跨环境合并身份,也不在客户端拼接评分。`_meta.search_complete=false` 表示部分库失败,
|
|
134
|
+
`failed_collection_ids` 标出缺失范围;sources 保留文档所属 collection_id。
|
|
135
|
+
|
|
136
|
+
新增 `tenants`、`users`、`collections inspect/update/delete/transfer/members`、
|
|
137
|
+
`documents list/inspect/chunks/assets/versions/history/switch-version/update/delete/reindex`、
|
|
138
|
+
`tasks list/cancel/jobs`、`conflicts list/resolve`、`ask`、`sessions list/create/inspect/delete`。
|
|
139
|
+
精确参数见各命令 `--help`。文档正文需 `inspect --content`,列表用 offset/limit 翻页。
|
|
140
|
+
写操作继续使用当前 JWT 和服务端 ACL;删除、转移、重建、版本切换和冲突决策要求 `--yes`。
|
|
141
|
+
版本切换必须携带读取到的 `--expected-current`,409 后重新读取,不强制覆盖。
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
numa kb preflight ./docs --collection-id KB --json
|
|
145
|
+
numa kb ask "这些规范有何不同?" --collection-ids KB_A KB_B --json
|
|
146
|
+
numa kb sessions create --collection-ids KB_A KB_B --title "规范对比" --json
|
|
147
|
+
numa kb documents versions DOC --json
|
|
148
|
+
numa kb documents switch-version DOC --target TARGET --expected-current CURRENT --reason "批准修订" --yes --json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`ask` 返回回答与检索来源,指定 `--session-id` 才保存本次问题/回答;流中断后先读取会话,
|
|
152
|
+
不自动重试写入。`preflight` 只发送摘要;根据候选决定上传新版本时,传入
|
|
153
|
+
`--decisions-file ./decisions.json --yes`,文件内容为数组:
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
[{"filename":"report.pdf","sha256":"预检返回的64位SHA256","duplicate_action":"new_version","existing_document_id":"目标文档ID","version_reason":"批准修订","version_confirmation":"SWITCH_CURRENT_VERSION"}]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
恢复必须使用同一决策文件。决策与摘要不匹配拒绝;旧代理上传不能应用版本决策时明确拒绝。
|
|
160
|
+
当前后端已退役 multipart 上传,旧兼容契约单独保存在 `openapi-legacy-contract.json`;
|
|
161
|
+
当前 `openapi-contract.json` 由现有后端 42 个操作离线生成,不能以旧契约断言当前支持代理上传。
|
|
162
|
+
|
|
163
|
+
统一 `numa-kb 1.1.0` Skill 覆盖全部流程,要求 CLI >=1.26.0。包版本号仅表示实现目标;
|
|
164
|
+
实际可用性以 npm latest 与技能仓库发布状态为准。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@numa-tech/numa",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.1",
|
|
4
4
|
"description": "Cross-platform CLI for the Numa internal developer platform with Keycloak authentication and MCP support.",
|
|
5
5
|
"author": "numa-tech",
|
|
6
6
|
"keywords": [
|
|
@@ -32,12 +32,14 @@
|
|
|
32
32
|
"docs/kb-access.md",
|
|
33
33
|
"docs/gitops-configuration.md",
|
|
34
34
|
"docs/design/kb-cli.md",
|
|
35
|
+
"docs/design/kb-web-workspace.md",
|
|
35
36
|
"scripts/export-kb-contract.py",
|
|
36
37
|
"src/kb/openapi-contract.json",
|
|
37
38
|
"docs/authorization-configuration-publication.md",
|
|
38
39
|
"docs/oss-policy-client-governance.md",
|
|
39
40
|
"docs/environment-configuration-publication.md",
|
|
40
|
-
"docs/keycloak-managed-import.md"
|
|
41
|
+
"docs/keycloak-managed-import.md",
|
|
42
|
+
"docs/design/kb-capability-upgrade.md"
|
|
41
43
|
],
|
|
42
44
|
"scripts": {
|
|
43
45
|
"clean": "node scripts/clean.mjs",
|
|
@@ -47,9 +49,9 @@
|
|
|
47
49
|
"root:typecheck": "tsc --noEmit",
|
|
48
50
|
"test": "turbo run test root:test",
|
|
49
51
|
"dev:oss-web": "tsx src/cli.ts oss web",
|
|
50
|
-
"test:oss-web": "node --import tsx --test src/oss/web.test.ts src/oss/web-session.test.ts src/oss/web-transfers.test.ts src/oss/web-command.test.ts src/oss/local-picker.test.ts src/oss/transfer-paths.test.ts src/oss/transfer-worker.test.ts src/oss/transfer-queue.test.ts src/oss/web-page.test.ts src/oss/file-operations.test.ts src/oss/download-links.test.ts",
|
|
52
|
+
"test:oss-web": "node --import tsx --test src/oss/web.test.ts src/oss/web-session.test.ts src/oss/web-transfers.test.ts src/oss/web-command.test.ts src/oss/local-picker.test.ts src/oss/transfer-paths.test.ts src/oss/transfer-worker.test.ts src/oss/transfer-queue.test.ts src/oss/web-page.test.ts src/oss/file-operations.test.ts src/oss/download-links.test.ts src/local-web/context-menu.test.ts",
|
|
51
53
|
"test:oss-auth-code": "node scripts/test-oss-auth-code.mjs",
|
|
52
|
-
"root:test": "node --import tsx --test --test-concurrency=4 src/mcp-gateway/profile.test.ts src/mcp-gateway/runtime.test.ts src/mcp-gateway/install.test.ts src/branding.test.ts src/app-config.test.ts src/platform-profile.test.ts src/profile-migration.test.ts src/identity.test.ts src/backend.test.ts src/oauth.test.ts src/web-console.test.ts src/cli-options.test.ts src/mail/client.test.ts src/wecom/client.test.ts src/wecom/commands.test.ts src/wecom/tools.test.ts src/ai-registry/client.test.ts src/ai-registry/bootstrap.test.ts src/ai-registry/install-defaults.test.ts src/ai-registry/workspace.test.ts src/ai-registry/namespace.test.ts src/compatibility/registry-instance.test.ts src/ai-registry/sync.test.ts src/ai-registry/bridge.test.ts src/ai-registry/skill-installer.test.ts src/ai-registry/skill-uninstaller.test.ts src/ai-registry/skill-dependencies.test.ts src/ai-registry/updates.test.ts src/maintenance/scheduler.test.ts src/maintenance/codex.test.ts src/ai-registry/mcp-installer.test.ts src/media/client.test.ts src/media/commands.test.ts src/media/transfers.test.ts src/oss/client.test.ts src/oss/governance.test.ts src/oss/commands.test.ts src/oss/auth-code.test.ts src/oss/clipboard.test.ts src/oss/web.test.ts src/oss/web-session.test.ts src/oss/web-transfers.test.ts src/oss/web-command.test.ts src/oss/local-picker.test.ts src/oss/transfer-paths.test.ts src/oss/transfer-worker.test.ts src/oss/transfer-queue.test.ts src/oss/web-page.test.ts src/oss/file-operations.test.ts src/oss/download-links.test.ts src/oss/bootstrap.test.ts src/oss/transfers.test.ts src/oss/search.test.ts src/oss/tools.test.ts src/mcp/install.test.ts src/mcp/catalog.test.ts src/tasks/runtime.test.ts src/tasks/stdio.test.ts src/tasks/wecom-notify.test.ts src/tasks/oss-query.test.ts src/interactive/wizard.test.ts src/pipeline/client.test.ts src/pipeline/commands.test.ts src/gitops/client.test.ts src/gitops/commands.test.ts src/gitops/workload-input.test.ts src/gitops/workload-client.test.ts src/gitops/workload-commands.test.ts src/repositories/client.test.ts src/repositories/commands.test.ts src/publications/scanner.test.ts src/publications/client.test.ts src/publications/commands.test.ts src/jenkins-jobs/client.test.ts src/jenkins-jobs/commands.test.ts src/clusters/client.test.ts src/clusters/commands.test.ts src/clusters/kubeconfig-discovery.test.ts src/teams/client.test.ts src/teams/commands.test.ts src/authorization/bootstrap-client.test.ts src/authorization/commands.test.ts src/authorization/access-request-client.test.ts src/authorization/access-request-commands.test.ts src/application-candidates/client.test.ts src/application-candidates/commands.test.ts src/application-onboarding/client.test.ts src/application-onboarding/commands.test.ts src/application-onboarding/tui.test.ts src/chatgpt-desktop.test.ts src/codex-integration.test.ts scripts/release-all.test.mjs src/authorization/enrollment.test.ts src/authorization/application-commands.test.ts src/authorization/application-client.test.ts src/authorization/application-schemas.test.ts src/authorization/enrollment-client.test.ts src/notion/client.test.ts src/notion/commands.test.ts src/notion/tools.test.ts src/notion/machine-cli.test.ts src/cloud/accounts.test.ts src/cloud/bootstrap.test.ts src/cloud/business-bootstrap.test.ts src/cloud/maintenance.test.ts src/cloud/wizard.test.ts src/cloud/oss-bootstrap.test.ts src/business-issuer.test.ts src/login-configuration.test.ts src/login-recovery.test.ts src/ai-registry/mcp-management.test.ts src/ai-registry/mcp-runtime.test.ts src/ai-registry/mcp-stdio.test.ts src/kb/client.test.ts src/kb/config.test.ts src/kb/commands.test.ts src/kb/login.test.ts src/kb/transfers.test.ts src/kb/contract.test.ts src/authorization/environment-governance-client.test.ts src/authorization/environment-governance-commands.test.ts src/authorization/environment-publication.test.ts src/authorization/managed-keycloak.test.ts src/authorization/managed-keycloak-producer.test.ts",
|
|
54
|
+
"root:test": "node --import tsx --test --test-concurrency=4 src/mcp-gateway/profile.test.ts src/mcp-gateway/runtime.test.ts src/mcp-gateway/install.test.ts src/branding.test.ts src/app-config.test.ts src/platform-profile.test.ts src/profile-migration.test.ts src/identity.test.ts src/backend.test.ts src/oauth.test.ts src/web-console.test.ts src/cli-options.test.ts src/mail/client.test.ts src/wecom/client.test.ts src/wecom/commands.test.ts src/wecom/tools.test.ts src/ai-registry/client.test.ts src/ai-registry/bootstrap.test.ts src/ai-registry/install-defaults.test.ts src/ai-registry/workspace.test.ts src/ai-registry/namespace.test.ts src/compatibility/registry-instance.test.ts src/ai-registry/sync.test.ts src/ai-registry/bridge.test.ts src/ai-registry/skill-installer.test.ts src/ai-registry/skill-uninstaller.test.ts src/ai-registry/skill-dependencies.test.ts src/ai-registry/updates.test.ts src/maintenance/scheduler.test.ts src/maintenance/codex.test.ts src/ai-registry/mcp-installer.test.ts src/media/client.test.ts src/media/commands.test.ts src/media/transfers.test.ts src/oss/client.test.ts src/oss/governance.test.ts src/oss/commands.test.ts src/oss/auth-code.test.ts src/oss/clipboard.test.ts src/oss/web.test.ts src/oss/web-session.test.ts src/oss/web-transfers.test.ts src/oss/web-command.test.ts src/oss/local-picker.test.ts src/oss/transfer-paths.test.ts src/oss/transfer-worker.test.ts src/oss/transfer-queue.test.ts src/oss/web-page.test.ts src/oss/file-operations.test.ts src/oss/download-links.test.ts src/oss/bootstrap.test.ts src/oss/transfers.test.ts src/oss/search.test.ts src/oss/tools.test.ts src/mcp/install.test.ts src/mcp/catalog.test.ts src/tasks/runtime.test.ts src/tasks/stdio.test.ts src/tasks/wecom-notify.test.ts src/tasks/oss-query.test.ts src/interactive/wizard.test.ts src/pipeline/client.test.ts src/pipeline/commands.test.ts src/gitops/client.test.ts src/gitops/commands.test.ts src/gitops/workload-input.test.ts src/gitops/workload-client.test.ts src/gitops/workload-commands.test.ts src/repositories/client.test.ts src/repositories/commands.test.ts src/publications/scanner.test.ts src/publications/client.test.ts src/publications/commands.test.ts src/jenkins-jobs/client.test.ts src/jenkins-jobs/commands.test.ts src/clusters/client.test.ts src/clusters/commands.test.ts src/clusters/kubeconfig-discovery.test.ts src/teams/client.test.ts src/teams/commands.test.ts src/authorization/bootstrap-client.test.ts src/authorization/commands.test.ts src/authorization/access-request-client.test.ts src/authorization/access-request-commands.test.ts src/application-candidates/client.test.ts src/application-candidates/commands.test.ts src/application-onboarding/client.test.ts src/application-onboarding/commands.test.ts src/application-onboarding/tui.test.ts src/chatgpt-desktop.test.ts src/codex-integration.test.ts scripts/release-all.test.mjs src/authorization/enrollment.test.ts src/authorization/application-commands.test.ts src/authorization/application-client.test.ts src/authorization/application-schemas.test.ts src/authorization/enrollment-client.test.ts src/notion/client.test.ts src/notion/commands.test.ts src/notion/tools.test.ts src/notion/machine-cli.test.ts src/cloud/accounts.test.ts src/cloud/bootstrap.test.ts src/cloud/business-bootstrap.test.ts src/cloud/maintenance.test.ts src/cloud/wizard.test.ts src/cloud/oss-bootstrap.test.ts src/business-issuer.test.ts src/login-configuration.test.ts src/login-recovery.test.ts src/ai-registry/mcp-management.test.ts src/ai-registry/mcp-runtime.test.ts src/ai-registry/mcp-stdio.test.ts src/kb/client.test.ts src/kb/config.test.ts src/kb/commands.test.ts src/kb/login.test.ts src/kb/transfers.test.ts src/kb/contract.test.ts src/authorization/environment-governance-client.test.ts src/authorization/environment-governance-commands.test.ts src/authorization/environment-publication.test.ts src/authorization/managed-keycloak.test.ts src/authorization/managed-keycloak-producer.test.ts src/kb/web.test.ts src/kb/web-queue.test.ts src/local-web/context-menu.test.ts src/kb/web-login.test.ts src/kb/capabilities.test.ts",
|
|
53
55
|
"pack:check": "turbo run pack:check root:pack:check",
|
|
54
56
|
"root:pack:check": "node scripts/check-root-package.mjs",
|
|
55
57
|
"boundaries": "node scripts/check-workspace-boundaries.mjs",
|
|
@@ -67,7 +69,7 @@
|
|
|
67
69
|
"release:resume": "node scripts/release-all.mjs --resume",
|
|
68
70
|
"release:promote": "node scripts/release-all.mjs --promote",
|
|
69
71
|
"prepack": "npm run root:build && npm run root:pack:check",
|
|
70
|
-
"test:kb": "node --import tsx --test --test-concurrency=4 src/kb/client.test.ts src/kb/config.test.ts src/kb/commands.test.ts src/kb/login.test.ts src/kb/transfers.test.ts src/kb/contract.test.ts"
|
|
72
|
+
"test:kb": "node --import tsx --test --test-concurrency=4 src/kb/client.test.ts src/kb/config.test.ts src/kb/commands.test.ts src/kb/login.test.ts src/kb/transfers.test.ts src/kb/contract.test.ts src/kb/web.test.ts src/kb/web-queue.test.ts src/local-web/context-menu.test.ts src/kb/web-login.test.ts src/kb/capabilities.test.ts"
|
|
71
73
|
},
|
|
72
74
|
"engines": {
|
|
73
75
|
"node": ">=20.12.0"
|
|
@@ -75,8 +77,8 @@
|
|
|
75
77
|
"dependencies": {
|
|
76
78
|
"@clack/prompts": "^1.7.0",
|
|
77
79
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
78
|
-
"@numa-tech/cli-auth": "1.
|
|
79
|
-
"@tokensrc/codex": "1.
|
|
80
|
+
"@numa-tech/cli-auth": "1.26.1",
|
|
81
|
+
"@tokensrc/codex": "1.26.1",
|
|
80
82
|
"ali-oss": "^6.23.0",
|
|
81
83
|
"commander": "^14.0.3",
|
|
82
84
|
"ink": "^6.8.0",
|