@dsh-workbench/core 1.1.14-beta.2 → 1.1.15
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/README.md +1 -1
- package/bin/dsh-workbench +6 -52
- package/client.js +59 -16
- package/cordis.patch.yml +5 -6
- package/dist/client/client.body.js +59 -16
- package/dist/client/index.js +59 -16
- package/dist/host/index.js +1873 -680
- package/dist/types/app/layouts/env-gate/gate-text.d.ts +2 -3
- package/dist/types/app/units/manifests.d.ts +2 -0
- package/dist/types/kernel/host/mount-units.d.ts +2 -1
- package/dist/types/modules/panels/model/diy-browser-act.d.ts +2 -3
- package/dist/types/modules/panels/model/diy-browser-clip.d.ts +14 -0
- package/dist/types/modules/panels/model/diy-browser-tools.d.ts +1 -14
- package/dist/types/modules/panels/model/diy-browser.d.ts +6 -6
- package/dist/types/modules/panels/model/diy-page.d.ts +3 -2
- package/dist/types/modules/panels/model/diy-record-open.d.ts +2 -2
- package/dist/types/modules/panels/model/diy-record.d.ts +14 -14
- package/dist/types/modules/panels/model/market-discover.d.ts +2 -2
- package/dist/types/modules/panels/model/market-follow-up.d.ts +2 -1
- package/dist/types/modules/panels/model/market-route.d.ts +2 -3
- package/dist/types/modules/panels/model/market-update-bundle.d.ts +2 -3
- package/dist/types/modules/panels/model/market-workbench-update.d.ts +2 -2
- package/dist/types/modules/panels/model/market.d.ts +2 -2
- package/dist/types/modules/panels/model/plugin-actions.d.ts +2 -3
- package/dist/types/modules/panels/model/plugin-section.d.ts +3 -4
- package/dist/types/shared/api/host/bundle-routes.d.ts +3 -3
- package/dist/types/shared/api/host/diy-artifacts.d.ts +1 -2
- package/dist/types/shared/api/host/diy-capture-live.d.ts +25 -0
- package/dist/types/shared/api/host/diy-components.d.ts +2 -2
- package/dist/types/shared/api/host/diy-release-staging.d.ts +6 -0
- package/dist/types/shared/api/host/diy.d.ts +1 -1
- package/dist/types/shared/api/host/git-exec.d.ts +1 -1
- package/dist/types/shared/api/host/git-process.d.ts +3 -0
- package/dist/types/shared/api/host/shipped-adapter-set.d.ts +4 -0
- package/dist/types/shared/api/host/workbench-seam.d.ts +3 -0
- package/package.json +3 -6
- package/scripts/seed-profile.sh +3 -3
- package/scripts/share-data-root.mjs +10 -13
- package/skills/workbench-module-diy/BUILD.md +38 -0
- package/skills/workbench-module-diy/SKILL.md +1 -0
- package/profile/sandbox.patch.yml +0 -28
package/dist/host/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { homedir, tmpdir } from "node:os";
|
|
2
2
|
import dayjs from "dayjs";
|
|
3
|
-
import fs, { accessSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync,
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
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
|
+
import writeFileAtomic from "write-file-atomic";
|
|
6
6
|
import utc from "dayjs/plugin/utc.js";
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
8
|
import { createHash, randomUUID } from "node:crypto";
|
|
8
9
|
import { isDeepStrictEqual } from "node:util";
|
|
9
|
-
import { createRequire } from "node:module";
|
|
10
|
-
import writeFileAtomic from "write-file-atomic";
|
|
11
10
|
import { execa } from "execa";
|
|
11
|
+
import { createRequire } from "node:module";
|
|
12
12
|
import { deflateSync, inflateSync } from "node:zlib";
|
|
13
13
|
//#region \0rolldown/runtime.js
|
|
14
14
|
var __create = Object.create;
|
|
@@ -566,7 +566,12 @@ var TOOL_KEYS = [
|
|
|
566
566
|
"key"
|
|
567
567
|
];
|
|
568
568
|
var TOOL_KEY_SHAPE = /^[a-z][a-zA-Z0-9]*$/;
|
|
569
|
-
var HOST_KEYS = [
|
|
569
|
+
var HOST_KEYS = [
|
|
570
|
+
"inject",
|
|
571
|
+
"optional",
|
|
572
|
+
"writes"
|
|
573
|
+
];
|
|
574
|
+
var UNIT_WRITE_SCOPES = ["unit", "workspace"];
|
|
570
575
|
function text$2(value) {
|
|
571
576
|
return typeof value === "string" ? value.trim() : "";
|
|
572
577
|
}
|
|
@@ -666,6 +671,14 @@ function declarationProblemsOf(subject, raw) {
|
|
|
666
671
|
if (host.inject === void 0) problems.push(`${subject}: dsh.workbench.host.inject 没写 —— 它声明这个模块要外壳的哪几个服务,外壳靠它判"能不能挂"。一个都不要也要写 \`"inject": []\``);
|
|
667
672
|
else problems.push(...stringArrayProblems(`${subject}: dsh.workbench.host.inject`, host.inject));
|
|
668
673
|
if (host.optional !== void 0) problems.push(...stringArrayProblems(`${subject}: dsh.workbench.host.optional`, host.optional));
|
|
674
|
+
if (host.writes !== void 0) {
|
|
675
|
+
const shape = stringArrayProblems(`${subject}: dsh.workbench.host.writes`, host.writes);
|
|
676
|
+
problems.push(...shape);
|
|
677
|
+
if (shape.length === 0) {
|
|
678
|
+
const outsideScopes = host.writes.filter((scope) => !UNIT_WRITE_SCOPES.includes(scope));
|
|
679
|
+
if (outsideScopes.length > 0) problems.push(`${subject}: dsh.workbench.host.writes 里有不认的写入域 ${listOf(outsideScopes)} —— 只认 "unit"(模块自己的数据目录)与 "workspace"(当前会话工作区)两档;模块拿不到产品域与任意路径,别写别的名字(缺省 [] = 不给执行缝门面)`);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
669
682
|
}
|
|
670
683
|
}
|
|
671
684
|
return problems;
|
|
@@ -707,7 +720,8 @@ function readDeclaration(subject, raw) {
|
|
|
707
720
|
})),
|
|
708
721
|
host: {
|
|
709
722
|
inject: [...declaration.host.inject ?? []],
|
|
710
|
-
...declaration.host.optional === void 0 ? {} : { optional: [...declaration.host.optional] }
|
|
723
|
+
...declaration.host.optional === void 0 ? {} : { optional: [...declaration.host.optional] },
|
|
724
|
+
writes: [...declaration.host.writes ?? []]
|
|
711
725
|
},
|
|
712
726
|
...declaration.needs === void 0 ? {} : { needs: declaration.needs }
|
|
713
727
|
},
|
|
@@ -734,6 +748,97 @@ var BUNDLE_REGISTRIES_ROUTE = "bundle-registries";
|
|
|
734
748
|
var BUNDLE_INSPECT_ROUTE = "bundle-inspect";
|
|
735
749
|
var MODULE_VERSIONS_ROUTE = "module-versions";
|
|
736
750
|
//#endregion
|
|
751
|
+
//#region ../shared/dist/host/fs-write.js
|
|
752
|
+
function modeOf(path, mode) {
|
|
753
|
+
if (typeof mode === "number") return mode;
|
|
754
|
+
try {
|
|
755
|
+
return statSync(path).mode & 511;
|
|
756
|
+
} catch {
|
|
757
|
+
return 420;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
function prepareOf(path, options) {
|
|
761
|
+
if (options.mkdir !== false) mkdirSync(dirname(path), { recursive: true });
|
|
762
|
+
return modeOf(path, options.mode);
|
|
763
|
+
}
|
|
764
|
+
function atomicOptionsOf(mode, options) {
|
|
765
|
+
return options.fsync === true ? {
|
|
766
|
+
mode,
|
|
767
|
+
fsync: true
|
|
768
|
+
} : { mode };
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* 把文本写进文件;默认原子写(temp + rename)、目录不存在就建、已有文件保留原权限。
|
|
772
|
+
* @param path 目标文件;父目录按 `mkdir` 决定是否创建。
|
|
773
|
+
* @param text 文本内容。
|
|
774
|
+
* @param options 原子/目录/权限/fsync;`mode` 默认 `'keep'`(新文件 0644)。
|
|
775
|
+
*/
|
|
776
|
+
function writeTextFile(path, text, options = {}) {
|
|
777
|
+
const mode = prepareOf(path, options);
|
|
778
|
+
if (options.atomic === false) {
|
|
779
|
+
writeFileSync(path, text, { mode });
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
writeFileAtomic.sync(path, text, atomicOptionsOf(mode, options));
|
|
783
|
+
}
|
|
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
|
+
/**
|
|
794
|
+
* `writeTextFile` 的异步版;语义与默认值相同。
|
|
795
|
+
* @param path 目标文件。
|
|
796
|
+
* @param text 文本内容。
|
|
797
|
+
* @param options 原子/目录/权限/fsync。
|
|
798
|
+
* @returns 落盘完成。
|
|
799
|
+
*/
|
|
800
|
+
async function writeTextFileAsync(path, text, options = {}) {
|
|
801
|
+
const mode = prepareOf(path, options);
|
|
802
|
+
if (options.atomic === false) {
|
|
803
|
+
writeFileSync(path, text, { mode });
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
await writeFileAtomic(path, text, atomicOptionsOf(mode, options));
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* 把二进制写进文件;语义与默认值同 `writeTextFile`。
|
|
810
|
+
* @param path 目标文件。
|
|
811
|
+
* @param bytes 二进制内容。
|
|
812
|
+
* @param options 原子/目录/权限/fsync。
|
|
813
|
+
*/
|
|
814
|
+
function writeBytesFile(path, bytes, options = {}) {
|
|
815
|
+
const mode = prepareOf(path, options);
|
|
816
|
+
if (options.atomic === false) {
|
|
817
|
+
writeFileSync(path, bytes, { mode });
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
writeFileAtomic.sync(path, Buffer.from(bytes), atomicOptionsOf(mode, options));
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* 把 JSON 写进文件:`JSON.stringify(value, null, 2)` 加末尾换行;语义与默认值同 `writeTextFile`。
|
|
824
|
+
* @param path 目标文件。
|
|
825
|
+
* @param value 可序列化的值。
|
|
826
|
+
* @param options 原子/目录/权限/fsync。
|
|
827
|
+
*/
|
|
828
|
+
function writeJsonFile(path, value, options = {}) {
|
|
829
|
+
writeTextFile(path, `${JSON.stringify(value, null, 2)}\n`, options);
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* `writeJsonFile` 的异步版;语义与默认值相同。
|
|
833
|
+
* @param path 目标文件。
|
|
834
|
+
* @param value 可序列化的值。
|
|
835
|
+
* @param options 原子/目录/权限/fsync。
|
|
836
|
+
* @returns 落盘完成。
|
|
837
|
+
*/
|
|
838
|
+
async function writeJsonFileAsync(path, value, options = {}) {
|
|
839
|
+
await writeTextFileAsync(path, `${JSON.stringify(value, null, 2)}\n`, options);
|
|
840
|
+
}
|
|
841
|
+
//#endregion
|
|
737
842
|
//#region ../shared/dist/host/installed-sources.js
|
|
738
843
|
var FILE_NAME = ".dsh-workbench-sources.json";
|
|
739
844
|
var FILE_VERSION = 1;
|
|
@@ -774,14 +879,10 @@ function packageSourceOf(profileDir, pkg) {
|
|
|
774
879
|
return readInstalledSources(profileDir)[pkg];
|
|
775
880
|
}
|
|
776
881
|
function writeInstalledSources(profileDir, sources) {
|
|
777
|
-
|
|
778
|
-
const temp = `${file}.tmp`;
|
|
779
|
-
const text = `${JSON.stringify({
|
|
882
|
+
writeTextFile(installedSourcesFile(profileDir), `${JSON.stringify({
|
|
780
883
|
version: FILE_VERSION,
|
|
781
884
|
packages: sources
|
|
782
|
-
}, null, 2)}\n
|
|
783
|
-
fs.writeFileSync(temp, text, { mode: 384 });
|
|
784
|
-
fs.renameSync(temp, file);
|
|
885
|
+
}, null, 2)}\n`, { mode: 384 });
|
|
785
886
|
}
|
|
786
887
|
function rememberPackageSource(profileDir, pkg, source) {
|
|
787
888
|
const trimmed = pkg.trim();
|
|
@@ -847,54 +948,58 @@ function refusalReason() {
|
|
|
847
948
|
return "这个包既不是工作台的模块,也不是运行时的插件,装了也用不上";
|
|
848
949
|
}
|
|
849
950
|
//#endregion
|
|
850
|
-
//#region ../shared/dist/host/
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
stderr: "",
|
|
860
|
-
truncated: false,
|
|
861
|
-
stalled: true
|
|
862
|
-
};
|
|
951
|
+
//#region ../shared/dist/host/time.js
|
|
952
|
+
dayjs.extend(utc);
|
|
953
|
+
/**
|
|
954
|
+
* 把时间戳/ISO 字符串转成毫秒;非法输入给 `NaN`(调用方据此回退)。
|
|
955
|
+
* @param value ISO 字符串、时间戳或 Date。
|
|
956
|
+
* @returns 毫秒时间戳或 `NaN`。
|
|
957
|
+
*/
|
|
958
|
+
function epochOf(value) {
|
|
959
|
+
return dayjs(value).valueOf();
|
|
863
960
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
961
|
+
/**
|
|
962
|
+
* 当前时间(毫秒)。宿主里要「现在几点」只走这里,测试可以点住 `Date.now`。
|
|
963
|
+
* @returns 毫秒时间戳。
|
|
964
|
+
*/
|
|
965
|
+
function now() {
|
|
966
|
+
return Date.now();
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* 当前时间的 ISO 字符串。
|
|
970
|
+
* @returns `new Date().toISOString()` 的等价物。
|
|
971
|
+
*/
|
|
972
|
+
function nowIso() {
|
|
973
|
+
return new Date(now()).toISOString();
|
|
974
|
+
}
|
|
975
|
+
//#endregion
|
|
976
|
+
//#region ../shared/dist/host/seam/intent.js
|
|
977
|
+
var SeamError = class extends Error {
|
|
978
|
+
code;
|
|
979
|
+
path;
|
|
980
|
+
scope;
|
|
981
|
+
constructor(code, message, extra = {}) {
|
|
982
|
+
super(message);
|
|
983
|
+
this.name = "SeamError";
|
|
984
|
+
this.code = code;
|
|
985
|
+
this.path = extra.path;
|
|
986
|
+
this.scope = extra.scope;
|
|
873
987
|
}
|
|
988
|
+
};
|
|
989
|
+
function productWrite() {
|
|
990
|
+
return { kind: "product" };
|
|
874
991
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
}
|
|
886
|
-
return await settleWithin((async () => {
|
|
887
|
-
const result = await (await shell.execute(spec)).result();
|
|
888
|
-
return {
|
|
889
|
-
exitCode: result.exitCode,
|
|
890
|
-
timedOut: result.timedOut === true,
|
|
891
|
-
aborted: result.aborted === true,
|
|
892
|
-
stdout: result.stdout.text,
|
|
893
|
-
stderr: result.stderr.text,
|
|
894
|
-
truncated: result.stdout.truncated || result.stderr.truncated,
|
|
895
|
-
stalled: false
|
|
896
|
-
};
|
|
897
|
-
})(), timeoutMs + GUARD_SLACK_MS) ?? stalledResult();
|
|
992
|
+
function workspaceWrite() {
|
|
993
|
+
return { kind: "workspace" };
|
|
994
|
+
}
|
|
995
|
+
function pathsWrite(paths) {
|
|
996
|
+
return {
|
|
997
|
+
kind: "paths",
|
|
998
|
+
paths
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
function noWrite() {
|
|
1002
|
+
return { kind: "none" };
|
|
898
1003
|
}
|
|
899
1004
|
//#endregion
|
|
900
1005
|
//#region ../shared/dist/host/shell-text.js
|
|
@@ -2406,6 +2511,73 @@ function npmRegistryOf(source, env) {
|
|
|
2406
2511
|
//#region ../shared/dist/contract/protocol/config-sync.js
|
|
2407
2512
|
var CONFIG_SYNC_ROUTE = "config-sync";
|
|
2408
2513
|
//#endregion
|
|
2514
|
+
//#region ../shared/dist/host/patch-blocks.js
|
|
2515
|
+
var ID_LINE = /^- id:[ \t]*"?([^"\s#]+)/;
|
|
2516
|
+
var INSERT_LINE = /^- insert:[ \t]*$/;
|
|
2517
|
+
var NESTED_ID = /^[ \t]+-[ \t]*id:[ \t]*"?([^"\s#]+)/;
|
|
2518
|
+
var NAME_LINE = /^[ \t]*-?[ \t]*name:[ \t]*['"]?([^'"\s#]+)/;
|
|
2519
|
+
function trimmedTail(lines) {
|
|
2520
|
+
let end = lines.length;
|
|
2521
|
+
while (end > 0 && (lines[end - 1] ?? "").trim() === "") end -= 1;
|
|
2522
|
+
return lines.slice(0, end);
|
|
2523
|
+
}
|
|
2524
|
+
function matched(text, pattern) {
|
|
2525
|
+
const found = pattern.exec(text);
|
|
2526
|
+
return found === null ? void 0 : found[1];
|
|
2527
|
+
}
|
|
2528
|
+
function blockOf$1(body, index) {
|
|
2529
|
+
const lines = body.split("\n");
|
|
2530
|
+
const first = lines[0] ?? "";
|
|
2531
|
+
const id = matched(first, ID_LINE);
|
|
2532
|
+
const nested = lines.flatMap((line) => {
|
|
2533
|
+
const one = matched(line, NESTED_ID);
|
|
2534
|
+
return one === void 0 ? [] : [one];
|
|
2535
|
+
});
|
|
2536
|
+
const insert = id === void 0 && INSERT_LINE.test(first);
|
|
2537
|
+
const label = id ?? nested.join(", ");
|
|
2538
|
+
const key = id !== void 0 ? `id:${id}` : insert ? `insert:${nested.join(", ")}` : `block:${String(index)}`;
|
|
2539
|
+
const names = lines.flatMap((line) => {
|
|
2540
|
+
const one = matched(line, NAME_LINE);
|
|
2541
|
+
return one === void 0 ? [] : [one];
|
|
2542
|
+
});
|
|
2543
|
+
return {
|
|
2544
|
+
key,
|
|
2545
|
+
id: label === "" ? key : label,
|
|
2546
|
+
insert,
|
|
2547
|
+
body,
|
|
2548
|
+
names
|
|
2549
|
+
};
|
|
2550
|
+
}
|
|
2551
|
+
function spannedBlocks(text) {
|
|
2552
|
+
const lines = text.split("\n");
|
|
2553
|
+
const starts = [];
|
|
2554
|
+
lines.forEach((line, index) => {
|
|
2555
|
+
if (/^- /.test(line) || line === "-") starts.push(index);
|
|
2556
|
+
});
|
|
2557
|
+
const blocks = [];
|
|
2558
|
+
for (let i = 0; i < starts.length; i += 1) {
|
|
2559
|
+
const from = starts[i] ?? 0;
|
|
2560
|
+
const to = i + 1 < starts.length ? starts[i + 1] ?? lines.length : lines.length;
|
|
2561
|
+
const body = trimmedTail(lines.slice(from, to)).join("\n");
|
|
2562
|
+
if (body.trim() === "") continue;
|
|
2563
|
+
blocks.push({
|
|
2564
|
+
...blockOf$1(body, blocks.length),
|
|
2565
|
+
from,
|
|
2566
|
+
to
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
return blocks;
|
|
2570
|
+
}
|
|
2571
|
+
function patchBlocks(text) {
|
|
2572
|
+
return spannedBlocks(text).map(({ from: _from, to: _to, ...block }) => block);
|
|
2573
|
+
}
|
|
2574
|
+
//#endregion
|
|
2575
|
+
//#region ../shared/dist/host/patch.js
|
|
2576
|
+
/** 把 patch 文本写回原文件:原子写,保留已有权限位(新文件 0644)。 */
|
|
2577
|
+
function writePatchFile(path, text) {
|
|
2578
|
+
writeTextFile(path, text, { mode: "keep" });
|
|
2579
|
+
}
|
|
2580
|
+
//#endregion
|
|
2409
2581
|
//#region ../shared/dist/contract/protocol/profile-create.js
|
|
2410
2582
|
var PROFILE_CREATE_ROUTE = "profile-create";
|
|
2411
2583
|
//#endregion
|
|
@@ -2448,17 +2620,6 @@ function parseMarkers(text) {
|
|
|
2448
2620
|
return refs;
|
|
2449
2621
|
}
|
|
2450
2622
|
//#endregion
|
|
2451
|
-
//#region ../shared/dist/host/time.js
|
|
2452
|
-
dayjs.extend(utc);
|
|
2453
|
-
/**
|
|
2454
|
-
* 把时间戳/ISO 字符串转成毫秒;非法输入给 `NaN`(调用方据此回退)。
|
|
2455
|
-
* @param value ISO 字符串、时间戳或 Date。
|
|
2456
|
-
* @returns 毫秒时间戳或 `NaN`。
|
|
2457
|
-
*/
|
|
2458
|
-
function epochOf(value) {
|
|
2459
|
-
return dayjs(value).valueOf();
|
|
2460
|
-
}
|
|
2461
|
-
//#endregion
|
|
2462
2623
|
//#region ../shared/dist/host/diy-debug.js
|
|
2463
2624
|
/**
|
|
2464
2625
|
* 把工具入参收成人话动作。
|
|
@@ -2764,6 +2925,121 @@ function createRecordStore() {
|
|
|
2764
2925
|
};
|
|
2765
2926
|
}
|
|
2766
2927
|
//#endregion
|
|
2928
|
+
//#region ../shared/dist/host/data-root.js
|
|
2929
|
+
function dshHome() {
|
|
2930
|
+
const configured = process.env.DSH_HOME?.trim();
|
|
2931
|
+
if (configured !== void 0 && configured !== "") return configured;
|
|
2932
|
+
return join(homedir(), ".dsh");
|
|
2933
|
+
}
|
|
2934
|
+
function workbenchRoot() {
|
|
2935
|
+
const configured = process.env.DSH_WORKBENCH_HOME?.trim();
|
|
2936
|
+
if (configured !== void 0 && configured !== "") return configured;
|
|
2937
|
+
return join(dshHome(), "workbench");
|
|
2938
|
+
}
|
|
2939
|
+
function workspacesRoot() {
|
|
2940
|
+
return join(workbenchRoot(), "workspaces");
|
|
2941
|
+
}
|
|
2942
|
+
//#endregion
|
|
2943
|
+
//#region ../shared/dist/host/env-policy.js
|
|
2944
|
+
/**
|
|
2945
|
+
* 把父进程环境里的字符串值拷成一层 env(丢掉 undefined)。
|
|
2946
|
+
* @param parent 父环境(通常是 `process.env`)。
|
|
2947
|
+
* @returns 可继续叠加的 env 层。
|
|
2948
|
+
*/
|
|
2949
|
+
function inheritedEnvOf(parent) {
|
|
2950
|
+
const out = {};
|
|
2951
|
+
for (const [key, value] of Object.entries(parent)) if (typeof value === "string") out[key] = value;
|
|
2952
|
+
return out;
|
|
2953
|
+
}
|
|
2954
|
+
function existingPointer(realHome, name) {
|
|
2955
|
+
const file = join(realHome, name);
|
|
2956
|
+
return existsSync(file) ? file : void 0;
|
|
2957
|
+
}
|
|
2958
|
+
/**
|
|
2959
|
+
* 产品域(运行时根)的受控 env:隔离 HOME、npm/pnpm 缓存都钉在运行时根,真 npmrc/gitconfig 只读指过去。
|
|
2960
|
+
* @param input 运行时根、可选的 HOME 与真实用户目录。
|
|
2961
|
+
* @returns 产品域 env。
|
|
2962
|
+
*/
|
|
2963
|
+
function productEnvOf(input) {
|
|
2964
|
+
const runtimeRoot = input.runtimeRoot;
|
|
2965
|
+
const realHome = input.realHome ?? homedir();
|
|
2966
|
+
const env = {
|
|
2967
|
+
HOME: input.home ?? join(runtimeRoot, "home"),
|
|
2968
|
+
DSH_HOME: dshHome(),
|
|
2969
|
+
DSH_WORKBENCH_HOME: runtimeRoot,
|
|
2970
|
+
npm_config_cache: join(runtimeRoot, "npm-cache"),
|
|
2971
|
+
NPM_CONFIG_CACHE: join(runtimeRoot, "npm-cache"),
|
|
2972
|
+
npm_config_logs_dir: join(runtimeRoot, "npm-logs"),
|
|
2973
|
+
npm_config_store_dir: join(runtimeRoot, "pnpm-store"),
|
|
2974
|
+
npm_config_cache_dir: join(runtimeRoot, "pnpm-cache"),
|
|
2975
|
+
npm_config_state_dir: join(runtimeRoot, "pnpm-state"),
|
|
2976
|
+
npm_config_update_notifier: "false",
|
|
2977
|
+
npm_config_fund: "false",
|
|
2978
|
+
NO_UPDATE_NOTIFIER: "1"
|
|
2979
|
+
};
|
|
2980
|
+
const npmrc = existingPointer(realHome, ".npmrc");
|
|
2981
|
+
if (npmrc !== void 0) env["NPM_CONFIG_USERCONFIG"] = npmrc;
|
|
2982
|
+
const gitconfig = existingPointer(realHome, ".gitconfig");
|
|
2983
|
+
if (gitconfig !== void 0) env["GIT_CONFIG_GLOBAL"] = gitconfig;
|
|
2984
|
+
return env;
|
|
2985
|
+
}
|
|
2986
|
+
/**
|
|
2987
|
+
* 模块单元的受控 env:在产品域基础上把 HOME 与 npm/pnpm 目录全钉到单元数据目录。
|
|
2988
|
+
* @param input 运行时根、单元数据目录、可选的 TRUE 用户目录。
|
|
2989
|
+
* @returns 单元 env。
|
|
2990
|
+
*/
|
|
2991
|
+
function unitEnvOf(input) {
|
|
2992
|
+
const unitRoot = input.unitRoot;
|
|
2993
|
+
return {
|
|
2994
|
+
...productEnvOf({
|
|
2995
|
+
...input,
|
|
2996
|
+
home: join(unitRoot, "home")
|
|
2997
|
+
}),
|
|
2998
|
+
npm_config_cache: join(unitRoot, "npm-cache"),
|
|
2999
|
+
NPM_CONFIG_CACHE: join(unitRoot, "npm-cache"),
|
|
3000
|
+
npm_config_logs_dir: join(unitRoot, "npm-logs"),
|
|
3001
|
+
npm_config_store_dir: join(unitRoot, "pnpm-store"),
|
|
3002
|
+
npm_config_cache_dir: join(unitRoot, "pnpm-cache"),
|
|
3003
|
+
npm_config_state_dir: join(unitRoot, "pnpm-state")
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3006
|
+
/**
|
|
3007
|
+
* git 的非交互 env:关掉终端提示/askpass;Windows 上不给 askpass 指 POSIX 路径(那台机器上没有)。
|
|
3008
|
+
* @param platform 宿主平台。
|
|
3009
|
+
* @returns git 非交互 env。
|
|
3010
|
+
*/
|
|
3011
|
+
function promptlessGitEnvOf(platform) {
|
|
3012
|
+
const shared = {
|
|
3013
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
3014
|
+
SSH_ASKPASS_REQUIRE: "never",
|
|
3015
|
+
GCM_INTERACTIVE: "never",
|
|
3016
|
+
GIT_SSH_COMMAND: "ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new"
|
|
3017
|
+
};
|
|
3018
|
+
if (platform === "win32") return shared;
|
|
3019
|
+
return {
|
|
3020
|
+
...shared,
|
|
3021
|
+
GIT_ASKPASS: "/usr/bin/false",
|
|
3022
|
+
SSH_ASKPASS: "/usr/bin/false"
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
/**
|
|
3026
|
+
* 在父环境上叠加 `promptlessGitEnvOf`,得到可传给执行缝的 git env。
|
|
3027
|
+
* @param parent 父环境。
|
|
3028
|
+
* @param platform 宿主平台。
|
|
3029
|
+
* @returns 只含字符串的 env。
|
|
3030
|
+
*/
|
|
3031
|
+
function promptlessProcessEnvOf(parent, platform) {
|
|
3032
|
+
const env = {
|
|
3033
|
+
...inheritedEnvOf(parent),
|
|
3034
|
+
...promptlessGitEnvOf(platform)
|
|
3035
|
+
};
|
|
3036
|
+
if (platform === "win32") {
|
|
3037
|
+
delete env["GIT_ASKPASS"];
|
|
3038
|
+
delete env["SSH_ASKPASS"];
|
|
3039
|
+
}
|
|
3040
|
+
return env;
|
|
3041
|
+
}
|
|
3042
|
+
//#endregion
|
|
2767
3043
|
//#region ../shared/dist/host/facts.js
|
|
2768
3044
|
function createFactLog(options = {}) {
|
|
2769
3045
|
const epoch = options.epoch ?? `e${randomUUID()}`;
|
|
@@ -2938,7 +3214,7 @@ function parseCrashReport(payload) {
|
|
|
2938
3214
|
workspace,
|
|
2939
3215
|
unit,
|
|
2940
3216
|
reason,
|
|
2941
|
-
at: atOf(record["at"]) ??
|
|
3217
|
+
at: atOf(record["at"]) ?? now()
|
|
2942
3218
|
}
|
|
2943
3219
|
};
|
|
2944
3220
|
}
|
|
@@ -3459,7 +3735,7 @@ function healthHandler(ctx, runtimes) {
|
|
|
3459
3735
|
profile: dir === void 0 ? null : dir.split("/").pop(),
|
|
3460
3736
|
profileDir: dir ?? null,
|
|
3461
3737
|
pid: process.pid,
|
|
3462
|
-
now: (
|
|
3738
|
+
now: nowIso(),
|
|
3463
3739
|
...runtimes === void 0 ? {} : { runtimes }
|
|
3464
3740
|
};
|
|
3465
3741
|
};
|
|
@@ -3498,7 +3774,7 @@ function registerPanelState(ctx, store) {
|
|
|
3498
3774
|
store.cards(parsed.value);
|
|
3499
3775
|
return {
|
|
3500
3776
|
ok: true,
|
|
3501
|
-
at:
|
|
3777
|
+
at: now()
|
|
3502
3778
|
};
|
|
3503
3779
|
}
|
|
3504
3780
|
}, {
|
|
@@ -3512,7 +3788,7 @@ function registerPanelState(ctx, store) {
|
|
|
3512
3788
|
store.crash(parsed.value);
|
|
3513
3789
|
return {
|
|
3514
3790
|
ok: true,
|
|
3515
|
-
at:
|
|
3791
|
+
at: now()
|
|
3516
3792
|
};
|
|
3517
3793
|
}
|
|
3518
3794
|
}]
|
|
@@ -3538,19 +3814,19 @@ function registerDiyRecordAct(ctx, resource, log) {
|
|
|
3538
3814
|
}
|
|
3539
3815
|
//#endregion
|
|
3540
3816
|
//#region ../shared/dist/host/long-task.js
|
|
3541
|
-
var LIVE = /* @__PURE__ */ new Set(["running", "needs-login"]);
|
|
3817
|
+
var LIVE$1 = /* @__PURE__ */ new Set(["running", "needs-login"]);
|
|
3542
3818
|
var RECENT_MS = 6e5;
|
|
3543
3819
|
function createLongTaskRegistry(deps) {
|
|
3544
3820
|
const runs = /* @__PURE__ */ new Map();
|
|
3545
3821
|
const controls = /* @__PURE__ */ new Map();
|
|
3546
|
-
const now = deps.now ?? (() => (
|
|
3822
|
+
const now$7 = deps.now ?? (() => nowIso());
|
|
3547
3823
|
let counter = 0;
|
|
3548
|
-
const nextId = deps.nextId ?? (() => `run-${
|
|
3824
|
+
const nextId = deps.nextId ?? (() => `run-${now()}-${counter += 1}`);
|
|
3549
3825
|
const recentMs = deps.recentMs ?? RECENT_MS;
|
|
3550
3826
|
const listeners = /* @__PURE__ */ new Set();
|
|
3551
3827
|
const live = (id) => {
|
|
3552
3828
|
const current = runs.get(id);
|
|
3553
|
-
if (current === void 0 || !LIVE.has(current.status)) return null;
|
|
3829
|
+
if (current === void 0 || !LIVE$1.has(current.status)) return null;
|
|
3554
3830
|
return current;
|
|
3555
3831
|
};
|
|
3556
3832
|
const notify = (change) => {
|
|
@@ -3564,7 +3840,7 @@ function createLongTaskRegistry(deps) {
|
|
|
3564
3840
|
const patched = {
|
|
3565
3841
|
...current,
|
|
3566
3842
|
...next,
|
|
3567
|
-
at: now()
|
|
3843
|
+
at: now$7()
|
|
3568
3844
|
};
|
|
3569
3845
|
runs.set(id, patched);
|
|
3570
3846
|
notify({
|
|
@@ -3581,25 +3857,25 @@ function createLongTaskRegistry(deps) {
|
|
|
3581
3857
|
return null;
|
|
3582
3858
|
},
|
|
3583
3859
|
current(workspace) {
|
|
3584
|
-
const floor = epochOf(now()) - recentMs;
|
|
3860
|
+
const floor = epochOf(now$7()) - recentMs;
|
|
3585
3861
|
const newest = /* @__PURE__ */ new Map();
|
|
3586
3862
|
for (const snapshot of runs.values()) {
|
|
3587
3863
|
if (snapshot.workspace !== workspace) continue;
|
|
3588
3864
|
newest.set(snapshot.target, snapshot);
|
|
3589
3865
|
}
|
|
3590
|
-
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) => ({
|
|
3591
3867
|
...deps.view(snapshot),
|
|
3592
3868
|
id: snapshot.id
|
|
3593
3869
|
}));
|
|
3594
3870
|
},
|
|
3595
3871
|
live() {
|
|
3596
|
-
return [...runs.values()].filter((snapshot) => LIVE.has(snapshot.status));
|
|
3872
|
+
return [...runs.values()].filter((snapshot) => LIVE$1.has(snapshot.status));
|
|
3597
3873
|
},
|
|
3598
3874
|
start(options) {
|
|
3599
3875
|
const running = this.active(options.workspace, options.target);
|
|
3600
3876
|
if (running !== null) return running;
|
|
3601
3877
|
const id = nextId();
|
|
3602
|
-
const startedAt = now();
|
|
3878
|
+
const startedAt = now$7();
|
|
3603
3879
|
runs.set(id, {
|
|
3604
3880
|
id,
|
|
3605
3881
|
workspace: options.workspace,
|
|
@@ -4010,7 +4286,7 @@ function defaultSleep(ms, signal) {
|
|
|
4010
4286
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
4011
4287
|
});
|
|
4012
4288
|
}
|
|
4013
|
-
function targetOf(flow, scope) {
|
|
4289
|
+
function targetOf$1(flow, scope) {
|
|
4014
4290
|
if (flow.target === void 0) return flow.id;
|
|
4015
4291
|
const value = resolveValue(flow.target, scope);
|
|
4016
4292
|
return typeof value === "string" && value.trim() !== "" ? value.trim() : flow.id;
|
|
@@ -4148,14 +4424,14 @@ async function runOne(options) {
|
|
|
4148
4424
|
async function runFlow(options) {
|
|
4149
4425
|
const { flow, input, actions, signal, runId, workspace, onStep } = options;
|
|
4150
4426
|
const sleep = options.sleep ?? defaultSleep;
|
|
4151
|
-
const now = options.now ?? (() =>
|
|
4427
|
+
const now$6 = options.now ?? (() => now());
|
|
4152
4428
|
const steps = {};
|
|
4153
4429
|
const emit = (event) => {
|
|
4154
4430
|
try {
|
|
4155
4431
|
onStep(event);
|
|
4156
4432
|
} catch {}
|
|
4157
4433
|
};
|
|
4158
|
-
const target = targetOf(flow, {
|
|
4434
|
+
const target = targetOf$1(flow, {
|
|
4159
4435
|
in: input,
|
|
4160
4436
|
steps
|
|
4161
4437
|
});
|
|
@@ -4218,7 +4494,7 @@ async function runFlow(options) {
|
|
|
4218
4494
|
});
|
|
4219
4495
|
},
|
|
4220
4496
|
sleep,
|
|
4221
|
-
now
|
|
4497
|
+
now: now$6
|
|
4222
4498
|
});
|
|
4223
4499
|
if (nested.status === "failed") {
|
|
4224
4500
|
const failed = nested.failedStep === void 0 ? step.id : `${step.id}.${nested.failedStep}`;
|
|
@@ -4263,7 +4539,7 @@ async function runFlow(options) {
|
|
|
4263
4539
|
runId,
|
|
4264
4540
|
workspace,
|
|
4265
4541
|
sleep,
|
|
4266
|
-
now,
|
|
4542
|
+
now: now$6,
|
|
4267
4543
|
emit
|
|
4268
4544
|
}));
|
|
4269
4545
|
}
|
|
@@ -4293,7 +4569,7 @@ async function runFlow(options) {
|
|
|
4293
4569
|
runId,
|
|
4294
4570
|
workspace,
|
|
4295
4571
|
sleep,
|
|
4296
|
-
now,
|
|
4572
|
+
now: now$6,
|
|
4297
4573
|
emit
|
|
4298
4574
|
});
|
|
4299
4575
|
emit({
|
|
@@ -4373,7 +4649,8 @@ function sessionIdOf(exec) {
|
|
|
4373
4649
|
* 建一份宿主 kit;每个挂载点一份,没有模块级状态。
|
|
4374
4650
|
* @param parts.libInstallCommand 拼「装这个共用依赖」的那条命令。
|
|
4375
4651
|
* @param parts.surface 宿主平台事实;由组合根按 profile 与宿主进程算好再交进来。
|
|
4376
|
-
* @
|
|
4652
|
+
* @param parts.seam 按 unit 绑定好的执行缝门面;模块清单没声明写入域时缺省不给。
|
|
4653
|
+
* @returns flow / sessionIdOf / sessionWorkspace / libInstallCommand / surface,有门面时带上 seam。
|
|
4377
4654
|
*/
|
|
4378
4655
|
function createHostKit(parts) {
|
|
4379
4656
|
return {
|
|
@@ -4381,7 +4658,8 @@ function createHostKit(parts) {
|
|
|
4381
4658
|
sessionIdOf,
|
|
4382
4659
|
sessionWorkspace,
|
|
4383
4660
|
libInstallCommand: parts.libInstallCommand,
|
|
4384
|
-
surface: parts.surface
|
|
4661
|
+
surface: parts.surface,
|
|
4662
|
+
...parts.seam === void 0 ? {} : { seam: parts.seam }
|
|
4385
4663
|
};
|
|
4386
4664
|
}
|
|
4387
4665
|
//#endregion
|
|
@@ -4751,41 +5029,26 @@ function createValueHandoff(options = {}) {
|
|
|
4751
5029
|
const ttlMs = options.ttlMs ?? 15e3;
|
|
4752
5030
|
const held = /* @__PURE__ */ new Map();
|
|
4753
5031
|
return {
|
|
4754
|
-
hand(entry, value, now =
|
|
5032
|
+
hand(entry, value, now$4 = now()) {
|
|
4755
5033
|
held.set(handoffId(entry), {
|
|
4756
5034
|
value,
|
|
4757
|
-
at: now
|
|
5035
|
+
at: now$4
|
|
4758
5036
|
});
|
|
4759
5037
|
},
|
|
4760
|
-
take(entry, now =
|
|
5038
|
+
take(entry, now$5 = now()) {
|
|
4761
5039
|
const id = handoffId(entry);
|
|
4762
5040
|
const hit = held.get(id);
|
|
4763
5041
|
if (hit === void 0) return void 0;
|
|
4764
5042
|
held.delete(id);
|
|
4765
|
-
return now - hit.at >= ttlMs ? void 0 : hit;
|
|
5043
|
+
return now$5 - hit.at >= ttlMs ? void 0 : hit;
|
|
4766
5044
|
}
|
|
4767
5045
|
};
|
|
4768
5046
|
}
|
|
4769
5047
|
//#endregion
|
|
4770
|
-
//#region ../shared/dist/host/
|
|
4771
|
-
function
|
|
4772
|
-
const
|
|
4773
|
-
|
|
4774
|
-
return join(homedir(), ".dsh");
|
|
4775
|
-
}
|
|
4776
|
-
function workbenchRoot() {
|
|
4777
|
-
const configured = process.env.DSH_WORKBENCH_HOME?.trim();
|
|
4778
|
-
if (configured !== void 0 && configured !== "") return configured;
|
|
4779
|
-
return join(dshHome(), "workbench");
|
|
4780
|
-
}
|
|
4781
|
-
function workspacesRoot() {
|
|
4782
|
-
return join(workbenchRoot(), "workspaces");
|
|
4783
|
-
}
|
|
4784
|
-
//#endregion
|
|
4785
|
-
//#region ../shared/dist/host/workspace-store.js
|
|
4786
|
-
function slug(raw, max = 40) {
|
|
4787
|
-
const cleaned = raw.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[-.]+|[-.]+$/g, "").slice(0, max);
|
|
4788
|
-
return cleaned === "" ? "workspace" : cleaned;
|
|
5048
|
+
//#region ../shared/dist/host/workspace-store.js
|
|
5049
|
+
function slug(raw, max = 40) {
|
|
5050
|
+
const cleaned = raw.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[-.]+|[-.]+$/g, "").slice(0, max);
|
|
5051
|
+
return cleaned === "" ? "workspace" : cleaned;
|
|
4789
5052
|
}
|
|
4790
5053
|
function workspaceKey(workspace) {
|
|
4791
5054
|
const abs = path.resolve(workspace);
|
|
@@ -4823,9 +5086,6 @@ function memoryStore(dir) {
|
|
|
4823
5086
|
};
|
|
4824
5087
|
}
|
|
4825
5088
|
function fileStore(dir) {
|
|
4826
|
-
const ensureFor = (file) => {
|
|
4827
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
4828
|
-
};
|
|
4829
5089
|
return {
|
|
4830
5090
|
dir,
|
|
4831
5091
|
read: (name) => {
|
|
@@ -4837,11 +5097,7 @@ function fileStore(dir) {
|
|
|
4837
5097
|
}
|
|
4838
5098
|
},
|
|
4839
5099
|
write: (name, value) => {
|
|
4840
|
-
|
|
4841
|
-
ensureFor(file);
|
|
4842
|
-
const tmp = `${file}.tmp`;
|
|
4843
|
-
fs.writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
4844
|
-
fs.renameSync(tmp, file);
|
|
5100
|
+
writeJsonFile(inside(dir, name), value);
|
|
4845
5101
|
},
|
|
4846
5102
|
remove: (name) => {
|
|
4847
5103
|
const file = inside(dir, name);
|
|
@@ -4890,6 +5146,718 @@ function createWorkspaceStore(options) {
|
|
|
4890
5146
|
};
|
|
4891
5147
|
}
|
|
4892
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
|
|
5176
|
+
//#region ../shared/dist/host/shell.js
|
|
5177
|
+
var DEFAULT_MAX_BYTES$3 = 8388608;
|
|
5178
|
+
var GUARD_SLACK_MS = 1e3;
|
|
5179
|
+
function stalledResult() {
|
|
5180
|
+
return {
|
|
5181
|
+
exitCode: null,
|
|
5182
|
+
timedOut: true,
|
|
5183
|
+
aborted: false,
|
|
5184
|
+
stdout: "",
|
|
5185
|
+
stderr: "",
|
|
5186
|
+
truncated: false,
|
|
5187
|
+
stalled: true
|
|
5188
|
+
};
|
|
5189
|
+
}
|
|
5190
|
+
async function settleWithin(work, limitMs) {
|
|
5191
|
+
let timer;
|
|
5192
|
+
const guard = new Promise((resolve) => {
|
|
5193
|
+
timer = setTimeout(() => resolve(null), limitMs);
|
|
5194
|
+
});
|
|
5195
|
+
try {
|
|
5196
|
+
return await Promise.race([work, guard]);
|
|
5197
|
+
} finally {
|
|
5198
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
5201
|
+
async function runShell(shell, command, options = {}) {
|
|
5202
|
+
const { workdir, timeoutMs = GIT_TIMEOUT_MS, signal, env, sandboxPolicy } = options;
|
|
5203
|
+
const spec = shell.resolve({
|
|
5204
|
+
command,
|
|
5205
|
+
...workdir === void 0 ? {} : { workdir },
|
|
5206
|
+
timeoutMs,
|
|
5207
|
+
onExpiry: "kill",
|
|
5208
|
+
stdoutMaxBytes: DEFAULT_MAX_BYTES$3,
|
|
5209
|
+
...signal === void 0 ? {} : { signal },
|
|
5210
|
+
...env === void 0 ? {} : { env },
|
|
5211
|
+
...sandboxPolicy === void 0 ? {} : { sandboxPolicy }
|
|
5212
|
+
});
|
|
5213
|
+
return await settleWithin((async () => {
|
|
5214
|
+
const result = await (await shell.execute(spec)).result();
|
|
5215
|
+
return {
|
|
5216
|
+
exitCode: result.exitCode,
|
|
5217
|
+
timedOut: result.timedOut === true,
|
|
5218
|
+
aborted: result.aborted === true,
|
|
5219
|
+
stdout: result.stdout.text,
|
|
5220
|
+
stderr: result.stderr.text,
|
|
5221
|
+
truncated: result.stdout.truncated || result.stderr.truncated,
|
|
5222
|
+
stalled: false,
|
|
5223
|
+
...result.sandbox === void 0 ? {} : { sandbox: result.sandbox }
|
|
5224
|
+
};
|
|
5225
|
+
})(), timeoutMs + GUARD_SLACK_MS) ?? stalledResult();
|
|
5226
|
+
}
|
|
5227
|
+
//#endregion
|
|
5228
|
+
//#region ../shared/dist/host/seam/policy.js
|
|
5229
|
+
function seamModeOf(value) {
|
|
5230
|
+
const text = value?.trim();
|
|
5231
|
+
return text === "read-only" || text === "workspace-write" || text === "danger-full-access" ? text : "workspace-write";
|
|
5232
|
+
}
|
|
5233
|
+
function rank(mode) {
|
|
5234
|
+
return mode === "read-only" ? 0 : mode === "workspace-write" ? 1 : 2;
|
|
5235
|
+
}
|
|
5236
|
+
function narrowerMode(left, right) {
|
|
5237
|
+
return rank(left) <= rank(right) ? left : right;
|
|
5238
|
+
}
|
|
5239
|
+
function insideOf(root, path) {
|
|
5240
|
+
const confined = resolve(root);
|
|
5241
|
+
const target = resolve(path);
|
|
5242
|
+
if (target === confined) return true;
|
|
5243
|
+
return target.startsWith(confined.endsWith(sep) ? confined : `${confined}${sep}`);
|
|
5244
|
+
}
|
|
5245
|
+
function productPolicy(ceiling, productRoot) {
|
|
5246
|
+
return {
|
|
5247
|
+
mode: ceiling === "read-only" ? "read-only" : ceiling === "danger-full-access" ? "danger-full-access" : "workspace-write",
|
|
5248
|
+
workspaceRoot: resolve(productRoot)
|
|
5249
|
+
};
|
|
5250
|
+
}
|
|
5251
|
+
function workspacePolicy(ceiling, grant) {
|
|
5252
|
+
if (grant === void 0) throw new SeamError("workspace-unknown", "这条会话解析不到工作区:先在左栏选一个工作区再跑需要写工作区的命令");
|
|
5253
|
+
const mode = narrowerMode(ceiling, grant.mode);
|
|
5254
|
+
if (mode === "read-only") throw new SeamError("read-only", "当前会话是只读模式:要写工作区,先在权限设置里切换模式");
|
|
5255
|
+
return {
|
|
5256
|
+
mode,
|
|
5257
|
+
workspaceRoot: resolve(grant.workspaceRoot)
|
|
5258
|
+
};
|
|
5259
|
+
}
|
|
5260
|
+
function atFilesystemRoot(path) {
|
|
5261
|
+
return parse(path).root === path;
|
|
5262
|
+
}
|
|
5263
|
+
function commonDirectory(paths) {
|
|
5264
|
+
const dirs = paths.map((path) => dirname(resolve(path)));
|
|
5265
|
+
let shared = dirs[0];
|
|
5266
|
+
if (shared === void 0) return void 0;
|
|
5267
|
+
for (const dir of dirs.slice(1)) while (shared !== dirname(shared) && !insideOf(shared, dir)) shared = dirname(shared);
|
|
5268
|
+
return shared;
|
|
5269
|
+
}
|
|
5270
|
+
function pathsPolicy(ceiling, paths) {
|
|
5271
|
+
if (paths.length === 0) throw new SeamError("paths-span-trees", "这次写入没有给出任何路径");
|
|
5272
|
+
if (!paths.every((path) => isAbsolute(path))) throw new SeamError("paths-span-trees", "外部路径必须是绝对路径");
|
|
5273
|
+
if (ceiling === "read-only") throw new SeamError("read-only", "当前部署是只读模式:这条命令不能写外部路径");
|
|
5274
|
+
const root = commonDirectory(paths);
|
|
5275
|
+
if (root === void 0) throw new SeamError("paths-span-trees", "这次写入没有给出任何路径");
|
|
5276
|
+
if (atFilesystemRoot(root)) throw new SeamError("paths-span-trees", paths.length > 1 ? "这次写入跨了多个目录:请分开做,或把产物改到工作台目录" : "不能把整个文件系统声明成写入域");
|
|
5277
|
+
return {
|
|
5278
|
+
mode: "workspace-write",
|
|
5279
|
+
workspaceRoot: root
|
|
5280
|
+
};
|
|
5281
|
+
}
|
|
5282
|
+
function denialScopeOf(path, policy, productRoot, grant) {
|
|
5283
|
+
if (path === void 0) return "unknown";
|
|
5284
|
+
if (insideOf(productRoot, path)) return "product";
|
|
5285
|
+
if (grant !== void 0 && insideOf(grant.workspaceRoot, path)) return "workspace";
|
|
5286
|
+
if (insideOf(policy.workspaceRoot, path)) return policy.workspaceRoot === resolve(productRoot) ? "product" : "workspace";
|
|
5287
|
+
return "external";
|
|
5288
|
+
}
|
|
5289
|
+
function policyOf(intent, ceiling, productRoot, grant) {
|
|
5290
|
+
if (intent.kind === "product" || intent.kind === "none") return productPolicy(ceiling, productRoot);
|
|
5291
|
+
if (intent.kind === "workspace") return workspacePolicy(ceiling, grant);
|
|
5292
|
+
return pathsPolicy(ceiling, intent.paths);
|
|
5293
|
+
}
|
|
5294
|
+
//#endregion
|
|
5295
|
+
//#region ../shared/dist/host/seam/env.js
|
|
5296
|
+
/**
|
|
5297
|
+
* 产品域(运行时根)的受控 env:隔离 HOME、npm/pnpm 缓存钉在运行时根、真 npmrc/gitconfig 只读指过去。
|
|
5298
|
+
* @param input 运行时根、可选的 HOME 与真实用户目录。
|
|
5299
|
+
* @returns 产品域 env。
|
|
5300
|
+
*/
|
|
5301
|
+
function seamProductEnv(input) {
|
|
5302
|
+
return productEnvOf(input);
|
|
5303
|
+
}
|
|
5304
|
+
/**
|
|
5305
|
+
* 模块单元的受控 env:HOME 与 npm/pnpm 目录钉到单元数据目录。
|
|
5306
|
+
* @param input 运行时根、单元数据目录、可选的 TRUE 用户目录。
|
|
5307
|
+
* @returns 单元 env。
|
|
5308
|
+
*/
|
|
5309
|
+
function seamUnitEnv(input) {
|
|
5310
|
+
return unitEnvOf(input);
|
|
5311
|
+
}
|
|
5312
|
+
//#endregion
|
|
5313
|
+
//#region ../shared/dist/host/seam/denial.js
|
|
5314
|
+
var PATTERNS = [
|
|
5315
|
+
/npm (?:error|ERR!) path (.+)$/m,
|
|
5316
|
+
/(?:EPERM|EACCES):[^,\n]*, (?:open|unlink|mkdir|scandir|rename|access|stat|lstat|read|write|copyfile|rmdir|opendir|utimes|chmod|chown) '([^']+)'/i,
|
|
5317
|
+
/(?:Operation not permitted|Permission denied)[^\n]*?'([^']+)'/i,
|
|
5318
|
+
/fatal: could not create work tree dir '([^']+)'/i,
|
|
5319
|
+
/mkdir: cannot create directory '([^']+)'/i,
|
|
5320
|
+
/cannot (?:create|mkdir) (?:directory )?'([^']+)'/i,
|
|
5321
|
+
/(?:Operation not permitted|Permission denied):\s*([^\s'\n]+)/i
|
|
5322
|
+
];
|
|
5323
|
+
function firstMatch(text) {
|
|
5324
|
+
for (const pattern of PATTERNS) {
|
|
5325
|
+
const value = pattern.exec(text)?.[1]?.trim();
|
|
5326
|
+
if (value !== void 0 && value !== "") return value;
|
|
5327
|
+
}
|
|
5328
|
+
}
|
|
5329
|
+
function deniedPathOf(stderr, stdout = "") {
|
|
5330
|
+
return firstMatch(stderr) ?? firstMatch(stdout);
|
|
5331
|
+
}
|
|
5332
|
+
function sandboxDeniedOf(facts) {
|
|
5333
|
+
return facts?.denied === true;
|
|
5334
|
+
}
|
|
5335
|
+
//#endregion
|
|
5336
|
+
//#region ../shared/dist/host/seam/host-env.js
|
|
5337
|
+
function hostEnvOf(env) {
|
|
5338
|
+
const merged = { ...process.env };
|
|
5339
|
+
for (const [key, value] of Object.entries(env ?? {})) if (value === void 0) delete merged[key];
|
|
5340
|
+
else merged[key] = value;
|
|
5341
|
+
return merged;
|
|
5342
|
+
}
|
|
5343
|
+
function definedEnvOf(env) {
|
|
5344
|
+
const out = {};
|
|
5345
|
+
for (const [key, value] of Object.entries(env ?? {})) if (value !== void 0) out[key] = value;
|
|
5346
|
+
return out;
|
|
5347
|
+
}
|
|
5348
|
+
//#endregion
|
|
5349
|
+
//#region ../shared/dist/host/seam/process.js
|
|
5350
|
+
function streamOf(readable) {
|
|
5351
|
+
if (readable === null || readable === void 0) return null;
|
|
5352
|
+
return { onData(listener) {
|
|
5353
|
+
const handler = (chunk) => listener(String(chunk));
|
|
5354
|
+
readable.on("data", handler);
|
|
5355
|
+
return () => {
|
|
5356
|
+
readable.off("data", handler);
|
|
5357
|
+
};
|
|
5358
|
+
} };
|
|
5359
|
+
}
|
|
5360
|
+
function createNodeProcessHost() {
|
|
5361
|
+
return { spawn(options) {
|
|
5362
|
+
const logFd = options.stdio === "log" && options.logFile !== void 0 ? openSync(options.logFile, "a") : void 0;
|
|
5363
|
+
const stdio = options.stdio === "pipe" ? "pipe" : logFd === void 0 ? "ignore" : [
|
|
5364
|
+
"ignore",
|
|
5365
|
+
logFd,
|
|
5366
|
+
logFd
|
|
5367
|
+
];
|
|
5368
|
+
let logClosed = false;
|
|
5369
|
+
const closeLog = () => {
|
|
5370
|
+
if (logClosed || logFd === void 0) return;
|
|
5371
|
+
logClosed = true;
|
|
5372
|
+
closeSync(logFd);
|
|
5373
|
+
};
|
|
5374
|
+
let child;
|
|
5375
|
+
try {
|
|
5376
|
+
child = execa(options.file, [...options.args], {
|
|
5377
|
+
...options.cwd === void 0 ? {} : { cwd: options.cwd },
|
|
5378
|
+
env: { ...options.env },
|
|
5379
|
+
detached: options.detached,
|
|
5380
|
+
stdio,
|
|
5381
|
+
windowsHide: options.windowsHide,
|
|
5382
|
+
reject: false,
|
|
5383
|
+
buffer: false
|
|
5384
|
+
});
|
|
5385
|
+
} catch (error) {
|
|
5386
|
+
closeLog();
|
|
5387
|
+
throw error;
|
|
5388
|
+
}
|
|
5389
|
+
if (logFd !== void 0) {
|
|
5390
|
+
child.nodeChildProcess.once("spawn", closeLog);
|
|
5391
|
+
child.nodeChildProcess.once("error", closeLog);
|
|
5392
|
+
}
|
|
5393
|
+
const stdin = child.stdin;
|
|
5394
|
+
return {
|
|
5395
|
+
pid: child.pid,
|
|
5396
|
+
stdin: stdin === null || stdin === void 0 ? null : { write: (chunk) => stdin.write(chunk) },
|
|
5397
|
+
stdout: streamOf(child.stdout),
|
|
5398
|
+
stderr: streamOf(child.stderr),
|
|
5399
|
+
onExit(listener) {
|
|
5400
|
+
const handler = (code, signal) => listener({
|
|
5401
|
+
code,
|
|
5402
|
+
signal
|
|
5403
|
+
});
|
|
5404
|
+
child.nodeChildProcess.on("exit", handler);
|
|
5405
|
+
return () => {
|
|
5406
|
+
child.nodeChildProcess.off("exit", handler);
|
|
5407
|
+
};
|
|
5408
|
+
},
|
|
5409
|
+
onError(listener) {
|
|
5410
|
+
child.nodeChildProcess.on("error", listener);
|
|
5411
|
+
return () => {
|
|
5412
|
+
child.nodeChildProcess.off("error", listener);
|
|
5413
|
+
};
|
|
5414
|
+
},
|
|
5415
|
+
unref() {
|
|
5416
|
+
child.nodeChildProcess.unref();
|
|
5417
|
+
},
|
|
5418
|
+
kill(signal) {
|
|
5419
|
+
child.kill(signal);
|
|
5420
|
+
}
|
|
5421
|
+
};
|
|
5422
|
+
} };
|
|
5423
|
+
}
|
|
5424
|
+
//#endregion
|
|
5425
|
+
//#region ../shared/dist/host/seam/run.js
|
|
5426
|
+
var DENIAL_MARKER = /\[sandbox: file access denied under [a-z-]+ mode\]/i;
|
|
5427
|
+
var DEFAULT_MAX_BYTES$2 = 8388608;
|
|
5428
|
+
function scopeText(scope) {
|
|
5429
|
+
if (scope === "product") return "工作台自己的目录";
|
|
5430
|
+
if (scope === "workspace") return "会话工作区";
|
|
5431
|
+
if (scope === "external") return "工作区之外的路径";
|
|
5432
|
+
return "没认出来的路径";
|
|
5433
|
+
}
|
|
5434
|
+
function denialMessageOf(scope, path, output) {
|
|
5435
|
+
const where = path === void 0 ? "" : `:${path}`;
|
|
5436
|
+
const marker = DENIAL_MARKER.test(output) ? "" : "(输出里没有沙箱标记,按拒绝处理)";
|
|
5437
|
+
const next = scope === "external" ? "要写工作区外的路径得先让用户点头:把目标改到工作区,或让工具补上审批后再试" : "这是工作台自己的声明问题,不是用户的配置问题:把命令、域与路径一起记下来交给维护者";
|
|
5438
|
+
return `被沙箱挡住了(${scopeText(scope)}${where})${marker}。${next}`;
|
|
5439
|
+
}
|
|
5440
|
+
function settle$1(result, denial) {
|
|
5441
|
+
return {
|
|
5442
|
+
exitCode: result.exitCode,
|
|
5443
|
+
timedOut: result.timedOut,
|
|
5444
|
+
aborted: result.aborted,
|
|
5445
|
+
stdout: result.stdout,
|
|
5446
|
+
stderr: result.stderr,
|
|
5447
|
+
truncated: result.truncated,
|
|
5448
|
+
stalled: result.stalled,
|
|
5449
|
+
...denial === void 0 ? {} : { denial }
|
|
5450
|
+
};
|
|
5451
|
+
}
|
|
5452
|
+
function targetOf(request) {
|
|
5453
|
+
const hasCommand = request.command !== void 0;
|
|
5454
|
+
const hasArgv = request.argv !== void 0;
|
|
5455
|
+
if (hasArgv && !hasCommand) {
|
|
5456
|
+
const argv = request.argv ?? [];
|
|
5457
|
+
if (argv.length === 0) throw new SeamError("scope-not-declared", "argv 是空的:至少给一个可执行文件");
|
|
5458
|
+
return { argv };
|
|
5459
|
+
}
|
|
5460
|
+
if (hasCommand && !hasArgv) return { line: request.command ?? "" };
|
|
5461
|
+
throw new SeamError("scope-not-declared", "这条执行请求要么给 argv,要么给一条命令行,不能两个都给或都不给");
|
|
5462
|
+
}
|
|
5463
|
+
function displayOf(target) {
|
|
5464
|
+
return "argv" in target ? quotedArgvLine(target.argv) : target.line;
|
|
5465
|
+
}
|
|
5466
|
+
function targetInvocationOf(target, shellKind) {
|
|
5467
|
+
if ("argv" in target) {
|
|
5468
|
+
const [file, ...args] = target.argv;
|
|
5469
|
+
if (file === void 0) throw new SeamError("scope-not-declared", "argv 是空的:至少给一个可执行文件");
|
|
5470
|
+
return {
|
|
5471
|
+
file,
|
|
5472
|
+
args
|
|
5473
|
+
};
|
|
5474
|
+
}
|
|
5475
|
+
return invocationOf(shellKind ?? shellKindOf(), target.line);
|
|
5476
|
+
}
|
|
5477
|
+
function sandboxedLineOf(target, shellKind) {
|
|
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)));
|
|
5481
|
+
}
|
|
5482
|
+
function createSeam(ports) {
|
|
5483
|
+
const runtimeRoot = ports.runtimeRoot ?? workbenchRoot();
|
|
5484
|
+
const ceiling = ports.ceiling ?? seamModeOf(process.env["DSH_PERMISSION_MODE"]);
|
|
5485
|
+
const allowHost = ports.allowHost === true;
|
|
5486
|
+
const processes = ports.processes ?? createNodeProcessHost();
|
|
5487
|
+
const limit = ports.factLimit ?? 80;
|
|
5488
|
+
const facts = [];
|
|
5489
|
+
function record(fact) {
|
|
5490
|
+
facts.push(fact);
|
|
5491
|
+
if (facts.length > limit) facts.splice(0, facts.length - limit);
|
|
5492
|
+
}
|
|
5493
|
+
function grantFor(session) {
|
|
5494
|
+
if (ports.grantOf === void 0 || session === void 0 || session === null) return void 0;
|
|
5495
|
+
try {
|
|
5496
|
+
return ports.grantOf(session);
|
|
5497
|
+
} catch {
|
|
5498
|
+
return;
|
|
5499
|
+
}
|
|
5500
|
+
}
|
|
5501
|
+
function requireHost(intent) {
|
|
5502
|
+
if (intent.kind !== "host") throw new SeamError("scope-not-declared", "这条调用要走宿主进程执行,但写入域不是 host");
|
|
5503
|
+
if (!allowHost) throw new SeamError("scope-not-declared", "宿主域只给宿主侧接线开出来;模块与普通调用点拿不到");
|
|
5504
|
+
}
|
|
5505
|
+
async function attempt(request, policy, line) {
|
|
5506
|
+
return runShell(ports.shell, line, {
|
|
5507
|
+
...request.workdir === void 0 ? {} : { workdir: request.workdir },
|
|
5508
|
+
...request.timeoutMs === void 0 ? {} : { timeoutMs: request.timeoutMs },
|
|
5509
|
+
...request.signal === void 0 ? {} : { signal: request.signal },
|
|
5510
|
+
env: {
|
|
5511
|
+
...seamProductEnv({
|
|
5512
|
+
runtimeRoot,
|
|
5513
|
+
realHome: ports.realHome
|
|
5514
|
+
}),
|
|
5515
|
+
...definedEnvOf(request.env)
|
|
5516
|
+
},
|
|
5517
|
+
sandboxPolicy: policy
|
|
5518
|
+
});
|
|
5519
|
+
}
|
|
5520
|
+
function healPolicyOf(intent, scope, granted) {
|
|
5521
|
+
if (scope === "product" && intent.kind !== "product") return productPolicy(ceiling, runtimeRoot);
|
|
5522
|
+
if (scope === "workspace" && intent.kind === "workspace") {
|
|
5523
|
+
if (granted === void 0) return void 0;
|
|
5524
|
+
try {
|
|
5525
|
+
return workspacePolicy(ceiling, granted);
|
|
5526
|
+
} catch {
|
|
5527
|
+
return;
|
|
5528
|
+
}
|
|
5529
|
+
}
|
|
5530
|
+
}
|
|
5531
|
+
async function runHost(request, target) {
|
|
5532
|
+
const invocation = targetInvocationOf(target, request.shellKind);
|
|
5533
|
+
const child = processes.spawn({
|
|
5534
|
+
file: invocation.file,
|
|
5535
|
+
args: invocation.args,
|
|
5536
|
+
env: hostEnvOf(request.env),
|
|
5537
|
+
...request.workdir === void 0 ? {} : { cwd: request.workdir },
|
|
5538
|
+
detached: false,
|
|
5539
|
+
stdio: "pipe",
|
|
5540
|
+
windowsHide: true
|
|
5541
|
+
});
|
|
5542
|
+
const state = {
|
|
5543
|
+
stdout: "",
|
|
5544
|
+
stderr: "",
|
|
5545
|
+
truncated: false
|
|
5546
|
+
};
|
|
5547
|
+
const append = (which, chunk) => {
|
|
5548
|
+
const total = state.stdout.length + state.stderr.length;
|
|
5549
|
+
const room = (request.maxBytes ?? DEFAULT_MAX_BYTES$2) - total;
|
|
5550
|
+
if (room <= 0) {
|
|
5551
|
+
state.truncated = true;
|
|
5552
|
+
return;
|
|
5553
|
+
}
|
|
5554
|
+
const text = chunk.length > room ? chunk.slice(0, room) : chunk;
|
|
5555
|
+
state[which] += text;
|
|
5556
|
+
if (chunk.length > room) state.truncated = true;
|
|
5557
|
+
};
|
|
5558
|
+
child.stdout?.onData((chunk) => append("stdout", chunk));
|
|
5559
|
+
child.stderr?.onData((chunk) => append("stderr", chunk));
|
|
5560
|
+
let timer;
|
|
5561
|
+
let timedOut = false;
|
|
5562
|
+
if (request.timeoutMs !== void 0) timer = setTimeout(() => {
|
|
5563
|
+
timedOut = true;
|
|
5564
|
+
child.kill("SIGKILL");
|
|
5565
|
+
}, request.timeoutMs);
|
|
5566
|
+
const exit = await new Promise((resolve) => {
|
|
5567
|
+
child.onExit((event) => resolve(event));
|
|
5568
|
+
child.onError((error) => {
|
|
5569
|
+
state.stderr = `${state.stderr}${String(error)}`;
|
|
5570
|
+
resolve({
|
|
5571
|
+
code: null,
|
|
5572
|
+
signal: null
|
|
5573
|
+
});
|
|
5574
|
+
});
|
|
5575
|
+
});
|
|
5576
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
5577
|
+
return {
|
|
5578
|
+
exitCode: exit.code,
|
|
5579
|
+
timedOut,
|
|
5580
|
+
aborted: false,
|
|
5581
|
+
stdout: state.stdout,
|
|
5582
|
+
stderr: state.stderr,
|
|
5583
|
+
truncated: state.truncated,
|
|
5584
|
+
stalled: false
|
|
5585
|
+
};
|
|
5586
|
+
}
|
|
5587
|
+
function spawnHost(request) {
|
|
5588
|
+
const invocation = targetInvocationOf(request.target, void 0);
|
|
5589
|
+
const logFile = request.logFile ?? join(runtimeRoot, "logs", `${request.label ?? "spawn"}-${String(now())}.log`);
|
|
5590
|
+
mkdirSync(dirname(logFile), { recursive: true });
|
|
5591
|
+
const child = processes.spawn({
|
|
5592
|
+
file: invocation.file,
|
|
5593
|
+
args: invocation.args,
|
|
5594
|
+
env: hostEnvOf(request.env),
|
|
5595
|
+
...request.cwd === void 0 ? {} : { cwd: request.cwd },
|
|
5596
|
+
detached: true,
|
|
5597
|
+
stdio: "log",
|
|
5598
|
+
logFile,
|
|
5599
|
+
windowsHide: true
|
|
5600
|
+
});
|
|
5601
|
+
child.unref();
|
|
5602
|
+
record({
|
|
5603
|
+
at: now(),
|
|
5604
|
+
verb: "spawn",
|
|
5605
|
+
command: displayOf(request.target),
|
|
5606
|
+
intent: "host",
|
|
5607
|
+
mode: "host",
|
|
5608
|
+
workspaceRoot: "",
|
|
5609
|
+
exitCode: null,
|
|
5610
|
+
timedOut: false,
|
|
5611
|
+
stalled: false
|
|
5612
|
+
});
|
|
5613
|
+
const display = displayOf(request.target);
|
|
5614
|
+
const errorListeners = /* @__PURE__ */ new Set();
|
|
5615
|
+
const exitWaiters = [];
|
|
5616
|
+
let settled;
|
|
5617
|
+
let failure;
|
|
5618
|
+
const settleExit = (event) => {
|
|
5619
|
+
if (settled !== void 0) return;
|
|
5620
|
+
settled = event;
|
|
5621
|
+
for (const waiter of exitWaiters.splice(0)) waiter(event);
|
|
5622
|
+
};
|
|
5623
|
+
child.onExit((event) => settleExit(event));
|
|
5624
|
+
child.onError((error) => {
|
|
5625
|
+
failure = error;
|
|
5626
|
+
settleExit({
|
|
5627
|
+
code: null,
|
|
5628
|
+
signal: null
|
|
5629
|
+
});
|
|
5630
|
+
if (errorListeners.size === 0) {
|
|
5631
|
+
ports.logger?.warn?.(`[dsh-workbench] 起进程没跑起来(${display}):${String(error)}`);
|
|
5632
|
+
return;
|
|
5633
|
+
}
|
|
5634
|
+
for (const listener of errorListeners) listener(error);
|
|
5635
|
+
});
|
|
5636
|
+
return {
|
|
5637
|
+
pid: child.pid,
|
|
5638
|
+
logFile,
|
|
5639
|
+
exit: () => settled === void 0 ? new Promise((resolve) => exitWaiters.push(resolve)) : Promise.resolve(settled),
|
|
5640
|
+
onError(listener) {
|
|
5641
|
+
if (failure !== void 0) {
|
|
5642
|
+
listener(failure);
|
|
5643
|
+
return () => {};
|
|
5644
|
+
}
|
|
5645
|
+
errorListeners.add(listener);
|
|
5646
|
+
return () => errorListeners.delete(listener);
|
|
5647
|
+
},
|
|
5648
|
+
kill: (signal) => child.kill(signal ?? "SIGTERM")
|
|
5649
|
+
};
|
|
5650
|
+
}
|
|
5651
|
+
function openHost(request) {
|
|
5652
|
+
const invocation = targetInvocationOf(request.target, void 0);
|
|
5653
|
+
const child = processes.spawn({
|
|
5654
|
+
file: invocation.file,
|
|
5655
|
+
args: invocation.args,
|
|
5656
|
+
env: hostEnvOf(request.env),
|
|
5657
|
+
...request.cwd === void 0 ? {} : { cwd: request.cwd },
|
|
5658
|
+
detached: false,
|
|
5659
|
+
stdio: "pipe",
|
|
5660
|
+
windowsHide: true
|
|
5661
|
+
});
|
|
5662
|
+
record({
|
|
5663
|
+
at: now(),
|
|
5664
|
+
verb: "open",
|
|
5665
|
+
command: displayOf(request.target),
|
|
5666
|
+
intent: "host",
|
|
5667
|
+
mode: "host",
|
|
5668
|
+
workspaceRoot: "",
|
|
5669
|
+
exitCode: null,
|
|
5670
|
+
timedOut: false,
|
|
5671
|
+
stalled: false
|
|
5672
|
+
});
|
|
5673
|
+
const display = displayOf(request.target);
|
|
5674
|
+
const errorListeners = /* @__PURE__ */ new Set();
|
|
5675
|
+
const exitListeners = /* @__PURE__ */ new Set();
|
|
5676
|
+
let failure;
|
|
5677
|
+
let settled;
|
|
5678
|
+
const settleExit = (event) => {
|
|
5679
|
+
if (settled !== void 0) return;
|
|
5680
|
+
settled = event;
|
|
5681
|
+
for (const listener of exitListeners) listener(event);
|
|
5682
|
+
};
|
|
5683
|
+
child.onExit((event) => settleExit(event));
|
|
5684
|
+
child.onError((error) => {
|
|
5685
|
+
failure = error;
|
|
5686
|
+
settleExit({
|
|
5687
|
+
code: null,
|
|
5688
|
+
signal: null
|
|
5689
|
+
});
|
|
5690
|
+
if (errorListeners.size === 0) {
|
|
5691
|
+
ports.logger?.warn?.(`[dsh-workbench] 双向通道起不来(${display}):${String(error)}`);
|
|
5692
|
+
return;
|
|
5693
|
+
}
|
|
5694
|
+
for (const listener of errorListeners) listener(error);
|
|
5695
|
+
});
|
|
5696
|
+
return {
|
|
5697
|
+
pid: child.pid,
|
|
5698
|
+
write: (chunk) => child.stdin?.write(chunk),
|
|
5699
|
+
onData(listener) {
|
|
5700
|
+
const offOut = child.stdout?.onData(listener) ?? (() => {});
|
|
5701
|
+
const offErr = child.stderr?.onData(listener) ?? (() => {});
|
|
5702
|
+
return () => {
|
|
5703
|
+
offOut();
|
|
5704
|
+
offErr();
|
|
5705
|
+
};
|
|
5706
|
+
},
|
|
5707
|
+
onExit(listener) {
|
|
5708
|
+
if (settled !== void 0) {
|
|
5709
|
+
listener(settled);
|
|
5710
|
+
return () => {};
|
|
5711
|
+
}
|
|
5712
|
+
exitListeners.add(listener);
|
|
5713
|
+
return () => exitListeners.delete(listener);
|
|
5714
|
+
},
|
|
5715
|
+
onError(listener) {
|
|
5716
|
+
if (failure !== void 0) {
|
|
5717
|
+
listener(failure);
|
|
5718
|
+
return () => {};
|
|
5719
|
+
}
|
|
5720
|
+
errorListeners.add(listener);
|
|
5721
|
+
return () => errorListeners.delete(listener);
|
|
5722
|
+
},
|
|
5723
|
+
kill: (signal) => child.kill(signal ?? "SIGTERM")
|
|
5724
|
+
};
|
|
5725
|
+
}
|
|
5726
|
+
return {
|
|
5727
|
+
async run(request) {
|
|
5728
|
+
const target = targetOf(request);
|
|
5729
|
+
if (request.intent.kind === "host") {
|
|
5730
|
+
requireHost(request.intent);
|
|
5731
|
+
const started = now();
|
|
5732
|
+
const result = await runHost(request, target);
|
|
5733
|
+
record({
|
|
5734
|
+
at: started,
|
|
5735
|
+
verb: "run",
|
|
5736
|
+
command: displayOf(target),
|
|
5737
|
+
intent: "host",
|
|
5738
|
+
mode: "host",
|
|
5739
|
+
workspaceRoot: "",
|
|
5740
|
+
exitCode: result.exitCode,
|
|
5741
|
+
timedOut: result.timedOut,
|
|
5742
|
+
stalled: result.stalled
|
|
5743
|
+
});
|
|
5744
|
+
return settle$1(result);
|
|
5745
|
+
}
|
|
5746
|
+
const granted = grantFor(request.session);
|
|
5747
|
+
const intent = request.intent;
|
|
5748
|
+
const policy = policyOf(intent, ceiling, runtimeRoot, granted);
|
|
5749
|
+
const line = sandboxedLineOf(target, request.shellKind);
|
|
5750
|
+
let result = await attempt(request, policy, line);
|
|
5751
|
+
if (!sandboxDeniedOf(result.sandbox)) {
|
|
5752
|
+
record({
|
|
5753
|
+
at: now(),
|
|
5754
|
+
verb: "run",
|
|
5755
|
+
command: line,
|
|
5756
|
+
intent: intent.kind,
|
|
5757
|
+
mode: policy.mode,
|
|
5758
|
+
workspaceRoot: policy.workspaceRoot,
|
|
5759
|
+
exitCode: result.exitCode,
|
|
5760
|
+
timedOut: result.timedOut,
|
|
5761
|
+
stalled: result.stalled
|
|
5762
|
+
});
|
|
5763
|
+
return settle$1(result);
|
|
5764
|
+
}
|
|
5765
|
+
const path = deniedPathOf(result.stderr, result.stdout);
|
|
5766
|
+
const scope = denialScopeOf(path, policy, runtimeRoot, granted);
|
|
5767
|
+
const healedPolicy = request.retry === "safe" ? healPolicyOf(intent, scope, granted) : void 0;
|
|
5768
|
+
let healed = false;
|
|
5769
|
+
if (healedPolicy !== void 0) {
|
|
5770
|
+
const retried = await attempt(request, healedPolicy, line).catch(() => void 0);
|
|
5771
|
+
if (retried !== void 0 && !sandboxDeniedOf(retried.sandbox)) {
|
|
5772
|
+
result = retried;
|
|
5773
|
+
healed = true;
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5776
|
+
const denial = {
|
|
5777
|
+
...path === void 0 ? {} : { path },
|
|
5778
|
+
scope,
|
|
5779
|
+
...healed ? { healed: true } : {}
|
|
5780
|
+
};
|
|
5781
|
+
record({
|
|
5782
|
+
at: now(),
|
|
5783
|
+
verb: "run",
|
|
5784
|
+
command: line,
|
|
5785
|
+
intent: intent.kind,
|
|
5786
|
+
mode: policy.mode,
|
|
5787
|
+
workspaceRoot: policy.workspaceRoot,
|
|
5788
|
+
exitCode: result.exitCode,
|
|
5789
|
+
timedOut: result.timedOut,
|
|
5790
|
+
stalled: result.stalled,
|
|
5791
|
+
denial
|
|
5792
|
+
});
|
|
5793
|
+
if (healed) return settle$1(result, denial);
|
|
5794
|
+
if (request.bestEffort === true) {
|
|
5795
|
+
ports.logger?.warn?.(`[dsh-workbench] ${denialMessageOf(scope, path, `${result.stderr}\n${result.stdout}`)}`);
|
|
5796
|
+
return settle$1(result, denial);
|
|
5797
|
+
}
|
|
5798
|
+
throw new SeamError("sandbox-denied", denialMessageOf(scope, path, `${result.stderr}\n${result.stdout}`), {
|
|
5799
|
+
...path === void 0 ? {} : { path },
|
|
5800
|
+
scope
|
|
5801
|
+
});
|
|
5802
|
+
},
|
|
5803
|
+
spawn(request) {
|
|
5804
|
+
requireHost(request.intent);
|
|
5805
|
+
return spawnHost(request);
|
|
5806
|
+
},
|
|
5807
|
+
open(request) {
|
|
5808
|
+
requireHost(request.intent);
|
|
5809
|
+
return openHost(request);
|
|
5810
|
+
},
|
|
5811
|
+
facts: () => facts
|
|
5812
|
+
};
|
|
5813
|
+
}
|
|
5814
|
+
//#endregion
|
|
5815
|
+
//#region ../shared/dist/host/seam/unit-seam.js
|
|
5816
|
+
function unitDataRoot(runtimeRoot, unit) {
|
|
5817
|
+
return join(runtimeRoot, "units", unit);
|
|
5818
|
+
}
|
|
5819
|
+
function createUnitSeam(ports) {
|
|
5820
|
+
const unitRoot = unitDataRoot(ports.runtimeRoot, ports.unit);
|
|
5821
|
+
mkdirSync(unitRoot, { recursive: true });
|
|
5822
|
+
const declared = new Set(ports.declared ?? []);
|
|
5823
|
+
const env = seamUnitEnv({
|
|
5824
|
+
runtimeRoot: ports.runtimeRoot,
|
|
5825
|
+
unitRoot,
|
|
5826
|
+
realHome: ports.realHome
|
|
5827
|
+
});
|
|
5828
|
+
return { async run(request) {
|
|
5829
|
+
const common = {
|
|
5830
|
+
command: request.command,
|
|
5831
|
+
...request.workdir === void 0 ? {} : { workdir: request.workdir },
|
|
5832
|
+
...request.timeoutMs === void 0 ? {} : { timeoutMs: request.timeoutMs },
|
|
5833
|
+
...request.signal === void 0 ? {} : { signal: request.signal },
|
|
5834
|
+
...request.retry === void 0 ? {} : { retry: request.retry }
|
|
5835
|
+
};
|
|
5836
|
+
if (request.scope === "unit") return ports.seam.run({
|
|
5837
|
+
...common,
|
|
5838
|
+
intent: { kind: "product" },
|
|
5839
|
+
env: {
|
|
5840
|
+
...env,
|
|
5841
|
+
...request.env ?? {}
|
|
5842
|
+
}
|
|
5843
|
+
});
|
|
5844
|
+
if (!declared.has("workspace")) throw new SeamError("scope-not-declared", `${ports.unit} 的清单没有声明 host.writes 里的 workspace:要写当前工作区,先在清单里声明`);
|
|
5845
|
+
if (request.session === void 0 || request.session === null) throw new SeamError("workspace-unknown", `${ports.unit} 请求写工作区,但这次调用没有会话`);
|
|
5846
|
+
return ports.seam.run({
|
|
5847
|
+
...common,
|
|
5848
|
+
intent: { kind: "workspace" },
|
|
5849
|
+
session: request.session,
|
|
5850
|
+
env: { ...request.env ?? {} }
|
|
5851
|
+
});
|
|
5852
|
+
} };
|
|
5853
|
+
}
|
|
5854
|
+
//#endregion
|
|
5855
|
+
//#region ../shared/dist/host/seam/service.js
|
|
5856
|
+
var WORKBENCH_SEAM = "workbenchSeam";
|
|
5857
|
+
function seamOf(ctx) {
|
|
5858
|
+
return ctx.get(WORKBENCH_SEAM, false);
|
|
5859
|
+
}
|
|
5860
|
+
//#endregion
|
|
4893
5861
|
//#region ../shared/dist/host/install-failure.js
|
|
4894
5862
|
var TARGET_FLAGS = [
|
|
4895
5863
|
"--prefix",
|
|
@@ -5352,89 +6320,6 @@ function buildSupplyIndex(declarations, options = {}) {
|
|
|
5352
6320
|
};
|
|
5353
6321
|
}
|
|
5354
6322
|
//#endregion
|
|
5355
|
-
//#region ../shared/dist/host/patch.js
|
|
5356
|
-
var ID_LINE = /^- id:[ \t]*"?([^"\s#]+)/;
|
|
5357
|
-
var INSERT_LINE = /^- insert:[ \t]*$/;
|
|
5358
|
-
var NESTED_ID = /^[ \t]+-[ \t]*id:[ \t]*"?([^"\s#]+)/;
|
|
5359
|
-
var NAME_LINE = /^[ \t]*-?[ \t]*name:[ \t]*['"]?([^'"\s#]+)/;
|
|
5360
|
-
function trimmedTail(lines) {
|
|
5361
|
-
let end = lines.length;
|
|
5362
|
-
while (end > 0 && (lines[end - 1] ?? "").trim() === "") end -= 1;
|
|
5363
|
-
return lines.slice(0, end);
|
|
5364
|
-
}
|
|
5365
|
-
function matched(text, pattern) {
|
|
5366
|
-
const found = pattern.exec(text);
|
|
5367
|
-
return found === null ? void 0 : found[1];
|
|
5368
|
-
}
|
|
5369
|
-
function blockOf$1(body, index) {
|
|
5370
|
-
const lines = body.split("\n");
|
|
5371
|
-
const first = lines[0] ?? "";
|
|
5372
|
-
const id = matched(first, ID_LINE);
|
|
5373
|
-
const nested = lines.flatMap((line) => {
|
|
5374
|
-
const one = matched(line, NESTED_ID);
|
|
5375
|
-
return one === void 0 ? [] : [one];
|
|
5376
|
-
});
|
|
5377
|
-
const insert = id === void 0 && INSERT_LINE.test(first);
|
|
5378
|
-
const label = id ?? nested.join(", ");
|
|
5379
|
-
const key = id !== void 0 ? `id:${id}` : insert ? `insert:${nested.join(", ")}` : `block:${String(index)}`;
|
|
5380
|
-
const names = lines.flatMap((line) => {
|
|
5381
|
-
const one = matched(line, NAME_LINE);
|
|
5382
|
-
return one === void 0 ? [] : [one];
|
|
5383
|
-
});
|
|
5384
|
-
return {
|
|
5385
|
-
key,
|
|
5386
|
-
id: label === "" ? key : label,
|
|
5387
|
-
insert,
|
|
5388
|
-
body,
|
|
5389
|
-
names
|
|
5390
|
-
};
|
|
5391
|
-
}
|
|
5392
|
-
function spannedBlocks(text) {
|
|
5393
|
-
const lines = text.split("\n");
|
|
5394
|
-
const starts = [];
|
|
5395
|
-
lines.forEach((line, index) => {
|
|
5396
|
-
if (/^- /.test(line) || line === "-") starts.push(index);
|
|
5397
|
-
});
|
|
5398
|
-
const blocks = [];
|
|
5399
|
-
for (let i = 0; i < starts.length; i += 1) {
|
|
5400
|
-
const from = starts[i] ?? 0;
|
|
5401
|
-
const to = i + 1 < starts.length ? starts[i + 1] ?? lines.length : lines.length;
|
|
5402
|
-
const body = trimmedTail(lines.slice(from, to)).join("\n");
|
|
5403
|
-
if (body.trim() === "") continue;
|
|
5404
|
-
blocks.push({
|
|
5405
|
-
...blockOf$1(body, blocks.length),
|
|
5406
|
-
from,
|
|
5407
|
-
to
|
|
5408
|
-
});
|
|
5409
|
-
}
|
|
5410
|
-
return blocks;
|
|
5411
|
-
}
|
|
5412
|
-
function patchBlocks(text) {
|
|
5413
|
-
return spannedBlocks(text).map(({ from: _from, to: _to, ...block }) => block);
|
|
5414
|
-
}
|
|
5415
|
-
function patchMode(path) {
|
|
5416
|
-
try {
|
|
5417
|
-
return statSync(path).mode & 511;
|
|
5418
|
-
} catch {
|
|
5419
|
-
return 420;
|
|
5420
|
-
}
|
|
5421
|
-
}
|
|
5422
|
-
function writePatchFile(path, text) {
|
|
5423
|
-
const mode = patchMode(path);
|
|
5424
|
-
const tmp = `${path}.wb-sync-${String(process.pid)}`;
|
|
5425
|
-
writeFileSync(tmp, text, { mode });
|
|
5426
|
-
try {
|
|
5427
|
-
renameSync(tmp, path);
|
|
5428
|
-
} catch (error) {
|
|
5429
|
-
try {
|
|
5430
|
-
unlinkSync(tmp);
|
|
5431
|
-
} catch {
|
|
5432
|
-
throw error;
|
|
5433
|
-
}
|
|
5434
|
-
throw error;
|
|
5435
|
-
}
|
|
5436
|
-
}
|
|
5437
|
-
//#endregion
|
|
5438
6323
|
//#region ../shared/dist/host/adapter-set.js
|
|
5439
6324
|
var CORE_MEMBERS = [
|
|
5440
6325
|
"data",
|
|
@@ -5492,7 +6377,6 @@ var FULL_METHODS = [
|
|
|
5492
6377
|
["runtime", "runners"],
|
|
5493
6378
|
["runtime", "health"],
|
|
5494
6379
|
["runtime", "seed"],
|
|
5495
|
-
["runtime", "warnUnwritableRoot"],
|
|
5496
6380
|
["plugins", "declarationLookup"],
|
|
5497
6381
|
["plugins", "createUnits"],
|
|
5498
6382
|
["actions", "configSyncHandler"],
|
|
@@ -6297,8 +7181,8 @@ var SUBSECTION = /^###[ \t]/;
|
|
|
6297
7181
|
function changelogVersion(value) {
|
|
6298
7182
|
return String(value ?? "").trim().replace(/^v/, "");
|
|
6299
7183
|
}
|
|
6300
|
-
function changelogDate(now =
|
|
6301
|
-
return dayjs(now).format("YYYY-MM-DD");
|
|
7184
|
+
function changelogDate(now$3 = new Date(now())) {
|
|
7185
|
+
return dayjs(now$3).format("YYYY-MM-DD");
|
|
6302
7186
|
}
|
|
6303
7187
|
function normalizeChangelogText(text) {
|
|
6304
7188
|
return String(text ?? "").replace(/\r\n?/g, "\n");
|
|
@@ -6458,6 +7342,82 @@ function normalizeWhitespace(rows) {
|
|
|
6458
7342
|
return out.join("\n").replace(/\n+$/, "");
|
|
6459
7343
|
}
|
|
6460
7344
|
//#endregion
|
|
7345
|
+
//#region src/shared/api/host/workbench-seam.ts
|
|
7346
|
+
function provideWorkbenchSeam(ctx) {
|
|
7347
|
+
const shell = ctx.get("shell");
|
|
7348
|
+
if (shell === void 0) return void 0;
|
|
7349
|
+
const runtimeRoot = workbenchRoot();
|
|
7350
|
+
const seam = createSeam({
|
|
7351
|
+
shell,
|
|
7352
|
+
runtimeRoot,
|
|
7353
|
+
realHome: homedir(),
|
|
7354
|
+
allowHost: true,
|
|
7355
|
+
logger: ctx.logger,
|
|
7356
|
+
grantOf: (session) => {
|
|
7357
|
+
const resolved = ctx.get("sandboxPolicy")?.resolve?.({ session });
|
|
7358
|
+
if (resolved === void 0 || resolved.mode === void 0 || resolved.workspaceRoot === void 0) return;
|
|
7359
|
+
return {
|
|
7360
|
+
mode: seamModeOf(resolved.mode),
|
|
7361
|
+
workspaceRoot: resolved.workspaceRoot
|
|
7362
|
+
};
|
|
7363
|
+
}
|
|
7364
|
+
});
|
|
7365
|
+
ctx.provide(WORKBENCH_SEAM, seam);
|
|
7366
|
+
ctx.logger?.info?.(`[dsh-workbench] 执行缝:产品域 ${runtimeRoot}`);
|
|
7367
|
+
return seam;
|
|
7368
|
+
}
|
|
7369
|
+
//#endregion
|
|
7370
|
+
//#region src/shared/api/host/git-exec.ts
|
|
7371
|
+
function gitNoPromptProcessEnv(parent = process.env, platform = process.platform) {
|
|
7372
|
+
return promptlessProcessEnvOf(parent, platform);
|
|
7373
|
+
}
|
|
7374
|
+
function withoutAskpassNoise(text) {
|
|
7375
|
+
return text.split("\n").filter((line) => !/unable to read askpass response/i.test(line)).join("\n").trim();
|
|
7376
|
+
}
|
|
7377
|
+
//#endregion
|
|
7378
|
+
//#region src/shared/api/host/git-process.ts
|
|
7379
|
+
var gitSeam;
|
|
7380
|
+
function promptlessEnv() {
|
|
7381
|
+
return gitNoPromptProcessEnv();
|
|
7382
|
+
}
|
|
7383
|
+
function provideGitProcessSeam(seam) {
|
|
7384
|
+
gitSeam = seam;
|
|
7385
|
+
}
|
|
7386
|
+
function createGitRunner(seam) {
|
|
7387
|
+
return async (args, options = {}) => {
|
|
7388
|
+
const outcome = await seam.run({
|
|
7389
|
+
argv: ["git", ...args],
|
|
7390
|
+
intent: { kind: "host" },
|
|
7391
|
+
env: promptlessEnv(),
|
|
7392
|
+
...options.cwd === void 0 ? {} : { workdir: options.cwd },
|
|
7393
|
+
...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
|
|
7394
|
+
});
|
|
7395
|
+
return {
|
|
7396
|
+
exitCode: outcome.exitCode ?? null,
|
|
7397
|
+
stdout: outcome.stdout,
|
|
7398
|
+
stderr: outcome.stderr,
|
|
7399
|
+
timedOut: outcome.timedOut
|
|
7400
|
+
};
|
|
7401
|
+
};
|
|
7402
|
+
}
|
|
7403
|
+
var runGitProcess = async (args, options = {}) => {
|
|
7404
|
+
if (gitSeam === void 0) throw new Error("git 命令还没接上执行缝(宿主域)—— 这个进程里没有可用的 shell 服务。");
|
|
7405
|
+
return createGitRunner(gitSeam)(args, options);
|
|
7406
|
+
};
|
|
7407
|
+
//#endregion
|
|
7408
|
+
//#region src/shared/api/host/shipped-adapter-set.ts
|
|
7409
|
+
function shippedAdapterSetSpec(moduleUrl) {
|
|
7410
|
+
const spec = JSON.parse(readFileSync(new URL("../../package.json", moduleUrl), "utf8")).workbench?.adapterSet;
|
|
7411
|
+
if (typeof spec !== "string" || spec.trim() === "") throw new Error("package.json 没写 workbench.adapterSet:外壳不知道该装哪份适配器集合");
|
|
7412
|
+
return spec.trim();
|
|
7413
|
+
}
|
|
7414
|
+
async function shippedAdapterSet(ctx) {
|
|
7415
|
+
const spec = shippedAdapterSetSpec(import.meta.url);
|
|
7416
|
+
const factory = (await import(spec)).createAdapterSet;
|
|
7417
|
+
if (typeof factory !== "function") throw new Error(`随包的适配器集合 ${spec} 没导出 createAdapterSet(ctx)`);
|
|
7418
|
+
return await factory(ctx);
|
|
7419
|
+
}
|
|
7420
|
+
//#endregion
|
|
6461
7421
|
//#region ../shared/dist/contract/protocol/dsh-plugins.js
|
|
6462
7422
|
var DSH_PLUGINS_ROUTE = "dsh-plugins";
|
|
6463
7423
|
//#endregion
|
|
@@ -6481,13 +7441,11 @@ function isFollowUpDismissed(runtimeRoot, id) {
|
|
|
6481
7441
|
return Object.prototype.hasOwnProperty.call(readDismissed(runtimeRoot), id);
|
|
6482
7442
|
}
|
|
6483
7443
|
function dismissFollowUp(runtimeRoot, id) {
|
|
6484
|
-
const file = fileOf$1(runtimeRoot);
|
|
6485
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
6486
7444
|
const dismissed = {
|
|
6487
7445
|
...readDismissed(runtimeRoot),
|
|
6488
|
-
[id]: { at: (
|
|
7446
|
+
[id]: { at: nowIso() }
|
|
6489
7447
|
};
|
|
6490
|
-
|
|
7448
|
+
writeJsonFile(fileOf$1(runtimeRoot), { dismissed });
|
|
6491
7449
|
}
|
|
6492
7450
|
//#endregion
|
|
6493
7451
|
//#region src/shared/api/host/migration-follow-up.ts
|
|
@@ -6609,7 +7567,7 @@ function titleOf(raw) {
|
|
|
6609
7567
|
}
|
|
6610
7568
|
async function latestIfReleaseReady(supply, input) {
|
|
6611
7569
|
const missing = await supply.missingReleaseParts({
|
|
6612
|
-
|
|
7570
|
+
seam: input.seam,
|
|
6613
7571
|
name: input.pkg,
|
|
6614
7572
|
registry: input.registry,
|
|
6615
7573
|
version: input.version,
|
|
@@ -6630,18 +7588,18 @@ async function workbenchVersion(supply, input) {
|
|
|
6630
7588
|
if (read === void 0) return void 0;
|
|
6631
7589
|
const spec = dependencyReader(input.dir)(PLUGIN_ID);
|
|
6632
7590
|
const registry = read.registry ?? input.fallback;
|
|
6633
|
-
const
|
|
6634
|
-
const queried = read.local === true || !input.check ||
|
|
6635
|
-
|
|
7591
|
+
const seam = input.seam;
|
|
7592
|
+
const queried = read.local === true || !input.check || seam === void 0 || registry === void 0 ? void 0 : await supply.latestVersion({
|
|
7593
|
+
seam,
|
|
6636
7594
|
name: read.pkg,
|
|
6637
7595
|
registry,
|
|
6638
7596
|
force: input.force
|
|
6639
7597
|
});
|
|
6640
|
-
const latest = queried === void 0 ||
|
|
7598
|
+
const latest = queried === void 0 || seam === void 0 || registry === void 0 || !supply.isNewerVersion(queried, read.version) ? void 0 : await latestIfReleaseReady(supply, {
|
|
6641
7599
|
pkg: read.pkg,
|
|
6642
7600
|
version: queried,
|
|
6643
7601
|
registry,
|
|
6644
|
-
|
|
7602
|
+
seam,
|
|
6645
7603
|
force: input.force,
|
|
6646
7604
|
logger: input.logger
|
|
6647
7605
|
});
|
|
@@ -6656,7 +7614,7 @@ async function workbenchVersion(supply, input) {
|
|
|
6656
7614
|
...registry === void 0 || read.local === true ? {} : { registry }
|
|
6657
7615
|
};
|
|
6658
7616
|
}
|
|
6659
|
-
function moduleVersionsHandler(ctx,
|
|
7617
|
+
function moduleVersionsHandler(ctx, seam, running, selfUpdate) {
|
|
6660
7618
|
return async (search) => {
|
|
6661
7619
|
const supply = supplyPortOf(ctx);
|
|
6662
7620
|
const manager = supply.readBundleManager(ctx);
|
|
@@ -6676,8 +7634,8 @@ function moduleVersionsHandler(ctx, shell, running, selfUpdate) {
|
|
|
6676
7634
|
const fromRegistry = module.fromRegistry !== false;
|
|
6677
7635
|
const registry = fromRegistry ? module.registry ?? fallback : void 0;
|
|
6678
7636
|
const spec = dependencyOf(module.pkg);
|
|
6679
|
-
const latest = local || !fromRegistry || !check ||
|
|
6680
|
-
|
|
7637
|
+
const latest = local || !fromRegistry || !check || seam === void 0 || registry === void 0 ? void 0 : await supply.latestVersion({
|
|
7638
|
+
seam,
|
|
6681
7639
|
name: module.pkg,
|
|
6682
7640
|
registry,
|
|
6683
7641
|
force
|
|
@@ -6686,8 +7644,8 @@ function moduleVersionsHandler(ctx, shell, running, selfUpdate) {
|
|
|
6686
7644
|
const declared = raw === void 0 ? [] : declarationProblemsOf(module.pkg, raw);
|
|
6687
7645
|
const title = titleOf(raw);
|
|
6688
7646
|
const refused = refusals.get(module.unit) ?? (declared.length === 0 ? void 0 : declared);
|
|
6689
|
-
const latestRaw = latest === void 0 || latest === module.version ||
|
|
6690
|
-
|
|
7647
|
+
const latestRaw = latest === void 0 || latest === module.version || seam === void 0 || registry === void 0 ? void 0 : await supply.registryDeclarationOf({
|
|
7648
|
+
seam,
|
|
6691
7649
|
name: module.pkg,
|
|
6692
7650
|
registry,
|
|
6693
7651
|
force
|
|
@@ -6727,7 +7685,7 @@ function moduleVersionsHandler(ctx, shell, running, selfUpdate) {
|
|
|
6727
7685
|
});
|
|
6728
7686
|
const workbench = await workbenchVersion(supply, {
|
|
6729
7687
|
dir,
|
|
6730
|
-
|
|
7688
|
+
seam,
|
|
6731
7689
|
fallback,
|
|
6732
7690
|
force,
|
|
6733
7691
|
check,
|
|
@@ -6755,23 +7713,23 @@ async function declarationRefusal(supply, spec, inspection, ctx, manager) {
|
|
|
6755
7713
|
if (inspection.status !== "accepted" || inspection.name === void 0) return void 0;
|
|
6756
7714
|
if (inspection.kind === "path") return refusedInspection(supply.declarationRefusalOf(inspection.name, supply.pathDeclarationOf(spec)));
|
|
6757
7715
|
if (inspection.kind !== "registry") return void 0;
|
|
6758
|
-
const
|
|
6759
|
-
if (
|
|
7716
|
+
const seam = seamOf(ctx);
|
|
7717
|
+
if (seam === void 0) return void 0;
|
|
6760
7718
|
const registry = typeof inspection.registry === "string" ? inspection.registry : (await manager.registries())?.resolved ?? void 0;
|
|
6761
7719
|
if (registry === void 0) return void 0;
|
|
6762
7720
|
const raw = await supply.registryDeclarationOf({
|
|
6763
|
-
|
|
7721
|
+
seam,
|
|
6764
7722
|
name: inspection.name,
|
|
6765
7723
|
registry
|
|
6766
7724
|
});
|
|
6767
7725
|
return refusedInspection(supply.declarationRefusalOf(inspection.name, raw));
|
|
6768
7726
|
}
|
|
6769
7727
|
async function packageOverride(supply, spec, inspection, ctx, manager, asked) {
|
|
6770
|
-
const
|
|
7728
|
+
const seam = seamOf(ctx);
|
|
6771
7729
|
const registry = (inspection.status === "accepted" && typeof inspection.registry === "string" ? inspection.registry : void 0) ?? (typeof asked === "string" ? asked : (await manager.registries())?.resolved ?? void 0);
|
|
6772
7730
|
const route = await supply.packageRouteOf({
|
|
6773
7731
|
spec,
|
|
6774
|
-
...
|
|
7732
|
+
...seam === void 0 ? {} : { seam },
|
|
6775
7733
|
...registry === void 0 ? {} : { registry }
|
|
6776
7734
|
});
|
|
6777
7735
|
if (route !== void 0 && route.kind === "runtime-plugin") {
|
|
@@ -6806,76 +7764,25 @@ function bundleInspectHandler(ctx) {
|
|
|
6806
7764
|
if (manager === void 0) return {
|
|
6807
7765
|
ok: false,
|
|
6808
7766
|
error: {
|
|
6809
|
-
code: "workbench/no-bundle-manager",
|
|
6810
|
-
message: "这个 profile 里没有插件管理器,装不了模块。"
|
|
6811
|
-
}
|
|
6812
|
-
};
|
|
6813
|
-
const asked = registryOf(search);
|
|
6814
|
-
const inspection = await manager.inspect(spec, asked);
|
|
6815
|
-
if (inspection === null) return {
|
|
6816
|
-
ok: false,
|
|
6817
|
-
error: {
|
|
6818
|
-
code: "workbench/bad-inspection",
|
|
6819
|
-
message: "插件管理器回的预检结果看不懂。"
|
|
6820
|
-
}
|
|
6821
|
-
};
|
|
6822
|
-
return {
|
|
6823
|
-
ok: true,
|
|
6824
|
-
inspection: await packageOverride(supply, spec, inspection, ctx, manager, asked) ?? await declarationRefusal(supply, spec, inspection, ctx, manager) ?? inspection
|
|
6825
|
-
};
|
|
6826
|
-
};
|
|
6827
|
-
}
|
|
6828
|
-
//#endregion
|
|
6829
|
-
//#region src/shared/api/host/git-exec.ts
|
|
6830
|
-
function gitNoPromptEnv(platform = process.platform) {
|
|
6831
|
-
const shared = {
|
|
6832
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
6833
|
-
SSH_ASKPASS_REQUIRE: "never",
|
|
6834
|
-
GCM_INTERACTIVE: "never",
|
|
6835
|
-
GIT_SSH_COMMAND: "ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new"
|
|
6836
|
-
};
|
|
6837
|
-
if (platform === "win32") return shared;
|
|
6838
|
-
return {
|
|
6839
|
-
...shared,
|
|
6840
|
-
GIT_ASKPASS: "/usr/bin/false",
|
|
6841
|
-
SSH_ASKPASS: "/usr/bin/false"
|
|
6842
|
-
};
|
|
6843
|
-
}
|
|
6844
|
-
function gitNoPromptProcessEnv(parent = process.env, platform = process.platform) {
|
|
6845
|
-
const env = {
|
|
6846
|
-
...parent,
|
|
6847
|
-
...gitNoPromptEnv(platform)
|
|
7767
|
+
code: "workbench/no-bundle-manager",
|
|
7768
|
+
message: "这个 profile 里没有插件管理器,装不了模块。"
|
|
7769
|
+
}
|
|
7770
|
+
};
|
|
7771
|
+
const asked = registryOf(search);
|
|
7772
|
+
const inspection = await manager.inspect(spec, asked);
|
|
7773
|
+
if (inspection === null) return {
|
|
7774
|
+
ok: false,
|
|
7775
|
+
error: {
|
|
7776
|
+
code: "workbench/bad-inspection",
|
|
7777
|
+
message: "插件管理器回的预检结果看不懂。"
|
|
7778
|
+
}
|
|
7779
|
+
};
|
|
7780
|
+
return {
|
|
7781
|
+
ok: true,
|
|
7782
|
+
inspection: await packageOverride(supply, spec, inspection, ctx, manager, asked) ?? await declarationRefusal(supply, spec, inspection, ctx, manager) ?? inspection
|
|
7783
|
+
};
|
|
6848
7784
|
};
|
|
6849
|
-
if (platform === "win32") {
|
|
6850
|
-
delete env.GIT_ASKPASS;
|
|
6851
|
-
delete env.SSH_ASKPASS;
|
|
6852
|
-
}
|
|
6853
|
-
return env;
|
|
6854
7785
|
}
|
|
6855
|
-
function withoutAskpassNoise(text) {
|
|
6856
|
-
return text.split("\n").filter((line) => !/unable to read askpass response/i.test(line)).join("\n").trim();
|
|
6857
|
-
}
|
|
6858
|
-
//#endregion
|
|
6859
|
-
//#region src/shared/api/host/git-process.ts
|
|
6860
|
-
var DEFAULT_MAX_BYTES$2 = 8388608;
|
|
6861
|
-
var runGitProcess = async (args, options = {}) => {
|
|
6862
|
-
const result = await execa("git", [...args], {
|
|
6863
|
-
...options.cwd === void 0 ? {} : { cwd: options.cwd },
|
|
6864
|
-
env: gitNoPromptProcessEnv(),
|
|
6865
|
-
stdin: "ignore",
|
|
6866
|
-
reject: false,
|
|
6867
|
-
...options.timeoutMs === void 0 ? {} : { timeout: options.timeoutMs },
|
|
6868
|
-
killSignal: "SIGKILL",
|
|
6869
|
-
maxBuffer: options.maxBytes ?? DEFAULT_MAX_BYTES$2,
|
|
6870
|
-
stripFinalNewline: false
|
|
6871
|
-
});
|
|
6872
|
-
return {
|
|
6873
|
-
exitCode: result.exitCode ?? null,
|
|
6874
|
-
stdout: result.stdout ?? "",
|
|
6875
|
-
stderr: result.stderr ?? "",
|
|
6876
|
-
timedOut: result.timedOut === true
|
|
6877
|
-
};
|
|
6878
|
-
};
|
|
6879
7786
|
//#endregion
|
|
6880
7787
|
//#region src/shared/api/host/git-probe.ts
|
|
6881
7788
|
var PROBE_TIMEOUT_MS = 1e4;
|
|
@@ -6910,10 +7817,10 @@ var PROBE_CACHE_MS = 6e4;
|
|
|
6910
7817
|
var probeCache = /* @__PURE__ */ new Map();
|
|
6911
7818
|
async function probeGitAddressCached(url, options = {}) {
|
|
6912
7819
|
const hit = probeCache.get(url);
|
|
6913
|
-
if (hit !== void 0 &&
|
|
7820
|
+
if (hit !== void 0 && now() - hit.at < PROBE_CACHE_MS) return hit.payload;
|
|
6914
7821
|
const payload = await probeGitAddress(url, options);
|
|
6915
7822
|
probeCache.set(url, {
|
|
6916
|
-
at:
|
|
7823
|
+
at: now(),
|
|
6917
7824
|
payload
|
|
6918
7825
|
});
|
|
6919
7826
|
return payload;
|
|
@@ -6947,18 +7854,15 @@ function createHostRoutes(input) {
|
|
|
6947
7854
|
[BUNDLE_REGISTRIES_ROUTE]: bundleRegistriesHandler(ctx),
|
|
6948
7855
|
[BUNDLE_INSPECT_ROUTE]: bundleInspectHandler(ctx),
|
|
6949
7856
|
[MARKET_GIT_PROBE_ROUTE]: gitProbeHandler(),
|
|
6950
|
-
[MODULE_VERSIONS_ROUTE]: moduleVersionsHandler(ctx, ctx
|
|
7857
|
+
[MODULE_VERSIONS_ROUTE]: moduleVersionsHandler(ctx, seamOf(ctx), running, () => profile.selfUpdateView(dshHome(), basename(profile.dirOf(ctx) ?? "")))
|
|
6951
7858
|
};
|
|
6952
7859
|
}
|
|
6953
|
-
var WORKSHOP_RELATIVE = "workbench/diy";
|
|
6954
7860
|
var WORKSHOP_TITLE = "工作台DIY";
|
|
6955
7861
|
//#endregion
|
|
6956
7862
|
//#region src/shared/api/host/diy.ts
|
|
6957
|
-
function workshopPathOf(
|
|
6958
|
-
const homePath = ctx.get("dshHomePath");
|
|
6959
|
-
if (typeof homePath !== "function") return null;
|
|
7863
|
+
function workshopPathOf(_ctx) {
|
|
6960
7864
|
try {
|
|
6961
|
-
return
|
|
7865
|
+
return join(workbenchRoot(), "diy");
|
|
6962
7866
|
} catch {
|
|
6963
7867
|
return null;
|
|
6964
7868
|
}
|
|
@@ -7135,7 +8039,7 @@ var panelsUnit = {
|
|
|
7135
8039
|
}))],
|
|
7136
8040
|
host: {
|
|
7137
8041
|
inject: [],
|
|
7138
|
-
optional: ["tools", "
|
|
8042
|
+
optional: ["tools", "workbenchSeam"]
|
|
7139
8043
|
}
|
|
7140
8044
|
};
|
|
7141
8045
|
//#endregion
|
|
@@ -8015,8 +8919,10 @@ function toolchainBinOf(root) {
|
|
|
8015
8919
|
function queryCommandOf(bin, only) {
|
|
8016
8920
|
return `${quoteShellArg(bin)} check --only ${only} --json`;
|
|
8017
8921
|
}
|
|
8018
|
-
async function runSurfaceQuery(
|
|
8019
|
-
const result = await
|
|
8922
|
+
async function runSurfaceQuery(seam, bin, only, root, fallbackBin) {
|
|
8923
|
+
const result = await seam.run({
|
|
8924
|
+
command: queryCommandOf(bin, only),
|
|
8925
|
+
intent: noWrite(),
|
|
8020
8926
|
workdir: root,
|
|
8021
8927
|
timeoutMs: SURFACE_TIMEOUT_MS
|
|
8022
8928
|
});
|
|
@@ -8024,7 +8930,9 @@ async function runSurfaceQuery(shell, bin, only, root, fallbackBin) {
|
|
|
8024
8930
|
if (parsed !== null) return withoutProblems(parsed);
|
|
8025
8931
|
const retry = fallbackBin === void 0 || fallbackBin === bin ? void 0 : fallbackBin;
|
|
8026
8932
|
if (retry !== void 0) {
|
|
8027
|
-
const secondParsed = jsonOf$1((await
|
|
8933
|
+
const secondParsed = jsonOf$1((await seam.run({
|
|
8934
|
+
command: queryCommandOf(retry, only),
|
|
8935
|
+
intent: noWrite(),
|
|
8028
8936
|
workdir: root,
|
|
8029
8937
|
timeoutMs: SURFACE_TIMEOUT_MS
|
|
8030
8938
|
})).stdout);
|
|
@@ -8790,11 +9698,11 @@ function componentsTool(ctx) {
|
|
|
8790
9698
|
})).find((one) => one.bin !== null) ?? null;
|
|
8791
9699
|
const bin = local?.bin ?? shellBin;
|
|
8792
9700
|
if (bin === null) throw new Error(`这个工作区(和它各模块)里还没有 dsh-wb-module(${join(root, "node_modules")} 下没有)—— 在它根目录跑一次 pnpm install 就有了;workbench_diy_status 会告诉你卡在哪一步`);
|
|
8793
|
-
const
|
|
8794
|
-
if (
|
|
9701
|
+
const seam = seamOf(ctx);
|
|
9702
|
+
if (seam === void 0) throw new Error("这台机器没给 shell 服务,读不了组件清单");
|
|
8795
9703
|
const workdir = local?.dir ?? shellRoot ?? root;
|
|
8796
9704
|
const fallbackBin = local !== null && shellBin !== null && shellBin !== bin ? shellBin : void 0;
|
|
8797
|
-
const [primitives, kit] = await Promise.all([wantsPrimitives(scope) ? runSurfaceQuery(
|
|
9705
|
+
const [primitives, kit] = await Promise.all([wantsPrimitives(scope) ? runSurfaceQuery(seam, bin, "primitives", workdir, fallbackBin) : Promise.resolve(void 0), wantsKit(scope) ? runSurfaceQuery(seam, bin, "kit", workdir, fallbackBin) : Promise.resolve(void 0)]);
|
|
8798
9706
|
const query = {
|
|
8799
9707
|
...primitives === void 0 ? {} : { primitives },
|
|
8800
9708
|
...kit === void 0 ? {} : { kit }
|
|
@@ -8825,12 +9733,10 @@ function registerComponentsTool(ctx, tools) {
|
|
|
8825
9733
|
}
|
|
8826
9734
|
//#endregion
|
|
8827
9735
|
//#region src/shared/api/host/diy-artifacts.ts
|
|
8828
|
-
function artifactsDirOf(
|
|
8829
|
-
const homePath = ctx.get("dshHomePath");
|
|
8830
|
-
if (typeof homePath !== "function") throw new Error("这台机器没有 dshHomePath 服务 —— 落不了截图与录制(取证目录由它给)");
|
|
9736
|
+
function artifactsDirOf(root) {
|
|
8831
9737
|
const name = basename(root).replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "workspace";
|
|
8832
9738
|
const hash = createHash("sha1").update(root).digest("hex").slice(0, 8);
|
|
8833
|
-
return
|
|
9739
|
+
return join(workbenchRoot(), "artifacts", `${name}-${hash}`);
|
|
8834
9740
|
}
|
|
8835
9741
|
function ensureDirOf(dir) {
|
|
8836
9742
|
mkdirSync(dir, { recursive: true });
|
|
@@ -8877,8 +9783,12 @@ var LOOK_TIMEOUT_MS = 3e4;
|
|
|
8877
9783
|
function plainLine(command, argv) {
|
|
8878
9784
|
return [command, ...argv].map(quoteShellArg).join(" ");
|
|
8879
9785
|
}
|
|
8880
|
-
async function runBrowserStep(
|
|
8881
|
-
const result = await
|
|
9786
|
+
async function runBrowserStep(seam, command, argv, timeoutMs = LOOK_TIMEOUT_MS, line = plainLine, intent = productWrite()) {
|
|
9787
|
+
const result = await seam.run({
|
|
9788
|
+
command: line(command, argv),
|
|
9789
|
+
intent,
|
|
9790
|
+
timeoutMs
|
|
9791
|
+
});
|
|
8882
9792
|
return {
|
|
8883
9793
|
ok: result.exitCode === 0 && !result.timedOut,
|
|
8884
9794
|
stdout: result.stdout.trim(),
|
|
@@ -8902,8 +9812,8 @@ function jsonOf(raw) {
|
|
|
8902
9812
|
function textOf$2(value) {
|
|
8903
9813
|
return typeof value === "string" ? value.trim() : "";
|
|
8904
9814
|
}
|
|
8905
|
-
async function bindCurrentTab(
|
|
8906
|
-
const step = await runBrowserStep(
|
|
9815
|
+
async function bindCurrentTab(seam, command, session, timeoutMs, line) {
|
|
9816
|
+
const step = await runBrowserStep(seam, command, [
|
|
8907
9817
|
"browser",
|
|
8908
9818
|
session,
|
|
8909
9819
|
"bind",
|
|
@@ -8934,8 +9844,8 @@ async function bindCurrentTab(shell, command, session, timeoutMs, line) {
|
|
|
8934
9844
|
reason: ""
|
|
8935
9845
|
};
|
|
8936
9846
|
}
|
|
8937
|
-
async function workbenchPageOf(
|
|
8938
|
-
const parsed = jsonOf((await runBrowserStep(
|
|
9847
|
+
async function workbenchPageOf(seam, command, session) {
|
|
9848
|
+
const parsed = jsonOf((await runBrowserStep(seam, command, [
|
|
8939
9849
|
"browser",
|
|
8940
9850
|
session,
|
|
8941
9851
|
"eval",
|
|
@@ -8963,22 +9873,22 @@ async function workbenchPageOf(shell, command, session) {
|
|
|
8963
9873
|
shown: parsed["shown"] === true
|
|
8964
9874
|
};
|
|
8965
9875
|
}
|
|
8966
|
-
async function readyPageOf(
|
|
8967
|
-
const already = await workbenchPageOf(
|
|
9876
|
+
async function readyPageOf(seam, command, session) {
|
|
9877
|
+
const already = await workbenchPageOf(seam, command, session);
|
|
8968
9878
|
if (already.ok) return {
|
|
8969
9879
|
ok: true,
|
|
8970
9880
|
bound: false,
|
|
8971
9881
|
page: already,
|
|
8972
9882
|
reason: ""
|
|
8973
9883
|
};
|
|
8974
|
-
const bound = await bindCurrentTab(
|
|
9884
|
+
const bound = await bindCurrentTab(seam, command, session);
|
|
8975
9885
|
if (!bound.ok) return {
|
|
8976
9886
|
ok: false,
|
|
8977
9887
|
bound: false,
|
|
8978
9888
|
page: already,
|
|
8979
9889
|
reason: bound.reason
|
|
8980
9890
|
};
|
|
8981
|
-
const after = await workbenchPageOf(
|
|
9891
|
+
const after = await workbenchPageOf(seam, command, session);
|
|
8982
9892
|
return {
|
|
8983
9893
|
ok: after.ok,
|
|
8984
9894
|
bound: true,
|
|
@@ -9033,8 +9943,8 @@ async function pageAt(ctx, exec, raw, options = {}) {
|
|
|
9033
9943
|
const pool = units.map((one) => one.unit);
|
|
9034
9944
|
const wanted = asked === "" && pool.length === 1 ? pool[0] ?? "" : asked;
|
|
9035
9945
|
if (wanted === "" && options.requireUnit !== false) throw new Error(`看一眼要一个 unit(装过的:${pool.join(", ") || "(空)"})`);
|
|
9036
|
-
const
|
|
9037
|
-
if (
|
|
9946
|
+
const seam = seamOf(ctx);
|
|
9947
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,动不了浏览器");
|
|
9038
9948
|
const held = ctx.get("workbenchDiyDebug")?.live(Date.now()) ?? null;
|
|
9039
9949
|
const sessionId = sessionIdOf(exec);
|
|
9040
9950
|
if (options.requirePact !== false) {
|
|
@@ -9044,7 +9954,7 @@ async function pageAt(ctx, exec, raw, options = {}) {
|
|
|
9044
9954
|
}
|
|
9045
9955
|
const command = cliCommandOf(ctx);
|
|
9046
9956
|
const session = asText(args["session"]) || "workbench";
|
|
9047
|
-
const handshake = await readyPageOf(
|
|
9957
|
+
const handshake = await readyPageOf(seam, command, session);
|
|
9048
9958
|
if (!handshake.ok && options.requireWorkbench !== false && !hostSurface().capabilities.pageDebug) throw new Error(desktopPageText(handshake.page));
|
|
9049
9959
|
if (!handshake.ok && !handshake.bound) throw new Error(`连不上浏览器:${handshake.reason}。先让用户把工作台那一页切到前台(只有第一次要这样);还不行就跑一次运行时自检(doctor)看扩展连没连上。`);
|
|
9050
9960
|
if (!handshake.ok && options.requireWorkbench !== false) {
|
|
@@ -9057,10 +9967,10 @@ async function pageAt(ctx, exec, raw, options = {}) {
|
|
|
9057
9967
|
if (options.requireBoard !== false && (board === "other" || board === "hidden")) throw new Error(boardGateText(situation, wanted, handshake.page, title));
|
|
9058
9968
|
return {
|
|
9059
9969
|
root,
|
|
9060
|
-
artifactsDir: artifactsDirOf(
|
|
9970
|
+
artifactsDir: artifactsDirOf(root),
|
|
9061
9971
|
unit: wanted,
|
|
9062
9972
|
args,
|
|
9063
|
-
|
|
9973
|
+
seam,
|
|
9064
9974
|
command,
|
|
9065
9975
|
session,
|
|
9066
9976
|
...surfaceOf(ctx)?.background === void 0 ? {} : { line: surfaceOf(ctx)?.background },
|
|
@@ -9220,8 +10130,8 @@ function registerDebugTools(ctx, tools, store) {
|
|
|
9220
10130
|
//#endregion
|
|
9221
10131
|
//#region src/modules/panels/model/diy-browser-act.ts
|
|
9222
10132
|
var ACT_TIMEOUT_MS = 15e3;
|
|
9223
|
-
async function actBrowser(
|
|
9224
|
-
const step = await runBrowserStep(
|
|
10133
|
+
async function actBrowser(seam, command, session, argv, timeoutMs = ACT_TIMEOUT_MS, line) {
|
|
10134
|
+
const step = await runBrowserStep(seam, command, [
|
|
9225
10135
|
"browser",
|
|
9226
10136
|
session,
|
|
9227
10137
|
...argv,
|
|
@@ -9244,20 +10154,6 @@ function actErrorOf(step) {
|
|
|
9244
10154
|
return step.stderr || step.stdout || "运行时报了个错";
|
|
9245
10155
|
}
|
|
9246
10156
|
var SWEEP_TIMEOUT_MS = 4e3;
|
|
9247
|
-
async function stopNetworkExact(shell, file, timeoutMs = SWEEP_TIMEOUT_MS) {
|
|
9248
|
-
const pidFile = quoteShellArg(`${file}.pid`);
|
|
9249
|
-
const reaperFile = quoteShellArg(`${file}.reaper`);
|
|
9250
|
-
return (await runShell(shell, [
|
|
9251
|
-
`p="$(cat ${pidFile} 2>/dev/null)"`,
|
|
9252
|
-
`r="$(cat ${reaperFile} 2>/dev/null)"`,
|
|
9253
|
-
`[ -n "$p" ] && pkill -P "$p" 2>/dev/null`,
|
|
9254
|
-
`[ -n "$p" ] && kill "$p" 2>/dev/null`,
|
|
9255
|
-
`[ -n "$r" ] && pkill -P "$r" 2>/dev/null`,
|
|
9256
|
-
`[ -n "$r" ] && kill "$r" 2>/dev/null`,
|
|
9257
|
-
`if [ "$(cat ${pidFile} 2>/dev/null)" = "$p" ]; then rm -f ${pidFile} ${reaperFile}; fi`,
|
|
9258
|
-
"true"
|
|
9259
|
-
].join("; "), { timeoutMs })).exitCode === 0;
|
|
9260
|
-
}
|
|
9261
10157
|
//#endregion
|
|
9262
10158
|
//#region src/modules/panels/model/diy-logs.ts
|
|
9263
10159
|
var LOGS_KEY = "__dshDiyLogs";
|
|
@@ -9379,7 +10275,7 @@ function registerLogTools(ctx, tools) {
|
|
|
9379
10275
|
});
|
|
9380
10276
|
return {
|
|
9381
10277
|
page,
|
|
9382
|
-
step: await actBrowser(page.
|
|
10278
|
+
step: await actBrowser(page.seam, page.command, page.session, ["eval", script])
|
|
9383
10279
|
};
|
|
9384
10280
|
}
|
|
9385
10281
|
const watchTool = {
|
|
@@ -9632,7 +10528,7 @@ function cropPngFile(input, output, rect) {
|
|
|
9632
10528
|
reason: cropped.reason
|
|
9633
10529
|
};
|
|
9634
10530
|
try {
|
|
9635
|
-
|
|
10531
|
+
writeBytesFile(output, cropped.bytes);
|
|
9636
10532
|
} catch (error) {
|
|
9637
10533
|
return {
|
|
9638
10534
|
ok: false,
|
|
@@ -9647,6 +10543,44 @@ function cropPngFile(input, output, rect) {
|
|
|
9647
10543
|
};
|
|
9648
10544
|
}
|
|
9649
10545
|
//#endregion
|
|
10546
|
+
//#region src/modules/panels/model/diy-browser-clip.ts
|
|
10547
|
+
function finite(value) {
|
|
10548
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
10549
|
+
}
|
|
10550
|
+
function clipOf(value) {
|
|
10551
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
10552
|
+
const raw = value;
|
|
10553
|
+
const x = finite(raw["x"]);
|
|
10554
|
+
const y = finite(raw["y"]);
|
|
10555
|
+
const width = finite(raw["width"]);
|
|
10556
|
+
const height = finite(raw["height"]);
|
|
10557
|
+
if (x === void 0 || y === void 0 || width === void 0 || height === void 0) return void 0;
|
|
10558
|
+
if (x < 0 || y < 0 || width <= 0 || height <= 0) return void 0;
|
|
10559
|
+
return {
|
|
10560
|
+
x,
|
|
10561
|
+
y,
|
|
10562
|
+
width,
|
|
10563
|
+
height
|
|
10564
|
+
};
|
|
10565
|
+
}
|
|
10566
|
+
function deviceClipRect(clip, dpr) {
|
|
10567
|
+
const scale = Number.isFinite(dpr) && dpr > 0 ? dpr : 1;
|
|
10568
|
+
return {
|
|
10569
|
+
x: Math.max(0, Math.round(clip.x * scale)),
|
|
10570
|
+
y: Math.max(0, Math.round(clip.y * scale)),
|
|
10571
|
+
width: Math.max(1, Math.round(clip.width * scale)),
|
|
10572
|
+
height: Math.max(1, Math.round(clip.height * scale))
|
|
10573
|
+
};
|
|
10574
|
+
}
|
|
10575
|
+
function clipNote(clip, viewport) {
|
|
10576
|
+
if (viewport.width <= 0 || viewport.height <= 0) return "没读到视口尺寸(那条量 dpr / innerWidth 的 eval 没回来)—— 给的是整屏图,裁不了";
|
|
10577
|
+
if (clip.x + clip.width <= viewport.width && clip.y + clip.height <= viewport.height && viewport.width > 0) return "";
|
|
10578
|
+
return `要裁的那块 ${String(clip.x)},${String(clip.y)} ${String(clip.width)}×${String(clip.height)} 不在视口里(视口 ${String(viewport.width)}×${String(viewport.height)})—— 给的是整屏图`;
|
|
10579
|
+
}
|
|
10580
|
+
function defaultShotPath(dir, unit, stamp) {
|
|
10581
|
+
return join(dir, `${unit === "" ? "page" : unit}-${String(stamp)}.png`);
|
|
10582
|
+
}
|
|
10583
|
+
//#endregion
|
|
9650
10584
|
//#region src/modules/panels/model/diy-schema.ts
|
|
9651
10585
|
var UNIT_PROP = {
|
|
9652
10586
|
type: "string",
|
|
@@ -9710,7 +10644,7 @@ function wrapFirst(argv, card) {
|
|
|
9710
10644
|
if (first === void 0 || first.startsWith("-")) return [...argv];
|
|
9711
10645
|
return [evalWrapped(first, card), ...argv.slice(1)];
|
|
9712
10646
|
}
|
|
9713
|
-
function argvOf(row, raw, unit) {
|
|
10647
|
+
function argvOf$1(row, raw, unit) {
|
|
9714
10648
|
const card = cardSelectorIn(unit);
|
|
9715
10649
|
const declared = stringsOf$1(raw["args"]);
|
|
9716
10650
|
const tokens = withToken(row.evalInCard === true && asText(raw["in"]) === "card" ? wrapFirst(declared, card) : declared, card);
|
|
@@ -9718,42 +10652,6 @@ function argvOf(row, raw, unit) {
|
|
|
9718
10652
|
if (row.css.kind === "flag") return prefixValue(tokens, row.css.flag, card);
|
|
9719
10653
|
return prefixPositional(tokens, row.css.at, card, row.css.onlyWhenFirst);
|
|
9720
10654
|
}
|
|
9721
|
-
function finite(value) {
|
|
9722
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
9723
|
-
}
|
|
9724
|
-
function clipOf(value) {
|
|
9725
|
-
if (typeof value !== "object" || value === null) return void 0;
|
|
9726
|
-
const raw = value;
|
|
9727
|
-
const x = finite(raw["x"]);
|
|
9728
|
-
const y = finite(raw["y"]);
|
|
9729
|
-
const width = finite(raw["width"]);
|
|
9730
|
-
const height = finite(raw["height"]);
|
|
9731
|
-
if (x === void 0 || y === void 0 || width === void 0 || height === void 0) return void 0;
|
|
9732
|
-
if (x < 0 || y < 0 || width <= 0 || height <= 0) return void 0;
|
|
9733
|
-
return {
|
|
9734
|
-
x,
|
|
9735
|
-
y,
|
|
9736
|
-
width,
|
|
9737
|
-
height
|
|
9738
|
-
};
|
|
9739
|
-
}
|
|
9740
|
-
function deviceClipRect(clip, dpr) {
|
|
9741
|
-
const scale = Number.isFinite(dpr) && dpr > 0 ? dpr : 1;
|
|
9742
|
-
return {
|
|
9743
|
-
x: Math.max(0, Math.round(clip.x * scale)),
|
|
9744
|
-
y: Math.max(0, Math.round(clip.y * scale)),
|
|
9745
|
-
width: Math.max(1, Math.round(clip.width * scale)),
|
|
9746
|
-
height: Math.max(1, Math.round(clip.height * scale))
|
|
9747
|
-
};
|
|
9748
|
-
}
|
|
9749
|
-
function clipNote(clip, viewport) {
|
|
9750
|
-
if (viewport.width <= 0 || viewport.height <= 0) return "没读到视口尺寸(那条量 dpr / innerWidth 的 eval 没回来)—— 给的是整屏图,裁不了";
|
|
9751
|
-
if (clip.x + clip.width <= viewport.width && clip.y + clip.height <= viewport.height && viewport.width > 0) return "";
|
|
9752
|
-
return `要裁的那块 ${String(clip.x)},${String(clip.y)} ${String(clip.width)}×${String(clip.height)} 不在视口里(视口 ${String(viewport.width)}×${String(viewport.height)})—— 给的是整屏图`;
|
|
9753
|
-
}
|
|
9754
|
-
function defaultShotPath(dir, unit, stamp) {
|
|
9755
|
-
return join(dir, `${unit === "" ? "page" : unit}-${String(stamp)}.png`);
|
|
9756
|
-
}
|
|
9757
10655
|
var BROWSER_TOOLS = [
|
|
9758
10656
|
{
|
|
9759
10657
|
tool: DIY_STATE_TOOL,
|
|
@@ -9934,7 +10832,7 @@ function parametersOf(row, command) {
|
|
|
9934
10832
|
};
|
|
9935
10833
|
}
|
|
9936
10834
|
async function viewportOf(page) {
|
|
9937
|
-
const parsed = jsonOf((await runBrowserStep(page.
|
|
10835
|
+
const parsed = jsonOf((await runBrowserStep(page.seam, page.command, [
|
|
9938
10836
|
"browser",
|
|
9939
10837
|
page.session,
|
|
9940
10838
|
"eval",
|
|
@@ -9960,8 +10858,8 @@ function hostOf$1(url) {
|
|
|
9960
10858
|
return "";
|
|
9961
10859
|
}
|
|
9962
10860
|
}
|
|
9963
|
-
async function onSiteOnce(
|
|
9964
|
-
const step = await runBrowserStep(
|
|
10861
|
+
async function onSiteOnce(seam, command, site, line) {
|
|
10862
|
+
const step = await runBrowserStep(seam, command, [
|
|
9965
10863
|
"browser",
|
|
9966
10864
|
BROWSER_READ_SESSION,
|
|
9967
10865
|
"get",
|
|
@@ -9971,7 +10869,7 @@ async function onSiteOnce(shell, command, site, line) {
|
|
|
9971
10869
|
], LOOK_TIMEOUT_MS, line);
|
|
9972
10870
|
const want = hostOf$1(site);
|
|
9973
10871
|
if (want === "" || hostOf$1(step.stdout.trim()) === want) return false;
|
|
9974
|
-
await runBrowserStep(
|
|
10872
|
+
await runBrowserStep(seam, command, [
|
|
9975
10873
|
"browser",
|
|
9976
10874
|
BROWSER_READ_SESSION,
|
|
9977
10875
|
"open",
|
|
@@ -9994,15 +10892,15 @@ function readRefusalOf(row, raw) {
|
|
|
9994
10892
|
async function runReadTool(row, ctx, raw, site) {
|
|
9995
10893
|
const refused = readRefusalOf(row, raw);
|
|
9996
10894
|
if (refused !== "") throw new Error(refused);
|
|
9997
|
-
const
|
|
9998
|
-
if (
|
|
10895
|
+
const seam = seamOf(ctx);
|
|
10896
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,驱动不了浏览器");
|
|
9999
10897
|
const command = cliCommandOf(ctx);
|
|
10000
10898
|
const requested = asNumber(raw["timeoutMs"]);
|
|
10001
10899
|
const timeoutMs = requested !== void 0 && requested > 0 ? requested : DEFAULT_TIMEOUT_MS$4;
|
|
10002
10900
|
const argv = stringsOf$1(raw["args"]);
|
|
10003
10901
|
const line = surfaceOf(ctx)?.background;
|
|
10004
|
-
const opened = await onSiteOnce(
|
|
10005
|
-
const step = await runBrowserStep(
|
|
10902
|
+
const opened = await onSiteOnce(seam, command, site, line);
|
|
10903
|
+
const step = await runBrowserStep(seam, command, [
|
|
10006
10904
|
"browser",
|
|
10007
10905
|
BROWSER_READ_SESSION,
|
|
10008
10906
|
...row.verb,
|
|
@@ -10032,27 +10930,31 @@ async function runBrowserTool(row, ctx, raw, exec) {
|
|
|
10032
10930
|
if (needsCardOf(row, page.args) && page.unit === "") throw new Error("要卡作用域(scope=card / $card / in=card)就得给一个 unit —— 这个工作区里不止一个模块时,我不知道你说哪一张卡。");
|
|
10033
10931
|
const requested = asNumber(page.args["timeoutMs"]);
|
|
10034
10932
|
const timeoutMs = requested !== void 0 && requested > 0 ? requested : DEFAULT_TIMEOUT_MS$4;
|
|
10035
|
-
const argv = argvOf(row, page.args, page.unit);
|
|
10933
|
+
const argv = argvOf$1(row, page.args, page.unit);
|
|
10036
10934
|
const clip = row.clip === true ? clipOf(page.args["clip"]) : void 0;
|
|
10037
10935
|
const clipAsked = row.clip === true && page.args["clip"] !== void 0;
|
|
10038
10936
|
let file = "";
|
|
10039
10937
|
let finalArgv = argv;
|
|
10938
|
+
let intent = productWrite();
|
|
10040
10939
|
if (row.clip === true) {
|
|
10041
10940
|
const given = argv[0] !== void 0 && !argv[0].startsWith("-") ? argv[0] : "";
|
|
10042
10941
|
if (given === "") {
|
|
10043
10942
|
file = defaultShotPath(page.artifactsDir, page.unit, Date.now());
|
|
10044
10943
|
finalArgv = [file, ...argv];
|
|
10045
10944
|
ensureDirOf(dirname(file));
|
|
10046
|
-
} else
|
|
10945
|
+
} else {
|
|
10946
|
+
file = given;
|
|
10947
|
+
intent = pathsWrite([file]);
|
|
10948
|
+
}
|
|
10047
10949
|
}
|
|
10048
|
-
const step = await runBrowserStep(page.
|
|
10950
|
+
const step = await runBrowserStep(page.seam, page.command, [
|
|
10049
10951
|
"browser",
|
|
10050
10952
|
page.session,
|
|
10051
10953
|
...row.verb,
|
|
10052
10954
|
...finalArgv,
|
|
10053
10955
|
"--window",
|
|
10054
10956
|
"background"
|
|
10055
|
-
], timeoutMs, page.line);
|
|
10957
|
+
], timeoutMs, page.line, intent);
|
|
10056
10958
|
const shot = {};
|
|
10057
10959
|
if (row.clip === true) {
|
|
10058
10960
|
shot["file"] = step.ok ? file : "";
|
|
@@ -10138,11 +11040,15 @@ function specTool(ctx) {
|
|
|
10138
11040
|
const args = asRecord$1(raw);
|
|
10139
11041
|
const argv = specArgvOf(asText(args["command"]));
|
|
10140
11042
|
if (argv.length === 0) throw new Error("给一条命令,site 与名字两个词 —— 例 `inventory publish`(`inventory/publish` 也认)。");
|
|
10141
|
-
const
|
|
10142
|
-
if (
|
|
11043
|
+
const seam = seamOf(ctx);
|
|
11044
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,问不了命令的形状");
|
|
10143
11045
|
const asked = asNumber(args["timeoutMs"]);
|
|
10144
11046
|
const timeoutMs = asked !== void 0 && asked > 0 ? asked : SPEC_TIMEOUT_MS;
|
|
10145
|
-
const result = await
|
|
11047
|
+
const result = await seam.run({
|
|
11048
|
+
command: (surfaceOf(ctx)?.background ?? plainLine)(cliCommandOf(ctx), argv),
|
|
11049
|
+
intent: productWrite(),
|
|
11050
|
+
timeoutMs
|
|
11051
|
+
});
|
|
10146
11052
|
const spec = jsonOf(result.stdout);
|
|
10147
11053
|
return plainJson({
|
|
10148
11054
|
ok: result.exitCode === 0 && !result.timedOut,
|
|
@@ -10160,8 +11066,169 @@ function registerSpecTool(ctx, tools) {
|
|
|
10160
11066
|
return [tools.register(specTool(ctx))];
|
|
10161
11067
|
}
|
|
10162
11068
|
//#endregion
|
|
11069
|
+
//#region src/shared/api/host/diy-release-staging.ts
|
|
11070
|
+
var STAGING_ROOT = "dsh-wb-release";
|
|
11071
|
+
function stagingDirOf(unit) {
|
|
11072
|
+
return join(tmpdir(), STAGING_ROOT, unit.replace(/[^A-Za-z0-9._-]+/g, "-"));
|
|
11073
|
+
}
|
|
11074
|
+
function journalFileOf(staging, unit) {
|
|
11075
|
+
return join(staging, "releases", `${unit}.json`);
|
|
11076
|
+
}
|
|
11077
|
+
function prepareStaging(unit, resume) {
|
|
11078
|
+
const staging = stagingDirOf(unit);
|
|
11079
|
+
if (!resume) rmSync(staging, {
|
|
11080
|
+
recursive: true,
|
|
11081
|
+
force: true
|
|
11082
|
+
});
|
|
11083
|
+
mkdirSync(staging, { recursive: true });
|
|
11084
|
+
if (resume) seedJournal(staging, unit);
|
|
11085
|
+
return staging;
|
|
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
|
+
}
|
|
11095
|
+
function cleanupStaging(unit) {
|
|
11096
|
+
rmSync(stagingDirOf(unit), {
|
|
11097
|
+
recursive: true,
|
|
11098
|
+
force: true
|
|
11099
|
+
});
|
|
11100
|
+
}
|
|
11101
|
+
function adoptTarballFile(file, unit) {
|
|
11102
|
+
if (file === "" || !existsSync(file)) return void 0;
|
|
11103
|
+
const dest = join(workbenchRoot(), "artifacts", unit, basename(file));
|
|
11104
|
+
if (dest === file) return dest;
|
|
11105
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
11106
|
+
copyFileSync(file, dest);
|
|
11107
|
+
rmSync(file, { force: true });
|
|
11108
|
+
return dest;
|
|
11109
|
+
}
|
|
11110
|
+
function mirrorJournal(staging, unit) {
|
|
11111
|
+
const from = journalFileOf(staging, unit);
|
|
11112
|
+
if (!existsSync(from)) return;
|
|
11113
|
+
const to = join(workbenchRoot(), "releases", `${unit}.json`);
|
|
11114
|
+
mkdirSync(dirname(to), { recursive: true });
|
|
11115
|
+
copyFileSync(from, to);
|
|
11116
|
+
}
|
|
11117
|
+
//#endregion
|
|
10163
11118
|
//#region src/modules/panels/model/diy-publish.ts
|
|
10164
11119
|
var PUBLISH_TIMEOUT_MS = 18e5;
|
|
11120
|
+
function adoptTarball(payload, unit) {
|
|
11121
|
+
const file = asText(payload["file"]);
|
|
11122
|
+
const dest = adoptTarballFile(file, unit);
|
|
11123
|
+
if (dest === void 0) return payload;
|
|
11124
|
+
const install = asText(payload["install"]);
|
|
11125
|
+
return {
|
|
11126
|
+
...payload,
|
|
11127
|
+
file: dest,
|
|
11128
|
+
...install === "" ? {} : { install: install.split(file).join(dest) }
|
|
11129
|
+
};
|
|
11130
|
+
}
|
|
11131
|
+
function needsUserPayload(payload) {
|
|
11132
|
+
const needs = asRecord$1(payload["needsUser"]);
|
|
11133
|
+
const stepText = asText(needs["text"]);
|
|
11134
|
+
const command = asText(needs["command"]);
|
|
11135
|
+
return plainJson({
|
|
11136
|
+
...payload,
|
|
11137
|
+
text: [
|
|
11138
|
+
`还差一步:${stepText}`,
|
|
11139
|
+
...command === "" ? [] : [`命令:${command}`],
|
|
11140
|
+
"做完跟我说「好了」,我接着发。"
|
|
11141
|
+
].join("\n")
|
|
11142
|
+
});
|
|
11143
|
+
}
|
|
11144
|
+
function resumablePayload(payload, unit) {
|
|
11145
|
+
const resumeInfo = asRecord$1(payload["resume"]);
|
|
11146
|
+
return plainJson({
|
|
11147
|
+
...payload,
|
|
11148
|
+
text: [`发布中断在第 ${asText(resumeInfo["phase"])} 步:${asText(payload["text"])}`, `再调一次 workbench_diy_publish(unit=${unit}, resume=true)就能接着发。`].join("\n")
|
|
11149
|
+
});
|
|
11150
|
+
}
|
|
11151
|
+
function outcomeOf$1(result, unit) {
|
|
11152
|
+
const payload = jsonOf(result.stdout);
|
|
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
|
+
};
|
|
11161
|
+
if (result.exitCode !== 0 || payload === void 0) {
|
|
11162
|
+
const tail = `${result.stderr}\n${result.stdout}`.trim().split("\n").slice(-6).join(" | ");
|
|
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}`}`;
|
|
11177
|
+
}
|
|
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 再发。";
|
|
11181
|
+
}
|
|
11182
|
+
function successPayloadOf(payload, found, options) {
|
|
11183
|
+
const name = asText(payload["name"]);
|
|
11184
|
+
const released = asText(payload["version"]);
|
|
11185
|
+
const registry = asText(payload["registry"]);
|
|
11186
|
+
const file = asText(payload["file"]);
|
|
11187
|
+
const install = asText(payload["install"]);
|
|
11188
|
+
const git = asRecord$1(payload["git"]);
|
|
11189
|
+
const targetOfPayload = asText(payload["target"]);
|
|
11190
|
+
const text = options.verify ? [
|
|
11191
|
+
`已核过 ${name}@${released}`,
|
|
11192
|
+
`安装:${install}`,
|
|
11193
|
+
"没有重新发布。"
|
|
11194
|
+
].join("\n") : payload["refresh"] === true ? [payload["refreshed"] === true ? `已刷新 ${name} 的市集元数据(版本 ${released} 不变)` : `${name} 的市集元数据本来就是这样,没动`, `市场索引:${asText(asRecord$1(payload["market"])["url"])}`].join("\n") : targetOfPayload === "tarball" ? [
|
|
11195
|
+
`安装包在 ${file}`,
|
|
11196
|
+
`本机装:${install}`,
|
|
11197
|
+
"文件可以从对话里带走,发给同事。"
|
|
11198
|
+
].join("\n") : targetOfPayload === "git" ? [
|
|
11199
|
+
`已推到 ${asText(git["url"])}(tag ${asText(git["tag"])})`,
|
|
11200
|
+
`安装:${install}`,
|
|
11201
|
+
"已核过 tag 里的产物。"
|
|
11202
|
+
].join("\n") : targetOfPayload === "market" ? [
|
|
11203
|
+
`已发到市场索引 ${asText(asRecord$1(payload["market"])["url"])}`,
|
|
11204
|
+
`安装:${install}`,
|
|
11205
|
+
"已从索引仓库侧核过产物。"
|
|
11206
|
+
].join("\n") : targetOfPayload === "local" ? "没发,留在本机。" : [
|
|
11207
|
+
`已发布 ${name}@${released} → ${registry}`,
|
|
11208
|
+
`同事安装:${install}`,
|
|
11209
|
+
"包内容已经从源上核过(dist/host.js / client.js / cordis.patch.yml 都在)。"
|
|
11210
|
+
].join("\n");
|
|
11211
|
+
const changelog = asRecord$1(payload["changelog"]);
|
|
11212
|
+
const changelogLine = asText(changelog["action"]) === "written" ? `更新日志:已写进 ${CHANGELOG_FILE} 的 ${asText(changelog["version"])}` : asText(changelog["action"]) === "kept" ? `更新日志:这一版已有(${asText(changelog["version"])}),没动` : "";
|
|
11213
|
+
const labelList = Array.isArray(payload["labels"]) ? payload["labels"].map((one) => asText(one)).filter((one) => one !== "") : [];
|
|
11214
|
+
const tail = [changelogLine, labelList.length === 0 || targetOfPayload !== "market" && payload["refresh"] !== true ? "" : `标签:${labelList.join(" · ")}`].filter((one) => one !== "").join("\n");
|
|
11215
|
+
return plainJson({
|
|
11216
|
+
...payload,
|
|
11217
|
+
unit: found.unit,
|
|
11218
|
+
text: tail === "" ? text : `${text}\n${tail}`
|
|
11219
|
+
});
|
|
11220
|
+
}
|
|
11221
|
+
function withPhasePayloads(first, second) {
|
|
11222
|
+
const merged = {
|
|
11223
|
+
...first,
|
|
11224
|
+
...second
|
|
11225
|
+
};
|
|
11226
|
+
delete merged["stop"];
|
|
11227
|
+
if (merged["changelog"] === void 0 && first["changelog"] !== void 0) merged["changelog"] = first["changelog"];
|
|
11228
|
+
if (merged["changes"] === void 0 && first["changes"] !== void 0) merged["changes"] = first["changes"];
|
|
11229
|
+
if (merged["labels"] === void 0 && first["labels"] !== void 0) merged["labels"] = first["labels"];
|
|
11230
|
+
return merged;
|
|
11231
|
+
}
|
|
10165
11232
|
function registerPublishTool(ctx, tools, deps) {
|
|
10166
11233
|
const publishTool = {
|
|
10167
11234
|
name: DIY_PUBLISH_TOOL,
|
|
@@ -10235,20 +11302,22 @@ function registerPublishTool(ctx, tools, deps) {
|
|
|
10235
11302
|
async execute(raw, exec) {
|
|
10236
11303
|
const { root } = await workspaceOf(ctx, exec);
|
|
10237
11304
|
const found = deps.resolve(root, raw);
|
|
10238
|
-
const
|
|
10239
|
-
if (
|
|
11305
|
+
const seam = seamOf(ctx);
|
|
11306
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,发布跑不起来");
|
|
10240
11307
|
const bin = toolchainBinOf(root) ?? toolchainBinOf(found.dir);
|
|
10241
11308
|
if (bin === null) throw new Error(`这个工作区里没有 dsh-wb-module(${join(root, "node_modules")} 下没有)—— 先 pnpm install`);
|
|
10242
|
-
const
|
|
10243
|
-
const
|
|
10244
|
-
const
|
|
10245
|
-
const
|
|
10246
|
-
const
|
|
10247
|
-
const
|
|
10248
|
-
const
|
|
10249
|
-
|
|
10250
|
-
|
|
10251
|
-
const
|
|
11309
|
+
const args = asRecord$1(raw);
|
|
11310
|
+
const target = asText(args["target"]);
|
|
11311
|
+
const source = asText(args["source"]);
|
|
11312
|
+
const version = asText(args["version"]);
|
|
11313
|
+
const license = asText(args["license"]);
|
|
11314
|
+
const changelogNote = asText(args["changelogNote"]);
|
|
11315
|
+
const changelogType = asText(args["changelogType"]);
|
|
11316
|
+
const labels = labelsOf(args["labels"]);
|
|
11317
|
+
let author = asText(args["author"]);
|
|
11318
|
+
const verify = args["verify"] === true;
|
|
11319
|
+
const resume = args["resume"] === true;
|
|
11320
|
+
const refresh = args["refresh"] === true;
|
|
10252
11321
|
if (!verify && target !== "local" && author === "" && (deps.previewOf(found.unit)?.author ?? "") === "") author = await deps.inferredAuthorOf(found.unit) ?? "";
|
|
10253
11322
|
const argv = [
|
|
10254
11323
|
bin,
|
|
@@ -10268,70 +11337,49 @@ function registerPublishTool(ctx, tools, deps) {
|
|
|
10268
11337
|
if (changelogNote !== "") argv.push("--changelog-note", changelogNote);
|
|
10269
11338
|
if (changelogType !== "") argv.push("--changelog-type", changelogType);
|
|
10270
11339
|
for (const label of labels) argv.push("--label", label);
|
|
10271
|
-
if (
|
|
10272
|
-
const
|
|
11340
|
+
if (refresh) argv.push("--refresh");
|
|
11341
|
+
const run = async (extra, request) => seam.run({
|
|
11342
|
+
command: [...argv, ...extra].map((one) => quoteShellArg(one)).join(" "),
|
|
11343
|
+
intent: request.intent,
|
|
11344
|
+
...request.session === void 0 ? {} : { session: request.session },
|
|
11345
|
+
...request.env === void 0 ? {} : { env: request.env },
|
|
10273
11346
|
workdir: root,
|
|
10274
11347
|
timeoutMs: PUBLISH_TIMEOUT_MS
|
|
10275
11348
|
});
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
|
|
10280
|
-
|
|
10281
|
-
return plainJson({
|
|
10282
|
-
...payload,
|
|
10283
|
-
text: [
|
|
10284
|
-
`还差一步:${stepText}`,
|
|
10285
|
-
...command === "" ? [] : [`命令:${command}`],
|
|
10286
|
-
"做完跟我说「好了」,我接着发。"
|
|
10287
|
-
].join("\n")
|
|
10288
|
-
});
|
|
11349
|
+
if (verify || refresh) {
|
|
11350
|
+
const outcome = outcomeOf$1(await run([], { intent: productWrite() }), found.unit);
|
|
11351
|
+
if (outcome.kind === "failed") throw outcome.error;
|
|
11352
|
+
if (outcome.kind !== "done") return plainJson(outcome.payload);
|
|
11353
|
+
return plainJson(successPayloadOf(outcome.payload, found, { verify }));
|
|
10289
11354
|
}
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
}
|
|
11355
|
+
const staging = prepareStaging(found.unit, resume);
|
|
11356
|
+
const env = { DSH_WB_RELEASE_HOME: staging };
|
|
11357
|
+
const session = exec.agent?.session;
|
|
11358
|
+
const firstOutcome = outcomeOf$1(await run(["--workspace-only"], {
|
|
11359
|
+
intent: workspaceWrite(),
|
|
11360
|
+
...session === void 0 ? {} : { session },
|
|
11361
|
+
env
|
|
11362
|
+
}), found.unit);
|
|
11363
|
+
mirrorJournal(staging, found.unit);
|
|
11364
|
+
if (firstOutcome.kind === "failed") throw firstOutcome.error;
|
|
11365
|
+
if (firstOutcome.kind !== "done") return plainJson(firstOutcome.payload);
|
|
11366
|
+
const first = firstOutcome.payload;
|
|
11367
|
+
if (first["stop"] !== "workspace-only" || asText(first["target"]) === "local") {
|
|
11368
|
+
cleanupStaging(found.unit);
|
|
11369
|
+
return plainJson(first);
|
|
10296
11370
|
}
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
}
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
const
|
|
10305
|
-
const
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
`已核过 ${name}@${released}`,
|
|
10310
|
-
`安装:${install}`,
|
|
10311
|
-
"没有重新发布。"
|
|
10312
|
-
].join("\n") : payload["refresh"] === true ? [payload["refreshed"] === true ? `已刷新 ${name} 的市集元数据(版本 ${released} 不变)` : `${name} 的市集元数据本来就是这样,没动`, `市场索引:${asText(asRecord$1(payload["market"])["url"])}`].join("\n") : targetOfPayload === "tarball" ? [
|
|
10313
|
-
`安装包在 ${file}`,
|
|
10314
|
-
`本机装:${install}`,
|
|
10315
|
-
"文件可以从对话里带走,发给同事。"
|
|
10316
|
-
].join("\n") : targetOfPayload === "git" ? [
|
|
10317
|
-
`已推到 ${asText(git["url"])}(tag ${asText(git["tag"])})`,
|
|
10318
|
-
`安装:${install}`,
|
|
10319
|
-
"已核过 tag 里的产物。"
|
|
10320
|
-
].join("\n") : targetOfPayload === "market" ? [
|
|
10321
|
-
`已发到市场索引 ${asText(asRecord$1(payload["market"])["url"])}`,
|
|
10322
|
-
`安装:${install}`,
|
|
10323
|
-
"已从索引仓库侧核过产物。"
|
|
10324
|
-
].join("\n") : targetOfPayload === "local" ? "没发,留在本机。" : [
|
|
10325
|
-
`已发布 ${name}@${released} → ${registry}`,
|
|
10326
|
-
`同事安装:${install}`,
|
|
10327
|
-
"包内容已经从源上核过(dist/host.js / client.js / cordis.patch.yml 都在)。"
|
|
10328
|
-
].join("\n");
|
|
10329
|
-
const changelog = asRecord$1(payload["changelog"]);
|
|
10330
|
-
const tail = [asText(changelog["action"]) === "written" ? `更新日志:已写进 ${CHANGELOG_FILE} 的 ${asText(changelog["version"])}` : asText(changelog["action"]) === "kept" ? `更新日志:这一版已有(${asText(changelog["version"])}),没动` : "", labels.length === 0 || targetOfPayload !== "market" && payload["refresh"] !== true ? "" : `标签:${labels.join(" · ")}`].filter((one) => one !== "").join("\n");
|
|
10331
|
-
return plainJson({
|
|
10332
|
-
...payload,
|
|
10333
|
-
text: tail === "" ? text : `${text}\n${tail}`
|
|
10334
|
-
});
|
|
11371
|
+
const indexOutcome = outcomeOf$1(await run(["--index-only"], {
|
|
11372
|
+
intent: productWrite(),
|
|
11373
|
+
env
|
|
11374
|
+
}), found.unit);
|
|
11375
|
+
mirrorJournal(staging, found.unit);
|
|
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);
|
|
11381
|
+
cleanupStaging(found.unit);
|
|
11382
|
+
return plainJson(successPayloadOf(merged, found, { verify: false }));
|
|
10335
11383
|
}
|
|
10336
11384
|
};
|
|
10337
11385
|
return tools.register(publishTool);
|
|
@@ -10711,7 +11759,7 @@ function ageText(at, now) {
|
|
|
10711
11759
|
function stampOf(at) {
|
|
10712
11760
|
return `${dayjs(at).utc().format("HH:mm:ss")}Z`;
|
|
10713
11761
|
}
|
|
10714
|
-
function readText$
|
|
11762
|
+
function readText$2(read, now) {
|
|
10715
11763
|
const when = `${stampOf(read.at)} · ${ageText(read.at, now)}`;
|
|
10716
11764
|
if (read.phase === "ready") return `${read.route} 读到了(${when})`;
|
|
10717
11765
|
if (read.phase === "loading") return `${read.route} 正在读(${when})`;
|
|
@@ -10728,7 +11776,7 @@ function renderPanel(options) {
|
|
|
10728
11776
|
const title = `—— ${row.unit}${row.name === "" ? "" : `(${row.name})`} · ${healthText(row.health)}`;
|
|
10729
11777
|
const report = row.report;
|
|
10730
11778
|
if (report === void 0) return [title, ` 没上报:卡片没渲染出来,或者用户那一页还没刷新到带这份补丁的版本`].join("\n");
|
|
10731
|
-
const reads = report.cards.slice(0, 6).map((one) => ` 数据 · ${readText$
|
|
11779
|
+
const reads = report.cards.slice(0, 6).map((one) => ` 数据 · ${readText$2(one, now)}`);
|
|
10732
11780
|
const more = report.cards.length > 6 ? [` 数据 · …还有 ${String(report.cards.length - 6)} 条`] : [];
|
|
10733
11781
|
const crash = report.crash === void 0 ? " 崩过:没有" : ` 崩过:是 —— ${report.crash.reason}(${stampOf(report.crash.at)} · 共 ${String(report.crash.count)} 次)`;
|
|
10734
11782
|
return [
|
|
@@ -11337,18 +12385,203 @@ function changelogOf(dir, version, note, kind) {
|
|
|
11337
12385
|
};
|
|
11338
12386
|
}
|
|
11339
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 filterStaticLines(raw) {
|
|
12409
|
+
const kept = [];
|
|
12410
|
+
for (const line of raw.split("\n")) {
|
|
12411
|
+
const text = line.trim();
|
|
12412
|
+
if (text === "") continue;
|
|
12413
|
+
let parsed;
|
|
12414
|
+
try {
|
|
12415
|
+
parsed = JSON.parse(text);
|
|
12416
|
+
} catch {
|
|
12417
|
+
kept.push(text);
|
|
12418
|
+
continue;
|
|
12419
|
+
}
|
|
12420
|
+
const record = plainOf(parsed);
|
|
12421
|
+
const entries = record === void 0 ? void 0 : record["entries"];
|
|
12422
|
+
if (record === void 0 || !Array.isArray(entries)) {
|
|
12423
|
+
kept.push(text);
|
|
12424
|
+
continue;
|
|
12425
|
+
}
|
|
12426
|
+
const inside = entries.filter((one) => !STATIC_URL.test(String(plainOf(one)?.["url"] ?? "")));
|
|
12427
|
+
if (inside.length === 0) continue;
|
|
12428
|
+
kept.push(JSON.stringify({
|
|
12429
|
+
...record,
|
|
12430
|
+
entries: inside
|
|
12431
|
+
}));
|
|
12432
|
+
}
|
|
12433
|
+
return kept.join("\n");
|
|
12434
|
+
}
|
|
12435
|
+
async function captureTickOnce(seam, command, session, file, timeoutMs = LIVE_SNAP_TIMEOUT_MS) {
|
|
12436
|
+
const step = await seam.run({
|
|
12437
|
+
argv: argvOf(command, session, [
|
|
12438
|
+
"network",
|
|
12439
|
+
"--all",
|
|
12440
|
+
"--raw"
|
|
12441
|
+
]),
|
|
12442
|
+
intent: productWrite(),
|
|
12443
|
+
timeoutMs
|
|
12444
|
+
});
|
|
12445
|
+
const round = step.exitCode === 0 ? "ok" : "fail";
|
|
12446
|
+
writeTextFile(`${file}.first`, round);
|
|
12447
|
+
const text = filterStaticLines(step.stdout);
|
|
12448
|
+
if (text !== "") appendTextFile(file, `${text}\n`);
|
|
12449
|
+
return round;
|
|
12450
|
+
}
|
|
12451
|
+
async function startLiveCapture(input) {
|
|
12452
|
+
const { seam, command, session, file } = input;
|
|
12453
|
+
stopLiveCapture(file);
|
|
12454
|
+
writeTextFile(file, "");
|
|
12455
|
+
for (const suffix of [
|
|
12456
|
+
".first",
|
|
12457
|
+
".last",
|
|
12458
|
+
".round",
|
|
12459
|
+
".pid",
|
|
12460
|
+
".reaper"
|
|
12461
|
+
]) rmSync(`${file}${suffix}`, { force: true });
|
|
12462
|
+
try {
|
|
12463
|
+
const arm = await seam.run({
|
|
12464
|
+
argv: argvOf(command, session, [
|
|
12465
|
+
"network",
|
|
12466
|
+
"--follow",
|
|
12467
|
+
"--all"
|
|
12468
|
+
]),
|
|
12469
|
+
intent: productWrite(),
|
|
12470
|
+
timeoutMs: LIVE_ARM_MS
|
|
12471
|
+
});
|
|
12472
|
+
if (!arm.timedOut && arm.exitCode !== 0) return false;
|
|
12473
|
+
const text = arm.stdout.trim();
|
|
12474
|
+
if (text !== "") appendTextFile(file, `${text}\n`);
|
|
12475
|
+
} catch {
|
|
12476
|
+
return false;
|
|
12477
|
+
}
|
|
12478
|
+
const entry = {
|
|
12479
|
+
file,
|
|
12480
|
+
seam,
|
|
12481
|
+
command,
|
|
12482
|
+
session,
|
|
12483
|
+
timer: void 0,
|
|
12484
|
+
stopped: false
|
|
12485
|
+
};
|
|
12486
|
+
LIVE.set(file, entry);
|
|
12487
|
+
scheduleTick(entry);
|
|
12488
|
+
return true;
|
|
12489
|
+
}
|
|
12490
|
+
function scheduleTick(entry) {
|
|
12491
|
+
entry.timer = setTimeout(() => {
|
|
12492
|
+
runTick(entry);
|
|
12493
|
+
}, LIVE_POLL_MS);
|
|
12494
|
+
entry.timer.unref?.();
|
|
12495
|
+
}
|
|
12496
|
+
async function runTick(entry) {
|
|
12497
|
+
if (entry.stopped) return;
|
|
12498
|
+
try {
|
|
12499
|
+
await captureTickOnce(entry.seam, entry.command, entry.session, entry.file);
|
|
12500
|
+
} catch {
|
|
12501
|
+
entry.timer = void 0;
|
|
12502
|
+
}
|
|
12503
|
+
if (entry.stopped) return;
|
|
12504
|
+
scheduleTick(entry);
|
|
12505
|
+
}
|
|
12506
|
+
function stopLiveCapture(file) {
|
|
12507
|
+
const live = LIVE.get(file);
|
|
12508
|
+
for (const suffix of [
|
|
12509
|
+
".pid",
|
|
12510
|
+
".reaper",
|
|
12511
|
+
".round"
|
|
12512
|
+
]) rmSync(`${file}${suffix}`, { force: true });
|
|
12513
|
+
if (live !== void 0) {
|
|
12514
|
+
live.stopped = true;
|
|
12515
|
+
if (live.timer !== void 0) clearTimeout(live.timer);
|
|
12516
|
+
LIVE.delete(file);
|
|
12517
|
+
}
|
|
12518
|
+
return live !== void 0 || existsSync(`${file}.last`) || existsSync(`${file}.first`);
|
|
12519
|
+
}
|
|
12520
|
+
function liveCaptureOf(file) {
|
|
12521
|
+
return LIVE.has(file);
|
|
12522
|
+
}
|
|
12523
|
+
function readText$1(file) {
|
|
12524
|
+
try {
|
|
12525
|
+
return readFileSync(file, "utf8");
|
|
12526
|
+
} catch {
|
|
12527
|
+
return "";
|
|
12528
|
+
}
|
|
12529
|
+
}
|
|
12530
|
+
function readCaptureLines(file) {
|
|
12531
|
+
return (readText$1(file).match(/\n/g) ?? []).length;
|
|
12532
|
+
}
|
|
12533
|
+
function readCaptureRound(file) {
|
|
12534
|
+
const text = readText$1(`${file}.first`).trim();
|
|
12535
|
+
return text === "ok" || text === "fail" ? text : "";
|
|
12536
|
+
}
|
|
12537
|
+
function readCaptureText(file) {
|
|
12538
|
+
const text = readText$1(file);
|
|
12539
|
+
if (text === "") return {
|
|
12540
|
+
text: "",
|
|
12541
|
+
truncated: false
|
|
12542
|
+
};
|
|
12543
|
+
const lines = text.split("\n");
|
|
12544
|
+
if (text.endsWith("\n")) lines.pop();
|
|
12545
|
+
const kept = lines.slice(-2e4);
|
|
12546
|
+
return {
|
|
12547
|
+
text: kept.length === 0 ? "" : `${kept.join("\n")}\n`,
|
|
12548
|
+
truncated: lines.length > LIVE_TAIL_LINES
|
|
12549
|
+
};
|
|
12550
|
+
}
|
|
12551
|
+
function readCaptureLinesWithKey(file, key) {
|
|
12552
|
+
const text = readText$1(file);
|
|
12553
|
+
if (text === "") return "";
|
|
12554
|
+
const compact = `"key":"${key}"`;
|
|
12555
|
+
const spaced = `"key": "${key}"`;
|
|
12556
|
+
const out = [];
|
|
12557
|
+
for (const line of text.split("\n")) {
|
|
12558
|
+
if (out.length >= 20) break;
|
|
12559
|
+
if (line.includes(compact) || line.includes(spaced)) out.push(line);
|
|
12560
|
+
}
|
|
12561
|
+
return out.length === 0 ? "" : `${out.join("\n")}\n`;
|
|
12562
|
+
}
|
|
12563
|
+
function keepCaptureFile(file) {
|
|
12564
|
+
const last = `${file}.last`;
|
|
12565
|
+
try {
|
|
12566
|
+
renameSync(file, last);
|
|
12567
|
+
return statSync(last).size > 0;
|
|
12568
|
+
} catch {
|
|
12569
|
+
return false;
|
|
12570
|
+
}
|
|
12571
|
+
}
|
|
12572
|
+
//#endregion
|
|
11340
12573
|
//#region src/modules/panels/model/diy-observers.ts
|
|
11341
12574
|
async function stopObservers(ctx, workspace, session = BROWSER_SCREEN_SESSION) {
|
|
11342
|
-
const
|
|
11343
|
-
if (workspace === null || workspace === void 0 || workspace === "" ||
|
|
12575
|
+
const seam = seamOf(ctx);
|
|
12576
|
+
if (workspace === null || workspace === void 0 || workspace === "" || seam === void 0) return {
|
|
11344
12577
|
net: false,
|
|
11345
12578
|
logs: false
|
|
11346
12579
|
};
|
|
11347
|
-
const file = join(artifactsDirOf(
|
|
12580
|
+
const file = join(artifactsDirOf(workspace), `net-${session}.jsonl`);
|
|
11348
12581
|
const command = cliCommandOf(ctx);
|
|
11349
|
-
const [
|
|
12582
|
+
const [stopped, step] = await Promise.all([Promise.resolve(stopLiveCapture(file)), actBrowser(seam, command, session, ["eval", LOGS_STOP_SCRIPT], SWEEP_TIMEOUT_MS)]);
|
|
11350
12583
|
return {
|
|
11351
|
-
net,
|
|
12584
|
+
net: stopped || !liveCaptureOf(file),
|
|
11352
12585
|
logs: typeof step.payload?.["unhooked"] === "number"
|
|
11353
12586
|
};
|
|
11354
12587
|
}
|
|
@@ -11498,7 +12731,7 @@ var REFUSAL$1 = {
|
|
|
11498
12731
|
"not-owner": "这场调试不是这个会话的"
|
|
11499
12732
|
};
|
|
11500
12733
|
function debugActResource(deps) {
|
|
11501
|
-
const now = () => deps.now?.() ??
|
|
12734
|
+
const now$2 = () => deps.now?.() ?? now();
|
|
11502
12735
|
const settled = (intent, delivered, summary, state) => ({
|
|
11503
12736
|
ok: true,
|
|
11504
12737
|
intent,
|
|
@@ -11519,7 +12752,7 @@ function debugActResource(deps) {
|
|
|
11519
12752
|
ok: false,
|
|
11520
12753
|
text: "现在没有调试态 —— 没什么可暂停 / 继续 / 结束的"
|
|
11521
12754
|
};
|
|
11522
|
-
const at = now();
|
|
12755
|
+
const at = now$2();
|
|
11523
12756
|
const held = store.live(at);
|
|
11524
12757
|
if (held === null) return {
|
|
11525
12758
|
ok: false,
|
|
@@ -11532,8 +12765,8 @@ function debugActResource(deps) {
|
|
|
11532
12765
|
};
|
|
11533
12766
|
const notice = deps.notice?.(intent, move.state) ?? debugNotice(intent, move.state);
|
|
11534
12767
|
const delivered = deliverDebugNotice(deps.agents(), held.sessionId, notice.message, NOTICE_MODE$1[intent]);
|
|
11535
|
-
if (intent !== "stop") return settled(intent, delivered, notice.summary, store.live(now()));
|
|
11536
|
-
return (store.settle?.() ?? Promise.resolve()).then(() => settled(intent, delivered, notice.summary, store.live(now())));
|
|
12768
|
+
if (intent !== "stop") return settled(intent, delivered, notice.summary, store.live(now$2()));
|
|
12769
|
+
return (store.settle?.() ?? Promise.resolve()).then(() => settled(intent, delivered, notice.summary, store.live(now$2())));
|
|
11537
12770
|
}
|
|
11538
12771
|
};
|
|
11539
12772
|
}
|
|
@@ -12153,19 +13386,13 @@ function pickOf(entries, key, at = null) {
|
|
|
12153
13386
|
//#endregion
|
|
12154
13387
|
//#region src/modules/panels/model/diy-record.ts
|
|
12155
13388
|
var OPEN_TIMEOUT_MS = 3e4;
|
|
12156
|
-
var START_TIMEOUT_MS = 8e3;
|
|
12157
13389
|
var SETTLE_MS = 2e3;
|
|
12158
13390
|
var WAIT_CAP_MS = 9e5;
|
|
12159
13391
|
var POLL_MS = 1e3;
|
|
12160
|
-
var CAPTURE_TAIL_LINES =
|
|
12161
|
-
var CAPTURE_POLL_S = 3;
|
|
12162
|
-
var CAPTURE_ARM_S = 2;
|
|
12163
|
-
var STATIC_URL_PATTERN = `^(data:)|(\\.(js|css|png|jpe?g|gif|svg|woff2?|ico|map|webp|mp4)(\\?|$))`;
|
|
13392
|
+
var CAPTURE_TAIL_LINES = LIVE_TAIL_LINES;
|
|
12164
13393
|
var RECORD_WATCH_MS = 3e3;
|
|
12165
13394
|
var RECORD_WATCH_TRIES = 5;
|
|
12166
|
-
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\")}});";
|
|
12167
13395
|
var DETAIL_MAX_BYTES = 6e4;
|
|
12168
|
-
var HOST_PID = String(process.pid);
|
|
12169
13396
|
var pauseFor = (ms) => new Promise((resolve) => {
|
|
12170
13397
|
setTimeout(resolve, ms);
|
|
12171
13398
|
});
|
|
@@ -12192,7 +13419,7 @@ async function takeShotOf(input, held, n) {
|
|
|
12192
13419
|
const path = shotFileOf(input.captureFile, held.unit, n);
|
|
12193
13420
|
const at = Date.now();
|
|
12194
13421
|
try {
|
|
12195
|
-
const step = await actBrowser(input.
|
|
13422
|
+
const step = await actBrowser(input.seam, input.command, BROWSER_RECORD_SESSION, ["screenshot", path]);
|
|
12196
13423
|
if (step.ok) input.store.shotTaken(input.sessionId, {
|
|
12197
13424
|
n,
|
|
12198
13425
|
at,
|
|
@@ -12214,39 +13441,17 @@ async function takeShotOf(input, held, n) {
|
|
|
12214
13441
|
}, Date.now());
|
|
12215
13442
|
}
|
|
12216
13443
|
}
|
|
12217
|
-
async function startRecordCapture(
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
|
|
12221
|
-
session,
|
|
12222
|
-
"network",
|
|
12223
|
-
"--follow",
|
|
12224
|
-
"--all",
|
|
12225
|
-
"--window",
|
|
12226
|
-
"background"
|
|
12227
|
-
]);
|
|
12228
|
-
const snap = makeLine(bin, [
|
|
12229
|
-
"browser",
|
|
13444
|
+
async function startRecordCapture(seam, command, session, file) {
|
|
13445
|
+
return startLiveCapture({
|
|
13446
|
+
seam,
|
|
13447
|
+
command,
|
|
12230
13448
|
session,
|
|
12231
|
-
|
|
12232
|
-
|
|
12233
|
-
"--raw",
|
|
12234
|
-
"--window",
|
|
12235
|
-
"background"
|
|
12236
|
-
]);
|
|
12237
|
-
return (await runShell(shell, [
|
|
12238
|
-
`f=${quoteShellArg(file)}`,
|
|
12239
|
-
`r=${quoteShellArg(`${file}.round`)}`,
|
|
12240
|
-
": > \"$f\"",
|
|
12241
|
-
"rm -f \"$f.last\" \"$f.first\" \"$r\"",
|
|
12242
|
-
`( ${arm} >> "$f" 2>/dev/null & p=$!; sleep ${String(CAPTURE_ARM_S)}; kill "$p" 2>/dev/null )`,
|
|
12243
|
-
`( 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"`,
|
|
12244
|
-
"kill -0 \"$(cat \"$f.pid\" 2>/dev/null)\" 2>/dev/null"
|
|
12245
|
-
].join("; "), { timeoutMs: START_TIMEOUT_MS })).exitCode === 0;
|
|
13449
|
+
file
|
|
13450
|
+
});
|
|
12246
13451
|
}
|
|
12247
13452
|
async function releaseLeftoversOf(input) {
|
|
12248
13453
|
await releaseQuietly({
|
|
12249
|
-
|
|
13454
|
+
seam: input.seam,
|
|
12250
13455
|
command: input.command,
|
|
12251
13456
|
session: BROWSER_RECORD_SESSION,
|
|
12252
13457
|
url: input.url,
|
|
@@ -12255,14 +13460,14 @@ async function releaseLeftoversOf(input) {
|
|
|
12255
13460
|
}
|
|
12256
13461
|
async function stopCaptureQuiet(input) {
|
|
12257
13462
|
try {
|
|
12258
|
-
return
|
|
13463
|
+
return stopLiveCapture(input.captureFile);
|
|
12259
13464
|
} catch {
|
|
12260
13465
|
return false;
|
|
12261
13466
|
}
|
|
12262
13467
|
}
|
|
12263
13468
|
async function closeRecordTabQuiet(input) {
|
|
12264
13469
|
try {
|
|
12265
|
-
return await closeRecordTab(input.
|
|
13470
|
+
return await closeRecordTab(input.seam, input.command, BROWSER_RECORD_SESSION, input.url);
|
|
12266
13471
|
} catch {
|
|
12267
13472
|
return false;
|
|
12268
13473
|
}
|
|
@@ -12290,7 +13495,7 @@ async function watchFinishStepOf(input) {
|
|
|
12290
13495
|
let page;
|
|
12291
13496
|
const started = Date.now();
|
|
12292
13497
|
try {
|
|
12293
|
-
page = recordPageOf((await actBrowser(input.
|
|
13498
|
+
page = recordPageOf((await actBrowser(input.seam, input.command, BROWSER_RECORD_SESSION, ["eval", RECORD_READ_SCRIPT])).payload);
|
|
12294
13499
|
} catch {
|
|
12295
13500
|
return {
|
|
12296
13501
|
stop: false,
|
|
@@ -12360,42 +13565,22 @@ function watchRecordFinish(input) {
|
|
|
12360
13565
|
};
|
|
12361
13566
|
setTimeout(tick, 0);
|
|
12362
13567
|
}
|
|
12363
|
-
|
|
12364
|
-
const
|
|
12365
|
-
const
|
|
12366
|
-
return Number.isFinite(value) ? value : 0;
|
|
12367
|
-
}
|
|
12368
|
-
async function captureRoundOf(shell, file, timeoutMs = START_TIMEOUT_MS) {
|
|
12369
|
-
const value = (await runShell(shell, `cat ${quoteShellArg(`${file}.first`)} 2>/dev/null || true`, { timeoutMs })).stdout.trim();
|
|
12370
|
-
return value === "ok" || value === "fail" ? value : "";
|
|
12371
|
-
}
|
|
12372
|
-
async function captureTextOf(shell, file, timeoutMs = START_TIMEOUT_MS) {
|
|
12373
|
-
const step = await runShell(shell, `tail -n ${String(CAPTURE_TAIL_LINES)} ${quoteShellArg(file)} 2>/dev/null || true`, { timeoutMs });
|
|
13568
|
+
function captureReadOf(file) {
|
|
13569
|
+
const totalLines = readCaptureLines(file);
|
|
13570
|
+
const text = readCaptureText(file);
|
|
12374
13571
|
return {
|
|
12375
|
-
text:
|
|
12376
|
-
truncated: step.truncated
|
|
12377
|
-
};
|
|
12378
|
-
}
|
|
12379
|
-
async function captureReadOf(shell, file, timeoutMs = START_TIMEOUT_MS) {
|
|
12380
|
-
const totalLines = await captureLinesOf(shell, file, timeoutMs);
|
|
12381
|
-
const read = await captureTextOf(shell, file, timeoutMs);
|
|
12382
|
-
const round = await captureRoundOf(shell, file, timeoutMs);
|
|
12383
|
-
return {
|
|
12384
|
-
text: read.text,
|
|
13572
|
+
text: text.text,
|
|
12385
13573
|
totalLines,
|
|
12386
13574
|
tailLimit: CAPTURE_TAIL_LINES,
|
|
12387
|
-
truncated:
|
|
12388
|
-
round
|
|
13575
|
+
truncated: text.truncated || totalLines > CAPTURE_TAIL_LINES,
|
|
13576
|
+
round: readCaptureRound(file)
|
|
12389
13577
|
};
|
|
12390
13578
|
}
|
|
12391
|
-
|
|
12392
|
-
return (
|
|
13579
|
+
function keepCaptureOf(file) {
|
|
13580
|
+
return keepCaptureFile(file);
|
|
12393
13581
|
}
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
const compact = quoteShellArg(`"key":"${key}"`);
|
|
12397
|
-
const spaced = quoteShellArg(`"key": "${key}"`);
|
|
12398
|
-
return (await runShell(shell, [`[ -f ${quoted} ] || exit 0`, `grep -m 20 -F ${compact} ${quoted} 2>/dev/null || grep -m 20 -F ${spaced} ${quoted} 2>/dev/null || true`].join("; "), { timeoutMs })).stdout;
|
|
13582
|
+
function captureLinesWithKeyOf(file, key) {
|
|
13583
|
+
return readCaptureLinesWithKey(file, key);
|
|
12399
13584
|
}
|
|
12400
13585
|
function bundleOf(capture, page, state) {
|
|
12401
13586
|
const entries = captureEntriesOf(capture.text);
|
|
@@ -12505,32 +13690,32 @@ function tabsOf(raw) {
|
|
|
12505
13690
|
return null;
|
|
12506
13691
|
}
|
|
12507
13692
|
}
|
|
12508
|
-
async function closeRecordTab(
|
|
12509
|
-
const pages = tabsOf((await actBrowser(
|
|
13693
|
+
async function closeRecordTab(seam, command, session, url) {
|
|
13694
|
+
const pages = tabsOf((await actBrowser(seam, command, session, ["tab", "list"])).stdout);
|
|
12510
13695
|
if (pages !== null && pages.length === 0) return true;
|
|
12511
13696
|
const pick = pages?.find((one) => typeof one["url"] === "string" && one["url"] === url) ?? pages?.[0];
|
|
12512
13697
|
const page = pick === void 0 ? "" : typeof pick["page"] === "string" ? pick["page"] : "";
|
|
12513
13698
|
if (page !== "") {
|
|
12514
|
-
if ((await actBrowser(
|
|
13699
|
+
if ((await actBrowser(seam, command, session, [
|
|
12515
13700
|
"tab",
|
|
12516
13701
|
"close",
|
|
12517
13702
|
page
|
|
12518
13703
|
])).ok) {
|
|
12519
|
-
for (const one of blanksOf(pages, page)) await actBrowser(
|
|
13704
|
+
for (const one of blanksOf(pages, page)) await actBrowser(seam, command, session, [
|
|
12520
13705
|
"tab",
|
|
12521
13706
|
"close",
|
|
12522
13707
|
one
|
|
12523
13708
|
]);
|
|
12524
|
-
await actBrowser(
|
|
13709
|
+
await actBrowser(seam, command, session, ["close"]);
|
|
12525
13710
|
return true;
|
|
12526
13711
|
}
|
|
12527
13712
|
}
|
|
12528
|
-
return (await actBrowser(
|
|
13713
|
+
return (await actBrowser(seam, command, session, ["close"])).ok;
|
|
12529
13714
|
}
|
|
12530
13715
|
async function releaseRecord(input) {
|
|
12531
|
-
const capture =
|
|
13716
|
+
const capture = stopLiveCapture(input.captureFile);
|
|
12532
13717
|
return {
|
|
12533
|
-
tab: await closeRecordTab(input.
|
|
13718
|
+
tab: await closeRecordTab(input.seam, input.command, input.session, input.url),
|
|
12534
13719
|
capture
|
|
12535
13720
|
};
|
|
12536
13721
|
}
|
|
@@ -12552,12 +13737,12 @@ function samePageOf(here, want) {
|
|
|
12552
13737
|
return here !== "" && norm(here) === norm(want);
|
|
12553
13738
|
}
|
|
12554
13739
|
async function whereAtOf(input) {
|
|
12555
|
-
const step = await actBrowser(input.
|
|
13740
|
+
const step = await actBrowser(input.seam, input.command, input.session, ["eval", PAGE_HREF_SCRIPT]);
|
|
12556
13741
|
const href = step.payload === void 0 ? "" : step.payload["href"];
|
|
12557
13742
|
return typeof href === "string" ? href : "";
|
|
12558
13743
|
}
|
|
12559
13744
|
async function settleOf(input) {
|
|
12560
|
-
await runBrowserStep(input.
|
|
13745
|
+
await runBrowserStep(input.seam, input.command, [
|
|
12561
13746
|
"browser",
|
|
12562
13747
|
input.session,
|
|
12563
13748
|
"wait",
|
|
@@ -12567,7 +13752,7 @@ async function settleOf(input) {
|
|
|
12567
13752
|
}
|
|
12568
13753
|
async function openRecordPage(input) {
|
|
12569
13754
|
await releaseQuietly(input);
|
|
12570
|
-
const opened = await runBrowserStep(input.
|
|
13755
|
+
const opened = await runBrowserStep(input.seam, input.command, [
|
|
12571
13756
|
"browser",
|
|
12572
13757
|
input.session,
|
|
12573
13758
|
"tab",
|
|
@@ -12584,7 +13769,7 @@ async function openRecordPage(input) {
|
|
|
12584
13769
|
await settleOf(input);
|
|
12585
13770
|
let at = await whereAtOf(input);
|
|
12586
13771
|
if (!samePageOf(at, input.url)) {
|
|
12587
|
-
await runBrowserStep(input.
|
|
13772
|
+
await runBrowserStep(input.seam, input.command, [
|
|
12588
13773
|
"browser",
|
|
12589
13774
|
input.session,
|
|
12590
13775
|
"open",
|
|
@@ -12652,7 +13837,7 @@ function registerRecordTools(ctx, store) {
|
|
|
12652
13837
|
output: OUTPUT$3,
|
|
12653
13838
|
async execute(raw, exec) {
|
|
12654
13839
|
const { root } = await workspaceOf(ctx, exec);
|
|
12655
|
-
const artifactsDir = artifactsDirOf(
|
|
13840
|
+
const artifactsDir = artifactsDirOf(root);
|
|
12656
13841
|
const args = asRecord$1(raw);
|
|
12657
13842
|
const asked = asText(args["unit"]);
|
|
12658
13843
|
const { units } = unitsOf(ctx, root);
|
|
@@ -12670,8 +13855,8 @@ function registerRecordTools(ctx, store) {
|
|
|
12670
13855
|
method,
|
|
12671
13856
|
urlContains
|
|
12672
13857
|
};
|
|
12673
|
-
const
|
|
12674
|
-
if (
|
|
13858
|
+
const seam = seamOf(ctx);
|
|
13859
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,开不了录制窗");
|
|
12675
13860
|
const command = cliCommandOf(ctx);
|
|
12676
13861
|
const sessionId = sessionIdOf(exec) ?? "";
|
|
12677
13862
|
const held = store.live(Date.now());
|
|
@@ -12690,21 +13875,21 @@ function registerRecordTools(ctx, store) {
|
|
|
12690
13875
|
}), held);
|
|
12691
13876
|
try {
|
|
12692
13877
|
const opened = await openRecordPage({
|
|
12693
|
-
|
|
13878
|
+
seam,
|
|
12694
13879
|
command,
|
|
12695
13880
|
session: BROWSER_RECORD_SESSION,
|
|
12696
13881
|
url,
|
|
12697
13882
|
captureFile
|
|
12698
13883
|
});
|
|
12699
13884
|
if (!opened.ok) throw new Error(opened.why);
|
|
12700
|
-
const armed = await actBrowser(
|
|
13885
|
+
const armed = await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_ARM_SCRIPT]);
|
|
12701
13886
|
const page = recordPageOf(armed.payload);
|
|
12702
13887
|
if (!armed.ok || !page.ok) throw new Error(`浮条没装上:${armed.stderr || armed.stdout || "那一页还在加载"} —— 等用户那一页站稳了再试。`);
|
|
12703
|
-
|
|
12704
|
-
const capture = await startRecordCapture(
|
|
13888
|
+
stopLiveCapture(captureFile);
|
|
13889
|
+
const capture = await startRecordCapture(seam, command, BROWSER_RECORD_SESSION, captureFile);
|
|
12705
13890
|
watchRecordFinish({
|
|
12706
13891
|
ctx,
|
|
12707
|
-
|
|
13892
|
+
seam,
|
|
12708
13893
|
command,
|
|
12709
13894
|
sessionId,
|
|
12710
13895
|
store,
|
|
@@ -12727,7 +13912,7 @@ function registerRecordTools(ctx, store) {
|
|
|
12727
13912
|
});
|
|
12728
13913
|
} catch (error) {
|
|
12729
13914
|
await releaseQuietly({
|
|
12730
|
-
|
|
13915
|
+
seam,
|
|
12731
13916
|
command,
|
|
12732
13917
|
session: BROWSER_RECORD_SESSION,
|
|
12733
13918
|
url,
|
|
@@ -12751,8 +13936,8 @@ function registerRecordTools(ctx, store) {
|
|
|
12751
13936
|
},
|
|
12752
13937
|
output: OUTPUT$3,
|
|
12753
13938
|
async execute(raw, exec) {
|
|
12754
|
-
const
|
|
12755
|
-
if (
|
|
13939
|
+
const seam = seamOf(ctx);
|
|
13940
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,读不了录制");
|
|
12756
13941
|
const sessionId = sessionIdOf(exec) ?? "";
|
|
12757
13942
|
const held = store.live(Date.now());
|
|
12758
13943
|
if (held === null) throw new Error(recordMissingText());
|
|
@@ -12777,14 +13962,14 @@ function registerRecordTools(ctx, store) {
|
|
|
12777
13962
|
let stepOk = true;
|
|
12778
13963
|
let page = recordPageOf(void 0);
|
|
12779
13964
|
if (!over) {
|
|
12780
|
-
await actBrowser(
|
|
12781
|
-
let step = await actBrowser(
|
|
13965
|
+
await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_ARM_SCRIPT]);
|
|
13966
|
+
let step = await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_READ_SCRIPT]);
|
|
12782
13967
|
page = recordPageOf(step.payload);
|
|
12783
13968
|
stepOk = step.ok;
|
|
12784
13969
|
sync(page);
|
|
12785
13970
|
while (budget > 0 && page.phase !== "finished" && Date.now() < deadline) {
|
|
12786
13971
|
await pauseFor(Math.min(POLL_MS, Math.max(0, deadline - Date.now())));
|
|
12787
|
-
step = await actBrowser(
|
|
13972
|
+
step = await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_READ_SCRIPT]);
|
|
12788
13973
|
const next = recordPageOf(step.payload);
|
|
12789
13974
|
if (next.ok) page = next;
|
|
12790
13975
|
sync(page);
|
|
@@ -12798,7 +13983,7 @@ function registerRecordTools(ctx, store) {
|
|
|
12798
13983
|
}
|
|
12799
13984
|
const state = store.live(Date.now()) ?? held;
|
|
12800
13985
|
const ledger = state.shots;
|
|
12801
|
-
const capture =
|
|
13986
|
+
const capture = captureReadOf(state.captureFile);
|
|
12802
13987
|
const bundle = bundleOf(capture, page, state);
|
|
12803
13988
|
const hookCaught = page.calls.length;
|
|
12804
13989
|
const nav = page.href !== "" && page.here !== "" && page.href !== page.here;
|
|
@@ -12882,9 +14067,9 @@ function registerRecordTools(ctx, store) {
|
|
|
12882
14067
|
output: OUTPUT$3,
|
|
12883
14068
|
async execute(raw, exec) {
|
|
12884
14069
|
const { root } = await workspaceOf(ctx, exec);
|
|
12885
|
-
const artifactsDir = artifactsDirOf(
|
|
12886
|
-
const
|
|
12887
|
-
if (
|
|
14070
|
+
const artifactsDir = artifactsDirOf(root);
|
|
14071
|
+
const seam = seamOf(ctx);
|
|
14072
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,取不了详情");
|
|
12888
14073
|
const args = asRecord$1(raw);
|
|
12889
14074
|
const unit = asText(args["unit"]);
|
|
12890
14075
|
const askedKey = asText(args["key"]).trim();
|
|
@@ -12908,8 +14093,8 @@ function registerRecordTools(ctx, store) {
|
|
|
12908
14093
|
if (key === "") {
|
|
12909
14094
|
if (index === null) throw new Error("给一个 key(清单里那一条的)或 index(清单里的序号)。");
|
|
12910
14095
|
if (held === null) throw new Error("收工之后只能用 key —— index 是\"那一场清单里\"的序号,这一场已经清掉了。");
|
|
12911
|
-
page = recordPageOf((await actBrowser(
|
|
12912
|
-
const bundle = bundleOf(
|
|
14096
|
+
page = recordPageOf((await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_READ_SCRIPT])).payload);
|
|
14097
|
+
const bundle = bundleOf(captureReadOf(file), page, held);
|
|
12913
14098
|
const row = bundle.rows[index];
|
|
12914
14099
|
if (row === void 0) throw new Error(`这一段清单里没有第 ${String(index)} 条(一共 ${String(bundle.rows.length)} 条)—— 先 ${DIY_RECORD_READ_TOOL} 拿一份新的清单。`);
|
|
12915
14100
|
key = row.key;
|
|
@@ -12919,11 +14104,11 @@ function registerRecordTools(ctx, store) {
|
|
|
12919
14104
|
if (only !== null) return detailOfCall(only, maxBytes);
|
|
12920
14105
|
}
|
|
12921
14106
|
}
|
|
12922
|
-
const candidates = pickAllOf(captureEntriesOf(
|
|
14107
|
+
const candidates = pickAllOf(captureEntriesOf(captureLinesWithKeyOf(file, key)), key);
|
|
12923
14108
|
const fromCapture = pickOf(candidates, key, at);
|
|
12924
14109
|
if (fromCapture !== null) return detailOfEntry(fromCapture, maxBytes, candidates.length);
|
|
12925
14110
|
const over = held === null || held.phase === "finished";
|
|
12926
|
-
const livePage = page !== null || over ? page : recordPageOf((await actBrowser(
|
|
14111
|
+
const livePage = page !== null || over ? page : recordPageOf((await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_READ_SCRIPT])).payload);
|
|
12927
14112
|
const call = livePage === null ? null : hookCallOf(livePage, key);
|
|
12928
14113
|
if (call !== null) return detailOfCall(call, maxBytes);
|
|
12929
14114
|
return plainJson({
|
|
@@ -12963,8 +14148,8 @@ function registerRecordTools(ctx, store) {
|
|
|
12963
14148
|
},
|
|
12964
14149
|
output: OUTPUT$3,
|
|
12965
14150
|
async execute(_raw, exec) {
|
|
12966
|
-
const
|
|
12967
|
-
if (
|
|
14151
|
+
const seam = seamOf(ctx);
|
|
14152
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,收不了工");
|
|
12968
14153
|
const sessionId = sessionIdOf(exec) ?? "";
|
|
12969
14154
|
const held = store.live(Date.now());
|
|
12970
14155
|
if (held === null) throw new Error(recordMissingText());
|
|
@@ -12973,14 +14158,14 @@ function registerRecordTools(ctx, store) {
|
|
|
12973
14158
|
code: "not-owner"
|
|
12974
14159
|
}, held);
|
|
12975
14160
|
const command = cliCommandOf(ctx);
|
|
12976
|
-
await actBrowser(
|
|
12977
|
-
const page = recordPageOf((await actBrowser(
|
|
12978
|
-
const bundle = bundleOf(
|
|
14161
|
+
await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_ARM_SCRIPT]);
|
|
14162
|
+
const page = recordPageOf((await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_STOP_SCRIPT])).payload);
|
|
14163
|
+
const bundle = bundleOf(captureReadOf(held.captureFile), page, held);
|
|
12979
14164
|
const hookCaught = page.calls.length;
|
|
12980
14165
|
const noBody = bodyMissingOf(bundle.merged);
|
|
12981
|
-
const kept =
|
|
14166
|
+
const kept = keepCaptureOf(held.captureFile);
|
|
12982
14167
|
const cleaned = await releaseQuietly({
|
|
12983
|
-
|
|
14168
|
+
seam,
|
|
12984
14169
|
command,
|
|
12985
14170
|
session: BROWSER_RECORD_SESSION,
|
|
12986
14171
|
url: held.url,
|
|
@@ -13277,11 +14462,11 @@ async function settle(deps, spec, options, change) {
|
|
|
13277
14462
|
};
|
|
13278
14463
|
}
|
|
13279
14464
|
async function installRouted(deps) {
|
|
13280
|
-
const { spec, options, registry,
|
|
14465
|
+
const { spec, options, registry, seam } = deps;
|
|
13281
14466
|
const supply = deps.supply ?? requireSupply();
|
|
13282
14467
|
const route = await supply.packageRouteOf({
|
|
13283
14468
|
spec,
|
|
13284
|
-
...
|
|
14469
|
+
...seam === void 0 ? {} : { seam },
|
|
13285
14470
|
...registry === void 0 ? {} : { registry }
|
|
13286
14471
|
});
|
|
13287
14472
|
if (route === void 0 || route.kind === "dsh-bundle") return installDshBundle(deps, spec, options);
|
|
@@ -13491,15 +14676,15 @@ async function updateModuleBundle(unit, deps) {
|
|
|
13491
14676
|
pkg
|
|
13492
14677
|
};
|
|
13493
14678
|
const registry = read.kind === "module" ? read.registry : void 0;
|
|
13494
|
-
const latest = deps.
|
|
13495
|
-
|
|
14679
|
+
const latest = deps.seam === void 0 || registry === void 0 ? void 0 : await supply.latestVersion({
|
|
14680
|
+
seam: deps.seam,
|
|
13496
14681
|
name: pkg,
|
|
13497
14682
|
registry,
|
|
13498
14683
|
force: true
|
|
13499
14684
|
});
|
|
13500
|
-
if (latest !== void 0 && deps.
|
|
14685
|
+
if (latest !== void 0 && deps.seam !== void 0 && registry !== void 0) {
|
|
13501
14686
|
const declared = await supply.registryDeclarationOf({
|
|
13502
|
-
|
|
14687
|
+
seam: deps.seam,
|
|
13503
14688
|
name: pkg,
|
|
13504
14689
|
registry,
|
|
13505
14690
|
force: true
|
|
@@ -13701,17 +14886,17 @@ async function updateWorkbenchOnce(deps) {
|
|
|
13701
14886
|
const read = deps.workbench?.(target);
|
|
13702
14887
|
if (read === void 0) throw new Error(`读不到 profile 里装的 ${target} —— 没法判断装的是哪一份,更新做不了。`);
|
|
13703
14888
|
if (read.local === true) throw new Error("这份工作台是从源码目录装进来的(开发副本),不从源上更新。");
|
|
13704
|
-
const
|
|
14889
|
+
const seam = deps.seam;
|
|
13705
14890
|
const registry = read.registry ?? await deps.profileRegistry?.();
|
|
13706
|
-
const askedLatest =
|
|
13707
|
-
|
|
14891
|
+
const askedLatest = seam === void 0 || registry === void 0 ? void 0 : await supply.latestVersion({
|
|
14892
|
+
seam,
|
|
13708
14893
|
name: read.pkg,
|
|
13709
14894
|
registry,
|
|
13710
14895
|
force: true
|
|
13711
14896
|
});
|
|
13712
|
-
if (
|
|
14897
|
+
if (seam !== void 0 && registry !== void 0 && askedLatest !== void 0) {
|
|
13713
14898
|
const missing = await supply.missingReleaseParts({
|
|
13714
|
-
|
|
14899
|
+
seam,
|
|
13715
14900
|
name: read.pkg,
|
|
13716
14901
|
registry,
|
|
13717
14902
|
version: askedLatest,
|
|
@@ -13774,25 +14959,29 @@ async function migrateWorkbench(input) {
|
|
|
13774
14959
|
}
|
|
13775
14960
|
//#endregion
|
|
13776
14961
|
//#region src/modules/panels/model/market-follow-up.ts
|
|
14962
|
+
var MIGRATION_TIMEOUT_MS = 12e4;
|
|
13777
14963
|
function createWorkbenchFollowUps(deps) {
|
|
13778
14964
|
const runMigration = deps.runMigration ?? (async (input) => {
|
|
13779
|
-
const
|
|
13780
|
-
|
|
13781
|
-
|
|
13782
|
-
|
|
13783
|
-
|
|
14965
|
+
const seam = deps.seam;
|
|
14966
|
+
if (seam === void 0) throw new Error("没有执行缝(shell 服务不可用),迁移跑不起来。");
|
|
14967
|
+
const outcome = await seam.run({
|
|
14968
|
+
argv: [
|
|
14969
|
+
process.execPath,
|
|
14970
|
+
input.script,
|
|
14971
|
+
"--migrate",
|
|
14972
|
+
"--force"
|
|
14973
|
+
],
|
|
14974
|
+
intent: { kind: "host" },
|
|
13784
14975
|
env: {
|
|
13785
|
-
...process.env,
|
|
13786
14976
|
ELECTRON_RUN_AS_NODE: "1",
|
|
13787
14977
|
...input.env
|
|
13788
14978
|
},
|
|
13789
|
-
|
|
13790
|
-
timeout: 12e4
|
|
14979
|
+
timeoutMs: MIGRATION_TIMEOUT_MS
|
|
13791
14980
|
});
|
|
13792
14981
|
return {
|
|
13793
|
-
exitCode:
|
|
13794
|
-
stdout:
|
|
13795
|
-
stderr:
|
|
14982
|
+
exitCode: outcome.exitCode ?? 1,
|
|
14983
|
+
stdout: outcome.stdout,
|
|
14984
|
+
stderr: outcome.stderr
|
|
13796
14985
|
};
|
|
13797
14986
|
});
|
|
13798
14987
|
function homeAndProfile() {
|
|
@@ -13849,7 +15038,7 @@ function pluginSweptText(pkg) {
|
|
|
13849
15038
|
return `清掉了 ${pkg}:先前要它的都放手了,也没别人在用,文件跟着删了。`;
|
|
13850
15039
|
}
|
|
13851
15040
|
function createPluginActions(options) {
|
|
13852
|
-
const { ledger,
|
|
15041
|
+
const { ledger, seam, command, runAll, logger } = options;
|
|
13853
15042
|
const supply = options.supply ?? requireSupply();
|
|
13854
15043
|
const pluginFilesOf = () => {
|
|
13855
15044
|
const dir = supply.vendorPluginsDir?.();
|
|
@@ -13911,16 +15100,16 @@ function createPluginActions(options) {
|
|
|
13911
15100
|
};
|
|
13912
15101
|
}
|
|
13913
15102
|
async function removeFiles(plugin) {
|
|
13914
|
-
if (
|
|
15103
|
+
if (seam === void 0) throw new Error("没有执行缝(shell 服务不可用),删不掉插件文件。");
|
|
13915
15104
|
await runAll([{ line: `${quoteShellArg(command)} plugin uninstall ${quoteShellArg(plugin)}` }]);
|
|
13916
15105
|
}
|
|
13917
15106
|
return {
|
|
13918
15107
|
async update(pkg) {
|
|
13919
15108
|
const { entry } = requireHeld(pkg);
|
|
13920
15109
|
if (entry.local === true || entry.registry === void 0) throw new Error(`「${entry.package}」是从本地装的,没有地方查新版。`);
|
|
13921
|
-
if (
|
|
15110
|
+
if (seam === void 0) throw new Error("没有执行缝(shell 服务不可用),更新做不了。");
|
|
13922
15111
|
const latest = await supply.latestVersion({
|
|
13923
|
-
|
|
15112
|
+
seam,
|
|
13924
15113
|
name: entry.package,
|
|
13925
15114
|
registry: entry.registry,
|
|
13926
15115
|
force: true
|
|
@@ -14060,7 +15249,7 @@ function restartLogLine(change) {
|
|
|
14060
15249
|
//#endregion
|
|
14061
15250
|
//#region src/modules/panels/model/market.ts
|
|
14062
15251
|
function createMarketService(options) {
|
|
14063
|
-
const { manifests, manifestsNow, probe,
|
|
15252
|
+
const { manifests, manifestsNow, probe, seam, logger, supply: supplyOption, onInstalled, onUninstalled, isReleased, bundles, installed, modules, workbench, registries, profileDir, ledger, references, dshHome, profileName } = options;
|
|
14064
15253
|
const supply = supplyOption ?? requireSupply();
|
|
14065
15254
|
const command = options.command;
|
|
14066
15255
|
const catalog = () => manifestsNow?.() ?? manifests;
|
|
@@ -14098,13 +15287,17 @@ function createMarketService(options) {
|
|
|
14098
15287
|
};
|
|
14099
15288
|
}
|
|
14100
15289
|
async function run(line, optional = false) {
|
|
14101
|
-
if (
|
|
15290
|
+
if (seam === void 0) throw new Error("没有执行缝(shell 服务不可用),装 / 卸做不了。");
|
|
14102
15291
|
const blocked = installWriteProblem({
|
|
14103
15292
|
command: line,
|
|
14104
15293
|
fallback: supply.modules.isolatedRoot()
|
|
14105
15294
|
});
|
|
14106
15295
|
if (blocked !== void 0) throw installError(blocked);
|
|
14107
|
-
const result = await
|
|
15296
|
+
const result = await seam.run({
|
|
15297
|
+
command: line,
|
|
15298
|
+
intent: productWrite(),
|
|
15299
|
+
timeoutMs: INSTALL_TIMEOUT_MS
|
|
15300
|
+
});
|
|
14108
15301
|
if (result.exitCode !== 0 && !optional) throw installError(diagnoseInstallFailure({
|
|
14109
15302
|
command: line,
|
|
14110
15303
|
exitCode: result.exitCode,
|
|
@@ -14214,7 +15407,7 @@ function createMarketService(options) {
|
|
|
14214
15407
|
registryFix,
|
|
14215
15408
|
...logger === void 0 ? {} : { logger }
|
|
14216
15409
|
}),
|
|
14217
|
-
...
|
|
15410
|
+
...seam === void 0 ? {} : { seam },
|
|
14218
15411
|
...installed === void 0 ? {} : { installed },
|
|
14219
15412
|
...ledger === void 0 ? {} : { ledger },
|
|
14220
15413
|
...logger === void 0 ? {} : { logger }
|
|
@@ -14222,7 +15415,7 @@ function createMarketService(options) {
|
|
|
14222
15415
|
}
|
|
14223
15416
|
const plugins = createPluginActions({
|
|
14224
15417
|
...ledger === void 0 ? {} : { ledger },
|
|
14225
|
-
...
|
|
15418
|
+
...seam === void 0 ? {} : { seam },
|
|
14226
15419
|
command,
|
|
14227
15420
|
runAll,
|
|
14228
15421
|
claimedBy: (plugin) => pluginUsersOf(plugin),
|
|
@@ -14377,7 +15570,7 @@ function createMarketService(options) {
|
|
|
14377
15570
|
supply,
|
|
14378
15571
|
modules,
|
|
14379
15572
|
installed,
|
|
14380
|
-
|
|
15573
|
+
seam,
|
|
14381
15574
|
manager,
|
|
14382
15575
|
...profile === void 0 ? {} : {
|
|
14383
15576
|
profile,
|
|
@@ -14399,7 +15592,7 @@ function createMarketService(options) {
|
|
|
14399
15592
|
supply,
|
|
14400
15593
|
asked: pkg,
|
|
14401
15594
|
...workbench === void 0 ? {} : { workbench },
|
|
14402
|
-
...
|
|
15595
|
+
...seam === void 0 ? {} : { seam },
|
|
14403
15596
|
...installed === void 0 ? {} : { installed },
|
|
14404
15597
|
...dshHome === void 0 ? {} : { dshHome },
|
|
14405
15598
|
...profileName === void 0 ? {} : { profileName },
|
|
@@ -14428,6 +15621,7 @@ function createMarketService(options) {
|
|
|
14428
15621
|
dshHome,
|
|
14429
15622
|
profileName,
|
|
14430
15623
|
workbench,
|
|
15624
|
+
...seam === void 0 ? {} : { seam },
|
|
14431
15625
|
...options.runMigration === void 0 ? {} : { runMigration: options.runMigration }
|
|
14432
15626
|
}),
|
|
14433
15627
|
updateDependency: updateDependencyRow$1
|
|
@@ -14436,18 +15630,11 @@ function createMarketService(options) {
|
|
|
14436
15630
|
}
|
|
14437
15631
|
//#endregion
|
|
14438
15632
|
//#region src/modules/panels/model/market-discover.ts
|
|
14439
|
-
var SANDBOX_BLOCK = [
|
|
14440
|
-
"- id: sandbox-policy",
|
|
14441
|
-
" config:",
|
|
14442
|
-
" mode: !!js process.env.DSH_PERMISSION_MODE ?? 'workspace-write'",
|
|
14443
|
-
" workspaceRoot: !!js dshHomePath('workbench')"
|
|
14444
|
-
].join("\n");
|
|
14445
15633
|
function explainProblem(problem, profilePath) {
|
|
14446
15634
|
if (problem.kind !== "sandbox" || profilePath === void 0 || profilePath === "") return problem;
|
|
14447
|
-
const file = join(profilePath, "cordis.patch.yml");
|
|
14448
15635
|
return {
|
|
14449
15636
|
...problem,
|
|
14450
|
-
detail:
|
|
15637
|
+
detail: `被系统拦住了:工作台按 $DSH_HOME 声明的可写根没盖住它的缓存目录,这条命令被沙箱挡下了。\n这是工作台自己的问题,不用改 profile —— 把这一屏和 $DSH_HOME 报给工作台维护者,或先在工作台外面手动加这个源(${profilePath})。`
|
|
14451
15638
|
};
|
|
14452
15639
|
}
|
|
14453
15640
|
var DEFAULT_PEER_TTL_MS = 6e5;
|
|
@@ -14507,7 +15694,7 @@ function createMarketDiscover(options) {
|
|
|
14507
15694
|
const git = version.source;
|
|
14508
15695
|
if (git.type !== "git") return void 0;
|
|
14509
15696
|
if (normalizedRepo(git.url) !== normalizedRepo(source.url)) return void 0;
|
|
14510
|
-
if (options.
|
|
15697
|
+
if (options.seam === void 0) return void 0;
|
|
14511
15698
|
const key = `${git.commit}|${git.path ?? ""}`;
|
|
14512
15699
|
const cached = peerCache.get(key);
|
|
14513
15700
|
if (cached !== void 0 && now() - cached.at < peerTtlMs) return cached.peer;
|
|
@@ -14515,7 +15702,12 @@ function createMarketDiscover(options) {
|
|
|
14515
15702
|
const directory = (git.path ?? "").replace(/^\/+|\/+$/g, "");
|
|
14516
15703
|
const file = directory === "" ? "package.json" : `${directory}/package.json`;
|
|
14517
15704
|
try {
|
|
14518
|
-
const result = await
|
|
15705
|
+
const result = await options.seam.run({
|
|
15706
|
+
command: `git -C ${quoteShellArg(repo)} show ${git.commit}:${quoteShellArg(file)}`,
|
|
15707
|
+
intent: noWrite(),
|
|
15708
|
+
retry: "safe",
|
|
15709
|
+
timeoutMs
|
|
15710
|
+
});
|
|
14519
15711
|
if (result.exitCode !== 0) throw new Error(result.stderr.trim());
|
|
14520
15712
|
const peer = JSON.parse(result.stdout).peerDependencies?.[CORE_PEER];
|
|
14521
15713
|
peerCache.set(key, {
|
|
@@ -14909,7 +16101,7 @@ function repoOf(root, source) {
|
|
|
14909
16101
|
return join(directoryOf(root, source), "repo.git");
|
|
14910
16102
|
}
|
|
14911
16103
|
function writeMeta(path, meta) {
|
|
14912
|
-
return
|
|
16104
|
+
return writeJsonFileAsync(path, meta);
|
|
14913
16105
|
}
|
|
14914
16106
|
function readMeta(path) {
|
|
14915
16107
|
try {
|
|
@@ -15045,7 +16237,7 @@ function createMarketIndexReader(options) {
|
|
|
15045
16237
|
};
|
|
15046
16238
|
const fetchedAt = now();
|
|
15047
16239
|
mkdirSync(directoryOf(options.root, source), { recursive: true });
|
|
15048
|
-
await
|
|
16240
|
+
await writeTextFileAsync(indexFileOf(options.root, source), text);
|
|
15049
16241
|
await writeMeta(metaFileOf(options.root, source), { fetchedAt });
|
|
15050
16242
|
return {
|
|
15051
16243
|
ok: true,
|
|
@@ -15196,7 +16388,7 @@ function createMarketIndexReader(options) {
|
|
|
15196
16388
|
const entries = rows.filter((one) => one.type === "blob" && one.file !== path && one.file.startsWith(`${path}/`) && one.file.endsWith(".json"));
|
|
15197
16389
|
const finish = async (index, text) => {
|
|
15198
16390
|
const fetchedAt = now();
|
|
15199
|
-
await
|
|
16391
|
+
await writeTextFileAsync(indexFileOf(options.root, source), text);
|
|
15200
16392
|
await writeMeta(metaFileOf(options.root, source), {
|
|
15201
16393
|
fetchedAt,
|
|
15202
16394
|
commit
|
|
@@ -15472,7 +16664,7 @@ function readPayload(path, logger) {
|
|
|
15472
16664
|
}
|
|
15473
16665
|
async function writePayload(path, payload) {
|
|
15474
16666
|
mkdirSync(dirname(path), { recursive: true });
|
|
15475
|
-
await
|
|
16667
|
+
await writeJsonFileAsync(path, payload);
|
|
15476
16668
|
}
|
|
15477
16669
|
function upsert(sources, source) {
|
|
15478
16670
|
const found = sources.findIndex((item) => item.id === source.id);
|
|
@@ -15660,9 +16852,7 @@ function createMarketSourceInitializer(options) {
|
|
|
15660
16852
|
const migrated = marketEntryFilesOfLegacy(readFileSync(legacyFile, "utf8"), indexPath);
|
|
15661
16853
|
if (migrated !== void 0) {
|
|
15662
16854
|
for (const file of migrated) {
|
|
15663
|
-
|
|
15664
|
-
mkdirSync(dirname(target), { recursive: true });
|
|
15665
|
-
writeFileSync(target, file.text);
|
|
16855
|
+
writeTextFile(join(workdir, ...file.path.split("/")), file.text);
|
|
15666
16856
|
written.push(file.path);
|
|
15667
16857
|
}
|
|
15668
16858
|
rmSync(legacyFile);
|
|
@@ -15671,12 +16861,12 @@ function createMarketSourceInitializer(options) {
|
|
|
15671
16861
|
}
|
|
15672
16862
|
if (!existsSync(indexMarker)) {
|
|
15673
16863
|
mkdirSync(indexDir, { recursive: true });
|
|
15674
|
-
|
|
16864
|
+
writeTextFile(indexMarker, MARKET_ENTRIES_README_LINES.join("\n"));
|
|
15675
16865
|
written.push(`${indexPath}/README.md`);
|
|
15676
16866
|
}
|
|
15677
16867
|
const readmeFile = join(workdir, "README.md");
|
|
15678
16868
|
if (!existsSync(readmeFile)) {
|
|
15679
|
-
|
|
16869
|
+
writeTextFile(readmeFile, MARKET_INDEX_README_LINES.join("\n"));
|
|
15680
16870
|
written.push("README.md");
|
|
15681
16871
|
}
|
|
15682
16872
|
}
|
|
@@ -16710,12 +17900,12 @@ var INSTALLED_PLUGINS_ROUTE = "panels/plugins";
|
|
|
16710
17900
|
//#endregion
|
|
16711
17901
|
//#region src/modules/panels/model/plugin-section.ts
|
|
16712
17902
|
async function installedPlugins(options) {
|
|
16713
|
-
const { ledger,
|
|
17903
|
+
const { ledger, seam, force = false, check = true } = options;
|
|
16714
17904
|
const supply = options.supply ?? requireSupply();
|
|
16715
17905
|
const rows = [];
|
|
16716
17906
|
for (const entry of ledger.list()) {
|
|
16717
|
-
const release =
|
|
16718
|
-
|
|
17907
|
+
const release = seam === void 0 || entry.local === true || entry.registry === void 0 ? void 0 : await supply.probeRelease({
|
|
17908
|
+
seam,
|
|
16719
17909
|
package: entry.package,
|
|
16720
17910
|
registry: entry.registry,
|
|
16721
17911
|
force,
|
|
@@ -16742,7 +17932,7 @@ function pluginRoutes(options) {
|
|
|
16742
17932
|
ok: true,
|
|
16743
17933
|
plugins: await installedPlugins({
|
|
16744
17934
|
ledger: options.ledger,
|
|
16745
|
-
...options.
|
|
17935
|
+
...options.seam === void 0 ? {} : { seam: options.seam },
|
|
16746
17936
|
...options.supply === void 0 ? {} : { supply: options.supply },
|
|
16747
17937
|
force: param(search, "force") === "1",
|
|
16748
17938
|
check: param(search, "check") !== "0"
|
|
@@ -17010,13 +18200,13 @@ var OWNED = [{
|
|
|
17010
18200
|
const record = {
|
|
17011
18201
|
...createRecordStore(),
|
|
17012
18202
|
release: (state) => {
|
|
17013
|
-
const
|
|
17014
|
-
if (
|
|
18203
|
+
const seamNow = seamOf(ctx);
|
|
18204
|
+
if (seamNow === void 0) return Promise.resolve({
|
|
17015
18205
|
tab: false,
|
|
17016
18206
|
capture: false
|
|
17017
18207
|
});
|
|
17018
18208
|
return releaseQuietly({
|
|
17019
|
-
|
|
18209
|
+
seam: seamNow,
|
|
17020
18210
|
command: cliCommandOf(ctx),
|
|
17021
18211
|
session: BROWSER_RECORD_SESSION,
|
|
17022
18212
|
url: state.url,
|
|
@@ -17026,7 +18216,7 @@ var OWNED = [{
|
|
|
17026
18216
|
};
|
|
17027
18217
|
ctx.provide(DIY_RECORD_SERVICE, record);
|
|
17028
18218
|
const profileDir = supply.profileDirOf(ctx);
|
|
17029
|
-
const
|
|
18219
|
+
const seam = seamOf(ctx);
|
|
17030
18220
|
const moduleManifests = () => {
|
|
17031
18221
|
const list = manifests();
|
|
17032
18222
|
if (installable === void 0) return list.filter((manifest) => supplyNeedsOf(manifest).length > 0);
|
|
@@ -17038,7 +18228,7 @@ var OWNED = [{
|
|
|
17038
18228
|
manifests: moduleManifests(),
|
|
17039
18229
|
manifestsNow: moduleManifests,
|
|
17040
18230
|
...probe === void 0 ? {} : { probe },
|
|
17041
|
-
|
|
18231
|
+
seam,
|
|
17042
18232
|
command: cliCommandOf(ctx),
|
|
17043
18233
|
supply,
|
|
17044
18234
|
bundles,
|
|
@@ -17099,7 +18289,7 @@ var OWNED = [{
|
|
|
17099
18289
|
return;
|
|
17100
18290
|
}
|
|
17101
18291
|
},
|
|
17102
|
-
|
|
18292
|
+
seam,
|
|
17103
18293
|
...logger === void 0 ? {} : { logger }
|
|
17104
18294
|
});
|
|
17105
18295
|
return {
|
|
@@ -17108,7 +18298,7 @@ var OWNED = [{
|
|
|
17108
18298
|
...pluginRoutes({
|
|
17109
18299
|
ledger,
|
|
17110
18300
|
supply,
|
|
17111
|
-
...
|
|
18301
|
+
...seam === void 0 ? {} : { seam }
|
|
17112
18302
|
}),
|
|
17113
18303
|
...diyDebugRoutes(debug),
|
|
17114
18304
|
...diyRecordRoutes(record),
|
|
@@ -17393,6 +18583,17 @@ function declaredEnvOf(manifest) {
|
|
|
17393
18583
|
for (const need of manifest.needs ?? []) if (need.kind === "env") out[need.name] = process.env[need.name];
|
|
17394
18584
|
return out;
|
|
17395
18585
|
}
|
|
18586
|
+
function unitSeamOf(seam, unit, manifest) {
|
|
18587
|
+
const declared = manifest.host.writes ?? [];
|
|
18588
|
+
if (seam === void 0 || declared.length === 0) return void 0;
|
|
18589
|
+
return createUnitSeam({
|
|
18590
|
+
seam,
|
|
18591
|
+
unit,
|
|
18592
|
+
runtimeRoot: workbenchRoot(),
|
|
18593
|
+
declared,
|
|
18594
|
+
realHome: homedir()
|
|
18595
|
+
});
|
|
18596
|
+
}
|
|
17396
18597
|
function mountUnits(units, deps) {
|
|
17397
18598
|
const store = deps.store ?? createWorkspaceStore({ root: null });
|
|
17398
18599
|
const missingData = async () => ({
|
|
@@ -17447,6 +18648,7 @@ function mountUnits(units, deps) {
|
|
|
17447
18648
|
});
|
|
17448
18649
|
let result;
|
|
17449
18650
|
try {
|
|
18651
|
+
const seam = unitSeamOf(deps.seam, unit, host.manifest);
|
|
17450
18652
|
result = host.face.mount({
|
|
17451
18653
|
manifest: host.manifest,
|
|
17452
18654
|
ctx: mountCtx,
|
|
@@ -17456,6 +18658,7 @@ function mountUnits(units, deps) {
|
|
|
17456
18658
|
...deps.installable === void 0 ? {} : { installable: deps.installable },
|
|
17457
18659
|
data,
|
|
17458
18660
|
...hostKit,
|
|
18661
|
+
...seam === void 0 ? {} : { seam },
|
|
17459
18662
|
env: declaredEnvOf(host.manifest),
|
|
17460
18663
|
...deps.libs === void 0 ? {} : { libs: deps.libs },
|
|
17461
18664
|
...deps.git === void 0 ? {} : { git: deps.git },
|
|
@@ -17608,7 +18811,7 @@ async function releaseHeld(store, held) {
|
|
|
17608
18811
|
}
|
|
17609
18812
|
}
|
|
17610
18813
|
function recordActResource(deps) {
|
|
17611
|
-
const now = () => deps.now?.() ??
|
|
18814
|
+
const now$1 = () => deps.now?.() ?? now();
|
|
17612
18815
|
return {
|
|
17613
18816
|
resource: "act",
|
|
17614
18817
|
async handle(payload) {
|
|
@@ -17618,7 +18821,7 @@ function recordActResource(deps) {
|
|
|
17618
18821
|
text: "不认识的意图(只认 peek / halt / stop)"
|
|
17619
18822
|
};
|
|
17620
18823
|
const store = deps.store();
|
|
17621
|
-
const at = now();
|
|
18824
|
+
const at = now$1();
|
|
17622
18825
|
const held = store?.live(at) ?? null;
|
|
17623
18826
|
if (store === void 0 || held === null) return {
|
|
17624
18827
|
ok: false,
|
|
@@ -17630,7 +18833,7 @@ function recordActResource(deps) {
|
|
|
17630
18833
|
intent,
|
|
17631
18834
|
delivered: deliverDebugNotice(deps.agents(), held.sessionId, notice.message, NOTICE_MODE[intent]),
|
|
17632
18835
|
summary: notice.summary,
|
|
17633
|
-
state: store.live(now())
|
|
18836
|
+
state: store.live(now$1())
|
|
17634
18837
|
};
|
|
17635
18838
|
const cleaned = await releaseHeld(store, held);
|
|
17636
18839
|
const move = store.stop(held.sessionId, at);
|
|
@@ -17641,7 +18844,7 @@ function recordActResource(deps) {
|
|
|
17641
18844
|
summary: notice.summary,
|
|
17642
18845
|
cleaned,
|
|
17643
18846
|
refused: move.ok ? "" : move.code,
|
|
17644
|
-
state: store.live(now())
|
|
18847
|
+
state: store.live(now$1())
|
|
17645
18848
|
};
|
|
17646
18849
|
}
|
|
17647
18850
|
};
|
|
@@ -18295,18 +19498,9 @@ function notifyLongTaskStop(agents, unit, change) {
|
|
|
18295
19498
|
//#region src/app/host.ts
|
|
18296
19499
|
var name = "dsh-workbench";
|
|
18297
19500
|
var inject = ["webServer"];
|
|
18298
|
-
function shippedAdapterSetSpec(moduleUrl) {
|
|
18299
|
-
const spec = JSON.parse(readFileSync(new URL("../../package.json", moduleUrl), "utf8")).workbench?.adapterSet;
|
|
18300
|
-
if (typeof spec !== "string" || spec.trim() === "") throw new Error("package.json 没写 workbench.adapterSet:外壳不知道该装哪份适配器集合");
|
|
18301
|
-
return spec.trim();
|
|
18302
|
-
}
|
|
18303
|
-
async function shippedAdapterSet(ctx) {
|
|
18304
|
-
const spec = shippedAdapterSetSpec(import.meta.url);
|
|
18305
|
-
const factory = (await import(spec)).createAdapterSet;
|
|
18306
|
-
if (typeof factory !== "function") throw new Error(`随包的适配器集合 ${spec} 没导出 createAdapterSet(ctx)`);
|
|
18307
|
-
return await factory(ctx);
|
|
18308
|
-
}
|
|
18309
19501
|
async function apply(ctx, config) {
|
|
19502
|
+
const seam = provideWorkbenchSeam(ctx);
|
|
19503
|
+
if (seam !== void 0) provideGitProcessSeam(seam);
|
|
18310
19504
|
const facts = createFactLog();
|
|
18311
19505
|
const values = createValueHandoff();
|
|
18312
19506
|
const panelState = createPanelStateStore();
|
|
@@ -18317,13 +19511,11 @@ async function apply(ctx, config) {
|
|
|
18317
19511
|
corePackageUrl: import.meta.url,
|
|
18318
19512
|
log: (message) => warn(ctx, message)
|
|
18319
19513
|
});
|
|
18320
|
-
adapters.runtime.warnUnwritableRoot(ctx);
|
|
18321
19514
|
if (loaded.ok) ctx.logger?.info?.(`[dsh-workbench] 适配器集合:${adapters.id}@${adapters.version}(${loaded.spec})`);
|
|
18322
19515
|
else if (loaded.failure !== "not-configured") ctx.logger?.warn?.(`[dsh-workbench] 适配器集合没生效(${loaded.failure}):${loaded.detail};先用默认集合`);
|
|
18323
19516
|
ctx.provide(WORKBENCH_ADAPTERS, adapters);
|
|
18324
19517
|
const runtimeCommands = adapters.runtime.commands(ctx);
|
|
18325
19518
|
const libRunners = adapters.libs === void 0 ? void 0 : withLibRunners(adapters.libs, adapters.runtime.runners(ctx, adapters.data), ctx.logger);
|
|
18326
|
-
const shell = ctx.get("shell");
|
|
18327
19519
|
const routes = createHostRoutes({
|
|
18328
19520
|
ctx,
|
|
18329
19521
|
facts,
|
|
@@ -18411,11 +19603,11 @@ async function apply(ctx, config) {
|
|
|
18411
19603
|
});
|
|
18412
19604
|
const releasedOf = (unit) => ctx.get("workbenchPanels")?.isReleased(unit) ?? false;
|
|
18413
19605
|
const probeOne = async (unit, force = false) => {
|
|
18414
|
-
if (
|
|
19606
|
+
if (seam === void 0) return void 0;
|
|
18415
19607
|
const manifest = manifestOf(unit);
|
|
18416
19608
|
if (manifest === void 0 || !hasEnvNeeds(manifest)) return void 0;
|
|
18417
19609
|
const common = {
|
|
18418
|
-
|
|
19610
|
+
seam,
|
|
18419
19611
|
runtimeCommands,
|
|
18420
19612
|
force
|
|
18421
19613
|
};
|
|
@@ -18435,7 +19627,7 @@ async function apply(ctx, config) {
|
|
|
18435
19627
|
deniedTools: (context) => deniedNow(context),
|
|
18436
19628
|
logger: ctx.logger
|
|
18437
19629
|
});
|
|
18438
|
-
const pluginTools =
|
|
19630
|
+
const pluginTools = seam === void 0 ? void 0 : adapters.plugins.createUnits({
|
|
18439
19631
|
registry: units,
|
|
18440
19632
|
rows: () => ctx.get("workbenchPanels")?.listPlugins(null) ?? [],
|
|
18441
19633
|
declared: (plugin) => adapters.probe.declaredPluginCommands(profileDir).get(plugin) ?? /* @__PURE__ */ new Set(),
|
|
@@ -18510,6 +19702,7 @@ async function apply(ctx, config) {
|
|
|
18510
19702
|
probe: (unit, options) => probeOne(unit, options?.force ?? false),
|
|
18511
19703
|
values,
|
|
18512
19704
|
surface,
|
|
19705
|
+
...seam === void 0 ? {} : { seam },
|
|
18513
19706
|
longTasks: {
|
|
18514
19707
|
scope: process.env["DSH_HOME"] ?? "",
|
|
18515
19708
|
bump: (names) => facts.bump([...names, LONG_TASKS_ROUTE]),
|
|
@@ -18593,12 +19786,12 @@ async function apply(ctx, config) {
|
|
|
18593
19786
|
}
|
|
18594
19787
|
rebindPost();
|
|
18595
19788
|
}
|
|
18596
|
-
if (
|
|
19789
|
+
if (seam === void 0) ctx.logger?.warn?.("[dsh-workbench] 没有 shell 服务:需要执行缝的套件会降级");
|
|
18597
19790
|
else {
|
|
18598
19791
|
adapters.probe.registerPreflight({
|
|
18599
19792
|
routes,
|
|
18600
19793
|
ctx,
|
|
18601
|
-
|
|
19794
|
+
seam,
|
|
18602
19795
|
runtimeCommands,
|
|
18603
19796
|
runtimeCapabilities: capabilities,
|
|
18604
19797
|
...runtimeCheck === void 0 ? {} : { runtimeCheck: () => runtimeCheck(ctx) },
|
|
@@ -18608,7 +19801,7 @@ async function apply(ctx, config) {
|
|
|
18608
19801
|
routes.capability = capabilityHandler(async (unit, force) => {
|
|
18609
19802
|
if (unit === void 0) {
|
|
18610
19803
|
const common = {
|
|
18611
|
-
|
|
19804
|
+
seam,
|
|
18612
19805
|
runtimeCommands,
|
|
18613
19806
|
force
|
|
18614
19807
|
};
|