dsh-bill-ilife 0.3.4 → 0.3.6

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 CHANGED
@@ -151,6 +151,14 @@ window.__ModuleLoader__.load({
151
151
  //#region src/dsh-ctx.ts
152
152
  /** 宿主目录选择命名空间的服务名(#736;出处与禁令见 cookbook §13)。 */
153
153
  const REMOTE_DIRECTORY_PICKER = "remote.directoryPicker";
154
+ /** 宿主「这条路没给」的回执码(#744):一次组合里只服务一种能力(native 或 browse),
155
+ * 另一条路上的动词一律回它。
156
+ *
157
+ * 出处:`@deepseek-ai/dsh-api-workspace-controller/lib/types/directory-picker.js` 的 `requireCapability()`
158
+ * ——`RemoteError('directory-picker/unavailable', …)`。三条动词在客户端命名空间上都在,
159
+ * 所以只有真调一次、看它拒没拒,才知道组合里服务哪种能力。
160
+ * 共用件不认识这个名字(只照比对),于是这条宿主事实住在本镜像里。 */
161
+ const DIRECTORY_PICKER_REFUSED = "directory-picker/unavailable";
154
162
  //#endregion
155
163
  //#region ../plugin-manager/dist/directory-browser-contract.js
156
164
  /** 目录浏览器的纯逻辑半(**不认识宿主**:只认两格原语)。
@@ -178,10 +186,15 @@ window.__ModuleLoader__.load({
178
186
  if (typeof raw !== "object" || raw === null) return false;
179
187
  return typeof raw.pick === "function";
180
188
  }
181
- /** 三态判定:先 native(有 pick),再 browse(有两格原语),都没有=`none`。 */
189
+ /** 三态判定:**先看两格浏览原语**(都在=应用内浏览器这条路在),再看单独的 `pick`,都没有=`none`。
190
+ *
191
+ * 为什么浏览优先:客户端命名空间上三条动词一定都在(由 `TYPERT_REMOTE.descriptors` 生成,
192
+ * 与组合里服务哪种能力无关),所以两格原语在=这台宿主**可能**给浏览;真调一次才知道给不给,
193
+ * 不给({@link CAPABILITY_REFUSED})再由调用方换系统对话框。反过来先认 `pick` 就会在
194
+ * Desktop 这种「只服务 browse」的宿主上每次都去唤一次系统对话框、被拒一次(#744 实测)。 */
182
195
  function pickerModeOf$1(raw) {
183
- if (hasPickFn(raw)) return "native";
184
- return isBrowseFace(raw) ? "browse" : "none";
196
+ if (isBrowseFace(raw)) return "browse";
197
+ return hasPickFn(raw) ? "native" : "none";
185
198
  }
186
199
  /** 平台回执 → 三态(**永不抛**):成功回的是信封里的 `value` 不是路径;`ok:false` 是「供不了」不是「取消」。
187
200
  *
@@ -202,11 +215,52 @@ window.__ModuleLoader__.load({
202
215
  } : { kind: "cancelled" };
203
216
  }
204
217
  const detail = answer.error?.message?.trim() ?? "";
218
+ const code = answer.error?.code;
205
219
  return {
206
220
  kind: "unavailable",
221
+ ...typeof code === "string" && code !== "" ? { code } : {},
207
222
  message: "打不开系统文件夹对话框" + (detail === "" ? "" : "(" + detail + ")") + ":请直接在框里填绝对路径。"
208
223
  };
209
224
  }
225
+ /** 取数原语失败:带上宿主给的回执码,好让调用方分辨「没这条路」与「这一层读不出来」。
226
+ *
227
+ * `code` 就是 `humanize`(状态那半)读的那一格,人话直接上图。 */
228
+ var BrowseAnswerError = class extends Error {
229
+ code;
230
+ constructor(code, message) {
231
+ super(message);
232
+ this.name = "BrowseAnswerError";
233
+ this.code = code;
234
+ }
235
+ };
236
+ /** 宿主 browse 原语的**信封** → 值,或按信封里的 `error` 抛。
237
+ *
238
+ * 为什么必须走这一道:`list`/`createDirectory` 与 `pick` 一样是 Remote 动词,客户端拿到的是
239
+ * `{ok:true,value}`/`{ok:false,error}`(`@deepseek-ai/dsh-api-gateway/lib/client.js` 的 `invoke()`)。
240
+ * 把信封当值用,图上就会是一层「读出来了但一行都没有」的空目录(#744 实测),
241
+ * 而不是报错——所以宁可在这里抛,也不许把它当列举结果传下去。
242
+ * 第一方消费方同口径:`@deepseek-ai/dsh-client-ui-workspace/lib/client.js:105-113` 也是这么拆的。 */
243
+ function readBrowseAnswer(raw, what) {
244
+ const answer = typeof raw === "object" && raw !== null ? raw : {};
245
+ if (answer.ok === true && answer.value !== void 0 && answer.value !== null) return answer.value;
246
+ const detail = answer.error?.message?.trim() ?? "";
247
+ const code = answer.error?.code;
248
+ throw new BrowseAnswerError(typeof code === "string" && code !== "" ? code : "browse-failed", detail === "" ? what + "没有回执" : detail);
249
+ }
250
+ /** 宿主命名空间 → 本件的取数面:把两条原语的信封拆开,成功给值、失败按码抛。
251
+ *
252
+ * 命名空间上两格原语不在就回 null(调用方据此不画入口)。**透传参数个数照宿主的规矩**:
253
+ * 描述符声明了可选 `AbortSignal`(`cancellation: {parameter:'signal'}`),而客户端按
254
+ * `values.length === 业务参数个数 + 1` 判有没有 signal——显式传一个 `undefined` 会被当成 signal
255
+ * 去 `AbortSignal.any([…, undefined])` 而炸。本件的界面不做取消,故**从不传 signal**。 */
256
+ function browseFaceOf(raw) {
257
+ if (!isBrowseFace(raw)) return null;
258
+ const namespace = raw;
259
+ return {
260
+ list: async (path) => readBrowseAnswer(path === void 0 ? await namespace.list() : await namespace.list(path), "列举目录"),
261
+ createDirectory: async (path, name) => readBrowseAnswer(await namespace.createDirectory(path, name), "新建文件夹")
262
+ };
263
+ }
210
264
  /** 一段路径里最后那个分隔符的位置(Windows 上 `\` 与 `/` 都算;POSIX 上 `\` 是合法文件名字符,不算)。 */
211
265
  function lastSeparator(path) {
212
266
  const back = path.lastIndexOf("\\");
@@ -376,7 +430,15 @@ window.__ModuleLoader__.load({
376
430
  listeners.add(listener);
377
431
  return () => listeners.delete(listener);
378
432
  },
379
- open: () => load(null),
433
+ async open() {
434
+ const wanted = deps.initialPath.trim();
435
+ if (wanted !== "") {
436
+ await load(wanted);
437
+ if (state.phase === "ready") return state;
438
+ }
439
+ await load(null);
440
+ return state;
441
+ },
380
442
  enter: (path) => load(path),
381
443
  async up() {
382
444
  const listing = state.listing;
@@ -476,6 +538,7 @@ window.__ModuleLoader__.load({
476
538
  });
477
539
  return {
478
540
  open: () => controller.open(),
541
+ subscribe: (listener) => controller.subscribe(listener),
479
542
  state: () => controller.getState(),
480
543
  summary: () => describe(controller.getState()),
481
544
  actions: {
@@ -492,6 +555,39 @@ window.__ModuleLoader__.load({
492
555
  }
493
556
  };
494
557
  }
558
+ /** 一条目录行点下去该走哪条路(**六家共用这一处策略**,单独写在各家就会各错各的)。
559
+ *
560
+ * 顺序是「先应用内浏览,被拒再换系统对话框」,因为客户端命名空间上三条动词都在、问不出组合里服务哪种
561
+ * 能力(见 `pickerModeOf`)。回执:
562
+ * - `undefined`=连两格浏览原语都没有(这条路没有,调用方自己想办法);
563
+ * - `'refused'`=宿主回了 `refusalCode`(组合里是系统对话框),图已收起;
564
+ * - `'open'`=图开着,后面的事都在图里。
565
+ *
566
+ * **状态由调用方持有**:`onRow` 收到刚建好的那条(或 null=收起),React 那边据此决定画不画图。
567
+ *
568
+ * `refusalCode` 是**宿主的回执码**(各家自己从宿主镜像里拿,本件只照比不认名):
569
+ * 它换名字本件照样跑,六家一起换。 */
570
+ function openRowBrowser(input) {
571
+ const face = browseFaceOf(input.picker);
572
+ if (face === null) return void 0;
573
+ const row = createDirectoryRowBrowser({
574
+ face,
575
+ initialPath: input.initialPath,
576
+ onPicked: (picked) => {
577
+ input.onChange(picked);
578
+ input.onRow(null);
579
+ },
580
+ onClosed: () => input.onRow(null)
581
+ });
582
+ input.onRow(row);
583
+ return row.open().then((settled) => {
584
+ if (settled.phase === "failed" && settled.failure?.code === input.refusalCode) {
585
+ input.onRow(null);
586
+ return "refused";
587
+ }
588
+ return "open";
589
+ });
590
+ }
495
591
  //#endregion
496
592
  //#region ../plugin-manager/dist/directory-browser-ui.js
497
593
  /** 文件浏览器的视图半:把 `BrowseState` 画成一张对话框。**不认识宿主**,也不自己做 IO。
@@ -648,7 +744,7 @@ window.__ModuleLoader__.load({
648
744
  },
649
745
  error: { color: "var(--dsw-alias-label-error, #e66)" }
650
746
  };
651
- function crumbRow(state, labels, onEnter) {
747
+ function crumbRow(state, onEnter) {
652
748
  const listing = state.listing;
653
749
  if (listing === null || listing.crumbs.length === 0) return null;
654
750
  const parts = [];
@@ -667,7 +763,7 @@ window.__ModuleLoader__.load({
667
763
  },
668
764
  title: crumb.path,
669
765
  onClick: () => onEnter(crumb.path)
670
- }, index === 0 ? labels.up : crumb.name));
766
+ }, crumb.name));
671
767
  });
672
768
  return react.createElement("div", { style: S$1.crumbs }, parts);
673
769
  }
@@ -733,7 +829,7 @@ window.__ModuleLoader__.load({
733
829
  const listing = state.listing ?? EMPTY_LISTING;
734
830
  const hidden = hiddenCount(listing);
735
831
  const body = [];
736
- body.push(crumbRow(state, labels, props.onEnter));
832
+ body.push(crumbRow(state, props.onEnter));
737
833
  body.push(react.createElement("div", { style: S$1.pathRow }, react.createElement("input", {
738
834
  style: S$1.input,
739
835
  value: state.draft,
@@ -814,11 +910,15 @@ window.__ModuleLoader__.load({
814
910
  /** 目录行接线的 React 半边:把操作半那组动作直接喂给组件。
815
911
  *
816
912
  * 操作半(`directory-browser-state.ts` 的 `createDirectoryRowBrowser`)不碰 React,
817
- * 这里只把它的 `actions` 摊进 props —— 于是「进哪一层」那套在 Node 里可测,这里只做接线。 */
913
+ * 这里只把它的 `actions` 摊进 props —— 于是「进哪一层」那套在 Node 里可测,这里只做接线。
914
+ *
915
+ * `useSyncExternalStore` 是**必需**的:列举是异步落定的,不订阅的话这一帧画完就再没有重画,
916
+ * 图上会永远停在「正在读取…」(#744 实测)。 */
818
917
  function DirectoryBrowserFromRow(props) {
918
+ const state = react.useSyncExternalStore(props.row.subscribe, props.row.state);
819
919
  return DirectoryBrowser({
820
920
  open: props.open,
821
- state: props.row.state(),
921
+ state,
822
922
  labels: props.labels,
823
923
  ...props.row.actions
824
924
  });
@@ -1078,12 +1178,7 @@ window.__ModuleLoader__.load({
1078
1178
  /** 平台回执/入口三态:**已经收进共用件**(票 #744),这里只同名转出。 */
1079
1179
  const readPickAnswer = readPickAnswer$1;
1080
1180
  const pickerModeOf = pickerModeOf$1;
1081
- /** 唤起一次系统文件夹选择器并归一结果(**永不抛**)。
1082
- *
1083
- * 为什么只留这一格没进共用件:共用件那份要按子路径 \
1084
- equire\ 进来,而在本仓的用例加载器里
1085
- * 取不到它的具名导出(那条子路径现在验证的是「六家束里能 require」)。这一格只有五行,
1086
- * 且与共用件逐字同形;等六家全部接通、加载器换成真 require 之后,可一并收口。 */
1181
+ /** 唤起一次系统文件夹选择器并归一结果(**永不抛**)。 */
1087
1182
  async function pickDirectory(picker) {
1088
1183
  try {
1089
1184
  return readPickAnswer(await picker.pick());
@@ -1094,6 +1189,12 @@ window.__ModuleLoader__.load({
1094
1189
  };
1095
1190
  }
1096
1191
  }
1192
+ /** 入口三态:有系统对话框(`pick`)/只有应用内浏览(`list` + `createDirectory`)/都没有。
1193
+ *
1194
+ * 判定取自共用件(一处定义)。本函数只读那个命名空间,不做 IO。 */
1195
+ function directoryEntryMode(picker) {
1196
+ return picker === null ? "none" : pickerModeOf(picker);
1197
+ }
1097
1198
  /** 「选择文件夹」按钮的真装配函数(目录行渲染出来的按钮,onClick 就是它)。
1098
1199
  *
1099
1200
  * 回 Promise 是为了本地可判(用例直接 await);接进 React 时调用方 `void` 掉。 */
@@ -1185,40 +1286,52 @@ window.__ModuleLoader__.load({
1185
1286
  setNotice(null);
1186
1287
  setError(null);
1187
1288
  }, []);
1188
- /** 三态入口的接线:`native`=系统对话框;`browse`=应用内浏览器(票 #744)。
1289
+ /** 目录行入口:三态(系统对话框/应用内浏览/都没有)。
1189
1290
  *
1190
- * 这台机器上(Windows 桌面版)宿主组合里只有 browse 那一档、`pick` 会被拒
1191
- * (根因见 `docs/agents/desktop-directory-picker-browse.md`),于是这一行改成
1192
- * 「开应用内浏览器」——同一个用户动作,不依赖系统对话框也能选到目录。 */
1193
- const { mode: entryMode, openBrowse, browseRow } = props;
1194
- const picker = pickerGone ? null : props.getPicker();
1195
- /** 目录行的入口动作:三态各一条路;都供不了就不给入口(`undefined` ⇒ Row 不画按钮)。 */
1196
- const onOpenRow = react.useCallback(async (key) => {
1197
- if (pickerModeOf(picker) === "native") {
1198
- setPicking(true);
1199
- setError(null);
1200
- const outcome = await pickDirectory(picker);
1201
- setPicking(false);
1202
- if (outcome.kind === "picked") onChange(key, outcome.path);
1203
- else if (outcome.kind === "unavailable") {
1204
- setError(outcome.message);
1205
- setPickerGone(true);
1206
- }
1207
- return;
1291
+ * **软依赖**:拿不到命名空间就没有入口(留文本框),被拒一次即收起入口,不留死按钮。
1292
+ * 状态全部住在组件自己(hook 顺序与旧版同形):浏览器那一份只存「当前开着的那条」。 */
1293
+ const picker = pickerGone ? null : props.pickerSource();
1294
+ const [browseRow, setBrowseRow] = react.useState(null);
1295
+ /** 唤起系统文件夹选择器那一条路(应用内浏览被拒时的兜底,也是命名空间只给 `pick` 时的唯一一条)。 */
1296
+ const openNative = react.useCallback(async (key) => {
1297
+ const face = pickerGone ? null : props.pickerSource();
1298
+ if (face === null) return;
1299
+ setPicking(true);
1300
+ setError(null);
1301
+ const outcome = await pickDirectory(face);
1302
+ setPicking(false);
1303
+ if (outcome.kind === "picked") onChange(key, outcome.path);
1304
+ else if (outcome.kind === "unavailable") {
1305
+ setError(outcome.message);
1306
+ setPickerGone(true);
1208
1307
  }
1209
- const opened = openBrowse(key, draft[key] ?? "", onChange);
1210
- if (opened === void 0) return;
1308
+ }, [
1309
+ pickerGone,
1310
+ onChange,
1311
+ props.pickerSource
1312
+ ]);
1313
+ /** 目录行的入口动作:**先应用内浏览,宿主没给这条路再换系统对话框**(判断在共用件里)。
1314
+ *
1315
+ * 两条都供不了就不给入口(`undefined` 时 `rowEntry` 为 null ⇒ Row 不画按钮)。 */
1316
+ const onOpenRow = react.useCallback(async (key) => {
1211
1317
  setError(null);
1212
- await opened;
1318
+ const opened = openRowBrowser({
1319
+ picker,
1320
+ initialPath: draft[key] ?? "",
1321
+ onChange: (next) => onChange(key, next),
1322
+ onRow: setBrowseRow,
1323
+ refusalCode: DIRECTORY_PICKER_REFUSED
1324
+ });
1325
+ if (opened === void 0 || await opened === "refused") await openNative(key);
1213
1326
  }, [
1214
1327
  picker,
1215
1328
  onChange,
1216
- openBrowse,
1217
- draft
1329
+ draft,
1330
+ openNative
1218
1331
  ]);
1219
- /** 给 Row 的三态入口:`none` 时给 null(不画按钮),否则把 mode 与动作一起递下去。 */
1220
- const rowEntry = entryMode === "none" ? null : {
1221
- mode: entryMode,
1332
+ /** 给 Row 的三态入口:`none` 时给 null(不画按钮,文本框照旧)。 */
1333
+ const rowEntry = pickerModeOf(picker) === "none" ? null : {
1334
+ mode: pickerModeOf(picker),
1222
1335
  onOpen: onOpenRow
1223
1336
  };
1224
1337
  const surface = state.kind === "ready" ? state.surface : null;
@@ -1299,7 +1412,7 @@ window.__ModuleLoader__.load({
1299
1412
  type: "button",
1300
1413
  disabled: busy,
1301
1414
  onClick: () => void load()
1302
- }, "重新读取")), picking ? react.createElement("div", { style: S.muted }, "已唤起系统文件夹对话框:选中后自动填上,取消则不动。") : null, browseRow !== null ? react.createElement("div", { style: S.muted }, "应用内文件夹浏览器已打开:选中后自动填上,取消则不动。") : null, browseRow !== null ? react.createElement(DirectoryBrowserFromRow, {
1415
+ }, "重新读取")), picking ? react.createElement("div", { style: S.muted }, "已唤起系统文件夹对话框:选中后自动填上,取消则不动。") : null, browseRow !== null ? react.createElement(DirectoryBrowserFromRow, {
1303
1416
  open: true,
1304
1417
  row: browseRow,
1305
1418
  labels: {
@@ -1331,43 +1444,22 @@ window.__ModuleLoader__.load({
1331
1444
  order: 70,
1332
1445
  label: () => SLOT_TITLE,
1333
1446
  channel: RPC_CHANNEL
1334
- }, () => {
1335
- return function Slot() {
1336
- const [browseRow, setBrowseRow] = react.useState(null);
1337
- const picker = resolveDirectoryPicker((name) => ctx.get?.(name));
1338
- const openBrowse = (key, path, onRowChange) => {
1339
- if (picker === null || pickerModeOf(picker) !== "browse") return void 0;
1340
- const row = createDirectoryRowBrowser({
1341
- face: picker,
1342
- initialPath: path,
1343
- onPicked: (picked) => {
1344
- onRowChange(key, picked);
1345
- setBrowseRow(null);
1346
- },
1347
- onClosed: () => setBrowseRow(null)
1348
- });
1349
- setBrowseRow(row);
1350
- return row.open();
1351
- };
1352
- return react.createElement(BillConfig, {
1353
- getCall,
1354
- getPicker,
1355
- mode: pickerModeOf(picker),
1356
- openBrowse,
1357
- browseRow
1358
- });
1359
- };
1360
- }));
1447
+ }, () => react.createElement(BillConfig, {
1448
+ getCall,
1449
+ pickerSource: getPicker
1450
+ })));
1361
1451
  }
1362
1452
  //#endregion
1363
1453
  exports.Row = Row;
1364
1454
  exports.apply = apply;
1365
1455
  exports.createBrowseHandler = createBrowseHandler;
1456
+ exports.directoryEntryMode = directoryEntryMode;
1366
1457
  exports.fetchConfigSurface = fetchConfigSurface;
1367
1458
  exports.fromDraft = fromDraft;
1368
1459
  exports.humanizeConfigFailure = humanizeConfigFailure;
1369
1460
  exports.inject = inject;
1370
1461
  exports.isDirectoryPicker = isDirectoryPicker;
1462
+ exports.openRowBrowser = openRowBrowser;
1371
1463
  exports.pickDirectory = pickDirectory;
1372
1464
  exports.pickerModeOf = pickerModeOf;
1373
1465
  exports.readPickAnswer = readPickAnswer;
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["pickerModeOf","readPickAnswer","S","React","sharedReadPickAnswer","sharedPickerModeOf","React"],"sources":["../src/contract.ts","../src/slot.ts","../src/settings.ts","../src/dsh-ctx.ts","../../plugin-manager/dist/directory-browser-contract.js","../../plugin-manager/dist/directory-browser-state.js","../../plugin-manager/dist/directory-browser-ui.js","../src/client.ts"],"sourcesContent":["/** dsh-bill-ilife 端口(port):DSH 世界与饼干记账 hexagon 之间的契约。\n *\n * browser 安全:零 node 导入。host 适配器(注册 RPC 通道)与 client\n * 适配器(调用 RPC)共用同一份定义;单测与 loader 回路以此为准。\n * 通道命名遵守宿主约束:单段且不等于 /api(见 cookbook「RPC 通道」)。\n *\n * 照 #56 卡路里样板同形(`packages/plugin-calorie/src/contract.ts`):记账插件原先\n * **没有任何 RPC 通道**,本件是它第一份端口定义(#677)。\n */\nexport const RPC_CHANNEL = '/ilife-bill-ilife' as const;\nexport const RPC_ENDPOINT_READ = 'read' as const;\n\n/** #677 配置端点:读设置页整面、保存一份取值、重置为默认。同走一条通道,靠端点名分发。 */\nexport const RPC_ENDPOINT_CONFIG_GET = 'config.get' as const;\nexport const RPC_ENDPOINT_CONFIG_SAVE = 'config.save' as const;\nexport const RPC_ENDPOINT_CONFIG_RESET = 'config.reset' as const;\n/** #706 配置体检端点:只读一次,回一份报告(判据由技能侧出,本包只透传,不重写一个字)。\n *\n * 端点名与技能侧那条只读命令同名(`<技能>.config.check`);报告形状的唯一真相在技能侧\n * `packages/skill-<技能>/src/health.ts`,面板侧镜像在 `packages/plugin-manager/src/health-contract.ts`。 */\nexport const RPC_ENDPOINT_CONFIG_CHECK = 'config.check' as const;\n\n/** 读请求载荷:与技能 CLI 的 key/params 同形(见 skill-bill SKILL.md)。 */\nexport interface ReadPayload {\n readonly key: string;\n readonly params: Record<string, unknown>;\n}\n\n/** 保存载荷:一份键值(键路径 → 值)。缺项由技能侧按默认值补齐,故只收用户真改的那些。 */\nexport interface SavePayload {\n readonly values: Record<string, unknown>;\n}\n\nexport interface RpcError {\n readonly code: string;\n readonly message: string;\n readonly details: Record<string, unknown>;\n}\n\nexport type RpcResult =\n | { readonly ok: true; readonly value: unknown }\n | { readonly ok: false; readonly error: RpcError };\n\nexport function ok(value: unknown): RpcResult {\n return { ok: true, value };\n}\n\nexport function fail(code: string, message: string): RpcResult {\n return { ok: false, error: { code, message, details: {} } };\n}\n\n/** 读载荷校验:形状不对返回 null,由调用方按 bad-request 回绝(不抛)。 */\nexport function parseReadPayload(raw: unknown): ReadPayload | null {\n if (typeof raw !== 'object' || raw === null) return null;\n const { key, params } = raw as { key?: unknown; params?: unknown };\n if (typeof key !== 'string' || key.length === 0) return null;\n if (typeof params !== 'object' || params === null || Array.isArray(params)) return null;\n return { key, params: params as Record<string, unknown> };\n}\n\n/** 保存载荷校验:形状不对返回 null,由调用方按 bad-request 回绝(不抛)。 */\nexport function parseSavePayload(raw: unknown): SavePayload | null {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return null;\n const { values } = raw as { values?: unknown };\n if (typeof values !== 'object' || values === null || Array.isArray(values)) return null;\n return { values: values as Record<string, unknown> };\n}\n\n/** 配置面回执:形状与技能侧 `bill.config.read` 的 data 逐字段同形(唯一定义地在那边的 cli/config.ts)。 */\nexport interface ConfigSurfaceReply {\n /** 配置文件绝对路径。 */\n readonly path: string;\n /** 数据目录绝对路径。 */\n readonly dataDir: string;\n /** 本次是不是「文件不存在、按默认值落了一份」。 */\n readonly created: boolean;\n /** 当前取值(文件里的 ⊕ 缺项按默认值补)。 */\n readonly values: Record<string, unknown>;\n}\n\n/** 信封守卫:供 client 拆包前校验(port 拥有信封定义,守卫住 port)。 */\nexport function isRpcResult(raw: unknown): raw is RpcResult {\n if (typeof raw !== 'object' || raw === null) return false;\n const okFlag = (raw as { ok?: unknown }).ok;\n return okFlag === true || okFlag === false;\n}\n","/** dsh-bill-ilife 槽位描述(P10 脚手架,P3 #4 定案)。\n *\n * 写死槽位原生组件:id 命名空间 ilife:cookie,order 95。\n * 注册与打开只走 registerTab/openTab 的路径导航;子页后缀(如 ilife:cookie:*)\n * 只在主面板内导航,不占栏。缺席纯条件渲染,不轮询(本文件无定时器;\n * 有界重试归一由 base-render 拥有,单品不自建)。\n */\n\nexport const SKILL = 'bill' as const;\nexport const SLOT_ID = 'ilife:cookie' as const;\nexport const SLOT_ORDER = 70 as const;\nexport const SLOT_TITLE = '饼干记账' as const;\nexport const PLUGIN = 'dsh-bill-ilife' as const;\nexport const MANAGER_PLUGIN = 'dsh-life-pack' as const;\n\nexport interface SlotDescriptor {\n readonly skill: string;\n readonly slotId: string;\n readonly order: number;\n readonly title: string;\n readonly kind: 'page';\n}\n\nexport function slotDescriptor(): SlotDescriptor {\n return { skill: SKILL, slotId: SLOT_ID, order: SLOT_ORDER, title: SLOT_TITLE, kind: 'page' };\n}\n\n/** 写死原生组件描述(纯数据,无按需加载,无外嵌页)。 */\nexport const TAB_COMPONENT = { kind: 'native', name: 'BillPanel' } as const;\n\nexport interface TabsPort {\n hasTabs(): boolean;\n registerTab(entry: { readonly id: string; readonly title: string; readonly order: number; readonly single: boolean; readonly component: unknown }): () => void;\n openTab(seed: { readonly type: string; readonly path: string }, scope?: { readonly sessionId?: string }): void;\n}\n\n/** 自注册:幂等(调用方持 disposer),失败返回 false 由调用方决定重试。 */\nexport function registerSingle(port: TabsPort): (() => void) | null {\n if (!port.hasTabs()) return null;\n return port.registerTab({ id: SLOT_ID, title: SLOT_TITLE, order: SLOT_ORDER, single: true, component: TAB_COMPONENT });\n}\n\n/** 内容型打开:path seed,折叠面板自动展开。 */\nexport function openSingle(port: TabsPort, sessionId?: string): void {\n if (!port.hasTabs()) return;\n port.openTab({ type: SLOT_ID, path: SLOT_ID }, sessionId ? { sessionId } : undefined);\n}\n","/** dsh-bill-ilife 设置页元数据(#677:从只读脚手架换成真配置页)。\n *\n * 本文件是**设置页的行表**:一个可配置项一行,写清它在配置文件里的键、中文标题、\n * 分级(常用 / 高级)、控件种类与人话指引。它是页面渲染的唯一依据。\n *\n * **本文件必须零 node 依赖**:`tsconfig.client.json` 把它收进 client 侧编译,\n * 浏览器产物不许带 node 内建。取值一律由宿主经技能 CLI 的 `bill.config.read` 端点现取\n * (见 bridge.ts)。\n *\n * 口径:\n * · 配置表形状(键与默认值)的**唯一定义地**是技能侧 `packages/skill-bill/src/config.ts`\n * 的 `BILL_CONFIG_DEFAULTS`(本文件是它在页面上的投影,逐键对齐由\n * `test/config-surface-677.test.mjs` 用编译产物锁死);\n * · **空串=按默认落点**(老落点由技能各取用处算),所以「空」不是「没配」,\n * 而是「还走改造前那个位置」,老数据不会看起来丢了——行文案按这个语义写;\n * · `db.dir` 那一行的落点由配置面回执的 `dataDir` 带回(解析好的绝对路径),页面上把它**预填**进\n * 该行(`prefillFrom: 'dataDir'`),用户不必自己拼路径(#743)。\n *\n * 清单出处:「六家技能的路径类配置全量调查」记账 8 项去掉 1 项包内固定(包内页面模板目录)\n * =**上设置页候选 7 项**;其中「产物文件名主体」在源码里是两个值,故行数为 8。\n */\nexport const SETTINGS_OWNER = 'dsh-bill-ilife' as const;\nexport const SETTINGS_SLOT = 'ilife:cookie' as const;\n\n/** 配置文件主体名:落点 `~/.ilife/bill.yaml`(`ILIFE_CONFIG_DIR` 可整体接管)。 */\nexport const CONFIG_STEM = 'bill' as const;\n\n/** 分级:常用项直接画在页面上,其余进默认收起的「高级」组(#675 冻结口径)。 */\nexport type ConfigTier = 'common' | 'advanced';\n\n/** 控件种类:只有这四种(前三种与受限 YAML 子集的字符串/数字/布尔一一对应,没有数组;\n * `directory` 是**字符串那一档的页面形态**——取值仍是串,只是多一个唤起系统文件夹选择器的入口,见 #736)。 */\nexport type ConfigControl = 'text' | 'number' | 'switch' | 'directory';\n\nexport interface ConfigItem {\n /** 配置文件里的键路径,一层嵌套用 `.` 连接,例 `html.helpStem`。 */\n readonly key: string;\n /** 页面上那一行的中文标题。 */\n readonly title: string;\n readonly tier: ConfigTier;\n readonly control: ConfigControl;\n /** 一行人话:这一项管什么、留空会怎样(一到两句,不写开发期口径)。 */\n readonly hint: string;\n /** 目录行的落点来源:这一行取值空着时,页面上拿配置面回执里的哪一格当它显示的绝对路径。\n * 只有 `db.dir` 标它——回执里的 `dataDir` 就是技能真会用的那个目录,逐字相同。 */\n readonly prefillFrom?: 'dataDir';\n}\n\n/** 常用项:改了就影响「库与产物落在哪」,放页面上。 */\nconst COMMON: readonly ConfigItem[] = [\n {\n key: 'db.dir',\n title: '数据目录',\n tier: 'common',\n control: 'directory',\n // 留空=技能按默认目录落文件;页面上把解析出来的绝对路径直接填进这一行(见 client.ts 的 toDraft)。\n prefillFrom: 'dataDir',\n hint: '库文件与备份的根目录。留空=用默认目录。',\n },\n {\n key: 'db.name',\n title: '库文件名',\n tier: 'common',\n control: 'text',\n hint: '数据目录下的数据库文件名。改名=换库,旧数据不会被读入。',\n },\n {\n key: 'db.goals',\n title: '预算/账户文件名',\n tier: 'common',\n control: 'text',\n hint: '与库同目录的预算与账户数据文件名。改名后旧文件不会再被读取。',\n },\n {\n key: 'html.dir',\n title: 'HELP 产物目录名',\n tier: 'common',\n control: 'text',\n hint: '数据目录下存放 HELP 与速查表的子目录名。改名后新产物落新目录。',\n },\n];\n\n/** 高级项:默认收起。改错了多半只是自己撞上麻烦,不确定就别动。 */\nconst ADVANCED: readonly ConfigItem[] = [\n {\n key: 'backup.dir',\n title: '备份目录',\n tier: 'advanced',\n control: 'directory',\n hint: '备份与恢复的读写目录。留空=库目录下的 backups。',\n },\n {\n key: 'backup.stem',\n title: '备份名主体',\n tier: 'advanced',\n control: 'text',\n hint: '备份文件名的前缀,后面自动加时间戳。',\n },\n {\n key: 'html.helpStem',\n title: '产物名主体 · HELP',\n tier: 'advanced',\n control: 'text',\n hint: 'HELP 文件名前缀。',\n },\n {\n key: 'html.quickRefStem',\n title: '产物名主体 · 速查表',\n tier: 'advanced',\n control: 'text',\n hint: '速查表文件名前缀。',\n },\n];\n\n/** 设置页的行表,顺序即页面顺序。 */\nexport const CONFIG_ITEMS: readonly ConfigItem[] = [...COMMON, ...ADVANCED];\n\n/** 常用项行数(页面上直接画出来的那些)。 */\nexport const COMMON_ITEM_COUNT = COMMON.length;\n\n/** 高级组标题与副文案。 */\nexport const ADVANCED_GROUP_TITLE = '高级' as const;\nexport const ADVANCED_GROUP_NOTE = '不常改。留空=用默认值。' as const;\n\n/** 按 `a.b` 路径从配置取值里读(缺层或类型不符一律回 undefined)。 */\nexport function readPath(values: Record<string, unknown>, key: string): unknown {\n const parts = key.split('.');\n let cur: unknown = values;\n for (const part of parts) {\n if (typeof cur !== 'object' || cur === null || Array.isArray(cur)) return undefined;\n cur = (cur as Record<string, unknown>)[part];\n }\n return cur;\n}\n\n/** 按 `a.b` 路径把值写进配置取值(中间层缺就建一层空组;已存在非组即覆盖成组)。 */\nexport function writePath(values: Record<string, unknown>, key: string, value: unknown): void {\n const parts = key.split('.');\n let cur: Record<string, unknown> = values;\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!;\n const next = cur[part];\n if (typeof next !== 'object' || next === null || Array.isArray(next)) cur[part] = {};\n cur = cur[part] as Record<string, unknown>;\n }\n cur[parts[parts.length - 1]!] = value;\n}\n","/** DSH ctx 最小镜像(只含本仓实际使用的面;**除末尾那枚服务名常量外全是 type-only**,\n * 构建期擦除——那枚常量是为 #736 的目录选择软依赖加的,是本文件唯一的运行期值)。\n *\n * skills 面出处见 `docs/agents/dsh-client-contract.md` §12(#56 卡路里样板,#150 记账同形):\n * `ctx.skills.registerProvider(create)`(`dsh-skill/lib/index.js:147`);\n * candidate 校验红线见 `validateCandidate`(同文件 `:452`);`get` 回定义须与候选同名(同文件 `:259`)。\n * slots 面出处见 cookbook §5(总管单卡方案):\n * `inject(key, cb)` 等声明同步跑否则等待(`dsh-client-ui-renderer/lib/client.js:1015`);\n * `register` list 必 `{id}`(`dsh-client-ui-slots/lib/index.js:72`);\n * 爱生活页签槽注册形见 cookbook §11(`{name:'ilife.config-tab', id, order, label}`)。\n * 禁止加镜外成员:要用新能力,先在 cookbook 落出处,再加镜像。用 `import type` 引用本文件。\n *\n * 本插件 host 侧用 skills 面,client 侧用 slots 面(最小静态注册,不取数,故无 connection)。\n */\n\n/** 爱生活页签槽注册项(cookbook §5 options 全形子集,只取本次用到的面)。 */\nexport interface SlotsRegisterOptions {\n readonly name: string;\n readonly id?: string;\n readonly order?: number;\n readonly label?: string | (() => string);\n readonly locale?: string;\n /** 本包自己那条 RPC 通道(单段):注册时交出去,总管据此**通用地**调配置体检(票 #706)。 */\n readonly channel?: string;\n readonly inject?: () => Record<string, unknown>;\n}\n\n/** 总管声明的爱生活页签槽面(client 侧只用 inject+register)。 */\nexport interface SlotsFace {\n inject(key: string, callback: () => (() => void) | Iterable<() => void>): () => void;\n register(options: SlotsRegisterOptions, component: unknown): () => void;\n}\n\n/** 记账 client 上下文(#677 起:从「只读 slots」扩到 connection——设置页要经宿主读改配置)。 */\nexport interface ClientCtx {\n readonly slots: SlotsFace;\n /** 宿主连接面:客户端经它把请求送进宿主注册的 /api 路由。缺席即由组件判「宿主连接缺席」。 */\n readonly connection?: ConnectionFace;\n /** 服务取用(本包暂不用,保留面以对齐兄弟件镜像)。 */\n get?(name: string): unknown;\n /** 卸载清理登记。 */\n effect?(fn: () => void, label?: string): void;\n}\n\n/** rpc.call 的调用口(每次取数时现取:connection 后到也不永久缺席)。 */\nexport type RpcCallFace = (\n path: string,\n channel: string,\n body: unknown,\n signal?: AbortSignal,\n) => Promise<unknown>;\n\n/** rpc.call 的回执形状(只列本包读的两处)。 */\nexport interface RpcCallResult {\n readonly ok: boolean;\n readonly value?: unknown;\n readonly error?: { readonly code?: string; readonly message?: string };\n}\n\n/** 宿主连接面(cookbook 的 connection 服务,本包只用 rpc.call)。 */\nexport interface ConnectionFace {\n readonly rpc?: { readonly call?: RpcCallFace };\n}\n\n/** 技能的调用策略(模型可调/用户可调)。 */\nexport interface SkillInvocationPolicy {\n readonly modelInvocable: boolean;\n readonly userInvocable: boolean;\n}\n\n/** 候选摘要(宿主 `list` 的返回项;`provider` 须等于注册名,否则宿主校验抛)。 */\nexport interface SkillCandidate {\n readonly name: string;\n readonly description: string;\n readonly invocation: SkillInvocationPolicy;\n readonly provider: string;\n readonly source: string;\n readonly rank: number;\n readonly locator: unknown;\n readonly resourceBase?: { readonly kind: 'directory'; readonly path: string };\n}\n\n/** 技能全文(宿主 `get` 的返回项;`content` 为 frontmatter 后正文)。 */\nexport interface SkillDefinition {\n readonly name: string;\n readonly description: string;\n readonly invocation: SkillInvocationPolicy;\n readonly provider: string;\n readonly source: string;\n readonly resourceBase?: { readonly kind: 'directory'; readonly path: string };\n readonly content: string;\n}\n\nexport interface SkillProvider {\n readonly name: string;\n list(options?: unknown): Promise<readonly SkillCandidate[]>;\n get(candidate: SkillCandidate, options?: unknown): Promise<SkillDefinition | undefined>;\n}\n\nexport interface SkillsFace {\n registerProvider(\n create: (control: { readonly signal: AbortSignal; invalidate(): void }) => SkillProvider,\n ): () => void;\n}\n\n/** RPC 处理函数:端点 + 载荷 → 信封回执(照 #56 卡路里样板同形)。 */\nexport type RpcHandler = (endpoint: string, payload: unknown) => Promise<unknown>;\n\n/** 宿主 fetch 路由注册面(`ctx.connection.fetch.register`)。 */\nexport interface HostConnectionFace {\n readonly fetch: { register(options: Record<string, unknown>): () => void };\n}\n\n/** 本插件用到的宿主面(apply 入参)。 */\nexport interface SkillHostCtx {\n readonly skills: SkillsFace;\n /** 模型工具注册表(#734):本包把技能唯一出口开成 agent 工具。 */\n readonly tools?: ToolsFace;\n /** 宿主连接面:设置页的三个配置端点注册在这上面(#677 起用)。 */\n readonly connection?: HostConnectionFace;\n /** 卸载清理登记。 */\n readonly effect?: (fn: () => void, label?: string) => void;\n readonly logger?: unknown;\n}\n\n/** 宿主目录选择命名空间的服务名(#736;出处与禁令见 cookbook §13)。 */\nexport const REMOTE_DIRECTORY_PICKER = 'remote.directoryPicker' as const;\n\n/** 宿主目录选择命名空间(DSH 平台提供)。本包**只用 `pick` 这一格**:不给 signal(平台那格可选)。\n *\n * **回执是信封,不是路径**(#743 真机缺陷的根因):客户端把每次 Remote 调用包成\n * `{ok:true,value}`/`{ok:false,error}`(`@deepseek-ai/dsh-api-gateway/lib/client.js` 的 `invoke()`:\n * 失败不抛、成功也不回裸值),第一方消费方 `@deepseek-ai/dsh-client-ui-workspace/lib/client.js`\n * 的 `pickDirectory()` 就照这个形状拆:`const r = await pick(); if (!r.ok) throw …; return r.value`。\n * `value` 为 `null`=用户取消;`ok:false`=这条路供不了(例如组合里是 browse 后端)。 */\nexport interface DirectoryPickerAnswer {\n readonly ok: boolean;\n readonly value?: string | null;\n readonly error?: { readonly code?: string; readonly message?: string };\n}\n\nexport interface DirectoryPickerFace {\n pick(): Promise<DirectoryPickerAnswer>;\n}\n\n/** 模型工具面镜像(#734;出处见 cookbook §14)。\n *\n * 出处:`@deepseek-ai/dsh-tools/lib/index.js` 的 `defineTool` 与 `ToolRuntime.register`——\n * 工具对象的形状是 `name`/`description`/`parameters`(JSON Schema)/`output{schema,render}`/\n * `execute`;`register` 只校验 `output.render` 是函数、`output.schema` 受支持、`timeoutMs` 为正、\n * 名字不是保留名 `run_code`,**不要求 `defineTool` 产物** ⇒ 本包按形状手写、零依赖。 */\nexport interface ToolTextPart {\n readonly type: 'text';\n readonly text: string;\n}\n\nexport interface ToolDefinitionMirror {\n readonly name: string;\n readonly description: string;\n readonly parameters: Record<string, unknown>;\n readonly output: {\n readonly schema: Record<string, unknown>;\n render(args: unknown, value: string): readonly ToolTextPart[];\n };\n execute(args: Record<string, unknown>, exec?: unknown): Promise<unknown>;\n}\n\nexport interface ToolsFace {\n register(definition: ToolDefinitionMirror): () => void;\n}\n","/** 目录浏览器的纯逻辑半(**不认识宿主**:只认两格原语)。\n *\n * 为什么在这里:六个单品插件的设置页各有一批「值是一个目录」的行,它们要的界面是同一个东西。\n * 界面的**取数**来自宿主给的两个原语(`list`/`createDirectory`),而这两个原语的形状是**调用方喂进来的**\n * ——本件不 import 宿主的任何实现,也不认识 `remote.directoryPicker` 这个名字(那两件住在各家的\n * `dsh-ctx.ts` 镜像里)。于是本件在 Windows 桌面、远程浏览器、SSH 三种部署下是同一份代码。\n *\n * 装什么:本次要浏览的那一层怎么走(进子目录/回上一级/跳面包屑)、路径怎么切出「目录部分」与\n * 「筛选词」、回执怎么认。**不装**任何 IO、不装任何持久化——`list`/`createDirectory` 由调用方执行。\n *\n * 与平台自带浏览器件的关系:`@deepseek-ai/dsh-client-ui-directory-picker-browse` 的 `DirectoryBrowser`\n * 是包内私有件(只经 `ui-workspace` 的流程空位暴露),插件侧取不到,所以这里是一份独立实现,\n * 只对齐它的**交互结果**(进/选/新建),不对齐它的 DOM。\n */\n/** 认一认某个值是不是可用的目录取数面(软依赖守卫用:认不出就当没有,绝不把页面带下来)。 */\nexport function isBrowseFace(raw) {\n if (typeof raw !== 'object' || raw === null)\n return false;\n const face = raw;\n return typeof face.list === 'function' && typeof face.createDirectory === 'function';\n}\n/** 认一认某个值有没有 `pick`(对应宿主 composition 里的 `native` 能力)。 */\nexport function hasPickFn(raw) {\n if (typeof raw !== 'object' || raw === null)\n return false;\n return typeof raw.pick === 'function';\n}\n/** 三态判定:先 native(有 pick),再 browse(有两格原语),都没有=`none`。 */\nexport function pickerModeOf(raw) {\n if (hasPickFn(raw))\n return 'native';\n return isBrowseFace(raw) ? 'browse' : 'none';\n}\n/** 平台回执 → 三态(**永不抛**):成功回的是信封里的 `value` 不是路径;`ok:false` 是「供不了」不是「取消」。\n *\n * 出处:`@deepseek-ai/dsh-api-gateway/lib/client.js` 的 `invoke()`(失败不抛、成功也不回裸值),\n * 第一方消费方 `@deepseek-ai/dsh-client-ui-workspace/lib/client.js:99-103` 就照这个信封拆。\n * 裸串照收(老形状兜底),认不出的形状当「供不了」报出来,不当取消吞掉(#743 的教训)。 */\nexport function readPickAnswer(raw) {\n if (typeof raw === 'string')\n return raw.trim() === '' ? { kind: 'cancelled' } : { kind: 'picked', path: raw };\n const answer = (typeof raw === 'object' && raw !== null ? raw : {});\n if (answer.ok === true) {\n const value = answer.value;\n return typeof value === 'string' && value.trim() !== '' ? { kind: 'picked', path: value } : { kind: 'cancelled' };\n }\n const detail = answer.error?.message?.trim() ?? '';\n return { kind: 'unavailable', message: '打不开系统文件夹对话框' + (detail === '' ? '' : '(' + detail + ')') + ':请直接在框里填绝对路径。' };\n}\n/** 一段路径里最后那个分隔符的位置(Windows 上 `\\` 与 `/` 都算;POSIX 上 `\\` 是合法文件名字符,不算)。 */\nfunction lastSeparator(path) {\n const back = path.lastIndexOf('\\\\');\n const slash = path.lastIndexOf('/');\n // 只含 `\\` 的路径按 Windows 规则解;两者都有时取更靠后的那个。\n return back > slash ? back : slash;\n}\n/** 上一层:`'C:\\\\a\\\\b\\\\'` → `'C:\\\\a'`;已经是根则回自身;相对名(没有任何分隔符)也回自身。\n *\n * 尾分隔符先吃掉再切——否则「回上一级」会在原地打转。**盘根单独认**:`C:\\` 掐掉尾分隔符会剩成\n * `C:`,而 `C:` 是「当前盘」不是「上一层」,回它就会多跑一趟、还可能跨到别的盘上去。\n * UNC(`\\\\server\\share`)不特殊处理:切到头就是共享名本身,浏览停在共享根。 */\nexport function parentOf(path) {\n const trimmed = path.replace(/[\\\\/]+$/, '');\n if (trimmed === '')\n return path;\n const sep = path.includes('\\\\') ? '\\\\' : '/';\n if (/^[A-Za-z]:$/.test(trimmed))\n return trimmed + sep;\n const cut = lastSeparator(trimmed);\n if (cut === -1)\n return trimmed;\n if (cut === 0)\n return path.slice(0, 1);\n if (cut === 2 && /^[A-Za-z]:/.test(trimmed))\n return trimmed.slice(0, 2) + sep;\n return trimmed.slice(0, cut);\n}\n/** 把「当前路径 + 一段输入」拼成一个目标路径。 */\nexport function joinPath(directory, name) {\n const sep = directory.includes('\\\\') ? '\\\\' : '/';\n const base = directory.replace(/[\\\\/]+$/, '');\n if (base === '')\n return sep + name;\n return base + sep + name;\n}\n/** 一层列举里,哪些行可进:隐藏目录按开关过滤,其余照宿主给的顺序。 */\nexport function visibleEntries(listing, showHidden) {\n return showHidden ? listing.entries : listing.entries.filter((entry) => !entry.hidden);\n}\n/** 这一层有多少个被藏起来的目录(用来决定「显示隐藏目录」那个开关要不要出现在图上)。 */\nexport function hiddenCount(listing) {\n return listing.entries.filter((entry) => entry.hidden).length;\n}\n/** 拿一段文本按「最后一个分隔符」切成「目录部分 + 筛选词」。\n *\n * 没打过任何分隔符时目录部分=null(那段文字还不指任何目录,只当筛选词用)。\n * Windows 上 `/` 也当分隔符(宿主那边两种都收),POSIX 上不。 */\nexport function splitDraft(draft) {\n const cut = lastSeparator(draft);\n if (cut === -1)\n return { directory: null, filter: draft };\n return { directory: draft.slice(0, cut + 1), filter: draft.slice(cut + 1) };\n}\n/** 按筛选词过一层目录(大小写不敏感;空筛选词=全留)。 */\nexport function filterEntries(entries, filter) {\n const needle = filter.trim().toLowerCase();\n if (needle === '')\n return entries;\n return entries.filter((entry) => entry.name.toLowerCase().includes(needle));\n}\n/** 给界面用的一句话位置说明:优先显示相对 home 的说法,省得整条绝对路径占满一行。 */\nexport function locationLabel(listing) {\n const home = listing.home.replace(/[\\\\/]+$/, '');\n if (home !== '' && listing.path !== home && listing.path.startsWith(home)) {\n const tail = listing.path.slice(home.length).replace(/^[\\\\/]+/, '');\n if (tail !== '')\n return '~' + (listing.path.includes('\\\\') ? '\\\\' : '/') + tail;\n }\n return listing.path;\n}\n/** 新建文件夹的名字合不合法(单个路径段:不许分隔符、不许空、不许 `.`/`..`)。\n *\n * 与宿主 browse 后端的校验同口径——这里先拦一道,是为了在图上给得出人话,不是替代宿主校验。 */\nexport function validateFolderName(name) {\n const trimmed = name.trim();\n if (trimmed === '')\n return { ok: false, reason: '文件夹名不能为空。' };\n if (/[\\\\/]/.test(trimmed))\n return { ok: false, reason: '文件夹名里不能带路径分隔符。' };\n if (trimmed === '.' || trimmed === '..')\n return { ok: false, reason: '「.」与「..」不是文件夹名。' };\n return { ok: true };\n}\n//# sourceMappingURL=directory-browser-contract.js.map","/** 文件浏览器的操作半:把「进哪一层、这一层显示什么、选到了什么」算成状态,**IO 全部由调用方注入**。\n *\n * 分两层的原因:本件(操作)在 Node 里就能直测——喂一个假的取数面,就能把「进目录/回上一级/\n * 跳面包屑/筛选/新建文件夹/失败出人话」全跑一遍;视图那半(`directory-browser-ui.ts`)\n * 只负责把这份状态画成 DOM。两边都不认识宿主。\n *\n * 数据来源只有两格:`list(path?)` 与 `createDirectory(path, name)`(调用方注入的 `DirectoryBrowseFace`)。\n */\nimport { filterEntries, joinPath, parentOf, splitDraft, validateFolderName, visibleEntries, } from './directory-browser-contract.js';\n/** 建一个浏览器控制器。取数、回调、初值全部由调用方给。 */\nexport function createBrowseController(deps) {\n const label = deps.failureLabel ?? '浏览失败';\n const listeners = new Set();\n let state = {\n phase: 'idle',\n listing: null,\n failure: null,\n selected: null,\n showHidden: false,\n draft: deps.initialPath,\n filter: '',\n creating: null,\n notice: null,\n };\n /** 每次列举带一个序号:晚回来的旧回执直接丢掉,不许覆盖新一层。 */\n let generation = 0;\n const emit = (next) => {\n state = { ...state, ...next };\n for (const listener of [...listeners])\n listener(state);\n };\n const humanize = (cause) => {\n const raw = (typeof cause === 'object' && cause !== null ? cause : {});\n const message = typeof raw.message === 'string' && raw.message.trim() !== '' ? raw.message.trim() : String(cause);\n const code = typeof raw.code === 'string' && raw.code !== '' ? raw.code : 'browse-failed';\n return { code, message: `${label}:${message}` };\n };\n const load = async (path) => {\n const mine = ++generation;\n emit({ phase: 'loading', failure: null, notice: null });\n try {\n const listing = path === null ? await deps.face.list() : await deps.face.list(path);\n if (mine !== generation)\n return;\n emit({\n phase: 'ready',\n listing,\n failure: null,\n selected: null,\n draft: listing.path,\n filter: '',\n });\n }\n catch (cause) {\n if (mine !== generation)\n return;\n emit({ phase: 'failed', listing: null, failure: humanize(cause), selected: null });\n }\n };\n /** 草稿指到的那一层已经列举过时,按末段筛当前这一层的行;否则不过滤。\n *\n * 两边都先掐掉尾分隔符再比:草稿那边切开后天然带尾分隔符(`C:\\a\\` + `b`),\n * 而这一层的 `path` 由宿主给、不带尾分隔符。 */\n const applyDraft = (draft) => {\n const { directory, filter } = splitDraft(draft);\n const listing = state.listing;\n const sameLevel = listing !== null && directory !== null && normalize(directory) === normalize(listing.path);\n emit({ draft, filter: sameLevel ? filter : '' });\n };\n const normalize = (path) => path.replace(/[\\\\/]+$/, '');\n return {\n getState: () => state,\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n open: () => load(null),\n enter: (path) => load(path),\n async up() {\n const listing = state.listing;\n if (listing === null)\n return;\n const parent = parentOf(listing.path);\n if (parent === listing.path)\n return;\n await load(parent);\n },\n pick() {\n const target = state.selected ?? state.listing?.path ?? null;\n if (target === null)\n return;\n deps.onPicked(target);\n },\n cancel() {\n deps.onClose();\n },\n close() {\n deps.onClose();\n },\n select(path) {\n emit({ selected: state.selected === path ? null : path, notice: null });\n },\n toggleHidden() {\n emit({ showHidden: !state.showHidden });\n },\n setDraft(draft) {\n applyDraft(draft);\n },\n async commitDraft() {\n const trimmed = state.draft.trim();\n if (trimmed === '')\n return;\n await load(trimmed);\n },\n setCreating(name) {\n emit({ creating: name, notice: null });\n },\n async createFolder(name) {\n const checked = validateFolderName(name);\n if (!checked.ok) {\n emit({ notice: checked.reason });\n return;\n }\n const base = state.selected ?? state.listing?.path ?? null;\n if (base === null) {\n emit({ notice: '还没有打开任何目录,无法新建文件夹。' });\n return;\n }\n try {\n const created = await deps.face.createDirectory(base, name.trim());\n emit({ creating: null, notice: null });\n await load(created);\n emit({ selected: created });\n }\n catch (cause) {\n const failure = humanize(cause);\n emit({ notice: failure.message, creating: null });\n }\n },\n };\n}\n/** 图上这一层要画的行(按隐藏开关与草稿末段过滤之后的)。 */\nexport function rowsOf(state) {\n if (state.listing === null)\n return [];\n return filterEntries(visibleEntries(state.listing, state.showHidden), state.filter);\n}\n/** 图上这一层能不能回上一级(根目录不能)。 */\nexport function canGoUp(state) {\n if (state.listing === null)\n return false;\n return parentOf(state.listing.path) !== state.listing.path;\n}\n/** 「打开」这颗按钮的目标:选中行优先,没选中就是当前层。 */\nexport function targetOf(state) {\n return state.selected ?? state.listing?.path ?? null;\n}\n/** 新建文件夹那一行要展示的位置(\"在 <这里> 下新建\")。 */\nexport function createBaseOf(state) {\n return state.selected ?? state.listing?.path ?? null;\n}\n/** 一段路径拼成可读的一句话(给用例与调试用;图上不直接显示)。 */\nexport function describe(state) {\n if (state.phase === 'failed')\n return `${state.failure?.code ?? 'browse-failed'} @ ${state.draft}`;\n if (state.listing === null)\n return `${state.phase}`;\n return `${state.listing.path}(${rowsOf(state).length} 行)`;\n}\n/** 一行子目录的完整路径(图上点行时用)。 */\nexport function entryPath(directory, entry) {\n return entry.path === '' ? joinPath(directory, entry.name) : entry.path;\n}\n/** 建一份「目录行接线」:给一个取数面与初值,回一组动作。\n *\n * 只做接线:把控制器算出来的状态与回调打包好,不取数、不画图、不认宿主。 */\nexport function createDirectoryRowBrowser(deps) {\n const controller = createBrowseController({\n face: deps.face,\n initialPath: deps.initialPath,\n onPicked: deps.onPicked,\n onClose: () => deps.onClosed?.(),\n ...(deps.failureLabel === undefined ? {} : { failureLabel: deps.failureLabel }),\n });\n return {\n open: () => controller.open(),\n state: () => controller.getState(),\n summary: () => describe(controller.getState()),\n actions: {\n onPick: () => controller.pick(),\n onClose: () => controller.cancel(),\n onEnter: (path) => void controller.enter(path),\n onUp: () => void controller.up(),\n onSelect: (path) => controller.select(path),\n onToggleHidden: () => controller.toggleHidden(),\n onDraft: (draft) => controller.setDraft(draft),\n onCommitDraft: () => void controller.commitDraft(),\n onCreate: (name) => void controller.createFolder(name),\n onCreatingChange: (name) => controller.setCreating(name),\n },\n };\n}\n//# sourceMappingURL=directory-browser-state.js.map","/** 文件浏览器的视图半:把 `BrowseState` 画成一张对话框。**不认识宿主**,也不自己做 IO。\n *\n * 手写 `React.createElement`(本包 client 束禁 JSX,见 `tsdown.config.ts` 的既成口径)。\n * 只读状态、只回调;进哪一层、选到什么由操作半(`directory-browser-state.ts`)算。\n */\nimport * as React from 'react';\nimport { rowsOf, targetOf } from './directory-browser-state.js';\nimport { hiddenCount, locationLabel } from './directory-browser-contract.js';\nconst EMPTY_LISTING = { path: '', home: '', crumbs: [], entries: [], truncated: false };\nconst S = {\n scrim: {\n position: 'fixed',\n inset: 0,\n background: 'rgba(0,0,0,0.35)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 9999,\n },\n dialog: {\n width: 680,\n maxWidth: '92vw',\n height: 500,\n maxHeight: '86vh',\n display: 'flex',\n flexDirection: 'column',\n background: 'var(--dsw-alias-bg-layer-1, #1f1f23)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 10,\n boxShadow: '0 12px 32px rgba(0,0,0,0.35)',\n overflow: 'hidden',\n },\n head: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '10px 12px',\n borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))',\n },\n title: { fontSize: 14, fontWeight: 700 },\n headTail: { marginLeft: 'auto', fontSize: 12, opacity: 0.72, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },\n crumbs: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 2, padding: '8px 12px 0', fontSize: 12 },\n chromeButton: {\n background: 'transparent',\n border: 'none',\n color: 'var(--dsw-alias-label-primary, inherit)',\n cursor: 'pointer',\n padding: '2px 4px',\n fontSize: 12,\n opacity: 0.85,\n },\n crumbSep: { opacity: 0.45, fontSize: 12 },\n pathRow: { display: 'flex', gap: 6, padding: '8px 12px' },\n input: {\n flex: 1,\n minWidth: 0,\n fontSize: 12,\n padding: '5px 8px',\n background: 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.12))',\n color: 'inherit',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 6,\n },\n button: {\n fontSize: 12,\n padding: '5px 10px',\n cursor: 'pointer',\n background: 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.12))',\n color: 'inherit',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 6,\n },\n buttonOff: { opacity: 0.45, cursor: 'default' },\n list: {\n flex: 1,\n minHeight: 0,\n overflowY: 'auto',\n margin: '0 12px',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))',\n borderRadius: 6,\n },\n row: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n width: '100%',\n padding: '6px 10px',\n textAlign: 'left',\n background: 'transparent',\n color: 'inherit',\n border: 'none',\n borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.12))',\n cursor: 'pointer',\n fontSize: 13,\n },\n rowSelected: { background: 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.18))' },\n empty: { padding: '14px 12px', fontSize: 12, opacity: 0.7 },\n footNote: { padding: '6px 12px 0', fontSize: 12, minHeight: 20 },\n foot: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '10px 12px',\n borderTop: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))',\n },\n target: { marginRight: 'auto', fontSize: 12, opacity: 0.8, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },\n error: { color: 'var(--dsw-alias-label-error, #e66)' },\n};\nfunction crumbRow(state, labels, onEnter) {\n const listing = state.listing;\n if (listing === null || listing.crumbs.length === 0)\n return null;\n const parts = [];\n listing.crumbs.forEach((crumb, index) => {\n if (index > 0)\n parts.push(React.createElement('span', { key: 'sep-' + crumb.path, style: S.crumbSep }, '›'));\n const isCurrent = crumb.path === listing.path;\n parts.push(React.createElement('button', {\n key: crumb.path,\n type: 'button',\n style: { ...S.chromeButton, fontWeight: isCurrent ? 700 : 400 },\n title: crumb.path,\n onClick: () => onEnter(crumb.path),\n }, index === 0 ? labels.up : crumb.name));\n });\n return React.createElement('div', { style: S.crumbs }, parts);\n}\nfunction entryRow(entry, selected, labels, onToggleSelect, onEnter) {\n return React.createElement('div', { key: entry.path, style: { ...S.row, padding: 0, ...(selected ? S.rowSelected : {}) } }, React.createElement('button', { type: 'button', style: { ...S.row, flex: 1, border: 'none' }, onClick: () => onEnter(entry.path) }, '📁 ' + entry.name), React.createElement('button', {\n type: 'button',\n style: { ...S.button, marginRight: 8, padding: '2px 8px' },\n 'aria-pressed': selected,\n title: selected ? labels.selected : labels.select,\n onClick: () => onToggleSelect(entry.path),\n }, selected ? '✓' : labels.select));\n}\nfunction createRow(labels, onName, onCancel, onCreate, creatingName) {\n if (creatingName === null)\n return null;\n return React.createElement('div', { style: { ...S.pathRow, borderTop: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))' } }, React.createElement('input', {\n style: S.input,\n value: creatingName,\n placeholder: labels.newFolder,\n 'aria-label': labels.newFolder,\n onChange: (event) => onName(event.currentTarget.value),\n onKeyDown: (event) => {\n if (event.key === 'Enter')\n onCreate(creatingName);\n if (event.key === 'Escape')\n onCancel();\n },\n }), React.createElement('button', { type: 'button', style: S.button, onClick: () => onCreate(creatingName) }, labels.createConfirm), React.createElement('button', { type: 'button', style: S.button, onClick: onCancel }, labels.createCancel));\n}\n/** 画一张文件浏览器对话框。`open=false` 时返回 null(本件不挂 portal,由调用方决定挂在哪)。 */\nexport function DirectoryBrowser(props) {\n const { open, state, labels } = props;\n if (!open)\n return null;\n const rows = rowsOf(state);\n const target = targetOf(state);\n const listing = state.listing ?? EMPTY_LISTING;\n const hidden = hiddenCount(listing);\n const body = [];\n body.push(crumbRow(state, labels, props.onEnter));\n body.push(React.createElement('div', { style: S.pathRow }, React.createElement('input', {\n style: S.input,\n value: state.draft,\n placeholder: labels.pathPlaceholder,\n 'aria-label': labels.pathPlaceholder,\n onChange: (event) => props.onDraft(event.currentTarget.value),\n onKeyDown: (event) => {\n if (event.key === 'Enter')\n props.onCommitDraft();\n },\n }), React.createElement('button', { type: 'button', style: S.button, onClick: props.onUp }, labels.up), React.createElement('button', { type: 'button', style: S.button, onClick: props.onCommitDraft }, labels.go)));\n body.push(React.createElement('div', { style: S.list, role: 'listbox', 'aria-label': labels.title }, rows.length === 0\n ? React.createElement('div', { style: S.empty }, state.phase === 'loading' ? labels.loading : labels.empty)\n : rows.map((entry) => entryRow(entry, state.selected === entry.path, labels, props.onSelect, props.onEnter))));\n body.push(createRow(labels, props.onDraft, () => props.onCreatingChange(null), props.onCreate, state.creating));\n body.push(React.createElement('div', { style: S.footNote }, hidden > 0\n ? React.createElement('label', { style: { fontSize: 12, cursor: 'pointer', marginRight: 10 } }, React.createElement('input', { type: 'checkbox', checked: state.showHidden, onChange: props.onToggleHidden }), ' ' + labels.showHidden(hidden))\n : null, state.creating === null\n ? React.createElement('button', { type: 'button', style: { ...S.chromeButton, textDecoration: 'underline' }, onClick: () => props.onCreatingChange('') }, labels.newFolder)\n : null, state.notice !== null ? React.createElement('div', { style: S.error }, state.notice) : null, state.phase === 'failed' && state.failure !== null\n ? React.createElement('div', { style: S.error }, state.failure.message)\n : null));\n body.push(React.createElement('div', { style: S.foot }, React.createElement('span', { style: S.target, title: target ?? '' }, target === null ? '' : labels.willPick + target), React.createElement('button', { type: 'button', style: { ...S.button, ...(target === null ? S.buttonOff : {}) }, onClick: props.onPick }, labels.open), React.createElement('button', { type: 'button', style: S.button, onClick: props.onClose }, labels.cancel)));\n return React.createElement('div', {\n style: S.scrim,\n role: 'presentation',\n onMouseDown: (event) => {\n if (event.target === event.currentTarget)\n props.onClose();\n },\n }, React.createElement('div', { style: S.dialog, role: 'dialog', 'aria-modal': true, 'aria-label': labels.title }, React.createElement('div', { style: S.head }, React.createElement('span', { style: S.title }, labels.title), React.createElement('span', { style: S.headTail, title: listing.path }, state.listing === null ? '' : locationLabel(listing)), React.createElement('button', { type: 'button', style: S.button, 'aria-label': labels.close, title: labels.close, onClick: props.onClose }, '✕')), body));\n}\n/** 目录行接线的 React 半边:把操作半那组动作直接喂给组件。\n *\n * 操作半(`directory-browser-state.ts` 的 `createDirectoryRowBrowser`)不碰 React,\n * 这里只把它的 `actions` 摊进 props —— 于是「进哪一层」那套在 Node 里可测,这里只做接线。 */\nexport function DirectoryBrowserFromRow(props) {\n return DirectoryBrowser({\n open: props.open,\n state: props.row.state(),\n labels: props.labels,\n ...props.row.actions,\n });\n}\nexport { createBrowseController, createDirectoryRowBrowser, describe, rowsOf, targetOf } from './directory-browser-state.js';\n//# sourceMappingURL=directory-browser-ui.js.map","/** dsh-bill-ilife client 适配器(六边形:port=contract,adapter=本文件)。\n *\n * 产物经 tsdown 打成 loader 工厂包(browser/CJS,见 tsdown.config.ts)。\n * 本文件传递闭包:禁 node、禁 DOM 直写(document/window/process)。\n * 注册一处:**技能设置页** → 爱生活页签槽(总管声明的 children,各技能自研自家配置页)。\n * 样式:React 内联 style + DSH 主题别名(var(--dsw-alias-*),带回退),无外部样式表、\n * 无 <style> 注入、无类名冲突;只用 dsh-ctx 镜像内成员(slots.inject/register、\n * connection.rpc.call 经闭包现取);取数只经 connection.rpc.call 进 host 通道;\n * 缺席/错误纯条件渲染,不返空冒充。组件 React.createElement 手写,不引入 JSX。\n *\n * #677:技能设置页从「只读设置行」换成**真配置页**——行表来自 settings.ts,\n * 取值与保存经宿主三个端点(`config.get`/`config.save`/`config.reset`),\n * 宿主再 spawn 技能 CLI 的 `bill.config.*` 三个 key。本文件仍禁 node:不读盘、不算默认值。\n * 照 CONTEXT.md,「技能设置页」只配置、不干活:本页没有查账、记一笔之类的入口。\n */\nimport * as React from 'react';\nimport { RPC_CHANNEL, RPC_ENDPOINT_CONFIG_GET, RPC_ENDPOINT_CONFIG_SAVE, RPC_ENDPOINT_CONFIG_RESET, isRpcResult } from './contract.js';\nimport type { ConfigSurfaceReply } from './contract.js';\nimport { PLUGIN, SLOT_ORDER, SLOT_TITLE } from './slot.js';\nimport { CONFIG_ITEMS, COMMON_ITEM_COUNT, ADVANCED_GROUP_TITLE, ADVANCED_GROUP_NOTE, readPath, writePath } from './settings.js';\nimport type { ConfigItem } from './settings.js';\nimport { REMOTE_DIRECTORY_PICKER } from './dsh-ctx.js';\nimport {\n pickerModeOf as sharedPickerModeOf,\n readPickAnswer as sharedReadPickAnswer,\n createDirectoryRowBrowser,\n} from 'dsh-life-pack/directory-browser';\nimport { DirectoryBrowserFromRow } from 'dsh-life-pack/directory-browser-ui';\nimport type { DirectoryBrowseFace, PickerMode, DirectoryRowBrowser, DirectoryRowEntry } from 'dsh-life-pack/directory-browser';\nimport type { ClientCtx, RpcCallFace, RpcCallResult, DirectoryPickerAnswer, DirectoryPickerFace } from './dsh-ctx.js';\n\n/** client 短名声明:只有这两个(#736 的目录选择走**可选查找**,不写进来——\n * 写进来=硬依赖,提供方缺席时整包被停靠,设置页会跟着装不上;见 cookbook §13)。 */\nexport const inject: readonly string[] = ['slots', 'connection'];\n\n/** 调用口取用器:每次取数时现取(connection 后到也不永久缺席)。 */\nexport type GetCall = () => unknown;\n\n/** 面板视觉(内联 style;颜色走 DSH 主题别名,深浅主题自适应,写死值只做回退)。\n *\n * **六家逐项同形**(备忘·卡路里·记账·作息·居家·大厨):同一项在这六份里逐字相同,\n * 改任一条要六家一起改,锁见 `test/panel-copy-743.test.mjs` 第 ⑤ 条。 */\nconst S = {\n card: {\n padding: '12px 14px',\n borderRadius: 10,\n border: '1px solid var(--dsw-alias-border, rgba(128,128,128,.35))',\n background: 'var(--dsw-alias-bg-base, transparent)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n lineHeight: 1.6,\n } as React.CSSProperties,\n title: { fontSize: '1.08em', fontWeight: 700, marginBottom: 8 } as React.CSSProperties,\n muted: { color: 'var(--dsw-alias-label-secondary, #9a9a9a)', fontSize: '0.92em'} as React.CSSProperties,\n error: { marginTop: 8, color: 'var(--dsw-alias-label-error, #b3261e)', fontSize: '1em', whiteSpace: 'pre-wrap' } as React.CSSProperties,\n okText: { marginTop: 8, color: 'var(--dsw-alias-state-success-primary, #12805c)', fontSize: '0.96em'} as React.CSSProperties,\n rows: { marginTop: 8, borderTop: '1px solid var(--dsw-alias-border, rgba(128,128,128,.25))', paddingTop: 8 } as React.CSSProperties,\n info: { color: 'var(--dsw-alias-label-secondary, #9a9a9a)', fontSize: '0.92em', overflowWrap: 'anywhere' } as React.CSSProperties,\n row: { marginBottom: 10 } as React.CSSProperties,\n label: { fontWeight: 600 } as React.CSSProperties,\n hint: { color: 'var(--dsw-alias-label-secondary, #9a9a9a)', fontSize: '0.92em', marginBottom: 4 } as React.CSSProperties,\n input: {\n width: '100%',\n boxSizing: 'border-box',\n padding: '4px 8px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-border, rgba(128,128,128,.45))',\n background: 'var(--dsw-alias-bg-base, transparent)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n } as React.CSSProperties,\n pickRow: { display: 'flex', gap: 6, alignItems: 'center' } as React.CSSProperties,\n btnPick: {\n flex: '0 0 auto',\n padding: '4px 10px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-border, rgba(128,128,128,.45))',\n background: 'var(--dsw-alias-bg-base, transparent)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n whiteSpace: 'nowrap',\n cursor: 'pointer',\n } as React.CSSProperties,\n bar: { display: 'flex', gap: 8, marginTop: 12, flexWrap: 'wrap' } as React.CSSProperties,\n btn: {\n padding: '4px 12px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-border, rgba(128,128,128,.45))',\n background: 'var(--dsw-alias-bg-base, transparent)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n cursor: 'pointer',\n } as React.CSSProperties,\n btnPrimary: {\n padding: '4px 12px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-brand-primary, #2f6fed)',\n background: 'var(--dsw-alias-brand-primary, #2f6fed)',\n color: '#fff',\n cursor: 'pointer',\n } as React.CSSProperties,\n advanced: { marginTop: 12 } as React.CSSProperties,\n summary: { cursor: 'pointer', fontWeight: 600 } as React.CSSProperties,\n};\n\n/** 请求超时毫秒:与 host 侧 SPAWN_TIMEOUT_MS 同级,UI 永不无限转圈。 */\nconst READ_TIMEOUT_MS = 20_000 as const;\n\n/** 有界等待:借平台 `AbortSignal.timeout` 的 abort 事件做**一次**超时拒绝。\n *\n * 为什么不自建定时器:本包的冻结边界是「无数据轮询」(`test/plugin-p10-boundaries.test.mjs`:\n * 单品 src 里出现计时器就要有清理 + 次数上限 + 例外依据三件套),而设置页等的是一次 RPC 回执,\n * 不是轮询;用平台自带的一次性超时既守住边界的本意,也照样把「不转圈」落到实处。\n * 同一个 signal 也交给 transport,双保险:它搭理 signal 就早收工,不理也有这一道兜底。\n */\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n const signal = AbortSignal.timeout(ms);\n const limit = new Promise<never>((_, reject) => {\n signal.addEventListener(\n 'abort',\n () => {\n const e = new Error(`配置超时(${Math.round(ms / 1000)}s):宿主未回,先查宿主日志`);\n e.name = 'TimeoutError';\n reject(e);\n },\n { once: true },\n );\n });\n return Promise.race([promise, limit]);\n}\n\ntype ConfigState =\n | { readonly kind: 'loading' }\n | { readonly kind: 'ready'; readonly surface: ConfigSurfaceReply }\n | { readonly kind: 'failed'; readonly message: string };\n\n/** 配置面报错 → 人话指引。\n *\n * 报文由技能侧给出(base-link-core 的 `ConfigError` 已带行号与文件名,技能 `cli/config.ts`\n * 原样交出,插件 bridge 原样带上),所以这里**不重写报文**,只按报错类补一句「接下来怎么办」。\n */\nexport function humanizeConfigFailure(code: string, message: string): string {\n const text = message.trim().length > 0 ? message.trim() : `(${code},宿主未给报文)`;\n if (/解析|YAML|parse/i.test(text)) return `${text}\\n改回「键: 值」的写法,或点「重置为默认」。`;\n if (/不认识|未知键|UNKNOWN_KEY/i.test(text)) return `${text}\\n删掉页面上没有的行,或点「重置为默认」。`;\n if (/类型|TYPE_MISMATCH/i.test(text)) return `${text}\\n按本页的控件形状填,或点「重置为默认」。`;\n if (/缺席|missing-cli/.test(text)) return `${text}\\n技能出口没找到:先确认技能包已装好,再点「重新读取」。`;\n return `${text}\\n改不动就点「重置为默认」,或照上面那条路径手工改配置文件。`;\n}\n\ntype ConfigOutcome =\n | { readonly ok: true; readonly surface: ConfigSurfaceReply }\n | { readonly ok: false; readonly message: string };\n\n/** 一次配置 RPC(永不抛,失败落字;UI 因此不转圈、不返空)。 */\nasync function configRpc(call: unknown, method: string, payload: Record<string, unknown>): Promise<ConfigOutcome> {\n if (typeof call !== 'function') return { ok: false, message: '宿主连接缺席:connection.rpc.call 不可用' };\n try {\n const raw: unknown = await withTimeout(\n (call as RpcCallFace)('/api', RPC_CHANNEL.slice(1), { method, payload }, AbortSignal.timeout(READ_TIMEOUT_MS)),\n READ_TIMEOUT_MS,\n );\n if (!isRpcResult(raw)) return { ok: false, message: '回执信封异常(非 ok 信封)' };\n const res = raw as RpcCallResult;\n if (!res.ok) return { ok: false, message: humanizeConfigFailure(res.error?.code ?? 'unknown', res.error?.message ?? '') };\n return { ok: true, surface: res.value as ConfigSurfaceReply };\n } catch (e) {\n if (e instanceof Error && e.name === 'AbortError') return { ok: false, message: `配置读取超时(${Math.round(READ_TIMEOUT_MS / 1000)}s):宿主未回` };\n if (e instanceof Error && e.name === 'TimeoutError') return { ok: false, message: e.message };\n return { ok: false, message: `配置失败:${e instanceof Error ? e.message : String(e)}` };\n }\n}\n\n/** 读设置页整面。 */\nexport function fetchConfigSurface(call: unknown): Promise<ConfigOutcome> {\n return configRpc(call, RPC_ENDPOINT_CONFIG_GET, {});\n}\n\n/** 保存(回全表;技能侧做组内合并)。 */\nexport function saveConfigSurface(call: unknown, values: Record<string, unknown>): Promise<ConfigOutcome> {\n return configRpc(call, RPC_ENDPOINT_CONFIG_SAVE, { values });\n}\n\n/** 重置为默认(技能侧先落 .bak;写回执不是整面,由组件写完重读一份)。 */\nexport function resetConfigSurface(call: unknown): Promise<ConfigOutcome> {\n return configRpc(call, RPC_ENDPOINT_CONFIG_RESET, {});\n}\n\n/** 把配置取值铺成「行键 → 输入框文本」(页面表单态;值缺项即空串,不返空留白)。\n *\n * `prefill` 是**落点回执**(配置面那几格解析出来的绝对路径):标了 `prefillFrom` 的行在取值空着时\n * 直接显示那条绝对路径——用户不必自己拼路径(#743),显示的就是技能真会用的那个目录(逐字相同)。 */\nexport function toDraft(\n values: Record<string, unknown>,\n prefill: { readonly dataDir?: string } = {},\n): Record<string, string> {\n const draft: Record<string, string> = {};\n for (const item of CONFIG_ITEMS) {\n const v = readPath(values, item.key);\n const raw = v === undefined || v === null ? '' : String(v);\n const fallback = item.prefillFrom === undefined ? undefined : prefill[item.prefillFrom];\n draft[item.key] = raw === '' && typeof fallback === 'string' && fallback !== '' ? fallback : raw;\n }\n return draft;\n}\n\n/** 表单态 → 配置取值(按控件种类还原类型;空串对文本项照收,语义由「按默认落点」承担)。 */\nexport function fromDraft(draft: Record<string, string>): Record<string, unknown> {\n const values: Record<string, unknown> = {};\n for (const item of CONFIG_ITEMS) {\n const raw = draft[item.key] ?? '';\n if (item.control === 'number') {\n const n = Number(raw);\n writePath(values, item.key, Number.isFinite(n) ? n : 0);\n } else if (item.control === 'switch') {\n writePath(values, item.key, raw === 'true');\n } else {\n writePath(values, item.key, raw);\n }\n }\n return values;\n}\n\n/** 目录选择的三种结果(注入 picker 的纯归一:选中/取消/这条路供不了)。 */\nexport type PickOutcome =\n | { readonly kind: 'picked'; readonly path: string }\n | { readonly kind: 'cancelled' }\n | { readonly kind: 'unavailable'; readonly message: string };\n\n/** 形状守卫:拿到的那个东西像不像目录选择命名空间(本包只认 `pick` 是函数)。 */\nexport function isDirectoryPicker(raw: unknown): raw is DirectoryPickerFace {\n if (typeof raw !== 'object' || raw === null) return false;\n return typeof (raw as { pick?: unknown }).pick === 'function';\n}\n\n/** 现取目录选择命名空间:**软依赖**,拿不到回 null(页面据此不出入口)。\n *\n * 为什么不用 `inject` 声明:见 cookbook §13——写进声明=硬依赖,提供方缺席时整包被停靠,\n * 设置页会跟着装不上。这里按平台给的「可选查找」办:缺席只是没有入口。\n * 守卫拒绝或提供方中途卸载会抛,一律当「没有」,绝不把设置页带下来。 */\nexport function resolveDirectoryPicker(getService: unknown): DirectoryPickerFace | null {\n if (typeof getService !== 'function') return null;\n try {\n const raw = (getService as (name: string) => unknown)(REMOTE_DIRECTORY_PICKER);\n return isDirectoryPicker(raw) ? raw : null;\n } catch {\n return null;\n }\n}\n\n/** 平台回执/入口三态:**已经收进共用件**(票 #744),这里只同名转出。 */\nexport const readPickAnswer = sharedReadPickAnswer;\nexport const pickerModeOf = sharedPickerModeOf;\n\n/** 唤起一次系统文件夹选择器并归一结果(**永不抛**)。\n *\n * 为什么只留这一格没进共用件:共用件那份要按子路径 \\\require\\ 进来,而在本仓的用例加载器里\n * 取不到它的具名导出(那条子路径现在验证的是「六家束里能 require」)。这一格只有五行,\n * 且与共用件逐字同形;等六家全部接通、加载器换成真 require 之后,可一并收口。 */\nexport async function pickDirectory(picker: DirectoryPickerFace): Promise<PickOutcome> {\n try {\n return readPickAnswer(await picker.pick());\n } catch (e) {\n const reason = e instanceof Error ? e.message : String(e);\n return { kind: 'unavailable', message: '打不开系统文件夹对话框(' + reason + '):请直接在框里填绝对路径。' };\n }\n}\n\n\n/** 「选择文件夹」按钮的真装配函数(目录行渲染出来的按钮,onClick 就是它)。\n *\n * 回 Promise 是为了本地可判(用例直接 await);接进 React 时调用方 `void` 掉。 */\nexport function createBrowseHandler(deps: {\n readonly picker: DirectoryPickerFace;\n readonly onChange: (key: string, next: string) => void;\n readonly onUnavailable: (message: string) => void;\n}): (key: string) => Promise<void> {\n return async (key: string) => {\n const outcome = await pickDirectory(deps.picker);\n if (outcome.kind === 'picked') deps.onChange(key, outcome.path);\n else if (outcome.kind === 'unavailable') deps.onUnavailable(outcome.message);\n };\n}\n\n/** 一行输入(四种控件对齐受限 YAML 子集:文本/数字/布尔/目录)。\n *\n * 目录档=文本框 + 一枚唤起系统文件夹选择器的按钮;`onBrowse` 缺席(命名空间拿不到、\n * 或这条路已被拒)时不画按钮,文本框照旧——那是该缝自己的契约(供不了就收起入口,不是失败)。 */\nexport function Row(props: {\n readonly item: ConfigItem;\n readonly value: string;\n readonly disabled: boolean;\n readonly onChange: (key: string, next: string) => void;\n /** 目录行才有:入口三态。`native`=画「选择文件夹…」;`browse`=画「浏览…」;`none`/缺席=不画按钮。 */\n readonly browser?: DirectoryRowEntry | null | undefined;\n}): React.ReactElement {\n const { item } = props;\n const onChange = (e: React.ChangeEvent<HTMLInputElement>) => props.onChange(item.key, e.target.value);\n const control =\n item.control === 'switch'\n ? React.createElement('input', {\n type: 'checkbox',\n checked: props.value === 'true',\n disabled: props.disabled,\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => props.onChange(item.key, e.target.checked ? 'true' : 'false'),\n })\n : React.createElement('input', {\n style: S.input,\n type: item.control === 'number' ? 'number' : 'text',\n value: props.value,\n disabled: props.disabled,\n spellCheck: false,\n onChange,\n });\n /** 三态入口:供不了(`none`/缺席)就不画,不摆一个点了没反应的死按钮。 */\n const entry = props.browser ?? null;\n const browse =\n item.control === 'directory' && entry !== null && entry.mode !== 'none'\n ? React.createElement(\n 'button',\n {\n style: S.btnPick,\n type: 'button',\n disabled: props.disabled,\n // 回这枚 Promise 是有意的:React 不看 onClick 的返回值,而用例能直接 await 它。\n onClick: () => entry.onOpen(item.key),\n },\n entry.mode === 'native' ? '选择文件夹…' : '浏览…',\n )\n : null;\n return React.createElement(\n 'div',\n { style: S.row },\n React.createElement('div', { style: S.label }, item.title),\n React.createElement('div', { style: S.hint }, item.hint),\n browse === null ? control : React.createElement('div', { style: S.pickRow }, control, browse),\n );\n}\n\n/** 技能设置页:承载饼干记账自己的全部可配置项(只配置,不干活)。\n *\n * 三态:loading(一次 RPC 内)/ready(真表单)/failed(人话报错 + 指引,不返空、不转圈)。 */\nfunction BillConfig(props: {\n getCall: GetCall;\n getPicker: () => DirectoryPickerFace | null;\n /** 目录行入口三态(由共用的目录浏览器包提供)。 */\n mode: PickerMode;\n /** 开一行目录行的应用内浏览器;返回 undefined=这条路供不了(不画入口)。 */\n openBrowse: (key: string, path: string, onChange: (k: string, v: string) => void) => Promise<void> | undefined;\n /** 当前打开的浏览器(`null`=没开)。 */\n browseRow: DirectoryRowBrowser | null;\n}): React.ReactElement {\n const [state, setState] = React.useState<ConfigState>({ kind: 'loading' });\n const [pickerGone, setPickerGone] = React.useState(false);\n const [picking, setPicking] = React.useState(false);\n const [draft, setDraft] = React.useState<Record<string, string>>({});\n const [busy, setBusy] = React.useState(false);\n const [notice, setNotice] = React.useState<string | null>(null);\n const [error, setError] = React.useState<string | null>(null);\n\n const apply = React.useCallback((surface: ConfigSurfaceReply) => {\n setState({ kind: 'ready', surface });\n setDraft(toDraft(surface.values, surface));\n }, []);\n\n const load = React.useCallback(async () => {\n setNotice(null);\n setError(null);\n const r = await fetchConfigSurface(props.getCall());\n if (r.ok) apply(r.surface);\n else setState({ kind: 'failed', message: r.message });\n }, [apply, props.getCall]);\n\n React.useEffect(() => {\n let alive = true;\n void (async () => {\n const r = await fetchConfigSurface(props.getCall());\n if (!alive) return;\n if (r.ok) apply(r.surface);\n else setState({ kind: 'failed', message: r.message });\n })();\n return () => {\n alive = false;\n };\n }, [apply, props.getCall]);\n\n const onChange = React.useCallback((key: string, next: string) => {\n setDraft((prev) => ({ ...prev, [key]: next }));\n setNotice(null);\n setError(null);\n }, []);\n\n /** 三态入口的接线:`native`=系统对话框;`browse`=应用内浏览器(票 #744)。\n *\n * 这台机器上(Windows 桌面版)宿主组合里只有 browse 那一档、`pick` 会被拒\n * (根因见 `docs/agents/desktop-directory-picker-browse.md`),于是这一行改成\n * 「开应用内浏览器」——同一个用户动作,不依赖系统对话框也能选到目录。 */\n const { mode: entryMode, openBrowse, browseRow } = props;\n const picker = pickerGone ? null : props.getPicker();\n\n /** 目录行的入口动作:三态各一条路;都供不了就不给入口(`undefined` ⇒ Row 不画按钮)。 */\n const onOpenRow = React.useCallback(\n async (key: string): Promise<void> => {\n const current = pickerModeOf(picker);\n if (current === 'native') {\n // 系统对话框是模态的:等它回来的这段时间给一行字,免得看着像「点了没反应」(#743)。\n setPicking(true);\n setError(null);\n const outcome = await pickDirectory(picker as DirectoryPickerFace);\n setPicking(false);\n if (outcome.kind === 'picked') onChange(key, outcome.path);\n else if (outcome.kind === 'unavailable') {\n setError(outcome.message);\n setPickerGone(true);\n }\n return;\n }\n const opened = openBrowse(key, draft[key] ?? '', onChange);\n if (opened === undefined) return;\n setError(null);\n await opened;\n },\n [picker, onChange, openBrowse, draft],\n );\n\n /** 给 Row 的三态入口:`none` 时给 null(不画按钮),否则把 mode 与动作一起递下去。 */\n const rowEntry: DirectoryRowEntry | null =\n entryMode === 'none' ? null : { mode: entryMode, onOpen: onOpenRow };\n\n const surface = state.kind === 'ready' ? state.surface : null;\n const dirty = surface !== null && CONFIG_ITEMS.some((i) => (draft[i.key] ?? '') !== (toDraft(surface.values, surface)[i.key] ?? ''));\n\n /** 写完(保存/重置)之后重新读一份整面:写回执是 `{path, values}`、重置回执是 `{path, backupPath}`,\n * 都不是整面——拿写回执当整面用,页头那行「数据目录」会显示成 undefined(#743 实测)。 */\n const writeThenReload = React.useCallback(async (done: string) => {\n setBusy(true);\n setNotice(null);\n setError(null);\n const r = await fetchConfigSurface(props.getCall());\n if (r.ok) {\n apply(r.surface);\n setNotice(done);\n } else {\n setState({ kind: 'failed', message: r.message });\n }\n setBusy(false);\n }, [apply, props.getCall]);\n\n const onSave = React.useCallback(async () => {\n setBusy(true);\n setNotice(null);\n setError(null);\n const r = await saveConfigSurface(props.getCall(), fromDraft(draft));\n setBusy(false);\n if (r.ok) {\n apply(r.surface);\n setNotice('已保存,立即生效(不用重启宿主)');\n } else setError(r.message);\n }, [apply, draft, props.getCall]);\n\n const onReset = React.useCallback(async () => {\n setBusy(true);\n setNotice(null);\n setError(null);\n const r = await resetConfigSurface(props.getCall());\n setBusy(false);\n if (r.ok) await writeThenReload('已重置为默认(原配置已另存一份 .bak)');\n else setError(r.message);\n }, [props.getCall, writeThenReload]);\n\n const head = React.createElement(\n 'div',\n null,\n React.createElement('div', { style: S.title }, `${SLOT_TITLE} · 配置`),\n surface !== null\n ? React.createElement(\n 'div',\n null,\n React.createElement('div', { style: S.info }, `配置文件 ${surface.path}`),\n React.createElement('div', { style: S.info }, `数据目录 ${surface.dataDir}`),\n surface.created ? React.createElement('div', { style: S.muted }, '(配置文件刚按默认值生成)') : null,\n )\n : null,\n );\n\n if (state.kind === 'loading') {\n return React.createElement('div', { style: S.card }, head, React.createElement('div', { style: S.muted }, '配置读取中…'));\n }\n\n if (state.kind === 'failed') {\n return React.createElement(\n 'div',\n { style: S.card },\n React.createElement('div', { style: S.title }, `${SLOT_TITLE} · 配置`),\n React.createElement('div', { style: S.error }, state.message),\n React.createElement(\n 'div',\n { style: S.bar },\n React.createElement('button', { style: S.btn, type: 'button', onClick: () => void load() }, '重试'),\n React.createElement('button', { style: S.btn, type: 'button', onClick: () => void onReset() }, '重置为默认'),\n ),\n );\n }\n\n const common = CONFIG_ITEMS.slice(0, COMMON_ITEM_COUNT);\n const advanced = CONFIG_ITEMS.slice(COMMON_ITEM_COUNT);\n const renderRow = (item: ConfigItem) =>\n React.createElement(Row, {\n key: item.key,\n item,\n value: draft[item.key] ?? '',\n disabled: busy,\n onChange,\n browser: rowEntry,\n });\n\n return React.createElement(\n 'div',\n { style: S.card },\n head,\n React.createElement('div', { style: S.rows }, common.map(renderRow)),\n React.createElement(\n 'details',\n { style: S.advanced },\n React.createElement('summary', { style: S.summary }, ADVANCED_GROUP_TITLE),\n React.createElement('div', { style: S.muted }, ADVANCED_GROUP_NOTE),\n advanced.map(renderRow),\n ),\n React.createElement(\n 'div',\n { style: S.bar },\n React.createElement(\n 'button',\n { style: dirty ? S.btnPrimary : S.btn, type: 'button', disabled: busy || !dirty, onClick: () => void onSave() },\n busy ? '处理中…' : '保存',\n ),\n React.createElement('button', { style: S.btn, type: 'button', disabled: busy, onClick: () => void onReset() }, '重置为默认'),\n React.createElement('button', { style: S.btn, type: 'button', disabled: busy, onClick: () => void load() }, '重新读取'),\n ),\n picking ? React.createElement('div', { style: S.muted }, '已唤起系统文件夹对话框:选中后自动填上,取消则不动。') : null,\n browseRow !== null ? React.createElement('div', { style: S.muted }, '应用内文件夹浏览器已打开:选中后自动填上,取消则不动。') : null,\n browseRow !== null\n ? React.createElement(DirectoryBrowserFromRow, {\n open: true,\n row: browseRow,\n labels: {\n title: '选择文件夹',\n close: '关闭',\n up: '上一级',\n pathPlaceholder: '直接填绝对路径,回车即进入',\n go: '转到',\n showHidden: (n: number) => '显示隐藏目录(' + n + ')',\n empty: '这个目录里没有子目录。',\n loading: '正在读取…',\n newFolder: '新建文件夹',\n createConfirm: '创建',\n createCancel: '取消',\n select: '选',\n selected: '已选',\n open: '选定这个目录',\n cancel: '取消',\n willPick: '将选定:',\n },\n })\n : null,\n notice !== null ? React.createElement('div', { style: S.okText }, notice) : null,\n error !== null ? React.createElement('div', { style: S.error }, error) : null,\n );\n}\n\nexport function apply(ctx: ClientCtx): void {\n // 调用口取用器透传给组件(每次取数现取;非函数由组件判缺席)。\n const getCall: GetCall = () => ctx.connection?.rpc?.call ?? null;\n // #736 目录选择:**软依赖**,每次渲染现取(提供方后到/中途卸载都不留痕);\n // 拿不到就只是没有入口,设置页照常(不声明、不停靠,见 cookbook §13)。\n const getPicker = (): DirectoryPickerFace | null =>\n typeof ctx.get === 'function' ? resolveDirectoryPicker((name: string) => ctx.get?.(name)) : null;\n\n // 技能设置页 → 爱生活页签槽(总管声明;总管缺席时 inject 等待,不断链)。\n ctx.slots.inject('ilife.config-tab', () =>\n ctx.slots.register(\n {\n name: 'ilife.config-tab',\n id: PLUGIN,\n order: SLOT_ORDER,\n label: () => SLOT_TITLE,\n // #706:本包自己那条 RPC 通道交给总管(各家都写这一格),总管据此调配置体检——\n // 它因此不必在源码里写死任何一家的通道名(零单品依赖照旧成立)。\n channel: RPC_CHANNEL,\n },\n () => {\n // 入口三态:有 pick(系统对话框)/只有 list + createDirectory(应用内浏览)/都没有。\n // 都供不了就没有入口——这是该缝自己的契约(供不了收起入口,不是失败)。\n return function Slot(): React.ReactElement {\n const [browseRow, setBrowseRow] = React.useState<DirectoryRowBrowser | null>(null);\n const picker = resolveDirectoryPicker((name: string) => ctx.get?.(name));\n const openBrowse = (\n key: string,\n path: string,\n onRowChange: (k: string, v: string) => void,\n ): Promise<void> | undefined => {\n if (picker === null || pickerModeOf(picker) !== 'browse') return undefined;\n const row = createDirectoryRowBrowser({\n face: picker as unknown as DirectoryBrowseFace,\n initialPath: path,\n onPicked: (picked) => {\n onRowChange(key, picked);\n setBrowseRow(null);\n },\n onClosed: () => setBrowseRow(null),\n });\n setBrowseRow(row);\n return row.open();\n };\n return React.createElement(BillConfig, {\n getCall,\n getPicker,\n mode: pickerModeOf(picker),\n openBrowse,\n browseRow,\n });\n };\n },\n ),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["pickerModeOf","readPickAnswer","S","React","sharedReadPickAnswer","sharedPickerModeOf","React"],"sources":["../src/contract.ts","../src/slot.ts","../src/settings.ts","../src/dsh-ctx.ts","../../plugin-manager/dist/directory-browser-contract.js","../../plugin-manager/dist/directory-browser-state.js","../../plugin-manager/dist/directory-browser-ui.js","../src/client.ts"],"sourcesContent":["/** dsh-bill-ilife 端口(port):DSH 世界与饼干记账 hexagon 之间的契约。\n *\n * browser 安全:零 node 导入。host 适配器(注册 RPC 通道)与 client\n * 适配器(调用 RPC)共用同一份定义;单测与 loader 回路以此为准。\n * 通道命名遵守宿主约束:单段且不等于 /api(见 cookbook「RPC 通道」)。\n *\n * 照 #56 卡路里样板同形(`packages/plugin-calorie/src/contract.ts`):记账插件原先\n * **没有任何 RPC 通道**,本件是它第一份端口定义(#677)。\n */\nexport const RPC_CHANNEL = '/ilife-bill-ilife' as const;\nexport const RPC_ENDPOINT_READ = 'read' as const;\n\n/** #677 配置端点:读设置页整面、保存一份取值、重置为默认。同走一条通道,靠端点名分发。 */\nexport const RPC_ENDPOINT_CONFIG_GET = 'config.get' as const;\nexport const RPC_ENDPOINT_CONFIG_SAVE = 'config.save' as const;\nexport const RPC_ENDPOINT_CONFIG_RESET = 'config.reset' as const;\n/** #706 配置体检端点:只读一次,回一份报告(判据由技能侧出,本包只透传,不重写一个字)。\n *\n * 端点名与技能侧那条只读命令同名(`<技能>.config.check`);报告形状的唯一真相在技能侧\n * `packages/skill-<技能>/src/health.ts`,面板侧镜像在 `packages/plugin-manager/src/health-contract.ts`。 */\nexport const RPC_ENDPOINT_CONFIG_CHECK = 'config.check' as const;\n\n/** 读请求载荷:与技能 CLI 的 key/params 同形(见 skill-bill SKILL.md)。 */\nexport interface ReadPayload {\n readonly key: string;\n readonly params: Record<string, unknown>;\n}\n\n/** 保存载荷:一份键值(键路径 → 值)。缺项由技能侧按默认值补齐,故只收用户真改的那些。 */\nexport interface SavePayload {\n readonly values: Record<string, unknown>;\n}\n\nexport interface RpcError {\n readonly code: string;\n readonly message: string;\n readonly details: Record<string, unknown>;\n}\n\nexport type RpcResult =\n | { readonly ok: true; readonly value: unknown }\n | { readonly ok: false; readonly error: RpcError };\n\nexport function ok(value: unknown): RpcResult {\n return { ok: true, value };\n}\n\nexport function fail(code: string, message: string): RpcResult {\n return { ok: false, error: { code, message, details: {} } };\n}\n\n/** 读载荷校验:形状不对返回 null,由调用方按 bad-request 回绝(不抛)。 */\nexport function parseReadPayload(raw: unknown): ReadPayload | null {\n if (typeof raw !== 'object' || raw === null) return null;\n const { key, params } = raw as { key?: unknown; params?: unknown };\n if (typeof key !== 'string' || key.length === 0) return null;\n if (typeof params !== 'object' || params === null || Array.isArray(params)) return null;\n return { key, params: params as Record<string, unknown> };\n}\n\n/** 保存载荷校验:形状不对返回 null,由调用方按 bad-request 回绝(不抛)。 */\nexport function parseSavePayload(raw: unknown): SavePayload | null {\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) return null;\n const { values } = raw as { values?: unknown };\n if (typeof values !== 'object' || values === null || Array.isArray(values)) return null;\n return { values: values as Record<string, unknown> };\n}\n\n/** 配置面回执:形状与技能侧 `bill.config.read` 的 data 逐字段同形(唯一定义地在那边的 cli/config.ts)。 */\nexport interface ConfigSurfaceReply {\n /** 配置文件绝对路径。 */\n readonly path: string;\n /** 数据目录绝对路径。 */\n readonly dataDir: string;\n /** 本次是不是「文件不存在、按默认值落了一份」。 */\n readonly created: boolean;\n /** 当前取值(文件里的 ⊕ 缺项按默认值补)。 */\n readonly values: Record<string, unknown>;\n}\n\n/** 信封守卫:供 client 拆包前校验(port 拥有信封定义,守卫住 port)。 */\nexport function isRpcResult(raw: unknown): raw is RpcResult {\n if (typeof raw !== 'object' || raw === null) return false;\n const okFlag = (raw as { ok?: unknown }).ok;\n return okFlag === true || okFlag === false;\n}\n","/** dsh-bill-ilife 槽位描述(P10 脚手架,P3 #4 定案)。\n *\n * 写死槽位原生组件:id 命名空间 ilife:cookie,order 95。\n * 注册与打开只走 registerTab/openTab 的路径导航;子页后缀(如 ilife:cookie:*)\n * 只在主面板内导航,不占栏。缺席纯条件渲染,不轮询(本文件无定时器;\n * 有界重试归一由 base-render 拥有,单品不自建)。\n */\n\nexport const SKILL = 'bill' as const;\nexport const SLOT_ID = 'ilife:cookie' as const;\nexport const SLOT_ORDER = 70 as const;\nexport const SLOT_TITLE = '饼干记账' as const;\nexport const PLUGIN = 'dsh-bill-ilife' as const;\nexport const MANAGER_PLUGIN = 'dsh-life-pack' as const;\n\nexport interface SlotDescriptor {\n readonly skill: string;\n readonly slotId: string;\n readonly order: number;\n readonly title: string;\n readonly kind: 'page';\n}\n\nexport function slotDescriptor(): SlotDescriptor {\n return { skill: SKILL, slotId: SLOT_ID, order: SLOT_ORDER, title: SLOT_TITLE, kind: 'page' };\n}\n\n/** 写死原生组件描述(纯数据,无按需加载,无外嵌页)。 */\nexport const TAB_COMPONENT = { kind: 'native', name: 'BillPanel' } as const;\n\nexport interface TabsPort {\n hasTabs(): boolean;\n registerTab(entry: { readonly id: string; readonly title: string; readonly order: number; readonly single: boolean; readonly component: unknown }): () => void;\n openTab(seed: { readonly type: string; readonly path: string }, scope?: { readonly sessionId?: string }): void;\n}\n\n/** 自注册:幂等(调用方持 disposer),失败返回 false 由调用方决定重试。 */\nexport function registerSingle(port: TabsPort): (() => void) | null {\n if (!port.hasTabs()) return null;\n return port.registerTab({ id: SLOT_ID, title: SLOT_TITLE, order: SLOT_ORDER, single: true, component: TAB_COMPONENT });\n}\n\n/** 内容型打开:path seed,折叠面板自动展开。 */\nexport function openSingle(port: TabsPort, sessionId?: string): void {\n if (!port.hasTabs()) return;\n port.openTab({ type: SLOT_ID, path: SLOT_ID }, sessionId ? { sessionId } : undefined);\n}\n","/** dsh-bill-ilife 设置页元数据(#677:从只读脚手架换成真配置页)。\n *\n * 本文件是**设置页的行表**:一个可配置项一行,写清它在配置文件里的键、中文标题、\n * 分级(常用 / 高级)、控件种类与人话指引。它是页面渲染的唯一依据。\n *\n * **本文件必须零 node 依赖**:`tsconfig.client.json` 把它收进 client 侧编译,\n * 浏览器产物不许带 node 内建。取值一律由宿主经技能 CLI 的 `bill.config.read` 端点现取\n * (见 bridge.ts)。\n *\n * 口径:\n * · 配置表形状(键与默认值)的**唯一定义地**是技能侧 `packages/skill-bill/src/config.ts`\n * 的 `BILL_CONFIG_DEFAULTS`(本文件是它在页面上的投影,逐键对齐由\n * `test/config-surface-677.test.mjs` 用编译产物锁死);\n * · **空串=按默认落点**(老落点由技能各取用处算),所以「空」不是「没配」,\n * 而是「还走改造前那个位置」,老数据不会看起来丢了——行文案按这个语义写;\n * · `db.dir` 那一行的落点由配置面回执的 `dataDir` 带回(解析好的绝对路径),页面上把它**预填**进\n * 该行(`prefillFrom: 'dataDir'`),用户不必自己拼路径(#743)。\n *\n * 清单出处:「六家技能的路径类配置全量调查」记账 8 项去掉 1 项包内固定(包内页面模板目录)\n * =**上设置页候选 7 项**;其中「产物文件名主体」在源码里是两个值,故行数为 8。\n */\nexport const SETTINGS_OWNER = 'dsh-bill-ilife' as const;\nexport const SETTINGS_SLOT = 'ilife:cookie' as const;\n\n/** 配置文件主体名:落点 `~/.ilife/bill.yaml`(`ILIFE_CONFIG_DIR` 可整体接管)。 */\nexport const CONFIG_STEM = 'bill' as const;\n\n/** 分级:常用项直接画在页面上,其余进默认收起的「高级」组(#675 冻结口径)。 */\nexport type ConfigTier = 'common' | 'advanced';\n\n/** 控件种类:只有这四种(前三种与受限 YAML 子集的字符串/数字/布尔一一对应,没有数组;\n * `directory` 是**字符串那一档的页面形态**——取值仍是串,只是多一个唤起系统文件夹选择器的入口,见 #736)。 */\nexport type ConfigControl = 'text' | 'number' | 'switch' | 'directory';\n\nexport interface ConfigItem {\n /** 配置文件里的键路径,一层嵌套用 `.` 连接,例 `html.helpStem`。 */\n readonly key: string;\n /** 页面上那一行的中文标题。 */\n readonly title: string;\n readonly tier: ConfigTier;\n readonly control: ConfigControl;\n /** 一行人话:这一项管什么、留空会怎样(一到两句,不写开发期口径)。 */\n readonly hint: string;\n /** 目录行的落点来源:这一行取值空着时,页面上拿配置面回执里的哪一格当它显示的绝对路径。\n * 只有 `db.dir` 标它——回执里的 `dataDir` 就是技能真会用的那个目录,逐字相同。 */\n readonly prefillFrom?: 'dataDir';\n}\n\n/** 常用项:改了就影响「库与产物落在哪」,放页面上。 */\nconst COMMON: readonly ConfigItem[] = [\n {\n key: 'db.dir',\n title: '数据目录',\n tier: 'common',\n control: 'directory',\n // 留空=技能按默认目录落文件;页面上把解析出来的绝对路径直接填进这一行(见 client.ts 的 toDraft)。\n prefillFrom: 'dataDir',\n hint: '库文件与备份的根目录。留空=用默认目录。',\n },\n {\n key: 'db.name',\n title: '库文件名',\n tier: 'common',\n control: 'text',\n hint: '数据目录下的数据库文件名。改名=换库,旧数据不会被读入。',\n },\n {\n key: 'db.goals',\n title: '预算/账户文件名',\n tier: 'common',\n control: 'text',\n hint: '与库同目录的预算与账户数据文件名。改名后旧文件不会再被读取。',\n },\n {\n key: 'html.dir',\n title: 'HELP 产物目录名',\n tier: 'common',\n control: 'text',\n hint: '数据目录下存放 HELP 与速查表的子目录名。改名后新产物落新目录。',\n },\n];\n\n/** 高级项:默认收起。改错了多半只是自己撞上麻烦,不确定就别动。 */\nconst ADVANCED: readonly ConfigItem[] = [\n {\n key: 'backup.dir',\n title: '备份目录',\n tier: 'advanced',\n control: 'directory',\n hint: '备份与恢复的读写目录。留空=库目录下的 backups。',\n },\n {\n key: 'backup.stem',\n title: '备份名主体',\n tier: 'advanced',\n control: 'text',\n hint: '备份文件名的前缀,后面自动加时间戳。',\n },\n {\n key: 'html.helpStem',\n title: '产物名主体 · HELP',\n tier: 'advanced',\n control: 'text',\n hint: 'HELP 文件名前缀。',\n },\n {\n key: 'html.quickRefStem',\n title: '产物名主体 · 速查表',\n tier: 'advanced',\n control: 'text',\n hint: '速查表文件名前缀。',\n },\n];\n\n/** 设置页的行表,顺序即页面顺序。 */\nexport const CONFIG_ITEMS: readonly ConfigItem[] = [...COMMON, ...ADVANCED];\n\n/** 常用项行数(页面上直接画出来的那些)。 */\nexport const COMMON_ITEM_COUNT = COMMON.length;\n\n/** 高级组标题与副文案。 */\nexport const ADVANCED_GROUP_TITLE = '高级' as const;\nexport const ADVANCED_GROUP_NOTE = '不常改。留空=用默认值。' as const;\n\n/** 按 `a.b` 路径从配置取值里读(缺层或类型不符一律回 undefined)。 */\nexport function readPath(values: Record<string, unknown>, key: string): unknown {\n const parts = key.split('.');\n let cur: unknown = values;\n for (const part of parts) {\n if (typeof cur !== 'object' || cur === null || Array.isArray(cur)) return undefined;\n cur = (cur as Record<string, unknown>)[part];\n }\n return cur;\n}\n\n/** 按 `a.b` 路径把值写进配置取值(中间层缺就建一层空组;已存在非组即覆盖成组)。 */\nexport function writePath(values: Record<string, unknown>, key: string, value: unknown): void {\n const parts = key.split('.');\n let cur: Record<string, unknown> = values;\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i]!;\n const next = cur[part];\n if (typeof next !== 'object' || next === null || Array.isArray(next)) cur[part] = {};\n cur = cur[part] as Record<string, unknown>;\n }\n cur[parts[parts.length - 1]!] = value;\n}\n","/** DSH ctx 最小镜像(只含本仓实际使用的面;**除末尾那枚服务名常量外全是 type-only**,\n * 构建期擦除——那枚常量是为 #736 的目录选择软依赖加的,是本文件唯一的运行期值)。\n *\n * skills 面出处见 `docs/agents/dsh-client-contract.md` §12(#56 卡路里样板,#150 记账同形):\n * `ctx.skills.registerProvider(create)`(`dsh-skill/lib/index.js:147`);\n * candidate 校验红线见 `validateCandidate`(同文件 `:452`);`get` 回定义须与候选同名(同文件 `:259`)。\n * slots 面出处见 cookbook §5(总管单卡方案):\n * `inject(key, cb)` 等声明同步跑否则等待(`dsh-client-ui-renderer/lib/client.js:1015`);\n * `register` list 必 `{id}`(`dsh-client-ui-slots/lib/index.js:72`);\n * 爱生活页签槽注册形见 cookbook §11(`{name:'ilife.config-tab', id, order, label}`)。\n * 禁止加镜外成员:要用新能力,先在 cookbook 落出处,再加镜像。用 `import type` 引用本文件。\n *\n * 本插件 host 侧用 skills 面,client 侧用 slots 面(最小静态注册,不取数,故无 connection)。\n */\n\n/** 爱生活页签槽注册项(cookbook §5 options 全形子集,只取本次用到的面)。 */\nexport interface SlotsRegisterOptions {\n readonly name: string;\n readonly id?: string;\n readonly order?: number;\n readonly label?: string | (() => string);\n readonly locale?: string;\n /** 本包自己那条 RPC 通道(单段):注册时交出去,总管据此**通用地**调配置体检(票 #706)。 */\n readonly channel?: string;\n readonly inject?: () => Record<string, unknown>;\n}\n\n/** 总管声明的爱生活页签槽面(client 侧只用 inject+register)。 */\nexport interface SlotsFace {\n inject(key: string, callback: () => (() => void) | Iterable<() => void>): () => void;\n register(options: SlotsRegisterOptions, component: unknown): () => void;\n}\n\n/** 记账 client 上下文(#677 起:从「只读 slots」扩到 connection——设置页要经宿主读改配置)。 */\nexport interface ClientCtx {\n readonly slots: SlotsFace;\n /** 宿主连接面:客户端经它把请求送进宿主注册的 /api 路由。缺席即由组件判「宿主连接缺席」。 */\n readonly connection?: ConnectionFace;\n /** 服务取用(本包暂不用,保留面以对齐兄弟件镜像)。 */\n get?(name: string): unknown;\n /** 卸载清理登记。 */\n effect?(fn: () => void, label?: string): void;\n}\n\n/** rpc.call 的调用口(每次取数时现取:connection 后到也不永久缺席)。 */\nexport type RpcCallFace = (\n path: string,\n channel: string,\n body: unknown,\n signal?: AbortSignal,\n) => Promise<unknown>;\n\n/** rpc.call 的回执形状(只列本包读的两处)。 */\nexport interface RpcCallResult {\n readonly ok: boolean;\n readonly value?: unknown;\n readonly error?: { readonly code?: string; readonly message?: string };\n}\n\n/** 宿主连接面(cookbook 的 connection 服务,本包只用 rpc.call)。 */\nexport interface ConnectionFace {\n readonly rpc?: { readonly call?: RpcCallFace };\n}\n\n/** 技能的调用策略(模型可调/用户可调)。 */\nexport interface SkillInvocationPolicy {\n readonly modelInvocable: boolean;\n readonly userInvocable: boolean;\n}\n\n/** 候选摘要(宿主 `list` 的返回项;`provider` 须等于注册名,否则宿主校验抛)。 */\nexport interface SkillCandidate {\n readonly name: string;\n readonly description: string;\n readonly invocation: SkillInvocationPolicy;\n readonly provider: string;\n readonly source: string;\n readonly rank: number;\n readonly locator: unknown;\n readonly resourceBase?: { readonly kind: 'directory'; readonly path: string };\n}\n\n/** 技能全文(宿主 `get` 的返回项;`content` 为 frontmatter 后正文)。 */\nexport interface SkillDefinition {\n readonly name: string;\n readonly description: string;\n readonly invocation: SkillInvocationPolicy;\n readonly provider: string;\n readonly source: string;\n readonly resourceBase?: { readonly kind: 'directory'; readonly path: string };\n readonly content: string;\n}\n\nexport interface SkillProvider {\n readonly name: string;\n list(options?: unknown): Promise<readonly SkillCandidate[]>;\n get(candidate: SkillCandidate, options?: unknown): Promise<SkillDefinition | undefined>;\n}\n\nexport interface SkillsFace {\n registerProvider(\n create: (control: { readonly signal: AbortSignal; invalidate(): void }) => SkillProvider,\n ): () => void;\n}\n\n/** RPC 处理函数:端点 + 载荷 → 信封回执(照 #56 卡路里样板同形)。 */\nexport type RpcHandler = (endpoint: string, payload: unknown) => Promise<unknown>;\n\n/** 宿主 fetch 路由注册面(`ctx.connection.fetch.register`)。 */\nexport interface HostConnectionFace {\n readonly fetch: { register(options: Record<string, unknown>): () => void };\n}\n\n/** 本插件用到的宿主面(apply 入参)。 */\nexport interface SkillHostCtx {\n readonly skills: SkillsFace;\n /** 模型工具注册表(#734):本包把技能唯一出口开成 agent 工具。 */\n readonly tools?: ToolsFace;\n /** 宿主连接面:设置页的三个配置端点注册在这上面(#677 起用)。 */\n readonly connection?: HostConnectionFace;\n /** 卸载清理登记。 */\n readonly effect?: (fn: () => void, label?: string) => void;\n readonly logger?: unknown;\n}\n\n/** 宿主目录选择命名空间的服务名(#736;出处与禁令见 cookbook §13)。 */\nexport const REMOTE_DIRECTORY_PICKER = 'remote.directoryPicker' as const;\n\n/** 宿主目录选择命名空间(DSH 平台提供):本包用它的 `pick`(系统对话框)与\n * `list`/`createDirectory`(应用内浏览那两格原语,由共用件的 `browseFaceOf` 拆信封)。不给 signal\n * (平台那格可选;描述符按「业务参数个数 + 1」判有没有 signal,显式传 `undefined` 会炸)。\n *\n * **回执是信封,不是路径**(#743 真机缺陷的根因):客户端把每次 Remote 调用包成\n * `{ok:true,value}`/`{ok:false,error}`(`@deepseek-ai/dsh-api-gateway/lib/client.js` 的 `invoke()`:\n * 失败不抛、成功也不回裸值),第一方消费方 `@deepseek-ai/dsh-client-ui-workspace/lib/client.js`\n * 的 `pickDirectory()` 就照这个形状拆:`const r = await pick(); if (!r.ok) throw …; return r.value`。\n * `value` 为 `null`=用户取消;`ok:false`=这条路供不了(例如组合里是 browse 后端)。 */\nexport interface DirectoryPickerAnswer {\n readonly ok: boolean;\n readonly value?: string | null;\n readonly error?: { readonly code?: string; readonly message?: string };\n}\n\n/** 宿主「这条路没给」的回执码(#744):一次组合里只服务一种能力(native 或 browse),\n * 另一条路上的动词一律回它。\n *\n * 出处:`@deepseek-ai/dsh-api-workspace-controller/lib/types/directory-picker.js` 的 `requireCapability()`\n * ——`RemoteError('directory-picker/unavailable', …)`。三条动词在客户端命名空间上都在,\n * 所以只有真调一次、看它拒没拒,才知道组合里服务哪种能力。\n * 共用件不认识这个名字(只照比对),于是这条宿主事实住在本镜像里。 */\nexport const DIRECTORY_PICKER_REFUSED = 'directory-picker/unavailable' as const;\n\nexport interface DirectoryPickerFace {\n pick(): Promise<DirectoryPickerAnswer>;\n}\n\n/** 模型工具面镜像(#734;出处见 cookbook §14)。\n *\n * 出处:`@deepseek-ai/dsh-tools/lib/index.js` 的 `defineTool` 与 `ToolRuntime.register`——\n * 工具对象的形状是 `name`/`description`/`parameters`(JSON Schema)/`output{schema,render}`/\n * `execute`;`register` 只校验 `output.render` 是函数、`output.schema` 受支持、`timeoutMs` 为正、\n * 名字不是保留名 `run_code`,**不要求 `defineTool` 产物** ⇒ 本包按形状手写、零依赖。 */\nexport interface ToolTextPart {\n readonly type: 'text';\n readonly text: string;\n}\n\nexport interface ToolDefinitionMirror {\n readonly name: string;\n readonly description: string;\n readonly parameters: Record<string, unknown>;\n readonly output: {\n readonly schema: Record<string, unknown>;\n render(args: unknown, value: string): readonly ToolTextPart[];\n };\n execute(args: Record<string, unknown>, exec?: unknown): Promise<unknown>;\n}\n\nexport interface ToolsFace {\n register(definition: ToolDefinitionMirror): () => void;\n}\n","/** 目录浏览器的纯逻辑半(**不认识宿主**:只认两格原语)。\n *\n * 为什么在这里:六个单品插件的设置页各有一批「值是一个目录」的行,它们要的界面是同一个东西。\n * 界面的**取数**来自宿主给的两个原语(`list`/`createDirectory`),而这两个原语的形状是**调用方喂进来的**\n * ——本件不 import 宿主的任何实现,也不认识 `remote.directoryPicker` 这个名字(那两件住在各家的\n * `dsh-ctx.ts` 镜像里)。于是本件在 Windows 桌面、远程浏览器、SSH 三种部署下是同一份代码。\n *\n * 装什么:本次要浏览的那一层怎么走(进子目录/回上一级/跳面包屑)、路径怎么切出「目录部分」与\n * 「筛选词」、回执怎么认。**不装**任何 IO、不装任何持久化——`list`/`createDirectory` 由调用方执行。\n *\n * 与平台自带浏览器件的关系:`@deepseek-ai/dsh-client-ui-directory-picker-browse` 的 `DirectoryBrowser`\n * 是包内私有件(只经 `ui-workspace` 的流程空位暴露),插件侧取不到,所以这里是一份独立实现,\n * 只对齐它的**交互结果**(进/选/新建),不对齐它的 DOM。\n */\n/** 认一认某个值是不是可用的目录取数面(软依赖守卫用:认不出就当没有,绝不把页面带下来)。 */\nexport function isBrowseFace(raw) {\n if (typeof raw !== 'object' || raw === null)\n return false;\n const face = raw;\n return typeof face.list === 'function' && typeof face.createDirectory === 'function';\n}\n/** 认一认某个值有没有 `pick`(对应宿主 composition 里的 `native` 能力)。 */\nexport function hasPickFn(raw) {\n if (typeof raw !== 'object' || raw === null)\n return false;\n return typeof raw.pick === 'function';\n}\n/** 三态判定:**先看两格浏览原语**(都在=应用内浏览器这条路在),再看单独的 `pick`,都没有=`none`。\n *\n * 为什么浏览优先:客户端命名空间上三条动词一定都在(由 `TYPERT_REMOTE.descriptors` 生成,\n * 与组合里服务哪种能力无关),所以两格原语在=这台宿主**可能**给浏览;真调一次才知道给不给,\n * 不给({@link CAPABILITY_REFUSED})再由调用方换系统对话框。反过来先认 `pick` 就会在\n * Desktop 这种「只服务 browse」的宿主上每次都去唤一次系统对话框、被拒一次(#744 实测)。 */\nexport function pickerModeOf(raw) {\n if (isBrowseFace(raw))\n return 'browse';\n return hasPickFn(raw) ? 'native' : 'none';\n}\n/** 平台回执 → 三态(**永不抛**):成功回的是信封里的 `value` 不是路径;`ok:false` 是「供不了」不是「取消」。\n *\n * 出处:`@deepseek-ai/dsh-api-gateway/lib/client.js` 的 `invoke()`(失败不抛、成功也不回裸值),\n * 第一方消费方 `@deepseek-ai/dsh-client-ui-workspace/lib/client.js:99-103` 就照这个信封拆。\n * 裸串照收(老形状兜底),认不出的形状当「供不了」报出来,不当取消吞掉(#743 的教训)。 */\nexport function readPickAnswer(raw) {\n if (typeof raw === 'string')\n return raw.trim() === '' ? { kind: 'cancelled' } : { kind: 'picked', path: raw };\n const answer = (typeof raw === 'object' && raw !== null ? raw : {});\n if (answer.ok === true) {\n const value = answer.value;\n return typeof value === 'string' && value.trim() !== '' ? { kind: 'picked', path: value } : { kind: 'cancelled' };\n }\n const detail = answer.error?.message?.trim() ?? '';\n const code = answer.error?.code;\n return {\n kind: 'unavailable',\n ...(typeof code === 'string' && code !== '' ? { code } : {}),\n message: '打不开系统文件夹对话框' + (detail === '' ? '' : '(' + detail + ')') + ':请直接在框里填绝对路径。',\n };\n}\n/** 取数原语失败:带上宿主给的回执码,好让调用方分辨「没这条路」与「这一层读不出来」。\n *\n * `code` 就是 `humanize`(状态那半)读的那一格,人话直接上图。 */\nexport class BrowseAnswerError extends Error {\n code;\n constructor(code, message) {\n super(message);\n this.name = 'BrowseAnswerError';\n this.code = code;\n }\n}\n/** 宿主 browse 原语的**信封** → 值,或按信封里的 `error` 抛。\n *\n * 为什么必须走这一道:`list`/`createDirectory` 与 `pick` 一样是 Remote 动词,客户端拿到的是\n * `{ok:true,value}`/`{ok:false,error}`(`@deepseek-ai/dsh-api-gateway/lib/client.js` 的 `invoke()`)。\n * 把信封当值用,图上就会是一层「读出来了但一行都没有」的空目录(#744 实测),\n * 而不是报错——所以宁可在这里抛,也不许把它当列举结果传下去。\n * 第一方消费方同口径:`@deepseek-ai/dsh-client-ui-workspace/lib/client.js:105-113` 也是这么拆的。 */\nexport function readBrowseAnswer(raw, what) {\n const answer = (typeof raw === 'object' && raw !== null ? raw : {});\n if (answer.ok === true && answer.value !== undefined && answer.value !== null)\n return answer.value;\n const detail = answer.error?.message?.trim() ?? '';\n const code = answer.error?.code;\n throw new BrowseAnswerError(typeof code === 'string' && code !== '' ? code : 'browse-failed', detail === '' ? what + '没有回执' : detail);\n}\n/** 宿主命名空间 → 本件的取数面:把两条原语的信封拆开,成功给值、失败按码抛。\n *\n * 命名空间上两格原语不在就回 null(调用方据此不画入口)。**透传参数个数照宿主的规矩**:\n * 描述符声明了可选 `AbortSignal`(`cancellation: {parameter:'signal'}`),而客户端按\n * `values.length === 业务参数个数 + 1` 判有没有 signal——显式传一个 `undefined` 会被当成 signal\n * 去 `AbortSignal.any([…, undefined])` 而炸。本件的界面不做取消,故**从不传 signal**。 */\nexport function browseFaceOf(raw) {\n if (!isBrowseFace(raw))\n return null;\n const namespace = raw;\n return {\n list: async (path) => readBrowseAnswer(path === undefined ? await namespace.list() : await namespace.list(path), '列举目录'),\n createDirectory: async (path, name) => readBrowseAnswer(await namespace.createDirectory(path, name), '新建文件夹'),\n };\n}\n/** 一段路径里最后那个分隔符的位置(Windows 上 `\\` 与 `/` 都算;POSIX 上 `\\` 是合法文件名字符,不算)。 */\nfunction lastSeparator(path) {\n const back = path.lastIndexOf('\\\\');\n const slash = path.lastIndexOf('/');\n // 只含 `\\` 的路径按 Windows 规则解;两者都有时取更靠后的那个。\n return back > slash ? back : slash;\n}\n/** 上一层:`'C:\\\\a\\\\b\\\\'` → `'C:\\\\a'`;已经是根则回自身;相对名(没有任何分隔符)也回自身。\n *\n * 尾分隔符先吃掉再切——否则「回上一级」会在原地打转。**盘根单独认**:`C:\\` 掐掉尾分隔符会剩成\n * `C:`,而 `C:` 是「当前盘」不是「上一层」,回它就会多跑一趟、还可能跨到别的盘上去。\n * UNC(`\\\\server\\share`)不特殊处理:切到头就是共享名本身,浏览停在共享根。 */\nexport function parentOf(path) {\n const trimmed = path.replace(/[\\\\/]+$/, '');\n if (trimmed === '')\n return path;\n const sep = path.includes('\\\\') ? '\\\\' : '/';\n if (/^[A-Za-z]:$/.test(trimmed))\n return trimmed + sep;\n const cut = lastSeparator(trimmed);\n if (cut === -1)\n return trimmed;\n if (cut === 0)\n return path.slice(0, 1);\n if (cut === 2 && /^[A-Za-z]:/.test(trimmed))\n return trimmed.slice(0, 2) + sep;\n return trimmed.slice(0, cut);\n}\n/** 把「当前路径 + 一段输入」拼成一个目标路径。 */\nexport function joinPath(directory, name) {\n const sep = directory.includes('\\\\') ? '\\\\' : '/';\n const base = directory.replace(/[\\\\/]+$/, '');\n if (base === '')\n return sep + name;\n return base + sep + name;\n}\n/** 一层列举里,哪些行可进:隐藏目录按开关过滤,其余照宿主给的顺序。 */\nexport function visibleEntries(listing, showHidden) {\n return showHidden ? listing.entries : listing.entries.filter((entry) => !entry.hidden);\n}\n/** 这一层有多少个被藏起来的目录(用来决定「显示隐藏目录」那个开关要不要出现在图上)。 */\nexport function hiddenCount(listing) {\n return listing.entries.filter((entry) => entry.hidden).length;\n}\n/** 拿一段文本按「最后一个分隔符」切成「目录部分 + 筛选词」。\n *\n * 没打过任何分隔符时目录部分=null(那段文字还不指任何目录,只当筛选词用)。\n * Windows 上 `/` 也当分隔符(宿主那边两种都收),POSIX 上不。 */\nexport function splitDraft(draft) {\n const cut = lastSeparator(draft);\n if (cut === -1)\n return { directory: null, filter: draft };\n return { directory: draft.slice(0, cut + 1), filter: draft.slice(cut + 1) };\n}\n/** 按筛选词过一层目录(大小写不敏感;空筛选词=全留)。 */\nexport function filterEntries(entries, filter) {\n const needle = filter.trim().toLowerCase();\n if (needle === '')\n return entries;\n return entries.filter((entry) => entry.name.toLowerCase().includes(needle));\n}\n/** 给界面用的一句话位置说明:优先显示相对 home 的说法,省得整条绝对路径占满一行。 */\nexport function locationLabel(listing) {\n const home = listing.home.replace(/[\\\\/]+$/, '');\n if (home !== '' && listing.path !== home && listing.path.startsWith(home)) {\n const tail = listing.path.slice(home.length).replace(/^[\\\\/]+/, '');\n if (tail !== '')\n return '~' + (listing.path.includes('\\\\') ? '\\\\' : '/') + tail;\n }\n return listing.path;\n}\n/** 新建文件夹的名字合不合法(单个路径段:不许分隔符、不许空、不许 `.`/`..`)。\n *\n * 与宿主 browse 后端的校验同口径——这里先拦一道,是为了在图上给得出人话,不是替代宿主校验。 */\nexport function validateFolderName(name) {\n const trimmed = name.trim();\n if (trimmed === '')\n return { ok: false, reason: '文件夹名不能为空。' };\n if (/[\\\\/]/.test(trimmed))\n return { ok: false, reason: '文件夹名里不能带路径分隔符。' };\n if (trimmed === '.' || trimmed === '..')\n return { ok: false, reason: '「.」与「..」不是文件夹名。' };\n return { ok: true };\n}\n//# sourceMappingURL=directory-browser-contract.js.map","/** 文件浏览器的操作半:把「进哪一层、这一层显示什么、选到了什么」算成状态,**IO 全部由调用方注入**。\n *\n * 分两层的原因:本件(操作)在 Node 里就能直测——喂一个假的取数面,就能把「进目录/回上一级/\n * 跳面包屑/筛选/新建文件夹/失败出人话」全跑一遍;视图那半(`directory-browser-ui.ts`)\n * 只负责把这份状态画成 DOM。两边都不认识宿主。\n *\n * 数据来源只有两格:`list(path?)` 与 `createDirectory(path, name)`(调用方注入的 `DirectoryBrowseFace`)。\n */\nimport { browseFaceOf, filterEntries, joinPath, parentOf, splitDraft, validateFolderName, visibleEntries, } from './directory-browser-contract.js';\n/** 建一个浏览器控制器。取数、回调、初值全部由调用方给。 */\nexport function createBrowseController(deps) {\n const label = deps.failureLabel ?? '浏览失败';\n const listeners = new Set();\n let state = {\n phase: 'idle',\n listing: null,\n failure: null,\n selected: null,\n showHidden: false,\n draft: deps.initialPath,\n filter: '',\n creating: null,\n notice: null,\n };\n /** 每次列举带一个序号:晚回来的旧回执直接丢掉,不许覆盖新一层。 */\n let generation = 0;\n const emit = (next) => {\n state = { ...state, ...next };\n for (const listener of [...listeners])\n listener(state);\n };\n const humanize = (cause) => {\n const raw = (typeof cause === 'object' && cause !== null ? cause : {});\n const message = typeof raw.message === 'string' && raw.message.trim() !== '' ? raw.message.trim() : String(cause);\n const code = typeof raw.code === 'string' && raw.code !== '' ? raw.code : 'browse-failed';\n return { code, message: `${label}:${message}` };\n };\n const load = async (path) => {\n const mine = ++generation;\n emit({ phase: 'loading', failure: null, notice: null });\n try {\n const listing = path === null ? await deps.face.list() : await deps.face.list(path);\n if (mine !== generation)\n return;\n emit({\n phase: 'ready',\n listing,\n failure: null,\n selected: null,\n draft: listing.path,\n filter: '',\n });\n }\n catch (cause) {\n if (mine !== generation)\n return;\n emit({ phase: 'failed', listing: null, failure: humanize(cause), selected: null });\n }\n };\n /** 草稿指到的那一层已经列举过时,按末段筛当前这一层的行;否则不过滤。\n *\n * 两边都先掐掉尾分隔符再比:草稿那边切开后天然带尾分隔符(`C:\\a\\` + `b`),\n * 而这一层的 `path` 由宿主给、不带尾分隔符。 */\n const applyDraft = (draft) => {\n const { directory, filter } = splitDraft(draft);\n const listing = state.listing;\n const sameLevel = listing !== null && directory !== null && normalize(directory) === normalize(listing.path);\n emit({ draft, filter: sameLevel ? filter : '' });\n };\n const normalize = (path) => path.replace(/[\\\\/]+$/, '');\n return {\n getState: () => state,\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n async open() {\n const wanted = deps.initialPath.trim();\n if (wanted !== '') {\n await load(wanted);\n if (state.phase === 'ready')\n return state;\n }\n await load(null);\n return state;\n },\n enter: (path) => load(path),\n async up() {\n const listing = state.listing;\n if (listing === null)\n return;\n const parent = parentOf(listing.path);\n if (parent === listing.path)\n return;\n await load(parent);\n },\n pick() {\n const target = state.selected ?? state.listing?.path ?? null;\n if (target === null)\n return;\n deps.onPicked(target);\n },\n cancel() {\n deps.onClose();\n },\n close() {\n deps.onClose();\n },\n select(path) {\n emit({ selected: state.selected === path ? null : path, notice: null });\n },\n toggleHidden() {\n emit({ showHidden: !state.showHidden });\n },\n setDraft(draft) {\n applyDraft(draft);\n },\n async commitDraft() {\n const trimmed = state.draft.trim();\n if (trimmed === '')\n return;\n await load(trimmed);\n },\n setCreating(name) {\n emit({ creating: name, notice: null });\n },\n async createFolder(name) {\n const checked = validateFolderName(name);\n if (!checked.ok) {\n emit({ notice: checked.reason });\n return;\n }\n const base = state.selected ?? state.listing?.path ?? null;\n if (base === null) {\n emit({ notice: '还没有打开任何目录,无法新建文件夹。' });\n return;\n }\n try {\n const created = await deps.face.createDirectory(base, name.trim());\n emit({ creating: null, notice: null });\n await load(created);\n emit({ selected: created });\n }\n catch (cause) {\n const failure = humanize(cause);\n emit({ notice: failure.message, creating: null });\n }\n },\n };\n}\n/** 图上这一层要画的行(按隐藏开关与草稿末段过滤之后的)。 */\nexport function rowsOf(state) {\n if (state.listing === null)\n return [];\n return filterEntries(visibleEntries(state.listing, state.showHidden), state.filter);\n}\n/** 图上这一层能不能回上一级(根目录不能)。 */\nexport function canGoUp(state) {\n if (state.listing === null)\n return false;\n return parentOf(state.listing.path) !== state.listing.path;\n}\n/** 「打开」这颗按钮的目标:选中行优先,没选中就是当前层。 */\nexport function targetOf(state) {\n return state.selected ?? state.listing?.path ?? null;\n}\n/** 新建文件夹那一行要展示的位置(\"在 <这里> 下新建\")。 */\nexport function createBaseOf(state) {\n return state.selected ?? state.listing?.path ?? null;\n}\n/** 一段路径拼成可读的一句话(给用例与调试用;图上不直接显示)。 */\nexport function describe(state) {\n if (state.phase === 'failed')\n return `${state.failure?.code ?? 'browse-failed'} @ ${state.draft}`;\n if (state.listing === null)\n return `${state.phase}`;\n return `${state.listing.path}(${rowsOf(state).length} 行)`;\n}\n/** 一行子目录的完整路径(图上点行时用)。 */\nexport function entryPath(directory, entry) {\n return entry.path === '' ? joinPath(directory, entry.name) : entry.path;\n}\n/** 建一份「目录行接线」:给一个取数面与初值,回一组动作。\n *\n * 只做接线:把控制器算出来的状态与回调打包好,不取数、不画图、不认宿主。 */\nexport function createDirectoryRowBrowser(deps) {\n const controller = createBrowseController({\n face: deps.face,\n initialPath: deps.initialPath,\n onPicked: deps.onPicked,\n onClose: () => deps.onClosed?.(),\n ...(deps.failureLabel === undefined ? {} : { failureLabel: deps.failureLabel }),\n });\n return {\n open: () => controller.open(),\n subscribe: (listener) => controller.subscribe(listener),\n state: () => controller.getState(),\n summary: () => describe(controller.getState()),\n actions: {\n onPick: () => controller.pick(),\n onClose: () => controller.cancel(),\n onEnter: (path) => void controller.enter(path),\n onUp: () => void controller.up(),\n onSelect: (path) => controller.select(path),\n onToggleHidden: () => controller.toggleHidden(),\n onDraft: (draft) => controller.setDraft(draft),\n onCommitDraft: () => void controller.commitDraft(),\n onCreate: (name) => void controller.createFolder(name),\n onCreatingChange: (name) => controller.setCreating(name),\n },\n };\n}\n/** 一条目录行点下去该走哪条路(**六家共用这一处策略**,单独写在各家就会各错各的)。\n *\n * 顺序是「先应用内浏览,被拒再换系统对话框」,因为客户端命名空间上三条动词都在、问不出组合里服务哪种\n * 能力(见 `pickerModeOf`)。回执:\n * - `undefined`=连两格浏览原语都没有(这条路没有,调用方自己想办法);\n * - `'refused'`=宿主回了 `refusalCode`(组合里是系统对话框),图已收起;\n * - `'open'`=图开着,后面的事都在图里。\n *\n * **状态由调用方持有**:`onRow` 收到刚建好的那条(或 null=收起),React 那边据此决定画不画图。\n *\n * `refusalCode` 是**宿主的回执码**(各家自己从宿主镜像里拿,本件只照比不认名):\n * 它换名字本件照样跑,六家一起换。 */\nexport function openRowBrowser(input) {\n const face = browseFaceOf(input.picker);\n if (face === null)\n return undefined;\n const row = createDirectoryRowBrowser({\n face,\n initialPath: input.initialPath,\n onPicked: (picked) => {\n input.onChange(picked);\n input.onRow(null);\n },\n onClosed: () => input.onRow(null),\n });\n input.onRow(row);\n return row.open().then((settled) => {\n if (settled.phase === 'failed' && settled.failure?.code === input.refusalCode) {\n input.onRow(null);\n return 'refused';\n }\n return 'open';\n });\n}\n//# sourceMappingURL=directory-browser-state.js.map","/** 文件浏览器的视图半:把 `BrowseState` 画成一张对话框。**不认识宿主**,也不自己做 IO。\n *\n * 手写 `React.createElement`(本包 client 束禁 JSX,见 `tsdown.config.ts` 的既成口径)。\n * 只读状态、只回调;进哪一层、选到什么由操作半(`directory-browser-state.ts`)算。\n */\nimport * as React from 'react';\nimport { rowsOf, targetOf } from './directory-browser-state.js';\nimport { hiddenCount, locationLabel } from './directory-browser-contract.js';\nconst EMPTY_LISTING = { path: '', home: '', crumbs: [], entries: [], truncated: false };\nconst S = {\n scrim: {\n position: 'fixed',\n inset: 0,\n background: 'rgba(0,0,0,0.35)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 9999,\n },\n dialog: {\n width: 680,\n maxWidth: '92vw',\n height: 500,\n maxHeight: '86vh',\n display: 'flex',\n flexDirection: 'column',\n background: 'var(--dsw-alias-bg-layer-1, #1f1f23)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 10,\n boxShadow: '0 12px 32px rgba(0,0,0,0.35)',\n overflow: 'hidden',\n },\n head: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '10px 12px',\n borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))',\n },\n title: { fontSize: 14, fontWeight: 700 },\n headTail: { marginLeft: 'auto', fontSize: 12, opacity: 0.72, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },\n crumbs: { display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 2, padding: '8px 12px 0', fontSize: 12 },\n chromeButton: {\n background: 'transparent',\n border: 'none',\n color: 'var(--dsw-alias-label-primary, inherit)',\n cursor: 'pointer',\n padding: '2px 4px',\n fontSize: 12,\n opacity: 0.85,\n },\n crumbSep: { opacity: 0.45, fontSize: 12 },\n pathRow: { display: 'flex', gap: 6, padding: '8px 12px' },\n input: {\n flex: 1,\n minWidth: 0,\n fontSize: 12,\n padding: '5px 8px',\n background: 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.12))',\n color: 'inherit',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 6,\n },\n button: {\n fontSize: 12,\n padding: '5px 10px',\n cursor: 'pointer',\n background: 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.12))',\n color: 'inherit',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.35))',\n borderRadius: 6,\n },\n buttonOff: { opacity: 0.45, cursor: 'default' },\n list: {\n flex: 1,\n minHeight: 0,\n overflowY: 'auto',\n margin: '0 12px',\n border: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))',\n borderRadius: 6,\n },\n row: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n width: '100%',\n padding: '6px 10px',\n textAlign: 'left',\n background: 'transparent',\n color: 'inherit',\n border: 'none',\n borderBottom: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.12))',\n cursor: 'pointer',\n fontSize: 13,\n },\n rowSelected: { background: 'var(--dsw-alias-bg-layer-2, rgba(128,128,128,0.18))' },\n empty: { padding: '14px 12px', fontSize: 12, opacity: 0.7 },\n footNote: { padding: '6px 12px 0', fontSize: 12, minHeight: 20 },\n foot: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: '10px 12px',\n borderTop: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))',\n },\n target: { marginRight: 'auto', fontSize: 12, opacity: 0.8, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },\n error: { color: 'var(--dsw-alias-label-error, #e66)' },\n};\nfunction crumbRow(state, onEnter) {\n const listing = state.listing;\n if (listing === null || listing.crumbs.length === 0)\n return null;\n const parts = [];\n listing.crumbs.forEach((crumb, index) => {\n if (index > 0)\n parts.push(React.createElement('span', { key: 'sep-' + crumb.path, style: S.crumbSep }, '›'));\n const isCurrent = crumb.path === listing.path;\n parts.push(React.createElement('button', {\n key: crumb.path,\n type: 'button',\n style: { ...S.chromeButton, fontWeight: isCurrent ? 700 : 400 },\n title: crumb.path,\n onClick: () => onEnter(crumb.path),\n }, \n // 每一格都写**它自己那一层的名字**:第一格是文件系统根(`C:\\`),不是「上一级」——\n // 「上一级」是路径行那颗按钮的活(#744 返修:这一格原先串了那颗按钮的文案)。\n crumb.name));\n });\n return React.createElement('div', { style: S.crumbs }, parts);\n}\nfunction entryRow(entry, selected, labels, onToggleSelect, onEnter) {\n return React.createElement('div', { key: entry.path, style: { ...S.row, padding: 0, ...(selected ? S.rowSelected : {}) } }, React.createElement('button', { type: 'button', style: { ...S.row, flex: 1, border: 'none' }, onClick: () => onEnter(entry.path) }, '📁 ' + entry.name), React.createElement('button', {\n type: 'button',\n style: { ...S.button, marginRight: 8, padding: '2px 8px' },\n 'aria-pressed': selected,\n title: selected ? labels.selected : labels.select,\n onClick: () => onToggleSelect(entry.path),\n }, selected ? '✓' : labels.select));\n}\nfunction createRow(labels, onName, onCancel, onCreate, creatingName) {\n if (creatingName === null)\n return null;\n return React.createElement('div', { style: { ...S.pathRow, borderTop: '1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25))' } }, React.createElement('input', {\n style: S.input,\n value: creatingName,\n placeholder: labels.newFolder,\n 'aria-label': labels.newFolder,\n onChange: (event) => onName(event.currentTarget.value),\n onKeyDown: (event) => {\n if (event.key === 'Enter')\n onCreate(creatingName);\n if (event.key === 'Escape')\n onCancel();\n },\n }), React.createElement('button', { type: 'button', style: S.button, onClick: () => onCreate(creatingName) }, labels.createConfirm), React.createElement('button', { type: 'button', style: S.button, onClick: onCancel }, labels.createCancel));\n}\n/** 画一张文件浏览器对话框。`open=false` 时返回 null(本件不挂 portal,由调用方决定挂在哪)。 */\nexport function DirectoryBrowser(props) {\n const { open, state, labels } = props;\n if (!open)\n return null;\n const rows = rowsOf(state);\n const target = targetOf(state);\n const listing = state.listing ?? EMPTY_LISTING;\n const hidden = hiddenCount(listing);\n const body = [];\n body.push(crumbRow(state, props.onEnter));\n body.push(React.createElement('div', { style: S.pathRow }, React.createElement('input', {\n style: S.input,\n value: state.draft,\n placeholder: labels.pathPlaceholder,\n 'aria-label': labels.pathPlaceholder,\n onChange: (event) => props.onDraft(event.currentTarget.value),\n onKeyDown: (event) => {\n if (event.key === 'Enter')\n props.onCommitDraft();\n },\n }), React.createElement('button', { type: 'button', style: S.button, onClick: props.onUp }, labels.up), React.createElement('button', { type: 'button', style: S.button, onClick: props.onCommitDraft }, labels.go)));\n body.push(React.createElement('div', { style: S.list, role: 'listbox', 'aria-label': labels.title }, rows.length === 0\n ? React.createElement('div', { style: S.empty }, state.phase === 'loading' ? labels.loading : labels.empty)\n : rows.map((entry) => entryRow(entry, state.selected === entry.path, labels, props.onSelect, props.onEnter))));\n body.push(createRow(labels, props.onDraft, () => props.onCreatingChange(null), props.onCreate, state.creating));\n body.push(React.createElement('div', { style: S.footNote }, hidden > 0\n ? React.createElement('label', { style: { fontSize: 12, cursor: 'pointer', marginRight: 10 } }, React.createElement('input', { type: 'checkbox', checked: state.showHidden, onChange: props.onToggleHidden }), ' ' + labels.showHidden(hidden))\n : null, state.creating === null\n ? React.createElement('button', { type: 'button', style: { ...S.chromeButton, textDecoration: 'underline' }, onClick: () => props.onCreatingChange('') }, labels.newFolder)\n : null, state.notice !== null ? React.createElement('div', { style: S.error }, state.notice) : null, state.phase === 'failed' && state.failure !== null\n ? React.createElement('div', { style: S.error }, state.failure.message)\n : null));\n body.push(React.createElement('div', { style: S.foot }, React.createElement('span', { style: S.target, title: target ?? '' }, target === null ? '' : labels.willPick + target), React.createElement('button', { type: 'button', style: { ...S.button, ...(target === null ? S.buttonOff : {}) }, onClick: props.onPick }, labels.open), React.createElement('button', { type: 'button', style: S.button, onClick: props.onClose }, labels.cancel)));\n return React.createElement('div', {\n style: S.scrim,\n role: 'presentation',\n onMouseDown: (event) => {\n if (event.target === event.currentTarget)\n props.onClose();\n },\n }, React.createElement('div', { style: S.dialog, role: 'dialog', 'aria-modal': true, 'aria-label': labels.title }, React.createElement('div', { style: S.head }, React.createElement('span', { style: S.title }, labels.title), React.createElement('span', { style: S.headTail, title: listing.path }, state.listing === null ? '' : locationLabel(listing)), React.createElement('button', { type: 'button', style: S.button, 'aria-label': labels.close, title: labels.close, onClick: props.onClose }, '✕')), body));\n}\n/** 目录行接线的 React 半边:把操作半那组动作直接喂给组件。\n *\n * 操作半(`directory-browser-state.ts` 的 `createDirectoryRowBrowser`)不碰 React,\n * 这里只把它的 `actions` 摊进 props —— 于是「进哪一层」那套在 Node 里可测,这里只做接线。\n *\n * `useSyncExternalStore` 是**必需**的:列举是异步落定的,不订阅的话这一帧画完就再没有重画,\n * 图上会永远停在「正在读取…」(#744 实测)。 */\nexport function DirectoryBrowserFromRow(props) {\n const state = React.useSyncExternalStore(props.row.subscribe, props.row.state);\n return DirectoryBrowser({\n open: props.open,\n state,\n labels: props.labels,\n ...props.row.actions,\n });\n}\nexport { createBrowseController, createDirectoryRowBrowser, describe, rowsOf, targetOf } from './directory-browser-state.js';\n//# sourceMappingURL=directory-browser-ui.js.map","/** dsh-bill-ilife client 适配器(六边形:port=contract,adapter=本文件)。\n *\n * 产物经 tsdown 打成 loader 工厂包(browser/CJS,见 tsdown.config.ts)。\n * 本文件传递闭包:禁 node、禁 DOM 直写(document/window/process)。\n * 注册一处:**技能设置页** → 爱生活页签槽(总管声明的 children,各技能自研自家配置页)。\n * 样式:React 内联 style + DSH 主题别名(var(--dsw-alias-*),带回退),无外部样式表、\n * 无 <style> 注入、无类名冲突;只用 dsh-ctx 镜像内成员(slots.inject/register、\n * connection.rpc.call 经闭包现取);取数只经 connection.rpc.call 进 host 通道;\n * 缺席/错误纯条件渲染,不返空冒充。组件 React.createElement 手写,不引入 JSX。\n *\n * #677:技能设置页从「只读设置行」换成**真配置页**——行表来自 settings.ts,\n * 取值与保存经宿主三个端点(`config.get`/`config.save`/`config.reset`),\n * 宿主再 spawn 技能 CLI 的 `bill.config.*` 三个 key。本文件仍禁 node:不读盘、不算默认值。\n * 照 CONTEXT.md,「技能设置页」只配置、不干活:本页没有查账、记一笔之类的入口。\n */\nimport * as React from 'react';\nimport { RPC_CHANNEL, RPC_ENDPOINT_CONFIG_GET, RPC_ENDPOINT_CONFIG_SAVE, RPC_ENDPOINT_CONFIG_RESET, isRpcResult } from './contract.js';\nimport type { ConfigSurfaceReply } from './contract.js';\nimport { PLUGIN, SLOT_ORDER, SLOT_TITLE } from './slot.js';\nimport { CONFIG_ITEMS, COMMON_ITEM_COUNT, ADVANCED_GROUP_TITLE, ADVANCED_GROUP_NOTE, readPath, writePath } from './settings.js';\nimport type { ConfigItem } from './settings.js';\nimport { DIRECTORY_PICKER_REFUSED, REMOTE_DIRECTORY_PICKER } from './dsh-ctx.js';\nimport { pickerModeOf as sharedPickerModeOf, readPickAnswer as sharedReadPickAnswer, openRowBrowser } from 'dsh-life-pack/directory-browser';\nimport { DirectoryBrowserFromRow } from 'dsh-life-pack/directory-browser-ui';\nimport type { PickerMode, DirectoryRowBrowser, DirectoryRowEntry } from 'dsh-life-pack/directory-browser';\nimport type { ClientCtx, RpcCallFace, RpcCallResult, DirectoryPickerAnswer, DirectoryPickerFace } from './dsh-ctx.js';\n\n/** client 短名声明:只有这两个(#736 的目录选择走**可选查找**,不写进来——\n * 写进来=硬依赖,提供方缺席时整包被停靠,设置页会跟着装不上;见 cookbook §13)。 */\nexport const inject: readonly string[] = ['slots', 'connection'];\n\n/** 调用口取用器:每次取数时现取(connection 后到也不永久缺席)。 */\nexport type GetCall = () => unknown;\n\n/** 面板视觉(内联 style;颜色走 DSH 主题别名,深浅主题自适应,写死值只做回退)。\n *\n * **六家逐项同形**(备忘·卡路里·记账·作息·居家·大厨):同一项在这六份里逐字相同,\n * 改任一条要六家一起改,锁见 `test/panel-copy-743.test.mjs` 第 ⑤ 条。 */\nconst S = {\n card: {\n padding: '12px 14px',\n borderRadius: 10,\n border: '1px solid var(--dsw-alias-border, rgba(128,128,128,.35))',\n background: 'var(--dsw-alias-bg-base, transparent)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n lineHeight: 1.6,\n } as React.CSSProperties,\n title: { fontSize: '1.08em', fontWeight: 700, marginBottom: 8 } as React.CSSProperties,\n muted: { color: 'var(--dsw-alias-label-secondary, #9a9a9a)', fontSize: '0.92em'} as React.CSSProperties,\n error: { marginTop: 8, color: 'var(--dsw-alias-label-error, #b3261e)', fontSize: '1em', whiteSpace: 'pre-wrap' } as React.CSSProperties,\n okText: { marginTop: 8, color: 'var(--dsw-alias-state-success-primary, #12805c)', fontSize: '0.96em'} as React.CSSProperties,\n rows: { marginTop: 8, borderTop: '1px solid var(--dsw-alias-border, rgba(128,128,128,.25))', paddingTop: 8 } as React.CSSProperties,\n info: { color: 'var(--dsw-alias-label-secondary, #9a9a9a)', fontSize: '0.92em', overflowWrap: 'anywhere' } as React.CSSProperties,\n row: { marginBottom: 10 } as React.CSSProperties,\n label: { fontWeight: 600 } as React.CSSProperties,\n hint: { color: 'var(--dsw-alias-label-secondary, #9a9a9a)', fontSize: '0.92em', marginBottom: 4 } as React.CSSProperties,\n input: {\n width: '100%',\n boxSizing: 'border-box',\n padding: '4px 8px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-border, rgba(128,128,128,.45))',\n background: 'var(--dsw-alias-bg-base, transparent)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n } as React.CSSProperties,\n pickRow: { display: 'flex', gap: 6, alignItems: 'center' } as React.CSSProperties,\n btnPick: {\n flex: '0 0 auto',\n padding: '4px 10px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-border, rgba(128,128,128,.45))',\n background: 'var(--dsw-alias-bg-base, transparent)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n whiteSpace: 'nowrap',\n cursor: 'pointer',\n } as React.CSSProperties,\n bar: { display: 'flex', gap: 8, marginTop: 12, flexWrap: 'wrap' } as React.CSSProperties,\n btn: {\n padding: '4px 12px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-border, rgba(128,128,128,.45))',\n background: 'var(--dsw-alias-bg-base, transparent)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n cursor: 'pointer',\n } as React.CSSProperties,\n btnPrimary: {\n padding: '4px 12px',\n borderRadius: 6,\n border: '1px solid var(--dsw-alias-brand-primary, #2f6fed)',\n background: 'var(--dsw-alias-brand-primary, #2f6fed)',\n color: '#fff',\n cursor: 'pointer',\n } as React.CSSProperties,\n advanced: { marginTop: 12 } as React.CSSProperties,\n summary: { cursor: 'pointer', fontWeight: 600 } as React.CSSProperties,\n};\n\n/** 请求超时毫秒:与 host 侧 SPAWN_TIMEOUT_MS 同级,UI 永不无限转圈。 */\nconst READ_TIMEOUT_MS = 20_000 as const;\n\n/** 有界等待:借平台 `AbortSignal.timeout` 的 abort 事件做**一次**超时拒绝。\n *\n * 为什么不自建定时器:本包的冻结边界是「无数据轮询」(`test/plugin-p10-boundaries.test.mjs`:\n * 单品 src 里出现计时器就要有清理 + 次数上限 + 例外依据三件套),而设置页等的是一次 RPC 回执,\n * 不是轮询;用平台自带的一次性超时既守住边界的本意,也照样把「不转圈」落到实处。\n * 同一个 signal 也交给 transport,双保险:它搭理 signal 就早收工,不理也有这一道兜底。\n */\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n const signal = AbortSignal.timeout(ms);\n const limit = new Promise<never>((_, reject) => {\n signal.addEventListener(\n 'abort',\n () => {\n const e = new Error(`配置超时(${Math.round(ms / 1000)}s):宿主未回,先查宿主日志`);\n e.name = 'TimeoutError';\n reject(e);\n },\n { once: true },\n );\n });\n return Promise.race([promise, limit]);\n}\n\ntype ConfigState =\n | { readonly kind: 'loading' }\n | { readonly kind: 'ready'; readonly surface: ConfigSurfaceReply }\n | { readonly kind: 'failed'; readonly message: string };\n\n/** 配置面报错 → 人话指引。\n *\n * 报文由技能侧给出(base-link-core 的 `ConfigError` 已带行号与文件名,技能 `cli/config.ts`\n * 原样交出,插件 bridge 原样带上),所以这里**不重写报文**,只按报错类补一句「接下来怎么办」。\n */\nexport function humanizeConfigFailure(code: string, message: string): string {\n const text = message.trim().length > 0 ? message.trim() : `(${code},宿主未给报文)`;\n if (/解析|YAML|parse/i.test(text)) return `${text}\\n改回「键: 值」的写法,或点「重置为默认」。`;\n if (/不认识|未知键|UNKNOWN_KEY/i.test(text)) return `${text}\\n删掉页面上没有的行,或点「重置为默认」。`;\n if (/类型|TYPE_MISMATCH/i.test(text)) return `${text}\\n按本页的控件形状填,或点「重置为默认」。`;\n if (/缺席|missing-cli/.test(text)) return `${text}\\n技能出口没找到:先确认技能包已装好,再点「重新读取」。`;\n return `${text}\\n改不动就点「重置为默认」,或照上面那条路径手工改配置文件。`;\n}\n\ntype ConfigOutcome =\n | { readonly ok: true; readonly surface: ConfigSurfaceReply }\n | { readonly ok: false; readonly message: string };\n\n/** 一次配置 RPC(永不抛,失败落字;UI 因此不转圈、不返空)。 */\nasync function configRpc(call: unknown, method: string, payload: Record<string, unknown>): Promise<ConfigOutcome> {\n if (typeof call !== 'function') return { ok: false, message: '宿主连接缺席:connection.rpc.call 不可用' };\n try {\n const raw: unknown = await withTimeout(\n (call as RpcCallFace)('/api', RPC_CHANNEL.slice(1), { method, payload }, AbortSignal.timeout(READ_TIMEOUT_MS)),\n READ_TIMEOUT_MS,\n );\n if (!isRpcResult(raw)) return { ok: false, message: '回执信封异常(非 ok 信封)' };\n const res = raw as RpcCallResult;\n if (!res.ok) return { ok: false, message: humanizeConfigFailure(res.error?.code ?? 'unknown', res.error?.message ?? '') };\n return { ok: true, surface: res.value as ConfigSurfaceReply };\n } catch (e) {\n if (e instanceof Error && e.name === 'AbortError') return { ok: false, message: `配置读取超时(${Math.round(READ_TIMEOUT_MS / 1000)}s):宿主未回` };\n if (e instanceof Error && e.name === 'TimeoutError') return { ok: false, message: e.message };\n return { ok: false, message: `配置失败:${e instanceof Error ? e.message : String(e)}` };\n }\n}\n\n/** 读设置页整面。 */\nexport function fetchConfigSurface(call: unknown): Promise<ConfigOutcome> {\n return configRpc(call, RPC_ENDPOINT_CONFIG_GET, {});\n}\n\n/** 保存(回全表;技能侧做组内合并)。 */\nexport function saveConfigSurface(call: unknown, values: Record<string, unknown>): Promise<ConfigOutcome> {\n return configRpc(call, RPC_ENDPOINT_CONFIG_SAVE, { values });\n}\n\n/** 重置为默认(技能侧先落 .bak;写回执不是整面,由组件写完重读一份)。 */\nexport function resetConfigSurface(call: unknown): Promise<ConfigOutcome> {\n return configRpc(call, RPC_ENDPOINT_CONFIG_RESET, {});\n}\n\n/** 把配置取值铺成「行键 → 输入框文本」(页面表单态;值缺项即空串,不返空留白)。\n *\n * `prefill` 是**落点回执**(配置面那几格解析出来的绝对路径):标了 `prefillFrom` 的行在取值空着时\n * 直接显示那条绝对路径——用户不必自己拼路径(#743),显示的就是技能真会用的那个目录(逐字相同)。 */\nexport function toDraft(\n values: Record<string, unknown>,\n prefill: { readonly dataDir?: string } = {},\n): Record<string, string> {\n const draft: Record<string, string> = {};\n for (const item of CONFIG_ITEMS) {\n const v = readPath(values, item.key);\n const raw = v === undefined || v === null ? '' : String(v);\n const fallback = item.prefillFrom === undefined ? undefined : prefill[item.prefillFrom];\n draft[item.key] = raw === '' && typeof fallback === 'string' && fallback !== '' ? fallback : raw;\n }\n return draft;\n}\n\n/** 表单态 → 配置取值(按控件种类还原类型;空串对文本项照收,语义由「按默认落点」承担)。 */\nexport function fromDraft(draft: Record<string, string>): Record<string, unknown> {\n const values: Record<string, unknown> = {};\n for (const item of CONFIG_ITEMS) {\n const raw = draft[item.key] ?? '';\n if (item.control === 'number') {\n const n = Number(raw);\n writePath(values, item.key, Number.isFinite(n) ? n : 0);\n } else if (item.control === 'switch') {\n writePath(values, item.key, raw === 'true');\n } else {\n writePath(values, item.key, raw);\n }\n }\n return values;\n}\n\n/** 目录选择的三种结果(注入 picker 的纯归一:选中/取消/这条路供不了)。 */\nexport type PickOutcome =\n | { readonly kind: 'picked'; readonly path: string }\n | { readonly kind: 'cancelled' }\n | { readonly kind: 'unavailable'; readonly message: string };\n\n/** 形状守卫:拿到的那个东西像不像目录选择命名空间(本包只认 `pick` 是函数)。 */\nexport function isDirectoryPicker(raw: unknown): raw is DirectoryPickerFace {\n if (typeof raw !== 'object' || raw === null) return false;\n return typeof (raw as { pick?: unknown }).pick === 'function';\n}\n\n/** 现取目录选择命名空间:**软依赖**,拿不到回 null(页面据此不出入口)。\n *\n * 为什么不用 `inject` 声明:见 cookbook §13——写进声明=硬依赖,提供方缺席时整包被停靠,\n * 设置页会跟着装不上。这里按平台给的「可选查找」办:缺席只是没有入口。\n * 守卫拒绝或提供方中途卸载会抛,一律当「没有」,绝不把设置页带下来。 */\nexport function resolveDirectoryPicker(getService: unknown): DirectoryPickerFace | null {\n if (typeof getService !== 'function') return null;\n try {\n const raw = (getService as (name: string) => unknown)(REMOTE_DIRECTORY_PICKER);\n return isDirectoryPicker(raw) ? raw : null;\n } catch {\n return null;\n }\n}\n\n/** 平台回执/入口三态:**已经收进共用件**(票 #744),这里只同名转出。 */\nexport const readPickAnswer = sharedReadPickAnswer;\nexport const pickerModeOf = sharedPickerModeOf;\n\n/** 唤起一次系统文件夹选择器并归一结果(**永不抛**)。 */\nexport async function pickDirectory(picker: DirectoryPickerFace): Promise<PickOutcome> {\n try {\n return readPickAnswer(await picker.pick());\n } catch (e) {\n const reason = e instanceof Error ? e.message : String(e);\n return { kind: 'unavailable', message: '打不开系统文件夹对话框(' + reason + '):请直接在框里填绝对路径。' };\n }\n}\n\n\n/** 入口三态:有系统对话框(`pick`)/只有应用内浏览(`list` + `createDirectory`)/都没有。\n *\n * 判定取自共用件(一处定义)。本函数只读那个命名空间,不做 IO。 */\nexport function directoryEntryMode(picker: DirectoryPickerFace | null): PickerMode {\n return picker === null ? 'none' : pickerModeOf(picker);\n}\n\n/** 开一行的应用内浏览器:**策略在共用件里**(`openRowBrowser`,一处定义),本处只透出。\n *\n * 为什么不留本地实现:先浏览还是先系统对话框、被拒之后换哪条路,是六家必须一致的一条判断;\n * 各写一份就会各错各的(#744 实测:先认 `pick` 的写法在 Desktop 上每次都吃一次拒绝)。 */\nexport { openRowBrowser };\n\n\n/** 「选择文件夹」按钮的真装配函数(目录行渲染出来的按钮,onClick 就是它)。\n *\n * 回 Promise 是为了本地可判(用例直接 await);接进 React 时调用方 `void` 掉。 */\nexport function createBrowseHandler(deps: {\n readonly picker: DirectoryPickerFace;\n readonly onChange: (key: string, next: string) => void;\n readonly onUnavailable: (message: string) => void;\n}): (key: string) => Promise<void> {\n return async (key: string) => {\n const outcome = await pickDirectory(deps.picker);\n if (outcome.kind === 'picked') deps.onChange(key, outcome.path);\n else if (outcome.kind === 'unavailable') deps.onUnavailable(outcome.message);\n };\n}\n\n/** 一行输入(四种控件对齐受限 YAML 子集:文本/数字/布尔/目录)。\n *\n * 目录档=文本框 + 一枚唤起系统文件夹选择器的按钮;`onBrowse` 缺席(命名空间拿不到、\n * 或这条路已被拒)时不画按钮,文本框照旧——那是该缝自己的契约(供不了就收起入口,不是失败)。 */\nexport function Row(props: {\n readonly item: ConfigItem;\n readonly value: string;\n readonly disabled: boolean;\n readonly onChange: (key: string, next: string) => void;\n /** 目录行才有:入口三态(`native`/`browse`/`none`)。 */\n readonly browser?: DirectoryRowEntry | null | undefined;\n}): React.ReactElement {\n const { item } = props;\n const onChange = (e: React.ChangeEvent<HTMLInputElement>) => props.onChange(item.key, e.target.value);\n const control =\n item.control === 'switch'\n ? React.createElement('input', {\n type: 'checkbox',\n checked: props.value === 'true',\n disabled: props.disabled,\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => props.onChange(item.key, e.target.checked ? 'true' : 'false'),\n })\n : React.createElement('input', {\n style: S.input,\n type: item.control === 'number' ? 'number' : 'text',\n value: props.value,\n disabled: props.disabled,\n spellCheck: false,\n onChange,\n });\n /** 三态入口:供不了(`none`/缺席)就不画,不摆一个点了没反应的死按钮。 */\n const entry = props.browser ?? null;\n const browse =\n item.control === 'directory' && entry !== null && entry.mode !== 'none'\n ? React.createElement(\n 'button',\n {\n style: S.btnPick,\n type: 'button',\n disabled: props.disabled,\n // 回这枚 Promise 是有意的:React 不看 onClick 的返回值,而用例能直接 await 它。\n onClick: () => entry.onOpen(item.key),\n },\n entry.mode === 'native' ? '选择文件夹…' : '浏览…',\n )\n : null;\n return React.createElement(\n 'div',\n { style: S.row },\n React.createElement('div', { style: S.label }, item.title),\n React.createElement('div', { style: S.hint }, item.hint),\n browse === null ? control : React.createElement('div', { style: S.pickRow }, control, browse),\n );\n}\n\n/** 技能设置页:承载饼干记账自己的全部可配置项(只配置,不干活)。\n *\n * 三态:loading(一次 RPC 内)/ready(真表单)/failed(人话报错 + 指引,不返空、不转圈)。 */\nfunction BillConfig(props: { getCall: GetCall; pickerSource: () => DirectoryPickerFace | null }): React.ReactElement {\n const [state, setState] = React.useState<ConfigState>({ kind: 'loading' });\n const [pickerGone, setPickerGone] = React.useState(false);\n const [picking, setPicking] = React.useState(false);\n const [draft, setDraft] = React.useState<Record<string, string>>({});\n const [busy, setBusy] = React.useState(false);\n const [notice, setNotice] = React.useState<string | null>(null);\n const [error, setError] = React.useState<string | null>(null);\n\n const apply = React.useCallback((surface: ConfigSurfaceReply) => {\n setState({ kind: 'ready', surface });\n setDraft(toDraft(surface.values, surface));\n }, []);\n\n const load = React.useCallback(async () => {\n setNotice(null);\n setError(null);\n const r = await fetchConfigSurface(props.getCall());\n if (r.ok) apply(r.surface);\n else setState({ kind: 'failed', message: r.message });\n }, [apply, props.getCall]);\n\n React.useEffect(() => {\n let alive = true;\n void (async () => {\n const r = await fetchConfigSurface(props.getCall());\n if (!alive) return;\n if (r.ok) apply(r.surface);\n else setState({ kind: 'failed', message: r.message });\n })();\n return () => {\n alive = false;\n };\n }, [apply, props.getCall]);\n\n const onChange = React.useCallback((key: string, next: string) => {\n setDraft((prev) => ({ ...prev, [key]: next }));\n setNotice(null);\n setError(null);\n }, []);\n\n /** 目录行入口:三态(系统对话框/应用内浏览/都没有)。\n *\n * **软依赖**:拿不到命名空间就没有入口(留文本框),被拒一次即收起入口,不留死按钮。\n * 状态全部住在组件自己(hook 顺序与旧版同形):浏览器那一份只存「当前开着的那条」。 */\n const picker = pickerGone ? null : props.pickerSource();\n const [browseRow, setBrowseRow] = React.useState<DirectoryRowBrowser | null>(null);\n\n /** 唤起系统文件夹选择器那一条路(应用内浏览被拒时的兜底,也是命名空间只给 `pick` 时的唯一一条)。 */\n const openNative = React.useCallback(\n async (key: string): Promise<void> => {\n const face = pickerGone ? null : props.pickerSource();\n if (face === null) return;\n // 系统对话框是模态的:等它回来的这段时间给一行字,免得看着像「点了没反应」(#743)。\n setPicking(true);\n setError(null);\n const outcome = await pickDirectory(face);\n setPicking(false);\n if (outcome.kind === 'picked') onChange(key, outcome.path);\n else if (outcome.kind === 'unavailable') {\n setError(outcome.message);\n setPickerGone(true);\n }\n },\n [pickerGone, onChange, props.pickerSource],\n );\n\n /** 目录行的入口动作:**先应用内浏览,宿主没给这条路再换系统对话框**(判断在共用件里)。\n *\n * 两条都供不了就不给入口(`undefined` 时 `rowEntry` 为 null ⇒ Row 不画按钮)。 */\n const onOpenRow = React.useCallback(\n async (key: string): Promise<void> => {\n setError(null);\n const opened = openRowBrowser({\n picker,\n initialPath: draft[key] ?? '',\n onChange: (next) => onChange(key, next),\n onRow: setBrowseRow,\n refusalCode: DIRECTORY_PICKER_REFUSED,\n });\n if (opened === undefined || (await opened) === 'refused') await openNative(key);\n },\n [picker, onChange, draft, openNative],\n );\n\n /** 给 Row 的三态入口:`none` 时给 null(不画按钮,文本框照旧)。 */\n const rowEntry: DirectoryRowEntry | null =\n pickerModeOf(picker) === 'none' ? null : { mode: pickerModeOf(picker), onOpen: onOpenRow };\n\n const surface = state.kind === 'ready' ? state.surface : null;\n const dirty = surface !== null && CONFIG_ITEMS.some((i) => (draft[i.key] ?? '') !== (toDraft(surface.values, surface)[i.key] ?? ''));\n\n /** 写完(保存/重置)之后重新读一份整面:写回执是 `{path, values}`、重置回执是 `{path, backupPath}`,\n * 都不是整面——拿写回执当整面用,页头那行「数据目录」会显示成 undefined(#743 实测)。 */\n const writeThenReload = React.useCallback(async (done: string) => {\n setBusy(true);\n setNotice(null);\n setError(null);\n const r = await fetchConfigSurface(props.getCall());\n if (r.ok) {\n apply(r.surface);\n setNotice(done);\n } else {\n setState({ kind: 'failed', message: r.message });\n }\n setBusy(false);\n }, [apply, props.getCall]);\n\n const onSave = React.useCallback(async () => {\n setBusy(true);\n setNotice(null);\n setError(null);\n const r = await saveConfigSurface(props.getCall(), fromDraft(draft));\n setBusy(false);\n if (r.ok) {\n apply(r.surface);\n setNotice('已保存,立即生效(不用重启宿主)');\n } else setError(r.message);\n }, [apply, draft, props.getCall]);\n\n const onReset = React.useCallback(async () => {\n setBusy(true);\n setNotice(null);\n setError(null);\n const r = await resetConfigSurface(props.getCall());\n setBusy(false);\n if (r.ok) await writeThenReload('已重置为默认(原配置已另存一份 .bak)');\n else setError(r.message);\n }, [props.getCall, writeThenReload]);\n\n const head = React.createElement(\n 'div',\n null,\n React.createElement('div', { style: S.title }, `${SLOT_TITLE} · 配置`),\n surface !== null\n ? React.createElement(\n 'div',\n null,\n React.createElement('div', { style: S.info }, `配置文件 ${surface.path}`),\n React.createElement('div', { style: S.info }, `数据目录 ${surface.dataDir}`),\n surface.created ? React.createElement('div', { style: S.muted }, '(配置文件刚按默认值生成)') : null,\n )\n : null,\n );\n\n if (state.kind === 'loading') {\n return React.createElement('div', { style: S.card }, head, React.createElement('div', { style: S.muted }, '配置读取中…'));\n }\n\n if (state.kind === 'failed') {\n return React.createElement(\n 'div',\n { style: S.card },\n React.createElement('div', { style: S.title }, `${SLOT_TITLE} · 配置`),\n React.createElement('div', { style: S.error }, state.message),\n React.createElement(\n 'div',\n { style: S.bar },\n React.createElement('button', { style: S.btn, type: 'button', onClick: () => void load() }, '重试'),\n React.createElement('button', { style: S.btn, type: 'button', onClick: () => void onReset() }, '重置为默认'),\n ),\n );\n }\n\n const common = CONFIG_ITEMS.slice(0, COMMON_ITEM_COUNT);\n const advanced = CONFIG_ITEMS.slice(COMMON_ITEM_COUNT);\n const renderRow = (item: ConfigItem) =>\n React.createElement(Row, {\n key: item.key,\n item,\n value: draft[item.key] ?? '',\n disabled: busy,\n onChange,\n browser: rowEntry,\n });\n\n return React.createElement(\n 'div',\n { style: S.card },\n head,\n React.createElement('div', { style: S.rows }, common.map(renderRow)),\n React.createElement(\n 'details',\n { style: S.advanced },\n React.createElement('summary', { style: S.summary }, ADVANCED_GROUP_TITLE),\n React.createElement('div', { style: S.muted }, ADVANCED_GROUP_NOTE),\n advanced.map(renderRow),\n ),\n React.createElement(\n 'div',\n { style: S.bar },\n React.createElement(\n 'button',\n { style: dirty ? S.btnPrimary : S.btn, type: 'button', disabled: busy || !dirty, onClick: () => void onSave() },\n busy ? '处理中…' : '保存',\n ),\n React.createElement('button', { style: S.btn, type: 'button', disabled: busy, onClick: () => void onReset() }, '重置为默认'),\n React.createElement('button', { style: S.btn, type: 'button', disabled: busy, onClick: () => void load() }, '重新读取'),\n ),\n picking ? React.createElement('div', { style: S.muted }, '已唤起系统文件夹对话框:选中后自动填上,取消则不动。') : null,\n browseRow !== null ? React.createElement(DirectoryBrowserFromRow, {\n open: true,\n row: browseRow,\n labels: {\n title: '选择文件夹',\n close: '关闭',\n up: '上一级',\n pathPlaceholder: '直接填绝对路径,回车即进入',\n go: '转到',\n showHidden: (n: number) => '显示隐藏目录(' + n + ')',\n empty: '这个目录里没有子目录。',\n loading: '正在读取…',\n newFolder: '新建文件夹',\n createConfirm: '创建',\n createCancel: '取消',\n select: '选',\n selected: '已选',\n open: '选定这个目录',\n cancel: '取消',\n willPick: '将选定:',\n },\n }) : null,\n notice !== null ? React.createElement('div', { style: S.okText }, notice) : null,\n error !== null ? React.createElement('div', { style: S.error }, error) : null,\n );\n}\n\nexport function apply(ctx: ClientCtx): void {\n // 调用口取用器透传给组件(每次取数现取;非函数由组件判缺席)。\n const getCall: GetCall = () => ctx.connection?.rpc?.call ?? null;\n // #736 目录选择:**软依赖**,每次渲染现取(提供方后到/中途卸载都不留痕);\n // 拿不到就只是没有入口,设置页照常(不声明、不停靠,见 cookbook §13)。\n const getPicker = (): DirectoryPickerFace | null =>\n typeof ctx.get === 'function' ? resolveDirectoryPicker((name: string) => ctx.get?.(name)) : null;\n\n // 技能设置页 → 爱生活页签槽(总管声明;总管缺席时 inject 等待,不断链)。\n ctx.slots.inject('ilife.config-tab', () =>\n ctx.slots.register(\n {\n name: 'ilife.config-tab',\n id: PLUGIN,\n order: SLOT_ORDER,\n label: () => SLOT_TITLE,\n // #706:本包自己那条 RPC 通道交给总管(各家都写这一格),总管据此调配置体检——\n // 它因此不必在源码里写死任何一家的通道名(零单品依赖照旧成立)。\n channel: RPC_CHANNEL,\n },\n () => React.createElement(BillConfig, { getCall, pickerSource: getPicker }),\n ),\n );\n}\n"],"mappings":"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"}
package/dist/dsh-ctx.d.ts CHANGED
@@ -119,7 +119,9 @@ export interface SkillHostCtx {
119
119
  }
120
120
  /** 宿主目录选择命名空间的服务名(#736;出处与禁令见 cookbook §13)。 */
121
121
  export declare const REMOTE_DIRECTORY_PICKER: "remote.directoryPicker";
122
- /** 宿主目录选择命名空间(DSH 平台提供)。本包**只用 `pick` 这一格**:不给 signal(平台那格可选)。
122
+ /** 宿主目录选择命名空间(DSH 平台提供):本包用它的 `pick`(系统对话框)与
123
+ * `list`/`createDirectory`(应用内浏览那两格原语,由共用件的 `browseFaceOf` 拆信封)。不给 signal
124
+ * (平台那格可选;描述符按「业务参数个数 + 1」判有没有 signal,显式传 `undefined` 会炸)。
123
125
  *
124
126
  * **回执是信封,不是路径**(#743 真机缺陷的根因):客户端把每次 Remote 调用包成
125
127
  * `{ok:true,value}`/`{ok:false,error}`(`@deepseek-ai/dsh-api-gateway/lib/client.js` 的 `invoke()`:
@@ -134,6 +136,14 @@ export interface DirectoryPickerAnswer {
134
136
  readonly message?: string;
135
137
  };
136
138
  }
139
+ /** 宿主「这条路没给」的回执码(#744):一次组合里只服务一种能力(native 或 browse),
140
+ * 另一条路上的动词一律回它。
141
+ *
142
+ * 出处:`@deepseek-ai/dsh-api-workspace-controller/lib/types/directory-picker.js` 的 `requireCapability()`
143
+ * ——`RemoteError('directory-picker/unavailable', …)`。三条动词在客户端命名空间上都在,
144
+ * 所以只有真调一次、看它拒没拒,才知道组合里服务哪种能力。
145
+ * 共用件不认识这个名字(只照比对),于是这条宿主事实住在本镜像里。 */
146
+ export declare const DIRECTORY_PICKER_REFUSED: "directory-picker/unavailable";
137
147
  export interface DirectoryPickerFace {
138
148
  pick(): Promise<DirectoryPickerAnswer>;
139
149
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dsh-ctx.d.ts","sourceRoot":"","sources":["../src/dsh-ctx.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dsh-ctx.d.ts","sourceRoot":"","sources":["../src/dsh-ctx.ts"],"names":[],"mappings":"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"}
package/dist/dsh-ctx.js CHANGED
@@ -14,4 +14,12 @@
14
14
  */
15
15
  /** 宿主目录选择命名空间的服务名(#736;出处与禁令见 cookbook §13)。 */
16
16
  export const REMOTE_DIRECTORY_PICKER = 'remote.directoryPicker';
17
+ /** 宿主「这条路没给」的回执码(#744):一次组合里只服务一种能力(native 或 browse),
18
+ * 另一条路上的动词一律回它。
19
+ *
20
+ * 出处:`@deepseek-ai/dsh-api-workspace-controller/lib/types/directory-picker.js` 的 `requireCapability()`
21
+ * ——`RemoteError('directory-picker/unavailable', …)`。三条动词在客户端命名空间上都在,
22
+ * 所以只有真调一次、看它拒没拒,才知道组合里服务哪种能力。
23
+ * 共用件不认识这个名字(只照比对),于是这条宿主事实住在本镜像里。 */
24
+ export const DIRECTORY_PICKER_REFUSED = 'directory-picker/unavailable';
17
25
  //# sourceMappingURL=dsh-ctx.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dsh-ctx.js","sourceRoot":"","sources":["../src/dsh-ctx.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAgHH,gDAAgD;AAChD,MAAM,CAAC,MAAM,uBAAuB,GAAG,wBAAiC,CAAC"}
1
+ {"version":3,"file":"dsh-ctx.js","sourceRoot":"","sources":["../src/dsh-ctx.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAgHH,gDAAgD;AAChD,MAAM,CAAC,MAAM,uBAAuB,GAAG,wBAAiC,CAAC;AAiBzE;;;;;;sCAMsC;AACtC,MAAM,CAAC,MAAM,wBAAwB,GAAG,8BAAuC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-bill-ilife",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "爱生活单品插件 P10 脚手架:记账(ilife:cookie order 95),硬依赖总管 dsh-life-pack",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,12 +20,17 @@
20
20
  "plugin",
21
21
  "ilife"
22
22
  ],
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/FeatherHunter/ilife.git",
26
+ "directory": "packages/plugin-bill-ilife"
27
+ },
23
28
  "license": "MIT",
24
29
  "engines": {
25
30
  "node": ">=22.13"
26
31
  },
27
32
  "dependencies": {
28
- "dsh-life-pack": "^0.3.3",
33
+ "dsh-life-pack": "^0.3.4",
29
34
  "skill-bill": "0.3.0"
30
35
  },
31
36
  "dsh": {