@dsh-workbench/core 1.1.14 → 1.1.16

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.
@@ -1,7 +1,7 @@
1
1
  import { homedir, tmpdir } from "node:os";
2
2
  import dayjs from "dayjs";
3
- import fs, { accessSync, closeSync, constants, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
4
- import path, { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
3
+ import fs, { accessSync, appendFileSync, closeSync, constants, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
4
+ import path, { basename, dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
5
5
  import writeFileAtomic from "write-file-atomic";
6
6
  import utc from "dayjs/plugin/utc.js";
7
7
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -782,6 +782,15 @@ function writeTextFile(path, text, options = {}) {
782
782
  writeFileAtomic.sync(path, text, atomicOptionsOf(mode, options));
783
783
  }
784
784
  /**
785
+ * 往文件末尾追加文本;文件或目录不存在就建。追加不是原子写,只给「日志/采集」这类只增不改的流。
786
+ * @param path 目标文件。
787
+ * @param text 追加内容。
788
+ */
789
+ function appendTextFile(path, text) {
790
+ mkdirSync(dirname(path), { recursive: true });
791
+ appendFileSync(path, text, { mode: modeOf(path, "keep") });
792
+ }
793
+ /**
785
794
  * `writeTextFile` 的异步版;语义与默认值相同。
786
795
  * @param path 目标文件。
787
796
  * @param text 文本内容。
@@ -3805,7 +3814,7 @@ function registerDiyRecordAct(ctx, resource, log) {
3805
3814
  }
3806
3815
  //#endregion
3807
3816
  //#region ../shared/dist/host/long-task.js
3808
- var LIVE = /* @__PURE__ */ new Set(["running", "needs-login"]);
3817
+ var LIVE$1 = /* @__PURE__ */ new Set(["running", "needs-login"]);
3809
3818
  var RECENT_MS = 6e5;
3810
3819
  function createLongTaskRegistry(deps) {
3811
3820
  const runs = /* @__PURE__ */ new Map();
@@ -3817,7 +3826,7 @@ function createLongTaskRegistry(deps) {
3817
3826
  const listeners = /* @__PURE__ */ new Set();
3818
3827
  const live = (id) => {
3819
3828
  const current = runs.get(id);
3820
- if (current === void 0 || !LIVE.has(current.status)) return null;
3829
+ if (current === void 0 || !LIVE$1.has(current.status)) return null;
3821
3830
  return current;
3822
3831
  };
3823
3832
  const notify = (change) => {
@@ -3854,13 +3863,13 @@ function createLongTaskRegistry(deps) {
3854
3863
  if (snapshot.workspace !== workspace) continue;
3855
3864
  newest.set(snapshot.target, snapshot);
3856
3865
  }
3857
- return [...newest.values()].filter((snapshot) => LIVE.has(snapshot.status) || epochOf(snapshot.at) >= floor).reverse().map((snapshot) => ({
3866
+ return [...newest.values()].filter((snapshot) => LIVE$1.has(snapshot.status) || epochOf(snapshot.at) >= floor).reverse().map((snapshot) => ({
3858
3867
  ...deps.view(snapshot),
3859
3868
  id: snapshot.id
3860
3869
  }));
3861
3870
  },
3862
3871
  live() {
3863
- return [...runs.values()].filter((snapshot) => LIVE.has(snapshot.status));
3872
+ return [...runs.values()].filter((snapshot) => LIVE$1.has(snapshot.status));
3864
3873
  },
3865
3874
  start(options) {
3866
3875
  const running = this.active(options.workspace, options.target);
@@ -5137,6 +5146,33 @@ function createWorkspaceStore(options) {
5137
5146
  };
5138
5147
  }
5139
5148
  //#endregion
5149
+ //#region ../shared/dist/host/shell-render.js
5150
+ var QUOTED_HEAD = /^\s*['"]/;
5151
+ function callOperatorOf(kind, script) {
5152
+ if (kind !== "pwsh" || !QUOTED_HEAD.test(script)) return script;
5153
+ return `& ${script}`;
5154
+ }
5155
+ function invocationOf(kind, script) {
5156
+ return kind === "pwsh" ? {
5157
+ file: "pwsh",
5158
+ args: [
5159
+ "-NoProfile",
5160
+ "-NonInteractive",
5161
+ "-Command",
5162
+ callOperatorOf("pwsh", script)
5163
+ ]
5164
+ } : {
5165
+ file: "bash",
5166
+ args: ["-c", script]
5167
+ };
5168
+ }
5169
+ function quotedArgvLine(argv, platform = process.platform) {
5170
+ return argv.map((one) => quoteShellArgFor(one, platform)).join(" ");
5171
+ }
5172
+ function quotedLineOf(invocation, platform = process.platform) {
5173
+ return quotedArgvLine([invocation.file, ...invocation.args], platform);
5174
+ }
5175
+ //#endregion
5140
5176
  //#region ../shared/dist/host/shell.js
5141
5177
  var DEFAULT_MAX_BYTES$3 = 8388608;
5142
5178
  var GUARD_SLACK_MS = 1e3;
@@ -5208,7 +5244,7 @@ function insideOf(root, path) {
5208
5244
  }
5209
5245
  function productPolicy(ceiling, productRoot) {
5210
5246
  return {
5211
- mode: ceiling === "read-only" ? "read-only" : "workspace-write",
5247
+ mode: ceiling === "read-only" ? "read-only" : ceiling === "danger-full-access" ? "danger-full-access" : "workspace-write",
5212
5248
  workspaceRoot: resolve(productRoot)
5213
5249
  };
5214
5250
  }
@@ -5221,6 +5257,9 @@ function workspacePolicy(ceiling, grant) {
5221
5257
  workspaceRoot: resolve(grant.workspaceRoot)
5222
5258
  };
5223
5259
  }
5260
+ function atFilesystemRoot(path) {
5261
+ return parse(path).root === path;
5262
+ }
5224
5263
  function commonDirectory(paths) {
5225
5264
  const dirs = paths.map((path) => dirname(resolve(path)));
5226
5265
  let shared = dirs[0];
@@ -5233,8 +5272,8 @@ function pathsPolicy(ceiling, paths) {
5233
5272
  if (!paths.every((path) => isAbsolute(path))) throw new SeamError("paths-span-trees", "外部路径必须是绝对路径");
5234
5273
  if (ceiling === "read-only") throw new SeamError("read-only", "当前部署是只读模式:这条命令不能写外部路径");
5235
5274
  const root = commonDirectory(paths);
5236
- if (root === void 0 || root === sep && paths.length > 1) throw new SeamError("paths-span-trees", "这次写入跨了多个目录:请分开做,或把产物改到工作台目录");
5237
- if (root === sep) throw new SeamError("paths-span-trees", "不能把整个文件系统声明成写入域");
5275
+ if (root === void 0) throw new SeamError("paths-span-trees", "这次写入没有给出任何路径");
5276
+ if (atFilesystemRoot(root)) throw new SeamError("paths-span-trees", paths.length > 1 ? "这次写入跨了多个目录:请分开做,或把产物改到工作台目录" : "不能把整个文件系统声明成写入域");
5238
5277
  return {
5239
5278
  mode: "workspace-write",
5240
5279
  workspaceRoot: root
@@ -5422,23 +5461,9 @@ function targetOf(request) {
5422
5461
  throw new SeamError("scope-not-declared", "这条执行请求要么给 argv,要么给一条命令行,不能两个都给或都不给");
5423
5462
  }
5424
5463
  function displayOf(target) {
5425
- return "argv" in target ? target.argv.map((one) => quoteShellArg(one)).join(" ") : target.line;
5464
+ return "argv" in target ? quotedArgvLine(target.argv) : target.line;
5426
5465
  }
5427
- function shellInvocationOf(shellKind, line) {
5428
- return shellKind === "pwsh" ? {
5429
- file: "pwsh",
5430
- args: [
5431
- "-NoProfile",
5432
- "-NonInteractive",
5433
- "-Command",
5434
- line
5435
- ]
5436
- } : {
5437
- file: "bash",
5438
- args: ["-c", line]
5439
- };
5440
- }
5441
- function invocationOf(target, shellKind) {
5466
+ function targetInvocationOf(target, shellKind) {
5442
5467
  if ("argv" in target) {
5443
5468
  const [file, ...args] = target.argv;
5444
5469
  if (file === void 0) throw new SeamError("scope-not-declared", "argv 是空的:至少给一个可执行文件");
@@ -5447,13 +5472,12 @@ function invocationOf(target, shellKind) {
5447
5472
  args
5448
5473
  };
5449
5474
  }
5450
- return shellInvocationOf(shellKind ?? shellKindOf(), target.line);
5475
+ return invocationOf(shellKind ?? shellKindOf(), target.line);
5451
5476
  }
5452
5477
  function sandboxedLineOf(target, shellKind) {
5453
- if ("argv" in target) return target.argv.map((one) => quoteShellArg(one)).join(" ");
5454
- if (shellKind === void 0) return target.line;
5455
- const invocation = shellInvocationOf(shellKind, target.line);
5456
- return [invocation.file, ...invocation.args].map((one) => quoteShellArg(one)).join(" ");
5478
+ const line = "argv" in target ? quotedArgvLine(target.argv) : target.line;
5479
+ if (shellKind === void 0) return callOperatorOf(shellKindOf(), line);
5480
+ return callOperatorOf(shellKindOf(), quotedLineOf(invocationOf(shellKind, line)));
5457
5481
  }
5458
5482
  function createSeam(ports) {
5459
5483
  const runtimeRoot = ports.runtimeRoot ?? workbenchRoot();
@@ -5505,7 +5529,7 @@ function createSeam(ports) {
5505
5529
  }
5506
5530
  }
5507
5531
  async function runHost(request, target) {
5508
- const invocation = invocationOf(target, request.shellKind);
5532
+ const invocation = targetInvocationOf(target, request.shellKind);
5509
5533
  const child = processes.spawn({
5510
5534
  file: invocation.file,
5511
5535
  args: invocation.args,
@@ -5561,7 +5585,7 @@ function createSeam(ports) {
5561
5585
  };
5562
5586
  }
5563
5587
  function spawnHost(request) {
5564
- const invocation = invocationOf(request.target, void 0);
5588
+ const invocation = targetInvocationOf(request.target, void 0);
5565
5589
  const logFile = request.logFile ?? join(runtimeRoot, "logs", `${request.label ?? "spawn"}-${String(now())}.log`);
5566
5590
  mkdirSync(dirname(logFile), { recursive: true });
5567
5591
  const child = processes.spawn({
@@ -5625,7 +5649,7 @@ function createSeam(ports) {
5625
5649
  };
5626
5650
  }
5627
5651
  function openHost(request) {
5628
- const invocation = invocationOf(request.target, void 0);
5652
+ const invocation = targetInvocationOf(request.target, void 0);
5629
5653
  const child = processes.spawn({
5630
5654
  file: invocation.file,
5631
5655
  args: invocation.args,
@@ -10130,25 +10154,6 @@ function actErrorOf(step) {
10130
10154
  return step.stderr || step.stdout || "运行时报了个错";
10131
10155
  }
10132
10156
  var SWEEP_TIMEOUT_MS = 4e3;
10133
- async function stopNetworkExact(seam, file, timeoutMs = SWEEP_TIMEOUT_MS) {
10134
- const pidFile = quoteShellArg(`${file}.pid`);
10135
- const reaperFile = quoteShellArg(`${file}.reaper`);
10136
- const line = [
10137
- `p="$(cat ${pidFile} 2>/dev/null)"`,
10138
- `r="$(cat ${reaperFile} 2>/dev/null)"`,
10139
- `[ -n "$p" ] && pkill -P "$p" 2>/dev/null`,
10140
- `[ -n "$p" ] && kill "$p" 2>/dev/null`,
10141
- `[ -n "$r" ] && pkill -P "$r" 2>/dev/null`,
10142
- `[ -n "$r" ] && kill "$r" 2>/dev/null`,
10143
- `if [ "$(cat ${pidFile} 2>/dev/null)" = "$p" ]; then rm -f ${pidFile} ${reaperFile}; fi`,
10144
- "true"
10145
- ].join("; ");
10146
- return (await seam.run({
10147
- command: line,
10148
- intent: productWrite(),
10149
- timeoutMs
10150
- })).exitCode === 0;
10151
- }
10152
10157
  //#endregion
10153
10158
  //#region src/modules/panels/model/diy-logs.ts
10154
10159
  var LOGS_KEY = "__dshDiyLogs";
@@ -10639,7 +10644,7 @@ function wrapFirst(argv, card) {
10639
10644
  if (first === void 0 || first.startsWith("-")) return [...argv];
10640
10645
  return [evalWrapped(first, card), ...argv.slice(1)];
10641
10646
  }
10642
- function argvOf(row, raw, unit) {
10647
+ function argvOf$1(row, raw, unit) {
10643
10648
  const card = cardSelectorIn(unit);
10644
10649
  const declared = stringsOf$1(raw["args"]);
10645
10650
  const tokens = withToken(row.evalInCard === true && asText(raw["in"]) === "card" ? wrapFirst(declared, card) : declared, card);
@@ -10925,7 +10930,7 @@ async function runBrowserTool(row, ctx, raw, exec) {
10925
10930
  if (needsCardOf(row, page.args) && page.unit === "") throw new Error("要卡作用域(scope=card / $card / in=card)就得给一个 unit —— 这个工作区里不止一个模块时,我不知道你说哪一张卡。");
10926
10931
  const requested = asNumber(page.args["timeoutMs"]);
10927
10932
  const timeoutMs = requested !== void 0 && requested > 0 ? requested : DEFAULT_TIMEOUT_MS$4;
10928
- const argv = argvOf(row, page.args, page.unit);
10933
+ const argv = argvOf$1(row, page.args, page.unit);
10929
10934
  const clip = row.clip === true ? clipOf(page.args["clip"]) : void 0;
10930
10935
  const clipAsked = row.clip === true && page.args["clip"] !== void 0;
10931
10936
  let file = "";
@@ -11076,8 +11081,17 @@ function prepareStaging(unit, resume) {
11076
11081
  force: true
11077
11082
  });
11078
11083
  mkdirSync(staging, { recursive: true });
11084
+ if (resume) seedJournal(staging, unit);
11079
11085
  return staging;
11080
11086
  }
11087
+ function seedJournal(staging, unit) {
11088
+ const to = journalFileOf(staging, unit);
11089
+ if (existsSync(to)) return;
11090
+ const from = join(workbenchRoot(), "releases", `${unit}.json`);
11091
+ if (!existsSync(from)) return;
11092
+ mkdirSync(dirname(to), { recursive: true });
11093
+ copyFileSync(from, to);
11094
+ }
11081
11095
  function cleanupStaging(unit) {
11082
11096
  rmSync(stagingDirOf(unit), {
11083
11097
  recursive: true,
@@ -11136,13 +11150,34 @@ function resumablePayload(payload, unit) {
11136
11150
  }
11137
11151
  function outcomeOf$1(result, unit) {
11138
11152
  const payload = jsonOf(result.stdout);
11139
- if (result.exitCode === 3 && payload !== void 0) return { payload: needsUserPayload(payload) };
11140
- if (result.exitCode === 4 && payload !== void 0) return { payload: resumablePayload(payload, unit) };
11153
+ if (result.exitCode === 3 && payload !== void 0) return {
11154
+ kind: "needs-user",
11155
+ payload: needsUserPayload(payload)
11156
+ };
11157
+ if (result.exitCode === 4 && payload !== void 0) return {
11158
+ kind: "resumable",
11159
+ payload: resumablePayload(payload, unit)
11160
+ };
11141
11161
  if (result.exitCode !== 0 || payload === void 0) {
11142
11162
  const tail = `${result.stderr}\n${result.stdout}`.trim().split("\n").slice(-6).join(" | ");
11143
- return { error: /* @__PURE__ */ new Error(`发布没走通(exit ${String(result.exitCode)})${tail === "" ? "" : `:${tail}`}`) };
11163
+ return {
11164
+ kind: "failed",
11165
+ error: /* @__PURE__ */ new Error(`发布没走通(exit ${String(result.exitCode)})${tail === "" ? "" : `:${tail}`}`)
11166
+ };
11167
+ }
11168
+ return {
11169
+ kind: "done",
11170
+ payload
11171
+ };
11172
+ }
11173
+ function publishProblemOf(payload) {
11174
+ if (payload["ok"] === false) {
11175
+ const reason = asText(payload["text"]);
11176
+ return `市场索引这一步回的是失败,这次没发出去${reason === "" ? "" : `:${reason}`}`;
11144
11177
  }
11145
- return { payload };
11178
+ if (asText(payload["target"]) !== "market") return void 0;
11179
+ if (asText(asRecord$1(payload["market"])["url"]) !== "" && asText(payload["install"]) !== "") return void 0;
11180
+ return "市场索引这一步没给出市场地址或安装命令 —— 这次没发出去。重发一次;再这样先把本地索引镜像同步到 origin 再发。";
11146
11181
  }
11147
11182
  function successPayloadOf(payload, found, options) {
11148
11183
  const name = asText(payload["name"]);
@@ -11313,7 +11348,8 @@ function registerPublishTool(ctx, tools, deps) {
11313
11348
  });
11314
11349
  if (verify || refresh) {
11315
11350
  const outcome = outcomeOf$1(await run([], { intent: productWrite() }), found.unit);
11316
- if ("error" in outcome) throw outcome.error;
11351
+ if (outcome.kind === "failed") throw outcome.error;
11352
+ if (outcome.kind !== "done") return plainJson(outcome.payload);
11317
11353
  return plainJson(successPayloadOf(outcome.payload, found, { verify }));
11318
11354
  }
11319
11355
  const staging = prepareStaging(found.unit, resume);
@@ -11325,7 +11361,8 @@ function registerPublishTool(ctx, tools, deps) {
11325
11361
  env
11326
11362
  }), found.unit);
11327
11363
  mirrorJournal(staging, found.unit);
11328
- if ("error" in firstOutcome) throw firstOutcome.error;
11364
+ if (firstOutcome.kind === "failed") throw firstOutcome.error;
11365
+ if (firstOutcome.kind !== "done") return plainJson(firstOutcome.payload);
11329
11366
  const first = firstOutcome.payload;
11330
11367
  if (first["stop"] !== "workspace-only" || asText(first["target"]) === "local") {
11331
11368
  cleanupStaging(found.unit);
@@ -11336,9 +11373,13 @@ function registerPublishTool(ctx, tools, deps) {
11336
11373
  env
11337
11374
  }), found.unit);
11338
11375
  mirrorJournal(staging, found.unit);
11339
- if ("error" in indexOutcome) throw indexOutcome.error;
11376
+ if (indexOutcome.kind === "failed") throw indexOutcome.error;
11377
+ if (indexOutcome.kind !== "done") return plainJson(indexOutcome.payload);
11378
+ const merged = adoptTarball(withPhasePayloads(first, indexOutcome.payload), found.unit);
11379
+ const problem = publishProblemOf(merged);
11380
+ if (problem !== void 0) throw new Error(problem);
11340
11381
  cleanupStaging(found.unit);
11341
- return plainJson(successPayloadOf(adoptTarball(withPhasePayloads(first, indexOutcome.payload), found.unit), found, { verify: false }));
11382
+ return plainJson(successPayloadOf(merged, found, { verify: false }));
11342
11383
  }
11343
11384
  };
11344
11385
  return tools.register(publishTool);
@@ -11718,7 +11759,7 @@ function ageText(at, now) {
11718
11759
  function stampOf(at) {
11719
11760
  return `${dayjs(at).utc().format("HH:mm:ss")}Z`;
11720
11761
  }
11721
- function readText$1(read, now) {
11762
+ function readText$2(read, now) {
11722
11763
  const when = `${stampOf(read.at)} · ${ageText(read.at, now)}`;
11723
11764
  if (read.phase === "ready") return `${read.route} 读到了(${when})`;
11724
11765
  if (read.phase === "loading") return `${read.route} 正在读(${when})`;
@@ -11735,7 +11776,7 @@ function renderPanel(options) {
11735
11776
  const title = `—— ${row.unit}${row.name === "" ? "" : `(${row.name})`} · ${healthText(row.health)}`;
11736
11777
  const report = row.report;
11737
11778
  if (report === void 0) return [title, ` 没上报:卡片没渲染出来,或者用户那一页还没刷新到带这份补丁的版本`].join("\n");
11738
- const reads = report.cards.slice(0, 6).map((one) => ` 数据 · ${readText$1(one, now)}`);
11779
+ const reads = report.cards.slice(0, 6).map((one) => ` 数据 · ${readText$2(one, now)}`);
11739
11780
  const more = report.cards.length > 6 ? [` 数据 · …还有 ${String(report.cards.length - 6)} 条`] : [];
11740
11781
  const crash = report.crash === void 0 ? " 崩过:没有" : ` 崩过:是 —— ${report.crash.reason}(${stampOf(report.crash.at)} · 共 ${String(report.crash.count)} 次)`;
11741
11782
  return [
@@ -12344,6 +12385,197 @@ function changelogOf(dir, version, note, kind) {
12344
12385
  };
12345
12386
  }
12346
12387
  //#endregion
12388
+ //#region src/shared/api/host/diy-capture-live.ts
12389
+ var LIVE_POLL_MS = 3e3;
12390
+ var LIVE_ARM_MS = 2e3;
12391
+ var LIVE_SNAP_TIMEOUT_MS = 15e3;
12392
+ var LIVE_TAIL_LINES = 2e4;
12393
+ var STATIC_URL = new RegExp(`^(data:)|(\\.(js|css|png|jpe?g|gif|svg|woff2?|ico|map|webp|mp4)(\\?|$))`, "i");
12394
+ var LIVE = /* @__PURE__ */ new Map();
12395
+ function plainOf(value) {
12396
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
12397
+ }
12398
+ function argvOf(command, session, args) {
12399
+ return [
12400
+ command === "" ? "runtime" : command,
12401
+ "browser",
12402
+ session,
12403
+ ...args,
12404
+ "--window",
12405
+ "background"
12406
+ ];
12407
+ }
12408
+ function parsedRecord(text) {
12409
+ let parsed;
12410
+ try {
12411
+ parsed = JSON.parse(text);
12412
+ } catch {
12413
+ return;
12414
+ }
12415
+ return plainOf(parsed);
12416
+ }
12417
+ function withoutStatic(record) {
12418
+ const inside = (Array.isArray(record["entries"]) ? record["entries"] : []).filter((one) => !STATIC_URL.test(String(plainOf(one)?.["url"] ?? "")));
12419
+ return inside.length === 0 ? "" : JSON.stringify({
12420
+ ...record,
12421
+ entries: inside
12422
+ });
12423
+ }
12424
+ function filterStaticLines(raw) {
12425
+ const document = parsedRecord(raw.trim());
12426
+ if (document !== void 0 && Array.isArray(document["entries"])) return withoutStatic(document);
12427
+ const kept = [];
12428
+ for (const line of raw.split("\n")) {
12429
+ const text = line.trim();
12430
+ if (text === "") continue;
12431
+ const record = parsedRecord(text);
12432
+ if (record === void 0 || !Array.isArray(record["entries"])) {
12433
+ kept.push(text);
12434
+ continue;
12435
+ }
12436
+ const compact = withoutStatic(record);
12437
+ if (compact !== "") kept.push(compact);
12438
+ }
12439
+ return kept.join("\n");
12440
+ }
12441
+ async function captureTickOnce(seam, command, session, file, timeoutMs = LIVE_SNAP_TIMEOUT_MS) {
12442
+ const step = await seam.run({
12443
+ argv: argvOf(command, session, [
12444
+ "network",
12445
+ "--all",
12446
+ "--raw"
12447
+ ]),
12448
+ intent: productWrite(),
12449
+ timeoutMs
12450
+ });
12451
+ const round = step.exitCode === 0 ? "ok" : "fail";
12452
+ writeTextFile(`${file}.first`, round);
12453
+ const text = filterStaticLines(step.stdout);
12454
+ if (text !== "") appendTextFile(file, `${text}\n`);
12455
+ return round;
12456
+ }
12457
+ async function startLiveCapture(input) {
12458
+ const { seam, command, session, file } = input;
12459
+ stopLiveCapture(file);
12460
+ writeTextFile(file, "");
12461
+ for (const suffix of [
12462
+ ".first",
12463
+ ".last",
12464
+ ".round",
12465
+ ".pid",
12466
+ ".reaper"
12467
+ ]) rmSync(`${file}${suffix}`, { force: true });
12468
+ try {
12469
+ const arm = await seam.run({
12470
+ argv: argvOf(command, session, [
12471
+ "network",
12472
+ "--follow",
12473
+ "--all"
12474
+ ]),
12475
+ intent: productWrite(),
12476
+ timeoutMs: LIVE_ARM_MS
12477
+ });
12478
+ if (!arm.timedOut && arm.exitCode !== 0) return false;
12479
+ const text = arm.stdout.trim();
12480
+ if (text !== "") appendTextFile(file, `${text}\n`);
12481
+ } catch {
12482
+ return false;
12483
+ }
12484
+ const entry = {
12485
+ file,
12486
+ seam,
12487
+ command,
12488
+ session,
12489
+ timer: void 0,
12490
+ stopped: false
12491
+ };
12492
+ LIVE.set(file, entry);
12493
+ scheduleTick(entry);
12494
+ return true;
12495
+ }
12496
+ function scheduleTick(entry) {
12497
+ entry.timer = setTimeout(() => {
12498
+ runTick(entry);
12499
+ }, LIVE_POLL_MS);
12500
+ entry.timer.unref?.();
12501
+ }
12502
+ async function runTick(entry) {
12503
+ if (entry.stopped) return;
12504
+ try {
12505
+ await captureTickOnce(entry.seam, entry.command, entry.session, entry.file);
12506
+ } catch {
12507
+ entry.timer = void 0;
12508
+ }
12509
+ if (entry.stopped) return;
12510
+ scheduleTick(entry);
12511
+ }
12512
+ function stopLiveCapture(file) {
12513
+ const live = LIVE.get(file);
12514
+ for (const suffix of [
12515
+ ".pid",
12516
+ ".reaper",
12517
+ ".round"
12518
+ ]) rmSync(`${file}${suffix}`, { force: true });
12519
+ if (live !== void 0) {
12520
+ live.stopped = true;
12521
+ if (live.timer !== void 0) clearTimeout(live.timer);
12522
+ LIVE.delete(file);
12523
+ }
12524
+ return live !== void 0 || existsSync(`${file}.last`) || existsSync(`${file}.first`);
12525
+ }
12526
+ function liveCaptureOf(file) {
12527
+ return LIVE.has(file);
12528
+ }
12529
+ function readText$1(file) {
12530
+ try {
12531
+ return readFileSync(file, "utf8");
12532
+ } catch {
12533
+ return "";
12534
+ }
12535
+ }
12536
+ function readCaptureLines(file) {
12537
+ return (readText$1(file).match(/\n/g) ?? []).length;
12538
+ }
12539
+ function readCaptureRound(file) {
12540
+ const text = readText$1(`${file}.first`).trim();
12541
+ return text === "ok" || text === "fail" ? text : "";
12542
+ }
12543
+ function readCaptureText(file) {
12544
+ const text = readText$1(file);
12545
+ if (text === "") return {
12546
+ text: "",
12547
+ truncated: false
12548
+ };
12549
+ const lines = text.split("\n");
12550
+ if (text.endsWith("\n")) lines.pop();
12551
+ const kept = lines.slice(-2e4);
12552
+ return {
12553
+ text: kept.length === 0 ? "" : `${kept.join("\n")}\n`,
12554
+ truncated: lines.length > LIVE_TAIL_LINES
12555
+ };
12556
+ }
12557
+ function readCaptureLinesWithKey(file, key) {
12558
+ const text = readText$1(file);
12559
+ if (text === "") return "";
12560
+ const compact = `"key":"${key}"`;
12561
+ const spaced = `"key": "${key}"`;
12562
+ const out = [];
12563
+ for (const line of text.split("\n")) {
12564
+ if (out.length >= 20) break;
12565
+ if (line.includes(compact) || line.includes(spaced)) out.push(line);
12566
+ }
12567
+ return out.length === 0 ? "" : `${out.join("\n")}\n`;
12568
+ }
12569
+ function keepCaptureFile(file) {
12570
+ const last = `${file}.last`;
12571
+ try {
12572
+ renameSync(file, last);
12573
+ return statSync(last).size > 0;
12574
+ } catch {
12575
+ return false;
12576
+ }
12577
+ }
12578
+ //#endregion
12347
12579
  //#region src/modules/panels/model/diy-observers.ts
12348
12580
  async function stopObservers(ctx, workspace, session = BROWSER_SCREEN_SESSION) {
12349
12581
  const seam = seamOf(ctx);
@@ -12353,9 +12585,9 @@ async function stopObservers(ctx, workspace, session = BROWSER_SCREEN_SESSION) {
12353
12585
  };
12354
12586
  const file = join(artifactsDirOf(workspace), `net-${session}.jsonl`);
12355
12587
  const command = cliCommandOf(ctx);
12356
- const [net, step] = await Promise.all([stopNetworkExact(seam, file), actBrowser(seam, command, session, ["eval", LOGS_STOP_SCRIPT], SWEEP_TIMEOUT_MS)]);
12588
+ const [stopped, step] = await Promise.all([Promise.resolve(stopLiveCapture(file)), actBrowser(seam, command, session, ["eval", LOGS_STOP_SCRIPT], SWEEP_TIMEOUT_MS)]);
12357
12589
  return {
12358
- net,
12590
+ net: stopped || !liveCaptureOf(file),
12359
12591
  logs: typeof step.payload?.["unhooked"] === "number"
12360
12592
  };
12361
12593
  }
@@ -13160,19 +13392,13 @@ function pickOf(entries, key, at = null) {
13160
13392
  //#endregion
13161
13393
  //#region src/modules/panels/model/diy-record.ts
13162
13394
  var OPEN_TIMEOUT_MS = 3e4;
13163
- var START_TIMEOUT_MS = 8e3;
13164
13395
  var SETTLE_MS = 2e3;
13165
13396
  var WAIT_CAP_MS = 9e5;
13166
13397
  var POLL_MS = 1e3;
13167
- var CAPTURE_TAIL_LINES = 2e4;
13168
- var CAPTURE_POLL_S = 3;
13169
- var CAPTURE_ARM_S = 2;
13170
- var STATIC_URL_PATTERN = `^(data:)|(\\.(js|css|png|jpe?g|gif|svg|woff2?|ico|map|webp|mp4)(\\?|$))`;
13398
+ var CAPTURE_TAIL_LINES = LIVE_TAIL_LINES;
13171
13399
  var RECORD_WATCH_MS = 3e3;
13172
13400
  var RECORD_WATCH_TRIES = 5;
13173
- var DROP_STATIC_JS = "let s=\"\";process.stdin.on(\"data\",(d)=>s+=d).on(\"end\",()=>{const re=new RegExp(process.argv[1],\"i\");for(const line of s.split(\"\\n\")){const t=line.trim();if(t===\"\")continue;let one;try{one=JSON.parse(t)}catch{process.stdout.write(t+\"\\n\");continue}if(Array.isArray(one.entries)){one.entries=one.entries.filter((e)=>!re.test(String((e||{}).url||\"\")));if(one.entries.length===0)continue}process.stdout.write(JSON.stringify(one)+\"\\n\")}});";
13174
13401
  var DETAIL_MAX_BYTES = 6e4;
13175
- var HOST_PID = String(process.pid);
13176
13402
  var pauseFor = (ms) => new Promise((resolve) => {
13177
13403
  setTimeout(resolve, ms);
13178
13404
  });
@@ -13221,40 +13447,13 @@ async function takeShotOf(input, held, n) {
13221
13447
  }, Date.now());
13222
13448
  }
13223
13449
  }
13224
- async function startRecordCapture(seam, command, session, file, makeLine = plainLine) {
13225
- const bin = command === "" ? "runtime" : command;
13226
- const arm = makeLine(bin, [
13227
- "browser",
13228
- session,
13229
- "network",
13230
- "--follow",
13231
- "--all",
13232
- "--window",
13233
- "background"
13234
- ]);
13235
- const snap = makeLine(bin, [
13236
- "browser",
13450
+ async function startRecordCapture(seam, command, session, file) {
13451
+ return startLiveCapture({
13452
+ seam,
13453
+ command,
13237
13454
  session,
13238
- "network",
13239
- "--all",
13240
- "--raw",
13241
- "--window",
13242
- "background"
13243
- ]);
13244
- const line = [
13245
- `f=${quoteShellArg(file)}`,
13246
- `r=${quoteShellArg(`${file}.round`)}`,
13247
- ": > \"$f\"",
13248
- "rm -f \"$f.last\" \"$f.first\" \"$r\"",
13249
- `( ${arm} >> "$f" 2>/dev/null & p=$!; sleep ${String(CAPTURE_ARM_S)}; kill "$p" 2>/dev/null )`,
13250
- `( while kill -0 ${HOST_PID} 2>/dev/null; do if ${snap} > "$r" 2>/dev/null; then printf ok > "$f.first"; else printf fail > "$f.first"; fi; if [ -s "$r" ]; then if command -v node >/dev/null 2>&1; then tr -d '\\n' < "$r" | node -e ${quoteShellArg(DROP_STATIC_JS)} ${quoteShellArg(STATIC_URL_PATTERN)} >> "$f"; else tr -d '\\n' < "$r" >> "$f"; printf '\\n' >> "$f"; fi; fi; rm -f "$r"; sleep ${String(CAPTURE_POLL_S)}; done & echo $! > "$f.pid" ) & echo $! > "$f.reaper"`,
13251
- "kill -0 \"$(cat \"$f.pid\" 2>/dev/null)\" 2>/dev/null"
13252
- ].join("; ");
13253
- return (await seam.run({
13254
- command: line,
13255
- intent: productWrite(),
13256
- timeoutMs: START_TIMEOUT_MS
13257
- })).exitCode === 0;
13455
+ file
13456
+ });
13258
13457
  }
13259
13458
  async function releaseLeftoversOf(input) {
13260
13459
  await releaseQuietly({
@@ -13267,7 +13466,7 @@ async function releaseLeftoversOf(input) {
13267
13466
  }
13268
13467
  async function stopCaptureQuiet(input) {
13269
13468
  try {
13270
- return await stopNetworkExact(input.seam, input.captureFile);
13469
+ return stopLiveCapture(input.captureFile);
13271
13470
  } catch {
13272
13471
  return false;
13273
13472
  }
@@ -13372,64 +13571,22 @@ function watchRecordFinish(input) {
13372
13571
  };
13373
13572
  setTimeout(tick, 0);
13374
13573
  }
13375
- async function captureLinesOf(seam, file, timeoutMs = START_TIMEOUT_MS) {
13376
- const step = await seam.run({
13377
- command: `wc -l < ${quoteShellArg(file)} 2>/dev/null || echo 0`,
13378
- intent: productWrite(),
13379
- timeoutMs
13380
- });
13381
- const value = Number.parseInt(step.stdout.trim() === "" ? "0" : step.stdout.trim(), 10);
13382
- return Number.isFinite(value) ? value : 0;
13383
- }
13384
- async function captureRoundOf(seam, file, timeoutMs = START_TIMEOUT_MS) {
13385
- const value = (await seam.run({
13386
- command: `cat ${quoteShellArg(`${file}.first`)} 2>/dev/null || true`,
13387
- intent: productWrite(),
13388
- timeoutMs
13389
- })).stdout.trim();
13390
- return value === "ok" || value === "fail" ? value : "";
13391
- }
13392
- async function captureTextOf(seam, file, timeoutMs = START_TIMEOUT_MS) {
13393
- const step = await seam.run({
13394
- command: `tail -n ${String(CAPTURE_TAIL_LINES)} ${quoteShellArg(file)} 2>/dev/null || true`,
13395
- intent: productWrite(),
13396
- timeoutMs
13397
- });
13574
+ function captureReadOf(file) {
13575
+ const totalLines = readCaptureLines(file);
13576
+ const text = readCaptureText(file);
13398
13577
  return {
13399
- text: step.stdout,
13400
- truncated: step.truncated
13401
- };
13402
- }
13403
- async function captureReadOf(seam, file, timeoutMs = START_TIMEOUT_MS) {
13404
- const totalLines = await captureLinesOf(seam, file, timeoutMs);
13405
- const read = await captureTextOf(seam, file, timeoutMs);
13406
- const round = await captureRoundOf(seam, file, timeoutMs);
13407
- return {
13408
- text: read.text,
13578
+ text: text.text,
13409
13579
  totalLines,
13410
13580
  tailLimit: CAPTURE_TAIL_LINES,
13411
- truncated: read.truncated || totalLines > CAPTURE_TAIL_LINES,
13412
- round
13581
+ truncated: text.truncated || totalLines > CAPTURE_TAIL_LINES,
13582
+ round: readCaptureRound(file)
13413
13583
  };
13414
13584
  }
13415
- async function keepCaptureOf(seam, file, timeoutMs = START_TIMEOUT_MS) {
13416
- const line = `[ -f ${quoteShellArg(file)} ] && mv ${quoteShellArg(file)} ${quoteShellArg(lastCaptureFileOf(file))} && [ -s ${quoteShellArg(lastCaptureFileOf(file))} ]`;
13417
- return (await seam.run({
13418
- command: line,
13419
- intent: productWrite(),
13420
- timeoutMs
13421
- })).exitCode === 0;
13422
- }
13423
- async function captureLinesWithKeyOf(seam, file, key, timeoutMs = START_TIMEOUT_MS) {
13424
- const quoted = quoteShellArg(file);
13425
- const compact = quoteShellArg(`"key":"${key}"`);
13426
- const spaced = quoteShellArg(`"key": "${key}"`);
13427
- const line = [`[ -f ${quoted} ] || exit 0`, `grep -m 20 -F ${compact} ${quoted} 2>/dev/null || grep -m 20 -F ${spaced} ${quoted} 2>/dev/null || true`].join("; ");
13428
- return (await seam.run({
13429
- command: line,
13430
- intent: productWrite(),
13431
- timeoutMs
13432
- })).stdout;
13585
+ function keepCaptureOf(file) {
13586
+ return keepCaptureFile(file);
13587
+ }
13588
+ function captureLinesWithKeyOf(file, key) {
13589
+ return readCaptureLinesWithKey(file, key);
13433
13590
  }
13434
13591
  function bundleOf(capture, page, state) {
13435
13592
  const entries = captureEntriesOf(capture.text);
@@ -13562,7 +13719,7 @@ async function closeRecordTab(seam, command, session, url) {
13562
13719
  return (await actBrowser(seam, command, session, ["close"])).ok;
13563
13720
  }
13564
13721
  async function releaseRecord(input) {
13565
- const capture = await stopNetworkExact(input.seam, input.captureFile);
13722
+ const capture = stopLiveCapture(input.captureFile);
13566
13723
  return {
13567
13724
  tab: await closeRecordTab(input.seam, input.command, input.session, input.url),
13568
13725
  capture
@@ -13734,7 +13891,7 @@ function registerRecordTools(ctx, store) {
13734
13891
  const armed = await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_ARM_SCRIPT]);
13735
13892
  const page = recordPageOf(armed.payload);
13736
13893
  if (!armed.ok || !page.ok) throw new Error(`浮条没装上:${armed.stderr || armed.stdout || "那一页还在加载"} —— 等用户那一页站稳了再试。`);
13737
- await stopNetworkExact(seam, captureFile);
13894
+ stopLiveCapture(captureFile);
13738
13895
  const capture = await startRecordCapture(seam, command, BROWSER_RECORD_SESSION, captureFile);
13739
13896
  watchRecordFinish({
13740
13897
  ctx,
@@ -13832,7 +13989,7 @@ function registerRecordTools(ctx, store) {
13832
13989
  }
13833
13990
  const state = store.live(Date.now()) ?? held;
13834
13991
  const ledger = state.shots;
13835
- const capture = await captureReadOf(seam, state.captureFile);
13992
+ const capture = captureReadOf(state.captureFile);
13836
13993
  const bundle = bundleOf(capture, page, state);
13837
13994
  const hookCaught = page.calls.length;
13838
13995
  const nav = page.href !== "" && page.here !== "" && page.href !== page.here;
@@ -13943,7 +14100,7 @@ function registerRecordTools(ctx, store) {
13943
14100
  if (index === null) throw new Error("给一个 key(清单里那一条的)或 index(清单里的序号)。");
13944
14101
  if (held === null) throw new Error("收工之后只能用 key —— index 是\"那一场清单里\"的序号,这一场已经清掉了。");
13945
14102
  page = recordPageOf((await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_READ_SCRIPT])).payload);
13946
- const bundle = bundleOf(await captureReadOf(seam, file), page, held);
14103
+ const bundle = bundleOf(captureReadOf(file), page, held);
13947
14104
  const row = bundle.rows[index];
13948
14105
  if (row === void 0) throw new Error(`这一段清单里没有第 ${String(index)} 条(一共 ${String(bundle.rows.length)} 条)—— 先 ${DIY_RECORD_READ_TOOL} 拿一份新的清单。`);
13949
14106
  key = row.key;
@@ -13953,7 +14110,7 @@ function registerRecordTools(ctx, store) {
13953
14110
  if (only !== null) return detailOfCall(only, maxBytes);
13954
14111
  }
13955
14112
  }
13956
- const candidates = pickAllOf(captureEntriesOf(await captureLinesWithKeyOf(seam, file, key)), key);
14113
+ const candidates = pickAllOf(captureEntriesOf(captureLinesWithKeyOf(file, key)), key);
13957
14114
  const fromCapture = pickOf(candidates, key, at);
13958
14115
  if (fromCapture !== null) return detailOfEntry(fromCapture, maxBytes, candidates.length);
13959
14116
  const over = held === null || held.phase === "finished";
@@ -14009,10 +14166,10 @@ function registerRecordTools(ctx, store) {
14009
14166
  const command = cliCommandOf(ctx);
14010
14167
  await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_ARM_SCRIPT]);
14011
14168
  const page = recordPageOf((await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_STOP_SCRIPT])).payload);
14012
- const bundle = bundleOf(await captureReadOf(seam, held.captureFile), page, held);
14169
+ const bundle = bundleOf(captureReadOf(held.captureFile), page, held);
14013
14170
  const hookCaught = page.calls.length;
14014
14171
  const noBody = bodyMissingOf(bundle.merged);
14015
- const kept = await keepCaptureOf(seam, held.captureFile);
14172
+ const kept = keepCaptureOf(held.captureFile);
14016
14173
  const cleaned = await releaseQuietly({
14017
14174
  seam,
14018
14175
  command,
@@ -7,4 +7,3 @@ export interface ActStep extends BrowserStep {
7
7
  export declare function actBrowser(seam: Seam, command: string, session: string, argv: readonly string[], timeoutMs?: number, line?: LineMaker): Promise<ActStep>;
8
8
  export declare function actErrorOf(step: ActStep): string;
9
9
  export declare const SWEEP_TIMEOUT_MS = 4000;
10
- export declare function stopNetworkExact(seam: Seam, file: string, timeoutMs?: number): Promise<boolean>;
@@ -1,14 +1,13 @@
1
1
  import type { DshHostContext } from '@dsh-workbench/shared';
2
2
  import type { RecordState, RecordStore } from '@dsh-workbench/shared/host';
3
3
  import { type Seam } from '@dsh-workbench/shared/host';
4
- import { type LineMaker } from './diy-browser';
5
4
  import type { CaptureEntry } from './diy-record-capture';
6
5
  import type { RecordCall, RecordListRow, RecordPageRead } from './diy-record-text';
7
6
  export declare const OPEN_TIMEOUT_MS = 30000;
8
7
  export declare const SETTLE_MS = 2000;
9
8
  export declare const WAIT_CAP_MS: number;
10
9
  export declare const POLL_MS = 1000;
11
- export declare const STATIC_URL_PATTERN = "^(data:)|(\\.(js|css|png|jpe?g|gif|svg|woff2?|ico|map|webp|mp4)(\\?|$))";
10
+ export { STATIC_URL_PATTERN } from '../../../shared/api/host/diy-capture-live';
12
11
  export declare const DETAIL_MAX_BYTES = 60000;
13
12
  export declare const pauseFor: (ms: number) => Promise<void>;
14
13
  export declare function waitOf(value: unknown): number;
@@ -17,7 +16,7 @@ export declare function captureFileOf(artifactsDir: string, unit: string): strin
17
16
  export declare function lastCaptureFileOf(file: string): string;
18
17
  export declare function shotFileOf(captureFile: string, unit: string, n: number): string;
19
18
  export declare function takeShotOf(input: WatchFinishDeps, held: RecordState, n: number): Promise<void>;
20
- export declare function startRecordCapture(seam: Seam, command: string, session: string, file: string, makeLine?: LineMaker): Promise<boolean>;
19
+ export declare function startRecordCapture(seam: Seam, command: string, session: string, file: string): Promise<boolean>;
21
20
  export interface WatchFinishDeps {
22
21
  readonly ctx: DshHostContext;
23
22
  readonly seam: Seam;
@@ -44,15 +43,15 @@ export interface CaptureRead {
44
43
  readonly truncated: boolean;
45
44
  readonly round: string;
46
45
  }
47
- export declare function captureLinesOf(seam: Seam, file: string, timeoutMs?: number): Promise<number>;
48
- export declare function captureRoundOf(seam: Seam, file: string, timeoutMs?: number): Promise<string>;
49
- export declare function captureTextOf(seam: Seam, file: string, timeoutMs?: number): Promise<{
46
+ export declare function captureLinesOf(file: string): number;
47
+ export declare function captureRoundOf(file: string): string;
48
+ export declare function captureTextOf(file: string): {
50
49
  readonly text: string;
51
50
  readonly truncated: boolean;
52
- }>;
53
- export declare function captureReadOf(seam: Seam, file: string, timeoutMs?: number): Promise<CaptureRead>;
54
- export declare function keepCaptureOf(seam: Seam, file: string, timeoutMs?: number): Promise<boolean>;
55
- export declare function captureLinesWithKeyOf(seam: Seam, file: string, key: string, timeoutMs?: number): Promise<string>;
51
+ };
52
+ export declare function captureReadOf(file: string): CaptureRead;
53
+ export declare function keepCaptureOf(file: string): boolean;
54
+ export declare function captureLinesWithKeyOf(file: string, key: string): string;
56
55
  export interface SegmentBundle {
57
56
  readonly capture: CaptureRead;
58
57
  readonly entries: readonly CaptureEntry[];
@@ -0,0 +1,25 @@
1
+ import { type Seam } from '@dsh-workbench/shared/host';
2
+ export declare const LIVE_POLL_MS = 3000;
3
+ export declare const LIVE_ARM_MS = 2000;
4
+ export declare const LIVE_SNAP_TIMEOUT_MS = 15000;
5
+ export declare const LIVE_TAIL_LINES = 20000;
6
+ export declare const LIVE_KEY_LINES = 20;
7
+ export declare const STATIC_URL_PATTERN = "^(data:)|(\\.(js|css|png|jpe?g|gif|svg|woff2?|ico|map|webp|mp4)(\\?|$))";
8
+ export declare function filterStaticLines(raw: string): string;
9
+ export declare function captureTickOnce(seam: Seam, command: string, session: string, file: string, timeoutMs?: number): Promise<string>;
10
+ export declare function startLiveCapture(input: {
11
+ readonly seam: Seam;
12
+ readonly command: string;
13
+ readonly session: string;
14
+ readonly file: string;
15
+ }): Promise<boolean>;
16
+ export declare function stopLiveCapture(file: string): boolean;
17
+ export declare function liveCaptureOf(file: string): boolean;
18
+ export declare function readCaptureLines(file: string): number;
19
+ export declare function readCaptureRound(file: string): string;
20
+ export declare function readCaptureText(file: string): {
21
+ readonly text: string;
22
+ readonly truncated: boolean;
23
+ };
24
+ export declare function readCaptureLinesWithKey(file: string, key: string): string;
25
+ export declare function keepCaptureFile(file: string): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-workbench/core",
3
- "version": "1.1.14",
3
+ "version": "1.1.16",
4
4
  "description": "个人工作台:Agent 工作台(左栏 + 常驻对话 + 自研右侧数据面板)。",
5
5
  "type": "module",
6
6
  "icon": "./icon.svg",
@@ -97,8 +97,8 @@
97
97
  "proper-lockfile": "^4.1.2",
98
98
  "which": "^7.0.0",
99
99
  "write-file-atomic": "^8.0.0",
100
- "@dsh-workbench/adapters-default": "1.1.14",
101
- "@dsh-workbench/shared": "1.1.14"
100
+ "@dsh-workbench/adapters-default": "1.1.16",
101
+ "@dsh-workbench/shared": "1.1.16"
102
102
  },
103
103
  "license": "MIT",
104
104
  "repository": {