@michengai/dsh-btw 0.1.0 → 0.1.3
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/CHANGELOG.md +23 -0
- package/CHANGELOG.zh-CN.md +23 -0
- package/README.md +21 -90
- package/README.zh-CN.md +21 -88
- package/assets/branding/dsh-btw-banner.png +0 -0
- package/lib/client.js +34 -7
- package/lib/index.js +113 -35
- package/lib/types/client/bubbles.d.ts +4 -1
- package/lib/types/locales.d.ts +2 -0
- package/lib/types/server/jobs.d.ts +16 -1
- package/package.json +2 -3
- package/docs/00-/344/272/244/346/216/245/345/205/245/345/217/243/00-/351/230/205/350/257/273/345/257/274/350/210/252.md +0 -12
- package/docs/00-/344/272/244/346/216/245/345/205/245/345/217/243/02-/345/275/223/345/211/215/347/212/266/346/200/201.md +0 -16
- package/docs/00-/344/272/244/346/216/245/345/205/245/345/217/243/03-/345/276/205/345/212/236/344/270/216/351/230/273/345/241/236.md +0 -11
- package/docs/01-/345/275/223/345/211/215/345/267/245/344/275/234/I005-/351/246/226/346/254/241/345/217/221/345/270/203/344/270/216/345/217/257/344/277/241/345/217/221/345/270/203/351/205/215/347/275/256/00-/350/277/255/344/273/243/346/200/273/350/247/210.md +0 -27
- package/docs/05-/345/267/245/347/250/213/344/272/244/344/273/230/01-/350/207/252/345/212/250/345/217/221/345/270/203.md +0 -58
- package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I001-/344/270/200/346/254/241/346/200/247/346/227/201/351/227/256/346/260/224/346/263/241/344/270/216/350/276/223/345/205/245/345/216/206/345/217/262/00-/350/277/255/344/273/243/346/200/273/350/247/210.md +0 -44
- package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I001-/344/270/200/346/254/241/346/200/247/346/227/201/351/227/256/346/260/224/346/263/241/344/270/216/350/276/223/345/205/245/345/216/206/345/217/262/01-/351/252/214/346/224/266/350/256/260/345/275/225.md +0 -32
- package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I002-/345/256/236/351/231/205DSH/346/217/222/344/273/266/346/216/245/345/205/245/00-/350/277/255/344/273/243/346/200/273/350/247/210.md +0 -45
- package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I002-/345/256/236/351/231/205DSH/346/217/222/344/273/266/346/216/245/345/205/245/01-/346/265/213/350/257/225/347/216/257/345/242/203/351/232/224/347/246/273.md +0 -38
- package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I002-/345/256/236/351/231/205DSH/346/217/222/344/273/266/346/216/245/345/205/245/02-/345/256/242/346/210/267/347/253/257/346/263/250/345/205/245/344/270/216/346/260/224/346/263/241/345/256/275/345/272/246/344/277/256/345/244/215.md +0 -28
- package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I003-/345/256/277/344/270/273/345/244/226/350/247/202/344/270/216/345/233/275/351/231/205/345/214/226/00-/350/277/255/344/273/243/346/200/273/350/247/210.md +0 -47
- package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I003-/345/256/277/344/270/273/345/244/226/350/247/202/344/270/216/345/233/275/351/231/205/345/214/226/01-/350/247/206/350/247/211/351/252/214/346/224/266.md +0 -35
- package/docs/07-/350/277/255/344/273/243/345/275/222/346/241/243/2026/I004-/344/273/223/345/272/223/344/270/216/350/207/252/345/212/250/345/217/221/345/270/203/00-/350/277/255/344/273/243/346/200/273/350/247/210.md +0 -38
package/lib/index.js
CHANGED
|
@@ -21,6 +21,7 @@ var zh = {
|
|
|
21
21
|
"error.empty": "\u8BF7\u8F93\u5165\u65C1\u95EE\u5185\u5BB9\u3002",
|
|
22
22
|
"error.length": "\u65C1\u95EE\u6700\u591A\u652F\u6301 8000 \u4E2A\u5B57\u7B26\u3002",
|
|
23
23
|
"error.capacity": "\u6B63\u5728\u5904\u7406\u7684\u65C1\u95EE\u8F83\u591A\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
|
|
24
|
+
"error.bubbleCapacity": "\u5F53\u524D\u4F1A\u8BDD\u7684\u65C1\u95EE\u6C14\u6CE1\u5DF2\u8FBE\u4E0A\u9650\uFF0C\u8BF7\u5148\u5173\u95ED\u65E7\u6C14\u6CE1\uFF1B\u5173\u95ED\u5931\u8D25\u7684\u6C14\u6CE1\u53EF\u91CD\u8BD5\u3002",
|
|
24
25
|
"error.close": "\u5173\u95ED\u5931\u8D25\uFF0C\u53EF\u91CD\u8BD5\u3002{detail}",
|
|
25
26
|
"error.requestLength": "\u65C1\u95EE\u5185\u5BB9\u8FC7\u957F\u3002",
|
|
26
27
|
"error.request": "\u65C1\u95EE\u8BF7\u6C42\u65E0\u6548\u3002",
|
|
@@ -32,7 +33,8 @@ var zh = {
|
|
|
32
33
|
"error.timeout": "\u65C1\u95EE\u8D85\u65F6\uFF0C\u5DF2\u53D6\u6D88\u3002",
|
|
33
34
|
"error.incomplete": "\u65C1\u95EE\u672A\u5B8C\u6210\uFF08{reason}\uFF09\u3002{detail}",
|
|
34
35
|
"error.noText": "\u6A21\u578B\u672A\u8FD4\u56DE\u6587\u5B57\u56DE\u7B54\u3002",
|
|
35
|
-
"error.cleanup": "\u8D44\u6E90\u6E05\u7406\u5931\u8D25\uFF0C\u8BF7\u518D\u6B21\u5173\u95ED\u91CD\u8BD5\u3002{detail}"
|
|
36
|
+
"error.cleanup": "\u8D44\u6E90\u6E05\u7406\u5931\u8D25\uFF0C\u8BF7\u518D\u6B21\u5173\u95ED\u91CD\u8BD5\u3002{detail}",
|
|
37
|
+
"error.cleanupTimeout": "\u8D44\u6E90\u91CA\u653E\u4ECD\u672A\u5B8C\u6210\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u5173\u95ED\u3002"
|
|
36
38
|
};
|
|
37
39
|
var en = {
|
|
38
40
|
"locale.id": "en",
|
|
@@ -56,6 +58,7 @@ var en = {
|
|
|
56
58
|
"error.empty": "Enter a side question.",
|
|
57
59
|
"error.length": "Side questions are limited to 8,000 characters.",
|
|
58
60
|
"error.capacity": "Too many side questions are running. Try again shortly.",
|
|
61
|
+
"error.bubbleCapacity": "This session has reached its bubble limit. Close older bubbles first; retry any failed closures.",
|
|
59
62
|
"error.close": "Could not close. Try again. {detail}",
|
|
60
63
|
"error.requestLength": "The side question request is too long.",
|
|
61
64
|
"error.request": "Invalid side question request.",
|
|
@@ -67,7 +70,8 @@ var en = {
|
|
|
67
70
|
"error.timeout": "The side question timed out and was cancelled.",
|
|
68
71
|
"error.incomplete": "The side question did not complete ({reason}).{detail}",
|
|
69
72
|
"error.noText": "The model returned no text.",
|
|
70
|
-
"error.cleanup": "Cleanup failed. Close again to retry. {detail}"
|
|
73
|
+
"error.cleanup": "Cleanup failed. Close again to retry. {detail}",
|
|
74
|
+
"error.cleanupTimeout": "Resources are still being released. Try closing again shortly."
|
|
71
75
|
};
|
|
72
76
|
function translate(locale = "zh") {
|
|
73
77
|
const dictionary = locale === "zh" ? zh : en;
|
|
@@ -102,13 +106,15 @@ function parseRequest(raw) {
|
|
|
102
106
|
// src/server/jobs.ts
|
|
103
107
|
var failure = (error) => ({ kind: "error", text: error instanceof Error ? error.message : String(error) });
|
|
104
108
|
var SideJobs = class {
|
|
105
|
-
constructor(start, timeoutMs = 9e4) {
|
|
109
|
+
constructor(start, timeoutMs = 9e4, options = {}) {
|
|
106
110
|
this.start = start;
|
|
107
111
|
this.timeoutMs = timeoutMs;
|
|
112
|
+
this.options = options;
|
|
108
113
|
}
|
|
109
114
|
jobs = /* @__PURE__ */ new Map();
|
|
110
115
|
retired = /* @__PURE__ */ new Map();
|
|
111
116
|
disposed = false;
|
|
117
|
+
idleNotified = false;
|
|
112
118
|
get size() {
|
|
113
119
|
return this.jobs.size;
|
|
114
120
|
}
|
|
@@ -119,7 +125,7 @@ var SideJobs = class {
|
|
|
119
125
|
if (this.disposed) return Promise.resolve(failure(t("error.stopped")));
|
|
120
126
|
if (this.jobs.has(key) || this.retired.has(key)) return Promise.resolve(failure(t("error.duplicate")));
|
|
121
127
|
if (this.jobs.size >= 8) return Promise.resolve(failure(t("error.capacity")));
|
|
122
|
-
const job = { controller: new AbortController(), finished: Promise.resolve({ kind: "success", text: "" }), t };
|
|
128
|
+
const job = { key, controller: new AbortController(), finished: Promise.resolve({ kind: "success", text: "" }), t };
|
|
123
129
|
this.jobs.set(key, job);
|
|
124
130
|
const cancel = () => job.controller.abort(new Error(t("error.cancelled")));
|
|
125
131
|
signal?.addEventListener("abort", cancel, { once: true });
|
|
@@ -129,10 +135,7 @@ var SideJobs = class {
|
|
|
129
135
|
job.finished = this.execute(job, { scope, id, question, signal: job.controller.signal, toolFilter: { allow: [] }, context, locale }).finally(() => {
|
|
130
136
|
clearTimeout(timer);
|
|
131
137
|
signal?.removeEventListener("abort", cancel);
|
|
132
|
-
|
|
133
|
-
this.jobs.delete(key);
|
|
134
|
-
this.retire(key);
|
|
135
|
-
}
|
|
138
|
+
this.release(job);
|
|
136
139
|
});
|
|
137
140
|
return job.finished;
|
|
138
141
|
}
|
|
@@ -143,38 +146,47 @@ var SideJobs = class {
|
|
|
143
146
|
const job = this.jobs.get(key);
|
|
144
147
|
if (!job) return { kind: "success", text: "" };
|
|
145
148
|
job.controller.abort(new Error(job.t("error.cancelled")));
|
|
146
|
-
await job.finished;
|
|
147
149
|
try {
|
|
148
150
|
await this.cleanup(job);
|
|
149
|
-
this.jobs.delete(key);
|
|
150
151
|
return { kind: "success", text: "" };
|
|
151
152
|
} catch (error) {
|
|
152
|
-
|
|
153
|
+
this.report(job, error);
|
|
154
|
+
return failure(job.t("error.cleanup", { detail: "" }).trim());
|
|
153
155
|
}
|
|
154
156
|
}
|
|
155
157
|
async dispose() {
|
|
156
158
|
this.disposed = true;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
159
|
+
await Promise.all([...this.jobs.values()].map(async (job) => {
|
|
160
|
+
job.controller.abort(new Error(job.t("error.cancelled")));
|
|
161
|
+
try {
|
|
162
|
+
await this.cleanup(job);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
this.report(job, error);
|
|
165
|
+
}
|
|
160
166
|
}));
|
|
161
|
-
|
|
162
|
-
if (errors.length) throw new Error(errors.map((error) => error.text).join("\n"));
|
|
167
|
+
this.notifyIdle();
|
|
163
168
|
}
|
|
164
169
|
async execute(job, request) {
|
|
165
170
|
let result;
|
|
166
|
-
let off = () => {
|
|
167
|
-
};
|
|
168
171
|
try {
|
|
169
172
|
request.signal.throwIfAborted();
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
const starting = this.start(request).then((run2) => {
|
|
174
|
+
job.run = run2;
|
|
175
|
+
void run2.result.catch(() => {
|
|
176
|
+
});
|
|
177
|
+
return run2;
|
|
178
|
+
});
|
|
179
|
+
job.starting = starting;
|
|
180
|
+
void starting.then(() => {
|
|
181
|
+
job.starting = void 0;
|
|
182
|
+
if (request.signal.aborted) this.cleanupLater(job);
|
|
183
|
+
}, (error) => {
|
|
184
|
+
job.starting = void 0;
|
|
185
|
+
if (request.signal.aborted) this.report(job, error);
|
|
186
|
+
this.release(job);
|
|
176
187
|
});
|
|
177
|
-
const
|
|
188
|
+
const run = await this.interruptible(starting, request.signal);
|
|
189
|
+
const response = await this.interruptible(run.result, request.signal);
|
|
178
190
|
request.signal.throwIfAborted();
|
|
179
191
|
const text = response.output.filter((block) => block.type === "text").map((block) => block.text ?? "").join("").trim();
|
|
180
192
|
if (response.stopReason !== "completed") throw new Error(job.t("error.incomplete", { reason: response.stopReason, detail: text ? `
|
|
@@ -184,27 +196,90 @@ ${text}` : "" }));
|
|
|
184
196
|
result = { kind: "success", text };
|
|
185
197
|
} catch (error) {
|
|
186
198
|
result = failure(error);
|
|
187
|
-
}
|
|
188
|
-
|
|
199
|
+
}
|
|
200
|
+
if (request.signal.aborted) {
|
|
201
|
+
this.cleanupLater(job);
|
|
202
|
+
return result;
|
|
189
203
|
}
|
|
190
204
|
try {
|
|
191
205
|
await this.cleanup(job);
|
|
192
206
|
} catch (error) {
|
|
193
|
-
|
|
207
|
+
this.report(job, error);
|
|
194
208
|
}
|
|
195
209
|
return result;
|
|
196
210
|
}
|
|
197
211
|
async cleanup(job) {
|
|
198
|
-
if (
|
|
212
|
+
if (job.starting) {
|
|
213
|
+
await this.bounded(job.starting.then(() => {
|
|
214
|
+
}, () => {
|
|
215
|
+
}), job);
|
|
216
|
+
}
|
|
217
|
+
if (!job.run) {
|
|
218
|
+
this.release(job);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
199
221
|
if (!job.cleanup) {
|
|
200
|
-
|
|
222
|
+
const run = job.run;
|
|
223
|
+
job.cleanup = Promise.resolve().then(() => run.dispose()).then(() => {
|
|
201
224
|
job.run = void 0;
|
|
225
|
+
this.release(job);
|
|
202
226
|
}).catch((error) => {
|
|
203
227
|
job.cleanup = void 0;
|
|
204
228
|
throw error;
|
|
205
229
|
});
|
|
206
230
|
}
|
|
207
|
-
await job.cleanup;
|
|
231
|
+
await this.bounded(job.cleanup, job);
|
|
232
|
+
}
|
|
233
|
+
cleanupLater(job) {
|
|
234
|
+
void this.cleanup(job).catch((error) => this.report(job, error));
|
|
235
|
+
}
|
|
236
|
+
async interruptible(promise, signal) {
|
|
237
|
+
let abort = () => {
|
|
238
|
+
};
|
|
239
|
+
const interrupted = new Promise((_, reject) => {
|
|
240
|
+
abort = () => reject(signal.reason);
|
|
241
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
242
|
+
if (signal.aborted) abort();
|
|
243
|
+
});
|
|
244
|
+
try {
|
|
245
|
+
return await Promise.race([promise, interrupted]);
|
|
246
|
+
} finally {
|
|
247
|
+
signal.removeEventListener("abort", abort);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async bounded(promise, job) {
|
|
251
|
+
let timer;
|
|
252
|
+
const timeout = new Promise((_, reject) => {
|
|
253
|
+
timer = setTimeout(() => reject(new Error(job.t("error.cleanupTimeout"))), this.options.cleanupTimeoutMs ?? 5e3);
|
|
254
|
+
timer.unref?.();
|
|
255
|
+
});
|
|
256
|
+
try {
|
|
257
|
+
return await Promise.race([promise, timeout]);
|
|
258
|
+
} finally {
|
|
259
|
+
clearTimeout(timer);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
release(job) {
|
|
263
|
+
if (job.starting || job.run || this.jobs.get(job.key) !== job) return;
|
|
264
|
+
this.jobs.delete(job.key);
|
|
265
|
+
this.retire(job.key);
|
|
266
|
+
this.notifyIdle();
|
|
267
|
+
}
|
|
268
|
+
report(job, error) {
|
|
269
|
+
const issue = new Error(job.t("error.cleanup", { detail: error instanceof Error ? error.message : String(error) }));
|
|
270
|
+
try {
|
|
271
|
+
if (this.options.onError) this.options.onError(issue);
|
|
272
|
+
else console.error(issue);
|
|
273
|
+
} catch (error2) {
|
|
274
|
+
console.error(error2);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
notifyIdle() {
|
|
278
|
+
if (!this.disposed || this.jobs.size || this.idleNotified) return;
|
|
279
|
+
this.idleNotified = true;
|
|
280
|
+
void Promise.resolve().then(() => this.options.onIdle?.()).catch((error) => {
|
|
281
|
+
console.error(error);
|
|
282
|
+
});
|
|
208
283
|
}
|
|
209
284
|
retire(key) {
|
|
210
285
|
this.retired.set(key, Date.now());
|
|
@@ -257,6 +332,7 @@ var PERSONA = `\u4F60\u662F\u5F53\u524D\u4E3B\u4EFB\u52A1\u4E4B\u5916\u7684\u4E0
|
|
|
257
332
|
function apply(ctx) {
|
|
258
333
|
ctx.effect(() => hideInternalCommands(ctx.commands));
|
|
259
334
|
const labels = /* @__PURE__ */ new Set();
|
|
335
|
+
const releaseGuard = ctx.extend({ fiber: ctx.root.fiber }).tools.guard(createAnswerOnlyGuard(labels));
|
|
260
336
|
const jobs = new SideJobs(async (request) => {
|
|
261
337
|
const provider = ctx.subagents.getProvider("fork");
|
|
262
338
|
if (!provider?.inheritsParentContext || !provider.capabilities.toolFilter || !provider.capabilities.persona) {
|
|
@@ -286,7 +362,13 @@ ${request.question}` }]
|
|
|
286
362
|
labels.delete(label);
|
|
287
363
|
throw error;
|
|
288
364
|
}
|
|
365
|
+
}, 9e4, {
|
|
366
|
+
onError: (error) => ctx.logger.warn(error),
|
|
367
|
+
onIdle: async () => {
|
|
368
|
+
await releaseGuard();
|
|
369
|
+
}
|
|
289
370
|
});
|
|
371
|
+
ctx.effect(() => () => jobs.dispose());
|
|
290
372
|
ctx.effect(() => ctx.commands.register({
|
|
291
373
|
name: RUN_COMMAND,
|
|
292
374
|
description: "BTW \u6C14\u6CE1\u5185\u90E8\u8BF7\u6C42",
|
|
@@ -310,10 +392,6 @@ ${request.question}` }]
|
|
|
310
392
|
return jobs.close(invocation.agent.session.header.id, id);
|
|
311
393
|
}
|
|
312
394
|
}));
|
|
313
|
-
ctx.effect(function* () {
|
|
314
|
-
yield ctx.tools.guard(createAnswerOnlyGuard(labels));
|
|
315
|
-
yield () => jobs.dispose();
|
|
316
|
-
});
|
|
317
395
|
}
|
|
318
396
|
export {
|
|
319
397
|
apply,
|
|
@@ -21,10 +21,13 @@ export declare class BubbleStore {
|
|
|
21
21
|
private readonly listeners;
|
|
22
22
|
private readonly active;
|
|
23
23
|
private readonly closing;
|
|
24
|
+
private readonly admitting;
|
|
25
|
+
private disposed;
|
|
24
26
|
constructor(transport: SideTransport, t?: BtwTranslate);
|
|
25
27
|
getSnapshot: () => readonly Bubble[];
|
|
26
28
|
subscribe: (listener: () => void) => (() => void);
|
|
27
|
-
|
|
29
|
+
/** 返回已接收请求的标识;满额先等待旧气泡关闭,失败时拒绝且不发送新问题。 */
|
|
30
|
+
ask(sessionId: string, question: string): Promise<string>;
|
|
28
31
|
close(id: string): Promise<void>;
|
|
29
32
|
dispose(): Promise<void>;
|
|
30
33
|
private update;
|
package/lib/types/locales.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export declare const zh: {
|
|
|
22
22
|
'error.empty': string;
|
|
23
23
|
'error.length': string;
|
|
24
24
|
'error.capacity': string;
|
|
25
|
+
'error.bubbleCapacity': string;
|
|
25
26
|
'error.close': string;
|
|
26
27
|
'error.requestLength': string;
|
|
27
28
|
'error.request': string;
|
|
@@ -34,6 +35,7 @@ export declare const zh: {
|
|
|
34
35
|
'error.incomplete': string;
|
|
35
36
|
'error.noText': string;
|
|
36
37
|
'error.cleanup': string;
|
|
38
|
+
'error.cleanupTimeout': string;
|
|
37
39
|
};
|
|
38
40
|
export type MessageKey = keyof typeof zh;
|
|
39
41
|
export type BtwTranslate = Translate<MessageKey>;
|
|
@@ -23,14 +23,22 @@ export interface ChildRequest {
|
|
|
23
23
|
locale: BtwLocale;
|
|
24
24
|
}
|
|
25
25
|
export type StartChild = (request: ChildRequest) => Promise<ChildRun>;
|
|
26
|
+
interface JobOptions {
|
|
27
|
+
cleanupTimeoutMs?: number;
|
|
28
|
+
onError?: (error: Error) => void;
|
|
29
|
+
/** 卸载后所有实际资源均释放时调用;用于释放执行保护。 */
|
|
30
|
+
onIdle?: () => void | Promise<void>;
|
|
31
|
+
}
|
|
26
32
|
/** 管理一次性任务;父会话和请求标识共同构成所有权边界。 */
|
|
27
33
|
export declare class SideJobs {
|
|
28
34
|
private readonly start;
|
|
29
35
|
private readonly timeoutMs;
|
|
36
|
+
private readonly options;
|
|
30
37
|
private readonly jobs;
|
|
31
38
|
private readonly retired;
|
|
32
39
|
private disposed;
|
|
33
|
-
|
|
40
|
+
private idleNotified;
|
|
41
|
+
constructor(start: StartChild, timeoutMs?: number, options?: JobOptions);
|
|
34
42
|
get size(): number;
|
|
35
43
|
ask(scope: string, id: string, question: string, signal?: AbortSignal, context?: unknown, locale?: BtwLocale): Promise<SideResult>;
|
|
36
44
|
/** 关闭可先于启动到达;短期墓碑阻止网络乱序重新启动请求。 */
|
|
@@ -38,6 +46,13 @@ export declare class SideJobs {
|
|
|
38
46
|
dispose(): Promise<void>;
|
|
39
47
|
private execute;
|
|
40
48
|
private cleanup;
|
|
49
|
+
private cleanupLater;
|
|
50
|
+
private interruptible;
|
|
51
|
+
private bounded;
|
|
52
|
+
private release;
|
|
53
|
+
private report;
|
|
54
|
+
private notifyIdle;
|
|
41
55
|
private retire;
|
|
42
56
|
private prune;
|
|
43
57
|
}
|
|
58
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@michengai/dsh-btw",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "DSH 一次性只读旁问气泡",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
"lib",
|
|
30
30
|
"assets",
|
|
31
31
|
"cordis.patch.yml",
|
|
32
|
-
"docs",
|
|
33
32
|
"README.md",
|
|
34
33
|
"README.zh-CN.md",
|
|
35
34
|
"CHANGELOG.md",
|
|
@@ -55,7 +54,7 @@
|
|
|
55
54
|
"build": "node scripts/build.mjs",
|
|
56
55
|
"typecheck": "tsc --noEmit",
|
|
57
56
|
"test": "vitest run",
|
|
58
|
-
"test:release": "node --test tests/release.test.mjs",
|
|
57
|
+
"test:release": "node --test tests/release.test.mjs tests/npm-oidc.test.mjs",
|
|
59
58
|
"check": "npm run typecheck && npm test && npm run test:release && npm run build",
|
|
60
59
|
"preview": "vite preview --host 127.0.0.1",
|
|
61
60
|
"dev": "vite --host 127.0.0.1",
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# 阅读导航
|
|
2
|
-
|
|
3
|
-
更新时间:2026-09-06 14:55(Asia/Shanghai)
|
|
4
|
-
|
|
5
|
-
- [当前状态](02-当前状态.md)
|
|
6
|
-
- [待办与阻塞](03-待办与阻塞.md)
|
|
7
|
-
- [自动测试与发布](../05-工程交付/01-自动发布.md)
|
|
8
|
-
- [I005 首次发布与可信发布配置](../01-当前工作/I005-首次发布与可信发布配置/00-迭代总览.md)
|
|
9
|
-
- [I004 仓库与自动发布](../07-迭代归档/2026/I004-仓库与自动发布/00-迭代总览.md)
|
|
10
|
-
- [I003 宿主外观与国际化](../07-迭代归档/2026/I003-宿主外观与国际化/00-迭代总览.md)
|
|
11
|
-
- [I002 实际 DSH 插件接入](../07-迭代归档/2026/I002-实际DSH插件接入/00-迭代总览.md)
|
|
12
|
-
- [I001 一次性旁问气泡与输入历史](../07-迭代归档/2026/I001-一次性旁问气泡与输入历史/00-迭代总览.md)
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# 当前状态
|
|
2
|
-
|
|
3
|
-
更新时间:2026-09-06 14:55(Asia/Shanghai)
|
|
4
|
-
|
|
5
|
-
- 阶段:用户已授权首次发布 `0.1.0`,活动迭代为 [I005](../01-当前工作/I005-首次发布与可信发布配置/00-迭代总览.md)。公开仓库和流程验收见 [I004](../07-迭代归档/2026/I004-仓库与自动发布/00-迭代总览.md)。
|
|
6
|
-
- 当前验收依据:[I003 宿主外观与国际化](../07-迭代归档/2026/I003-宿主外观与国际化/00-迭代总览.md);基础能力见 [I001](../07-迭代归档/2026/I001-一次性旁问气泡与输入历史/00-迭代总览.md)。
|
|
7
|
-
- 目标宿主:DSH 0.1.2-rc.1,Web 客户端。
|
|
8
|
-
- 边界:只提供一次性 fork 旁问、运行时禁用全部工具及独立气泡,不占侧边栏,不创建持续侧聊。上下键输入历史已迁至 Codex UI,BTW 不再包含采集、持久化或键盘绑定代码。
|
|
9
|
-
- 迁出验证:21 项单元/运行时测试、构建通过;桌面/移动浏览器 17 项通过、1 项不适用跳过,包含 BTW 不拦截上下键、不写历史存储的回归。历史导航及旧数据迁移由 Codex UI 独立验收。
|
|
10
|
-
- 待验证:真实模型旁问及 Codex UI 样式共存。详见[待办](03-待办与阻塞.md)。
|
|
11
|
-
- `web` profile 保留 BTW 本地开发链接;按用户最新要求移除 Codex UI 加载和依赖配置,以测试原生界面。其安装目录链接仍保留,但不再配置加载。
|
|
12
|
-
- 仓库为 [MichengAI/dsh-btw](https://github.com/MichengAI/dsh-btw),默认分支 `main`;CI 与发布 dry run 均通过,源码和工作流已推送。README 包含三张实际界面截图。
|
|
13
|
-
- 按用户确认统一为 Apache-2.0,LICENSE、包元数据和徽章保持一致;默认 README 为英文,完整中文说明位于 `README.zh-CN.md`,两份文档和三张截图均纳入安装包。
|
|
14
|
-
- 版本仍为 `0.1.0`,尚未创建版本标签、GitHub Release 或发布 npm。标签发布会自动测试并生成双语 Release 和安装包;npm 发布开关关闭,待完成 Trusted Publisher 配置。详见[自动发布](../05-工程交付/01-自动发布.md)。
|
|
15
|
-
- 桌面端完整退出后再启动会补装内置插件;当前热重载不会补装。3 个未启用的目录链接因工具策略未删除。
|
|
16
|
-
- 已接入宿主浅深色令牌、locale 服务及中英文文案,目录过滤代码和本地链接产物已更新;两套宿主真实 RPC 回归通过。此前运行后端未加载隐藏修复,用户随后提供的菜单截图显示“旁问”分类,但不足以验证整个命令目录,完整隐藏验收仍见[待办](03-待办与阻塞.md)。后端 HMR 的 `root: []` 不监听插件 JS,须重新加载实际后端,不能仅刷新浏览器。
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# 待办与阻塞
|
|
2
|
-
|
|
3
|
-
更新时间:2026-09-06 14:34(Asia/Shanghai)
|
|
4
|
-
|
|
5
|
-
| 事项 | 负责人 | 优先级 | 下一步与解除条件 |
|
|
6
|
-
| --- | --- | --- | --- |
|
|
7
|
-
| 启用 npm 自动发布 | 用户 | P2 | 在 npm 包设置中为 `MichengAI/dsh-btw` 的 `publish.yml` 配置 Trusted Publisher,再将仓库变量 `NPM_PUBLISH_ENABLED` 改为 `true`。当前关闭,不阻塞 GitHub Release 和安装包。步骤见[自动发布](../05-工程交付/01-自动发布.md)。 |
|
|
8
|
-
| 内部命令隐藏生效 | 用户、Codex | P1 | 文件已安装,两套真实 RPC 回归通过;目前运行中的后端仍未加载修复。等允许中断任务时重新加载实际后端,再确认菜单不含 `btw-run`、`btw-close`。仅浏览器刷新不能更新后端。 |
|
|
9
|
-
| 实际 DSH 使用验收 | 用户 | P1 | 等主任务结束后重新加载,在原输入框复测 `/btw`、关闭,以及浅深色和中英文切换;BTW 只保留旁问,上下键输入历史已移至 Codex UI,由该项目验收。真实模型行为及样式共存仍由用户反馈。 |
|
|
10
|
-
|
|
11
|
-
没有阻塞本地代码交付的外部依赖。
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# I005 首次发布与可信发布配置
|
|
2
|
-
|
|
3
|
-
状态:进行中
|
|
4
|
-
负责人:Codex
|
|
5
|
-
优先级:P1
|
|
6
|
-
更新时间:2026-09-06 14:55(Asia/Shanghai)
|
|
7
|
-
|
|
8
|
-
## 目标与边界
|
|
9
|
-
|
|
10
|
-
按用户“你来发布”的授权,首次公开发布 `@michengai/dsh-btw@0.1.0`,生成对应 GitHub Release,并配置 npm Trusted Publisher。不更改插件行为、不发布额外测试版本、不重启 DSH。
|
|
11
|
-
|
|
12
|
-
## 任务与验收
|
|
13
|
-
|
|
14
|
-
- [x] 确认 npm 目标版本尚不存在、账号已登录、Apache-2.0 与中英文 README 一致。
|
|
15
|
-
- [x] 两份 README 增加 npm 安装方式,首发版本号保持 `0.1.0`。
|
|
16
|
-
- [ ] 重新构建并校验安装包,首次发布到 npm,核对线上版本与许可证。
|
|
17
|
-
- [ ] 推送 `v0.1.0` 标签,验证 GitHub Release 标题、双语正文和安装包附件。
|
|
18
|
-
- [ ] 在 npm 中配置 `MichengAI/dsh-btw`、`publish.yml`、允许 `npm publish` 的 Trusted Publisher,随后启用仓库发布变量。
|
|
19
|
-
- [ ] 刷新发布文档、状态及待办,归档本迭代。
|
|
20
|
-
|
|
21
|
-
## 已完成与待处理
|
|
22
|
-
|
|
23
|
-
准备阶段源码提交 `5b9782b` 的 CI 已通过;本次只更新发布文档。npm 首个包必须先发布才能配置 Trusted Publisher,期间保持 `NPM_PUBLISH_ENABLED=false`,避免标签工作流重复发布已存在版本。
|
|
24
|
-
|
|
25
|
-
阻塞:无。关联提交:未提交。
|
|
26
|
-
|
|
27
|
-
阅读顺序:本总览 → [自动发布](../../05-工程交付/01-自动发布.md) → [中文 README](../../../README.zh-CN.md)。
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# 自动测试与发布
|
|
2
|
-
|
|
3
|
-
更新时间:2026-09-06 14:49(Asia/Shanghai)
|
|
4
|
-
|
|
5
|
-
## 仓库与触发条件
|
|
6
|
-
|
|
7
|
-
仓库:<https://github.com/MichengAI/dsh-btw>,默认分支 `main`。
|
|
8
|
-
|
|
9
|
-
| 入口 | 行为 |
|
|
10
|
-
| --- | --- |
|
|
11
|
-
| 推送 `main` 或提交 Pull Request | Node.js 22、24 类型检查、单元测试、真实宿主运行时测试和构建;Node.js 24 额外执行 Chromium 桌面及移动端回归。 |
|
|
12
|
-
| Actions → 发布 → Run workflow | 默认 `dry_run=true`,执行相同测试、版本说明校验和打包,上传工作流产物,不创建 Release、不发布 npm。 |
|
|
13
|
-
| 推送 `v*` 标签 | 测试通过后校验版本、生成双语说明、打包并创建 GitHub Release;npm 发布受仓库变量控制。 |
|
|
14
|
-
|
|
15
|
-
正式发布要求标签严格等于 `v` 加 `package.json` 版本。`CHANGELOG.zh-CN.md` 和 `CHANGELOG.md` 必须各自包含唯一且非空的同版本章节。Release 标题为版本标签,正文为中文在前、英文在后,与两份 CHANGELOG 对应章节一致。
|
|
16
|
-
|
|
17
|
-
## npm Trusted Publishing
|
|
18
|
-
|
|
19
|
-
发布工作流为 `.github/workflows/publish.yml`,使用 GitHub OIDC,不在仓库保存 npm 令牌。
|
|
20
|
-
|
|
21
|
-
新仓库默认设置 `NPM_PUBLISH_ENABLED=false`:GitHub Release 和安装包可独立发布。完成 npm 端设置后再启用:
|
|
22
|
-
|
|
23
|
-
1. 确认 npm 包 `@michengai/dsh-btw` 已创建并归当前发布者管理;尚未创建时,按 npm 当前要求先完成首次发布。
|
|
24
|
-
2. 在 npm 包设置的 Trusted Publisher 中选择 GitHub Actions,填写组织或用户名 `MichengAI`、仓库 `dsh-btw`、工作流文件 `publish.yml`,不指定 Environment。允许的操作需要启用直接 `npm publish`;2026-09-03 后新建连接默认只允许 `npm stage publish`,与本工作流不匹配。
|
|
25
|
-
3. 在 GitHub 仓库 Actions Variables 中将 `NPM_PUBLISH_ENABLED` 改为字符串 `true`。
|
|
26
|
-
|
|
27
|
-
启用后通过 Node.js 24 自带的新版 npm 执行 `npm publish <安装包> --access public --provenance`。首次启用前确认 npm 支持当前 CLI 的 OIDC。不要以任意新增版本号作为测试手段。
|
|
28
|
-
|
|
29
|
-
官方 [Trusted Publishing](https://docs.npmjs.com/trusted-publishers) 要求在已存在包的设置中配置;[Staged Publishing](https://docs.npmjs.com/staged-publishing/) 也明确不支持全新包。首次公开发布会占用版本号,需先确认发布范围;不能用暂存发布替代新包初始化。
|
|
30
|
-
|
|
31
|
-
同一 npm 版本不能覆盖。若 npm 已成功发布而后续 Release 步骤失败,可暂时关闭 npm 发布开关,再重跑标签工作流,恢复 GitHub Release 和附件;不要改写已经共享的标签。
|
|
32
|
-
|
|
33
|
-
## 发布步骤
|
|
34
|
-
|
|
35
|
-
先更新 `package.json`、锁文件及两份 CHANGELOG,完成审核和提交推送。推荐先在 Actions 手动运行默认的 dry run,确认产物内容,再创建标签。
|
|
36
|
-
|
|
37
|
-
```powershell
|
|
38
|
-
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
39
|
-
$OutputEncoding = [System.Text.Encoding]::UTF8
|
|
40
|
-
|
|
41
|
-
$releaseVersion = (Get-Content package.json -Raw -Encoding UTF8 | ConvertFrom-Json).version
|
|
42
|
-
git tag "v$releaseVersion"
|
|
43
|
-
git push origin "v$releaseVersion"
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
工作流测试失败时不会进入发布阶段。`scripts/check-package.mjs` 校验实际 tar 包中的入口、类型、截图、更新日志以及文件范围。
|
|
47
|
-
|
|
48
|
-
## 提交与产物范围
|
|
49
|
-
|
|
50
|
-
提交源码、测试、浏览器模拟宿主、构建配置、锁文件、Actions、项目文档和 README 截图。模拟宿主 `preview` 是浏览器测试的依赖,并非用户独立使用页面。
|
|
51
|
-
|
|
52
|
-
`node_modules`、`lib`、`dist`、`artifacts`、测试报告、缓存、日志、环境变量文件、备份和 `.tgz` 不进入 Git。截图交付物位于 `assets/screenshots`;自动截图保存在被忽略的 `artifacts`,CI 失败时作为工作流证据保留 7 天。
|
|
53
|
-
|
|
54
|
-
本地与 CI 均使用锁文件中的官方 npm 下载地址,沿用项目的 `legacy-peer-deps=true`。CI 通过 `DSH_RUNTIME_ROOT` 指向开发依赖中的真实运行时,不依赖机器预装的 DSH,也不读取模型凭据。
|
|
55
|
-
|
|
56
|
-
## 恢复
|
|
57
|
-
|
|
58
|
-
仓库代码通过正常修复提交恢复,不强制推送。停止 npm 发布时将 `NPM_PUBLISH_ENABLED` 设为 `false`。已发布 npm 版本不能通过代码回退覆盖,修复后发布新版本;已安装客户端可按需要安装此前可用版本。
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# I001 一次性旁问气泡与输入历史
|
|
2
|
-
|
|
3
|
-
状态:已完成
|
|
4
|
-
负责人:Codex
|
|
5
|
-
优先级:P1
|
|
6
|
-
更新时间:2026-09-06 11:33(Asia/Shanghai)
|
|
7
|
-
|
|
8
|
-
## 目标与边界
|
|
9
|
-
|
|
10
|
-
独立实现 DSH BTW 插件并交付本地安装包。每次提问使用 fork 子代理,继承已完成回合;空白名单与执行层 guard 共同限制工具。气泡使用输入框上方的 list slot,不接管侧边栏。上下键召回本地输入历史。
|
|
11
|
-
|
|
12
|
-
不包含多轮侧聊、侧聊恢复、自动回填主会话、持久子会话归档、真实凭据配置修改或发布。
|
|
13
|
-
|
|
14
|
-
## 任务
|
|
15
|
-
|
|
16
|
-
- [x] 后端请求、权限与清理。
|
|
17
|
-
- [x] 独立气泡与宿主 RPC 接入。
|
|
18
|
-
- [x] 输入历史、草稿和 IME 边界。
|
|
19
|
-
- [x] 自动测试、浏览器验证、打包。
|
|
20
|
-
|
|
21
|
-
## 设计依据
|
|
22
|
-
|
|
23
|
-
- JasonQQ/dsh-btw-plugin:借鉴 commands + 一次性 fork 的最小职责划分。
|
|
24
|
-
- kaieye/dsh-AIR:借鉴取消、迟到结果与关闭失败保留状态;不复用其持久侧会话和侧栏。
|
|
25
|
-
- narumiruna/pi-extensions/packages/pi-btw:借鉴固定上下文、取消后的结果检查、默认不回写主上下文。其自身为直接模型调用,与本项目 fork 路线不同。
|
|
26
|
-
- 以实际安装的 DSH 0.1.2-rc.1 类型及实现为准;不复制第三方实现。
|
|
27
|
-
|
|
28
|
-
## 验收
|
|
29
|
-
|
|
30
|
-
20 项单元及真实宿主运行时测试通过。9 项浏览器测试通过,1 项窄屏工作区切换测试因预览不展示该入口而跳过。类型检查、构建、服务端产物导入及客户端产物加载通过。详细范围见[验收记录](01-验收记录.md)。
|
|
31
|
-
|
|
32
|
-
## 阻塞与待处理
|
|
33
|
-
|
|
34
|
-
没有本地交付阻塞。真实模型与用户 DSH 界面的联调转入[项目待办](../../../00-交接入口/03-待办与阻塞.md),不将隔离测试描述为实际安装验证。
|
|
35
|
-
|
|
36
|
-
## 已完成
|
|
37
|
-
|
|
38
|
-
完成请求身份隔离、90 秒超时、启动与取消竞态、关闭失败重试、迟到答案抑制、草稿与 IME 保护、工作区历史隔离和容量裁剪。
|
|
39
|
-
|
|
40
|
-
真实 ToolRuntime 验证空白名单仍保留子作用域工具和 `run_code`,新增 guard 拒绝这些执行且不影响主任务。真实 Cordis 验证子代理清理完成后才卸载保护。
|
|
41
|
-
|
|
42
|
-
关联提交:未提交。
|
|
43
|
-
|
|
44
|
-
阅读顺序:本总览 → [验收记录](01-验收记录.md) → [README](../../../../README.md)。
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# I001 验收记录
|
|
2
|
-
|
|
3
|
-
更新时间:2026-09-06 11:33(Asia/Shanghai)
|
|
4
|
-
|
|
5
|
-
## 环境与结果
|
|
6
|
-
|
|
7
|
-
- Windows PowerShell、Node.js 24.20.0,目标宿主 DSH 0.1.2-rc.1。
|
|
8
|
-
- `npm run check`:类型检查、20 项测试及构建通过。
|
|
9
|
-
- 浏览器测试:Microsoft Edge,1280×900 与 390×844;累计 9 项通过,1 项跳过。最终补充的 2 项构建包装配测试单独复测通过;其他 7 项在前次完整运行通过。
|
|
10
|
-
- `npm audit --registry=https://registry.npmjs.org --json`:报告已知漏洞 0。镜像不支持审计端点,已改用官方源完成查询。
|
|
11
|
-
- 服务端构建产物可直接导入,客户端构建产物可按宿主 `window.__ModuleLoader__.load` 协议加载;外部 React 与 JSX runtime 使用真实 React 18 运行时。
|
|
12
|
-
- 本地交付包:项目 `artifacts/michengai-dsh-btw-0.1.0.tgz`。未发布或安装到用户 profile。
|
|
13
|
-
|
|
14
|
-
## 验证范围
|
|
15
|
-
|
|
16
|
-
| 范围 | 证据 |
|
|
17
|
-
| --- | --- |
|
|
18
|
-
| 请求与清理 | 7 项 jobs 测试:正常释放、关闭、取消竞态、失败重试、身份隔离等 |
|
|
19
|
-
| 气泡状态 | 4 项 bubbles 测试:独立更新、迟到结果丢弃、关闭重试、同步异常恢复 |
|
|
20
|
-
| 历史 | 5 项 history 测试:工作区、上下边界、草稿、损坏存储、容量裁剪与重读 |
|
|
21
|
-
| 工具限制 | 2 项 guard 单测;真实 ToolRuntime 验证自有工具及 `run_code` 被拒绝、主任务工具可执行 |
|
|
22
|
-
| 热卸载 | 真实 Cordis 验证清理期间 guard 不被提前卸载 |
|
|
23
|
-
| 浏览器 | 气泡位置、Markdown 代码块、复制、折叠、关闭、IME、历史持久化、工作区切换与构建包加载 |
|
|
24
|
-
| 视觉 | 桌面及手机截图已人工检查;无横向溢出,气泡不覆盖输入框;截图位于项目 artifacts 目录 |
|
|
25
|
-
|
|
26
|
-
同步请求异常与历史超量先由新增测试复现失败,再修复为通过。预览因 Node 专属 crypto 导入空白的问题已改为 Node 22+ 和浏览器共同支持的 Web Crypto。构建包装配测试曾遗漏宿主支持的 `react/jsx-runtime`,补齐真实运行时后通过。
|
|
27
|
-
|
|
28
|
-
## 限制与恢复
|
|
29
|
-
|
|
30
|
-
真实宿主只在内存中装配工具运行时和 Cordis,未调用真实模型。浏览器使用模拟宿主与 textarea;真实 Lexical 输入框、模型上下文及已安装侧栏共存待联调。关闭释放运行资源,不承诺删除宿主审计日志。
|
|
31
|
-
|
|
32
|
-
停用方式见 README:移除 Cordis patch 并重启宿主;历史仅存在浏览器 `michengai.btw.history.v1`,停用不删除主会话记录。此交付尚未修改宿主配置,无配置回滚需求。
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# I002 实际 DSH 插件接入
|
|
2
|
-
|
|
3
|
-
状态:已完成
|
|
4
|
-
负责人:Codex
|
|
5
|
-
优先级:P1
|
|
6
|
-
更新时间:2026-09-06 12:26(Asia/Shanghai)
|
|
7
|
-
|
|
8
|
-
## 目标与边界
|
|
9
|
-
|
|
10
|
-
用户明确要求在实际 DSH 内使用,不接受模拟独立页面作为功能交付。把已有插件装入实际 Web profile,使用原聊天输入框及现有侧栏。无需将 preview 代码部署到 DSH。
|
|
11
|
-
|
|
12
|
-
## 任务与已完成
|
|
13
|
-
|
|
14
|
-
- [x] 确认正在运行的 DSH Codex Desktop 使用 `.dsh/profiles/web`,已装 `dsh-better-sidebar`。
|
|
15
|
-
- [x] 核对官方 `dsh plugin --profile web add` 自动安装及 bundle 装配机制,版本 0.1.2-rc.1。
|
|
16
|
-
- [x] README 优先说明真实 DSH 安装入口,明确模拟页面仅供开发测试。
|
|
17
|
-
- [x] 安装插件并核对宿主重载、装配健康状态。
|
|
18
|
-
- [x] 按用户“安装到本地,我自己测试”的要求交接实际使用验收。
|
|
19
|
-
- [x] 修复用户截图中的客户端命名空间注入报错与气泡宽度,重新构建并验证本地安装产物,见[修复记录](02-客户端注入与气泡宽度修复.md)。
|
|
20
|
-
|
|
21
|
-
## 阻塞与待处理
|
|
22
|
-
|
|
23
|
-
用户已明确要求安装到本地并自行测试。实际交互验收转入[项目待办](../../../00-交接入口/03-待办与阻塞.md),不阻塞本次安装交付。
|
|
24
|
-
|
|
25
|
-
随后用户要求先卸载其他插件避免干扰。已完成启用项及依赖移除,见[测试环境隔离](01-测试环境隔离.md);该记录描述当前测试配置,上述安装验收保留为执行证据。
|
|
26
|
-
|
|
27
|
-
实际 DSH 地址有认证;独立浏览器未继承桌面端认证,无法直接打开。未读取凭据或绕过认证。
|
|
28
|
-
|
|
29
|
-
## 验收
|
|
30
|
-
|
|
31
|
-
当前 `web` profile 新增 `@michengai/dsh-btw` 依赖及 bundle,`node_modules/@michengai/dsh-btw` 通过 Windows Junction 指向本仓库。沿用现有本地插件的 `link:` 约定;`pnpm-lock.yaml` 同步了对应 importer。
|
|
32
|
-
|
|
33
|
-
常规 tarball 安装在复核既有锁文件时,被 pnpm 11.25 的最小发布时间策略拦截,涉及 3 个既有插件。本次未调整策略、未重装被拦截的包,改为链接本次自主开发的代码;原有 10 个依赖版本均保持一致。
|
|
34
|
-
|
|
35
|
-
DSH 在 2026-09-06 12:07:14(Asia/Shanghai)自动重载,12:07:19 报告 `healthy`。从真实 profile 解析并导入插件成功,`apply` 为函数,依赖服务为 commands、subagents、tools。
|
|
36
|
-
|
|
37
|
-
实际 UI 与模型由用户测试,未宣称已经完成。功能只使用聊天输入框上方的气泡,不新增或改动侧栏。
|
|
38
|
-
|
|
39
|
-
## 恢复方式
|
|
40
|
-
|
|
41
|
-
安装前 package.json、pnpm-lock.yaml、pnpm-workspace.yaml 备份保存在项目 `artifacts/profile-backup-20260906-1205`,该目录已忽略版本控制。停用时从 profile bundle 移除本插件即可触发重载;完整卸载使用 README 的官方 remove 命令。若 pnpm 仍因既有包策略阻塞,应仅撤回本插件依赖、锁文件 importer 与 Junction,保留之后的其他配置改动,禁止递归删除 Junction 指向的源仓库。
|
|
42
|
-
|
|
43
|
-
关联提交:未提交。
|
|
44
|
-
|
|
45
|
-
阅读顺序:本总览 → [README](../../../../README.md) → [当前状态](../../../00-交接入口/02-当前状态.md)。
|