dsh-task-ledger 0.0.130 → 0.0.131
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/client.js +137 -43
- package/dist/client.js.map +1 -1
- package/dist/host/repositories/identity.repository.d.ts +1 -1
- package/dist/host/repositories/identity.repository.js +2 -1
- package/dist/host/routes.js +25 -2
- package/dist/host/services/identity.service.d.ts +7 -1
- package/dist/host/services/identity.service.js +2 -2
- package/package.json +1 -1
- package/skill/SKILL.md +2 -2
- package/skill/package.json +1 -1
- package/skill/references/init-identity.md +7 -26
package/dist/client.js
CHANGED
|
@@ -55790,6 +55790,30 @@ window.__ModuleLoader__.load({
|
|
|
55790
55790
|
clearTimeout(timer);
|
|
55791
55791
|
}
|
|
55792
55792
|
}
|
|
55793
|
+
/**
|
|
55794
|
+
* 逐行读 NDJSON 流:每拆出一行交给 `handle`,它返回 true = 到此为止。
|
|
55795
|
+
* 返回值 `true` 表示被 `handle` 收摊、`false` 表示流读完了还没收摊(调用方按「连接中断」处理)。
|
|
55796
|
+
*/
|
|
55797
|
+
async function readLines(res, handle) {
|
|
55798
|
+
if (res.body === null) throw new HttpError("响应没有内容(响应流不可读)", null);
|
|
55799
|
+
const reader = res.body.getReader();
|
|
55800
|
+
const decoder = new TextDecoder();
|
|
55801
|
+
let buffer = "";
|
|
55802
|
+
try {
|
|
55803
|
+
for (;;) {
|
|
55804
|
+
const chunk = await reader.read();
|
|
55805
|
+
if (chunk.done) break;
|
|
55806
|
+
buffer += decoder.decode(chunk.value, { stream: true });
|
|
55807
|
+
const lines = buffer.split("\n");
|
|
55808
|
+
buffer = lines.pop() ?? "";
|
|
55809
|
+
for (const line of lines) if (handle(line)) return true;
|
|
55810
|
+
}
|
|
55811
|
+
buffer += decoder.decode();
|
|
55812
|
+
return handle(buffer);
|
|
55813
|
+
} finally {
|
|
55814
|
+
reader.cancel().catch(() => void 0);
|
|
55815
|
+
}
|
|
55816
|
+
}
|
|
55793
55817
|
/** 本机身份:进面板的**第一个**请求(没身份时快照会 500,所以必须先问它)。 */
|
|
55794
55818
|
function fetchIdentity() {
|
|
55795
55819
|
return fetchJson("/identity");
|
|
@@ -55827,13 +55851,75 @@ window.__ModuleLoader__.load({
|
|
|
55827
55851
|
body: JSON.stringify(body)
|
|
55828
55852
|
}, body.action === "rename" ? RENAME_TIMEOUT_MS : void 0);
|
|
55829
55853
|
}
|
|
55830
|
-
/**
|
|
55831
|
-
function
|
|
55832
|
-
|
|
55833
|
-
|
|
55834
|
-
|
|
55835
|
-
|
|
55854
|
+
/** 解析一行;不是我们的形状就返回 null,交给外层继续读。 */
|
|
55855
|
+
function forgetEventOf(line) {
|
|
55856
|
+
if (line.trim() === "") return null;
|
|
55857
|
+
let raw;
|
|
55858
|
+
try {
|
|
55859
|
+
raw = JSON.parse(line);
|
|
55860
|
+
} catch {
|
|
55861
|
+
return null;
|
|
55862
|
+
}
|
|
55863
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
55864
|
+
const event = raw;
|
|
55865
|
+
if (event.type === "step" && typeof event.step === "string" && isStep(event.step)) return {
|
|
55866
|
+
type: "step",
|
|
55867
|
+
step: event.step,
|
|
55868
|
+
workspace: typeof event.workspace === "string" ? event.workspace : ""
|
|
55869
|
+
};
|
|
55870
|
+
if (event.type === "done" && event.report !== null && typeof event.report === "object") return {
|
|
55871
|
+
type: "done",
|
|
55872
|
+
report: event.report
|
|
55873
|
+
};
|
|
55874
|
+
if (event.type === "error") return {
|
|
55875
|
+
type: "error",
|
|
55876
|
+
error: typeof event.error === "string" ? event.error : "摘人失败",
|
|
55877
|
+
code: typeof event.code === "string" ? event.code : null
|
|
55878
|
+
};
|
|
55879
|
+
return null;
|
|
55880
|
+
}
|
|
55881
|
+
/**
|
|
55882
|
+
* 从名册摘掉一个人:每本登记过他的台账各删各的(还有任务的那本由 host 回报为 kept)。
|
|
55883
|
+
*
|
|
55884
|
+
* host 回的是 **NDJSON 流**:逐本台账各跑一条 skill 写命令,每进一个阶段推一行
|
|
55885
|
+
* `{type:"step",step,workspace}` 喂给 `onStep` 翻进度文案,末行 `{type:"done",report}`
|
|
55886
|
+
* 带汇总;跑挂时推 `{type:"error",error,code}` ⇒ 这里抛 HttpError(码照旧)。
|
|
55887
|
+
*/
|
|
55888
|
+
async function postForgetIdentity(uuid, onStep) {
|
|
55889
|
+
const controller = new AbortController();
|
|
55890
|
+
const timer = setTimeout(() => {
|
|
55891
|
+
controller.abort(new DOMException(`请求超时(${RENAME_TIMEOUT_MS}ms 未响应)`, "TimeoutError"));
|
|
55836
55892
|
}, RENAME_TIMEOUT_MS);
|
|
55893
|
+
try {
|
|
55894
|
+
const res = await fetch(`${API_PREFIX}/identity/forget`, {
|
|
55895
|
+
method: "POST",
|
|
55896
|
+
headers: { "content-type": "application/json" },
|
|
55897
|
+
body: JSON.stringify({ uuid }),
|
|
55898
|
+
signal: controller.signal,
|
|
55899
|
+
cache: "no-store"
|
|
55900
|
+
});
|
|
55901
|
+
if (!res.ok) throw errorOf(res.status, await res.text().catch(() => ""));
|
|
55902
|
+
const reports = [];
|
|
55903
|
+
await readLines(res, (line) => {
|
|
55904
|
+
const event = forgetEventOf(line);
|
|
55905
|
+
if (event === null) return false;
|
|
55906
|
+
if (event.type === "step") {
|
|
55907
|
+
onStep?.({
|
|
55908
|
+
step: event.step,
|
|
55909
|
+
workspace: event.workspace
|
|
55910
|
+
});
|
|
55911
|
+
return false;
|
|
55912
|
+
}
|
|
55913
|
+
if (event.type === "error") throw new HttpError(event.error, event.code);
|
|
55914
|
+
reports.push(event.report);
|
|
55915
|
+
return true;
|
|
55916
|
+
});
|
|
55917
|
+
const report = reports[0];
|
|
55918
|
+
if (report === void 0) throw new HttpError("摘人没有返回结果(连接中断)", null);
|
|
55919
|
+
return report;
|
|
55920
|
+
} finally {
|
|
55921
|
+
clearTimeout(timer);
|
|
55922
|
+
}
|
|
55837
55923
|
}
|
|
55838
55924
|
/**
|
|
55839
55925
|
* 任务写动作(认领 / 释放)的超时:host 要跑一次「取最新 → 改 → 提交 → 推送」,
|
|
@@ -55893,15 +55979,7 @@ window.__ModuleLoader__.load({
|
|
|
55893
55979
|
cache: "no-store"
|
|
55894
55980
|
});
|
|
55895
55981
|
if (!res.ok) throw errorOf(res.status, await res.text().catch(() => ""));
|
|
55896
|
-
if (res
|
|
55897
|
-
const reader = res.body.getReader();
|
|
55898
|
-
const decoder = new TextDecoder();
|
|
55899
|
-
let buffer = "";
|
|
55900
|
-
/**
|
|
55901
|
-
* 处理一行:翻一行进度文案、`done` 返回 true(该收摊了)、`error` 直接抛。
|
|
55902
|
-
* 坏行 / 空行当没看见 —— 一条读不懂的行不该让整条命令算失败。
|
|
55903
|
-
*/
|
|
55904
|
-
const handle = (line) => {
|
|
55982
|
+
if (await readLines(res, (line) => {
|
|
55905
55983
|
const event = actionEventOf(line);
|
|
55906
55984
|
if (event === null) return false;
|
|
55907
55985
|
if (event.type === "step") {
|
|
@@ -55910,21 +55988,7 @@ window.__ModuleLoader__.load({
|
|
|
55910
55988
|
}
|
|
55911
55989
|
if (event.type === "error") throw new HttpError(event.error, event.code);
|
|
55912
55990
|
return true;
|
|
55913
|
-
};
|
|
55914
|
-
try {
|
|
55915
|
-
for (;;) {
|
|
55916
|
-
const chunk = await reader.read();
|
|
55917
|
-
if (chunk.done) break;
|
|
55918
|
-
buffer += decoder.decode(chunk.value, { stream: true });
|
|
55919
|
-
const lines = buffer.split("\n");
|
|
55920
|
-
buffer = lines.pop() ?? "";
|
|
55921
|
-
for (const line of lines) if (handle(line)) return { ok: true };
|
|
55922
|
-
}
|
|
55923
|
-
buffer += decoder.decode();
|
|
55924
|
-
if (handle(buffer)) return { ok: true };
|
|
55925
|
-
} finally {
|
|
55926
|
-
reader.cancel().catch(() => void 0);
|
|
55927
|
-
}
|
|
55991
|
+
})) return { ok: true };
|
|
55928
55992
|
throw new HttpError("任务动作没有返回结果(连接中断)", null);
|
|
55929
55993
|
} finally {
|
|
55930
55994
|
clearTimeout(timer);
|
|
@@ -64860,6 +64924,7 @@ body[data-ds-dark-theme]{
|
|
|
64860
64924
|
settingsForgetDoneCount: "已移除 {count} 本台账",
|
|
64861
64925
|
settingsForgetPartialTitle: "@{name} 移除未完成",
|
|
64862
64926
|
settingsForgetFailedTitle: "@{name} 移除失败",
|
|
64927
|
+
settingsForgetStep: "{step}({workspace})",
|
|
64863
64928
|
settingsCreateDone: "已创建身份 @{name}",
|
|
64864
64929
|
settingsSwitchDone: "已切换到 @{name}",
|
|
64865
64930
|
settingsRosterEmpty: "所列台账中暂无已创建的身份。",
|
|
@@ -65139,6 +65204,7 @@ body[data-ds-dark-theme]{
|
|
|
65139
65204
|
settingsForgetDoneCount: "Removed from {count} ledger(s)",
|
|
65140
65205
|
settingsForgetPartialTitle: "Identity @{name} partially removed",
|
|
65141
65206
|
settingsForgetFailedTitle: "Failed to remove identity @{name}",
|
|
65207
|
+
settingsForgetStep: "{step} ({workspace})",
|
|
65142
65208
|
settingsCreateDone: "Created identity @{name}",
|
|
65143
65209
|
settingsSwitchDone: "Switched to @{name}",
|
|
65144
65210
|
settingsRosterEmpty: "No identity has been created in the listed ledgers.",
|
|
@@ -65598,7 +65664,8 @@ body[data-ds-dark-theme]{
|
|
|
65598
65664
|
* (2026-11-03,统一走 `/task/action`);继续走 `/task/continue` 起会话(2026-11-03 晚);
|
|
65599
65665
|
* **完成仍未接**,先只摆按钮。
|
|
65600
65666
|
*/
|
|
65601
|
-
/** 写命令的阶段 → 底栏进度文案(skill 那边新增阶段、这边没对上 ⇒ 继续显示上一句)。
|
|
65667
|
+
/** 写命令的阶段 → 底栏进度文案(skill 那边新增阶段、这边没对上 ⇒ 继续显示上一句)。
|
|
65668
|
+
* 设置页的「移除身份」也逐本跑写命令,同一个映射,别抄第二份。 */
|
|
65602
65669
|
const STEP_LABEL = {
|
|
65603
65670
|
check: "actionStepCheck",
|
|
65604
65671
|
sync: "actionStepSync",
|
|
@@ -66608,20 +66675,28 @@ body[data-ds-dark-theme]{
|
|
|
66608
66675
|
notify(t("settingsSwitchDone", { name: picked.name ?? shown }), { variant: "success" });
|
|
66609
66676
|
});
|
|
66610
66677
|
};
|
|
66611
|
-
/**
|
|
66612
|
-
|
|
66678
|
+
/**
|
|
66679
|
+
* 小叉:先开确认框,真删在框里跑(`removeUser`)—— 逐本台账各跑一条写命令,一本几秒,
|
|
66680
|
+
* 所以底栏左侧跟着 skill 的阶段翻文案(用户 2026-11-05)。用声明式 `ConfirmModal`:
|
|
66681
|
+
* Promise 版的 `notice` 只能在开框时给一次,运行中改不了。
|
|
66682
|
+
*/
|
|
66683
|
+
const [forgetRow, setForgetRow] = (0, react$1.useState)(null);
|
|
66684
|
+
const [forgetStep, setForgetStep] = (0, react$1.useState)(null);
|
|
66685
|
+
const forgetUser = (row) => {
|
|
66613
66686
|
if (pending !== null) return;
|
|
66614
|
-
|
|
66615
|
-
|
|
66616
|
-
|
|
66617
|
-
|
|
66618
|
-
|
|
66619
|
-
|
|
66620
|
-
|
|
66687
|
+
setError(null);
|
|
66688
|
+
setForgetStep(null);
|
|
66689
|
+
setForgetRow(row);
|
|
66690
|
+
};
|
|
66691
|
+
/** 从所有登记过他的台账里摘掉;哪本里还有任务,那本不删并由 host 回报。 */
|
|
66692
|
+
const removeUser = async () => {
|
|
66693
|
+
const row = forgetRow;
|
|
66694
|
+
if (row === null || pending !== null) return;
|
|
66621
66695
|
setPending(`forget:${row.uuid}`);
|
|
66622
66696
|
setError(null);
|
|
66697
|
+
setForgetStep(null);
|
|
66623
66698
|
try {
|
|
66624
|
-
const report = await postForgetIdentity(row.uuid);
|
|
66699
|
+
const report = await postForgetIdentity(row.uuid, setForgetStep);
|
|
66625
66700
|
if (report.failed.length === 0 && report.kept.length === 0) notify(t("settingsForgetDone", { name: row.name }), { variant: "success" });
|
|
66626
66701
|
else {
|
|
66627
66702
|
const lines = [];
|
|
@@ -66642,10 +66717,13 @@ body[data-ds-dark-theme]{
|
|
|
66642
66717
|
}
|
|
66643
66718
|
setRoster(null);
|
|
66644
66719
|
onSwitched();
|
|
66720
|
+
setForgetRow(null);
|
|
66645
66721
|
} catch (failure) {
|
|
66646
66722
|
setError(failure instanceof Error ? failure.message : String(failure));
|
|
66723
|
+
throw failure;
|
|
66647
66724
|
} finally {
|
|
66648
66725
|
setPending(null);
|
|
66726
|
+
setForgetStep(null);
|
|
66649
66727
|
}
|
|
66650
66728
|
};
|
|
66651
66729
|
/** 点「使用」先确认:切换身份会影响此后所有台账记录的归属,不能一下点上。 */
|
|
@@ -67147,6 +67225,22 @@ body[data-ds-dark-theme]{
|
|
|
67147
67225
|
}
|
|
67148
67226
|
}))
|
|
67149
67227
|
}))) : null;
|
|
67228
|
+
/** 移除确认框:底栏左侧那行跟着 skill 的阶段翻(还没收到第一个阶段就先说「正在同步」)。 */
|
|
67229
|
+
const forgetDialog = forgetRow === null ? null : (0, react$1.createElement)(ConfirmModal, {
|
|
67230
|
+
title: t("settingsDeleteConfirmTitle"),
|
|
67231
|
+
message: t("settingsDeleteConfirmBody", { name: forgetRow.name }),
|
|
67232
|
+
confirmText: t("settingsDeleteConfirmOk"),
|
|
67233
|
+
cancelText: t("settingsCancel"),
|
|
67234
|
+
tone: "danger",
|
|
67235
|
+
notice: forgetStep === null ? t("actionSyncing") : t("settingsForgetStep", {
|
|
67236
|
+
step: t(STEP_LABEL[forgetStep.step]),
|
|
67237
|
+
workspace: forgetStep.workspace
|
|
67238
|
+
}),
|
|
67239
|
+
onCancel: () => {
|
|
67240
|
+
if (pending === null) setForgetRow(null);
|
|
67241
|
+
},
|
|
67242
|
+
onConfirm: removeUser
|
|
67243
|
+
});
|
|
67150
67244
|
return (0, react$1.createElement)("div", { className: "dtl-set" }, (0, react$1.createElement)("div", { className: "dtl-hero" }, (0, react$1.createElement)("div", { className: "dtl-hero__col" }, (0, react$1.createElement)(Card, {
|
|
67151
67245
|
accent: hasIdentity ? "success" : "danger",
|
|
67152
67246
|
bar: { side: "left" }
|
|
@@ -67183,7 +67277,7 @@ body[data-ds-dark-theme]{
|
|
|
67183
67277
|
addDirFromPicker(event.currentTarget);
|
|
67184
67278
|
}
|
|
67185
67279
|
}, t("settingsDirsAdd")))
|
|
67186
|
-
}), (0, react$1.createElement)("div", { className: "dtl-set__body" }, dirsBody)), userModal);
|
|
67280
|
+
}), (0, react$1.createElement)("div", { className: "dtl-set__body" }, dirsBody)), userModal, forgetDialog);
|
|
67187
67281
|
}
|
|
67188
67282
|
//#endregion
|
|
67189
67283
|
//#region src/client/board-home.ts
|
|
@@ -67679,7 +67773,7 @@ body[data-ds-dark-theme]{
|
|
|
67679
67773
|
variant: "outline",
|
|
67680
67774
|
size: "md",
|
|
67681
67775
|
onClick: onBack
|
|
67682
|
-
}, `← ${t("backToConversation")}`), (0, react$1.createElement)("div", { style: titleStyle }, t("panelTitle"), (0, react$1.createElement)("span", { style: buildTagStyle }, `v0.0.
|
|
67776
|
+
}, `← ${t("backToConversation")}`), (0, react$1.createElement)("div", { style: titleStyle }, t("panelTitle"), (0, react$1.createElement)("span", { style: buildTagStyle }, `v0.0.131 261105-090901-727`))), (0, react$1.createElement)("div", { style: {
|
|
67683
67777
|
display: "flex",
|
|
67684
67778
|
alignItems: "center",
|
|
67685
67779
|
gap: "var(--dtl-space-3)"
|