@dsh-workbench/core 1.1.14-beta.1 → 1.1.14
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 +1665 -623
- 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 +3 -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 +12 -11
- 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-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, closeSync, constants, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
|
+
import path, { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
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,88 @@ 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
|
+
* `writeTextFile` 的异步版;语义与默认值相同。
|
|
786
|
+
* @param path 目标文件。
|
|
787
|
+
* @param text 文本内容。
|
|
788
|
+
* @param options 原子/目录/权限/fsync。
|
|
789
|
+
* @returns 落盘完成。
|
|
790
|
+
*/
|
|
791
|
+
async function writeTextFileAsync(path, text, options = {}) {
|
|
792
|
+
const mode = prepareOf(path, options);
|
|
793
|
+
if (options.atomic === false) {
|
|
794
|
+
writeFileSync(path, text, { mode });
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
await writeFileAtomic(path, text, atomicOptionsOf(mode, options));
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* 把二进制写进文件;语义与默认值同 `writeTextFile`。
|
|
801
|
+
* @param path 目标文件。
|
|
802
|
+
* @param bytes 二进制内容。
|
|
803
|
+
* @param options 原子/目录/权限/fsync。
|
|
804
|
+
*/
|
|
805
|
+
function writeBytesFile(path, bytes, options = {}) {
|
|
806
|
+
const mode = prepareOf(path, options);
|
|
807
|
+
if (options.atomic === false) {
|
|
808
|
+
writeFileSync(path, bytes, { mode });
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
writeFileAtomic.sync(path, Buffer.from(bytes), atomicOptionsOf(mode, options));
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* 把 JSON 写进文件:`JSON.stringify(value, null, 2)` 加末尾换行;语义与默认值同 `writeTextFile`。
|
|
815
|
+
* @param path 目标文件。
|
|
816
|
+
* @param value 可序列化的值。
|
|
817
|
+
* @param options 原子/目录/权限/fsync。
|
|
818
|
+
*/
|
|
819
|
+
function writeJsonFile(path, value, options = {}) {
|
|
820
|
+
writeTextFile(path, `${JSON.stringify(value, null, 2)}\n`, options);
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* `writeJsonFile` 的异步版;语义与默认值相同。
|
|
824
|
+
* @param path 目标文件。
|
|
825
|
+
* @param value 可序列化的值。
|
|
826
|
+
* @param options 原子/目录/权限/fsync。
|
|
827
|
+
* @returns 落盘完成。
|
|
828
|
+
*/
|
|
829
|
+
async function writeJsonFileAsync(path, value, options = {}) {
|
|
830
|
+
await writeTextFileAsync(path, `${JSON.stringify(value, null, 2)}\n`, options);
|
|
831
|
+
}
|
|
832
|
+
//#endregion
|
|
737
833
|
//#region ../shared/dist/host/installed-sources.js
|
|
738
834
|
var FILE_NAME = ".dsh-workbench-sources.json";
|
|
739
835
|
var FILE_VERSION = 1;
|
|
@@ -774,14 +870,10 @@ function packageSourceOf(profileDir, pkg) {
|
|
|
774
870
|
return readInstalledSources(profileDir)[pkg];
|
|
775
871
|
}
|
|
776
872
|
function writeInstalledSources(profileDir, sources) {
|
|
777
|
-
|
|
778
|
-
const temp = `${file}.tmp`;
|
|
779
|
-
const text = `${JSON.stringify({
|
|
873
|
+
writeTextFile(installedSourcesFile(profileDir), `${JSON.stringify({
|
|
780
874
|
version: FILE_VERSION,
|
|
781
875
|
packages: sources
|
|
782
|
-
}, null, 2)}\n
|
|
783
|
-
fs.writeFileSync(temp, text, { mode: 384 });
|
|
784
|
-
fs.renameSync(temp, file);
|
|
876
|
+
}, null, 2)}\n`, { mode: 384 });
|
|
785
877
|
}
|
|
786
878
|
function rememberPackageSource(profileDir, pkg, source) {
|
|
787
879
|
const trimmed = pkg.trim();
|
|
@@ -847,54 +939,58 @@ function refusalReason() {
|
|
|
847
939
|
return "这个包既不是工作台的模块,也不是运行时的插件,装了也用不上";
|
|
848
940
|
}
|
|
849
941
|
//#endregion
|
|
850
|
-
//#region ../shared/dist/host/
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
stderr: "",
|
|
860
|
-
truncated: false,
|
|
861
|
-
stalled: true
|
|
862
|
-
};
|
|
942
|
+
//#region ../shared/dist/host/time.js
|
|
943
|
+
dayjs.extend(utc);
|
|
944
|
+
/**
|
|
945
|
+
* 把时间戳/ISO 字符串转成毫秒;非法输入给 `NaN`(调用方据此回退)。
|
|
946
|
+
* @param value ISO 字符串、时间戳或 Date。
|
|
947
|
+
* @returns 毫秒时间戳或 `NaN`。
|
|
948
|
+
*/
|
|
949
|
+
function epochOf(value) {
|
|
950
|
+
return dayjs(value).valueOf();
|
|
863
951
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
952
|
+
/**
|
|
953
|
+
* 当前时间(毫秒)。宿主里要「现在几点」只走这里,测试可以点住 `Date.now`。
|
|
954
|
+
* @returns 毫秒时间戳。
|
|
955
|
+
*/
|
|
956
|
+
function now() {
|
|
957
|
+
return Date.now();
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* 当前时间的 ISO 字符串。
|
|
961
|
+
* @returns `new Date().toISOString()` 的等价物。
|
|
962
|
+
*/
|
|
963
|
+
function nowIso() {
|
|
964
|
+
return new Date(now()).toISOString();
|
|
965
|
+
}
|
|
966
|
+
//#endregion
|
|
967
|
+
//#region ../shared/dist/host/seam/intent.js
|
|
968
|
+
var SeamError = class extends Error {
|
|
969
|
+
code;
|
|
970
|
+
path;
|
|
971
|
+
scope;
|
|
972
|
+
constructor(code, message, extra = {}) {
|
|
973
|
+
super(message);
|
|
974
|
+
this.name = "SeamError";
|
|
975
|
+
this.code = code;
|
|
976
|
+
this.path = extra.path;
|
|
977
|
+
this.scope = extra.scope;
|
|
873
978
|
}
|
|
979
|
+
};
|
|
980
|
+
function productWrite() {
|
|
981
|
+
return { kind: "product" };
|
|
874
982
|
}
|
|
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();
|
|
983
|
+
function workspaceWrite() {
|
|
984
|
+
return { kind: "workspace" };
|
|
985
|
+
}
|
|
986
|
+
function pathsWrite(paths) {
|
|
987
|
+
return {
|
|
988
|
+
kind: "paths",
|
|
989
|
+
paths
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
function noWrite() {
|
|
993
|
+
return { kind: "none" };
|
|
898
994
|
}
|
|
899
995
|
//#endregion
|
|
900
996
|
//#region ../shared/dist/host/shell-text.js
|
|
@@ -2406,6 +2502,73 @@ function npmRegistryOf(source, env) {
|
|
|
2406
2502
|
//#region ../shared/dist/contract/protocol/config-sync.js
|
|
2407
2503
|
var CONFIG_SYNC_ROUTE = "config-sync";
|
|
2408
2504
|
//#endregion
|
|
2505
|
+
//#region ../shared/dist/host/patch-blocks.js
|
|
2506
|
+
var ID_LINE = /^- id:[ \t]*"?([^"\s#]+)/;
|
|
2507
|
+
var INSERT_LINE = /^- insert:[ \t]*$/;
|
|
2508
|
+
var NESTED_ID = /^[ \t]+-[ \t]*id:[ \t]*"?([^"\s#]+)/;
|
|
2509
|
+
var NAME_LINE = /^[ \t]*-?[ \t]*name:[ \t]*['"]?([^'"\s#]+)/;
|
|
2510
|
+
function trimmedTail(lines) {
|
|
2511
|
+
let end = lines.length;
|
|
2512
|
+
while (end > 0 && (lines[end - 1] ?? "").trim() === "") end -= 1;
|
|
2513
|
+
return lines.slice(0, end);
|
|
2514
|
+
}
|
|
2515
|
+
function matched(text, pattern) {
|
|
2516
|
+
const found = pattern.exec(text);
|
|
2517
|
+
return found === null ? void 0 : found[1];
|
|
2518
|
+
}
|
|
2519
|
+
function blockOf$1(body, index) {
|
|
2520
|
+
const lines = body.split("\n");
|
|
2521
|
+
const first = lines[0] ?? "";
|
|
2522
|
+
const id = matched(first, ID_LINE);
|
|
2523
|
+
const nested = lines.flatMap((line) => {
|
|
2524
|
+
const one = matched(line, NESTED_ID);
|
|
2525
|
+
return one === void 0 ? [] : [one];
|
|
2526
|
+
});
|
|
2527
|
+
const insert = id === void 0 && INSERT_LINE.test(first);
|
|
2528
|
+
const label = id ?? nested.join(", ");
|
|
2529
|
+
const key = id !== void 0 ? `id:${id}` : insert ? `insert:${nested.join(", ")}` : `block:${String(index)}`;
|
|
2530
|
+
const names = lines.flatMap((line) => {
|
|
2531
|
+
const one = matched(line, NAME_LINE);
|
|
2532
|
+
return one === void 0 ? [] : [one];
|
|
2533
|
+
});
|
|
2534
|
+
return {
|
|
2535
|
+
key,
|
|
2536
|
+
id: label === "" ? key : label,
|
|
2537
|
+
insert,
|
|
2538
|
+
body,
|
|
2539
|
+
names
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
function spannedBlocks(text) {
|
|
2543
|
+
const lines = text.split("\n");
|
|
2544
|
+
const starts = [];
|
|
2545
|
+
lines.forEach((line, index) => {
|
|
2546
|
+
if (/^- /.test(line) || line === "-") starts.push(index);
|
|
2547
|
+
});
|
|
2548
|
+
const blocks = [];
|
|
2549
|
+
for (let i = 0; i < starts.length; i += 1) {
|
|
2550
|
+
const from = starts[i] ?? 0;
|
|
2551
|
+
const to = i + 1 < starts.length ? starts[i + 1] ?? lines.length : lines.length;
|
|
2552
|
+
const body = trimmedTail(lines.slice(from, to)).join("\n");
|
|
2553
|
+
if (body.trim() === "") continue;
|
|
2554
|
+
blocks.push({
|
|
2555
|
+
...blockOf$1(body, blocks.length),
|
|
2556
|
+
from,
|
|
2557
|
+
to
|
|
2558
|
+
});
|
|
2559
|
+
}
|
|
2560
|
+
return blocks;
|
|
2561
|
+
}
|
|
2562
|
+
function patchBlocks(text) {
|
|
2563
|
+
return spannedBlocks(text).map(({ from: _from, to: _to, ...block }) => block);
|
|
2564
|
+
}
|
|
2565
|
+
//#endregion
|
|
2566
|
+
//#region ../shared/dist/host/patch.js
|
|
2567
|
+
/** 把 patch 文本写回原文件:原子写,保留已有权限位(新文件 0644)。 */
|
|
2568
|
+
function writePatchFile(path, text) {
|
|
2569
|
+
writeTextFile(path, text, { mode: "keep" });
|
|
2570
|
+
}
|
|
2571
|
+
//#endregion
|
|
2409
2572
|
//#region ../shared/dist/contract/protocol/profile-create.js
|
|
2410
2573
|
var PROFILE_CREATE_ROUTE = "profile-create";
|
|
2411
2574
|
//#endregion
|
|
@@ -2448,17 +2611,6 @@ function parseMarkers(text) {
|
|
|
2448
2611
|
return refs;
|
|
2449
2612
|
}
|
|
2450
2613
|
//#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
2614
|
//#region ../shared/dist/host/diy-debug.js
|
|
2463
2615
|
/**
|
|
2464
2616
|
* 把工具入参收成人话动作。
|
|
@@ -2764,6 +2916,121 @@ function createRecordStore() {
|
|
|
2764
2916
|
};
|
|
2765
2917
|
}
|
|
2766
2918
|
//#endregion
|
|
2919
|
+
//#region ../shared/dist/host/data-root.js
|
|
2920
|
+
function dshHome() {
|
|
2921
|
+
const configured = process.env.DSH_HOME?.trim();
|
|
2922
|
+
if (configured !== void 0 && configured !== "") return configured;
|
|
2923
|
+
return join(homedir(), ".dsh");
|
|
2924
|
+
}
|
|
2925
|
+
function workbenchRoot() {
|
|
2926
|
+
const configured = process.env.DSH_WORKBENCH_HOME?.trim();
|
|
2927
|
+
if (configured !== void 0 && configured !== "") return configured;
|
|
2928
|
+
return join(dshHome(), "workbench");
|
|
2929
|
+
}
|
|
2930
|
+
function workspacesRoot() {
|
|
2931
|
+
return join(workbenchRoot(), "workspaces");
|
|
2932
|
+
}
|
|
2933
|
+
//#endregion
|
|
2934
|
+
//#region ../shared/dist/host/env-policy.js
|
|
2935
|
+
/**
|
|
2936
|
+
* 把父进程环境里的字符串值拷成一层 env(丢掉 undefined)。
|
|
2937
|
+
* @param parent 父环境(通常是 `process.env`)。
|
|
2938
|
+
* @returns 可继续叠加的 env 层。
|
|
2939
|
+
*/
|
|
2940
|
+
function inheritedEnvOf(parent) {
|
|
2941
|
+
const out = {};
|
|
2942
|
+
for (const [key, value] of Object.entries(parent)) if (typeof value === "string") out[key] = value;
|
|
2943
|
+
return out;
|
|
2944
|
+
}
|
|
2945
|
+
function existingPointer(realHome, name) {
|
|
2946
|
+
const file = join(realHome, name);
|
|
2947
|
+
return existsSync(file) ? file : void 0;
|
|
2948
|
+
}
|
|
2949
|
+
/**
|
|
2950
|
+
* 产品域(运行时根)的受控 env:隔离 HOME、npm/pnpm 缓存都钉在运行时根,真 npmrc/gitconfig 只读指过去。
|
|
2951
|
+
* @param input 运行时根、可选的 HOME 与真实用户目录。
|
|
2952
|
+
* @returns 产品域 env。
|
|
2953
|
+
*/
|
|
2954
|
+
function productEnvOf(input) {
|
|
2955
|
+
const runtimeRoot = input.runtimeRoot;
|
|
2956
|
+
const realHome = input.realHome ?? homedir();
|
|
2957
|
+
const env = {
|
|
2958
|
+
HOME: input.home ?? join(runtimeRoot, "home"),
|
|
2959
|
+
DSH_HOME: dshHome(),
|
|
2960
|
+
DSH_WORKBENCH_HOME: runtimeRoot,
|
|
2961
|
+
npm_config_cache: join(runtimeRoot, "npm-cache"),
|
|
2962
|
+
NPM_CONFIG_CACHE: join(runtimeRoot, "npm-cache"),
|
|
2963
|
+
npm_config_logs_dir: join(runtimeRoot, "npm-logs"),
|
|
2964
|
+
npm_config_store_dir: join(runtimeRoot, "pnpm-store"),
|
|
2965
|
+
npm_config_cache_dir: join(runtimeRoot, "pnpm-cache"),
|
|
2966
|
+
npm_config_state_dir: join(runtimeRoot, "pnpm-state"),
|
|
2967
|
+
npm_config_update_notifier: "false",
|
|
2968
|
+
npm_config_fund: "false",
|
|
2969
|
+
NO_UPDATE_NOTIFIER: "1"
|
|
2970
|
+
};
|
|
2971
|
+
const npmrc = existingPointer(realHome, ".npmrc");
|
|
2972
|
+
if (npmrc !== void 0) env["NPM_CONFIG_USERCONFIG"] = npmrc;
|
|
2973
|
+
const gitconfig = existingPointer(realHome, ".gitconfig");
|
|
2974
|
+
if (gitconfig !== void 0) env["GIT_CONFIG_GLOBAL"] = gitconfig;
|
|
2975
|
+
return env;
|
|
2976
|
+
}
|
|
2977
|
+
/**
|
|
2978
|
+
* 模块单元的受控 env:在产品域基础上把 HOME 与 npm/pnpm 目录全钉到单元数据目录。
|
|
2979
|
+
* @param input 运行时根、单元数据目录、可选的 TRUE 用户目录。
|
|
2980
|
+
* @returns 单元 env。
|
|
2981
|
+
*/
|
|
2982
|
+
function unitEnvOf(input) {
|
|
2983
|
+
const unitRoot = input.unitRoot;
|
|
2984
|
+
return {
|
|
2985
|
+
...productEnvOf({
|
|
2986
|
+
...input,
|
|
2987
|
+
home: join(unitRoot, "home")
|
|
2988
|
+
}),
|
|
2989
|
+
npm_config_cache: join(unitRoot, "npm-cache"),
|
|
2990
|
+
NPM_CONFIG_CACHE: join(unitRoot, "npm-cache"),
|
|
2991
|
+
npm_config_logs_dir: join(unitRoot, "npm-logs"),
|
|
2992
|
+
npm_config_store_dir: join(unitRoot, "pnpm-store"),
|
|
2993
|
+
npm_config_cache_dir: join(unitRoot, "pnpm-cache"),
|
|
2994
|
+
npm_config_state_dir: join(unitRoot, "pnpm-state")
|
|
2995
|
+
};
|
|
2996
|
+
}
|
|
2997
|
+
/**
|
|
2998
|
+
* git 的非交互 env:关掉终端提示/askpass;Windows 上不给 askpass 指 POSIX 路径(那台机器上没有)。
|
|
2999
|
+
* @param platform 宿主平台。
|
|
3000
|
+
* @returns git 非交互 env。
|
|
3001
|
+
*/
|
|
3002
|
+
function promptlessGitEnvOf(platform) {
|
|
3003
|
+
const shared = {
|
|
3004
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
3005
|
+
SSH_ASKPASS_REQUIRE: "never",
|
|
3006
|
+
GCM_INTERACTIVE: "never",
|
|
3007
|
+
GIT_SSH_COMMAND: "ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new"
|
|
3008
|
+
};
|
|
3009
|
+
if (platform === "win32") return shared;
|
|
3010
|
+
return {
|
|
3011
|
+
...shared,
|
|
3012
|
+
GIT_ASKPASS: "/usr/bin/false",
|
|
3013
|
+
SSH_ASKPASS: "/usr/bin/false"
|
|
3014
|
+
};
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* 在父环境上叠加 `promptlessGitEnvOf`,得到可传给执行缝的 git env。
|
|
3018
|
+
* @param parent 父环境。
|
|
3019
|
+
* @param platform 宿主平台。
|
|
3020
|
+
* @returns 只含字符串的 env。
|
|
3021
|
+
*/
|
|
3022
|
+
function promptlessProcessEnvOf(parent, platform) {
|
|
3023
|
+
const env = {
|
|
3024
|
+
...inheritedEnvOf(parent),
|
|
3025
|
+
...promptlessGitEnvOf(platform)
|
|
3026
|
+
};
|
|
3027
|
+
if (platform === "win32") {
|
|
3028
|
+
delete env["GIT_ASKPASS"];
|
|
3029
|
+
delete env["SSH_ASKPASS"];
|
|
3030
|
+
}
|
|
3031
|
+
return env;
|
|
3032
|
+
}
|
|
3033
|
+
//#endregion
|
|
2767
3034
|
//#region ../shared/dist/host/facts.js
|
|
2768
3035
|
function createFactLog(options = {}) {
|
|
2769
3036
|
const epoch = options.epoch ?? `e${randomUUID()}`;
|
|
@@ -2938,7 +3205,7 @@ function parseCrashReport(payload) {
|
|
|
2938
3205
|
workspace,
|
|
2939
3206
|
unit,
|
|
2940
3207
|
reason,
|
|
2941
|
-
at: atOf(record["at"]) ??
|
|
3208
|
+
at: atOf(record["at"]) ?? now()
|
|
2942
3209
|
}
|
|
2943
3210
|
};
|
|
2944
3211
|
}
|
|
@@ -3459,7 +3726,7 @@ function healthHandler(ctx, runtimes) {
|
|
|
3459
3726
|
profile: dir === void 0 ? null : dir.split("/").pop(),
|
|
3460
3727
|
profileDir: dir ?? null,
|
|
3461
3728
|
pid: process.pid,
|
|
3462
|
-
now: (
|
|
3729
|
+
now: nowIso(),
|
|
3463
3730
|
...runtimes === void 0 ? {} : { runtimes }
|
|
3464
3731
|
};
|
|
3465
3732
|
};
|
|
@@ -3498,7 +3765,7 @@ function registerPanelState(ctx, store) {
|
|
|
3498
3765
|
store.cards(parsed.value);
|
|
3499
3766
|
return {
|
|
3500
3767
|
ok: true,
|
|
3501
|
-
at:
|
|
3768
|
+
at: now()
|
|
3502
3769
|
};
|
|
3503
3770
|
}
|
|
3504
3771
|
}, {
|
|
@@ -3512,7 +3779,7 @@ function registerPanelState(ctx, store) {
|
|
|
3512
3779
|
store.crash(parsed.value);
|
|
3513
3780
|
return {
|
|
3514
3781
|
ok: true,
|
|
3515
|
-
at:
|
|
3782
|
+
at: now()
|
|
3516
3783
|
};
|
|
3517
3784
|
}
|
|
3518
3785
|
}]
|
|
@@ -3543,9 +3810,9 @@ var RECENT_MS = 6e5;
|
|
|
3543
3810
|
function createLongTaskRegistry(deps) {
|
|
3544
3811
|
const runs = /* @__PURE__ */ new Map();
|
|
3545
3812
|
const controls = /* @__PURE__ */ new Map();
|
|
3546
|
-
const now = deps.now ?? (() => (
|
|
3813
|
+
const now$7 = deps.now ?? (() => nowIso());
|
|
3547
3814
|
let counter = 0;
|
|
3548
|
-
const nextId = deps.nextId ?? (() => `run-${
|
|
3815
|
+
const nextId = deps.nextId ?? (() => `run-${now()}-${counter += 1}`);
|
|
3549
3816
|
const recentMs = deps.recentMs ?? RECENT_MS;
|
|
3550
3817
|
const listeners = /* @__PURE__ */ new Set();
|
|
3551
3818
|
const live = (id) => {
|
|
@@ -3564,7 +3831,7 @@ function createLongTaskRegistry(deps) {
|
|
|
3564
3831
|
const patched = {
|
|
3565
3832
|
...current,
|
|
3566
3833
|
...next,
|
|
3567
|
-
at: now()
|
|
3834
|
+
at: now$7()
|
|
3568
3835
|
};
|
|
3569
3836
|
runs.set(id, patched);
|
|
3570
3837
|
notify({
|
|
@@ -3581,7 +3848,7 @@ function createLongTaskRegistry(deps) {
|
|
|
3581
3848
|
return null;
|
|
3582
3849
|
},
|
|
3583
3850
|
current(workspace) {
|
|
3584
|
-
const floor = epochOf(now()) - recentMs;
|
|
3851
|
+
const floor = epochOf(now$7()) - recentMs;
|
|
3585
3852
|
const newest = /* @__PURE__ */ new Map();
|
|
3586
3853
|
for (const snapshot of runs.values()) {
|
|
3587
3854
|
if (snapshot.workspace !== workspace) continue;
|
|
@@ -3599,7 +3866,7 @@ function createLongTaskRegistry(deps) {
|
|
|
3599
3866
|
const running = this.active(options.workspace, options.target);
|
|
3600
3867
|
if (running !== null) return running;
|
|
3601
3868
|
const id = nextId();
|
|
3602
|
-
const startedAt = now();
|
|
3869
|
+
const startedAt = now$7();
|
|
3603
3870
|
runs.set(id, {
|
|
3604
3871
|
id,
|
|
3605
3872
|
workspace: options.workspace,
|
|
@@ -4010,7 +4277,7 @@ function defaultSleep(ms, signal) {
|
|
|
4010
4277
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
4011
4278
|
});
|
|
4012
4279
|
}
|
|
4013
|
-
function targetOf(flow, scope) {
|
|
4280
|
+
function targetOf$1(flow, scope) {
|
|
4014
4281
|
if (flow.target === void 0) return flow.id;
|
|
4015
4282
|
const value = resolveValue(flow.target, scope);
|
|
4016
4283
|
return typeof value === "string" && value.trim() !== "" ? value.trim() : flow.id;
|
|
@@ -4148,14 +4415,14 @@ async function runOne(options) {
|
|
|
4148
4415
|
async function runFlow(options) {
|
|
4149
4416
|
const { flow, input, actions, signal, runId, workspace, onStep } = options;
|
|
4150
4417
|
const sleep = options.sleep ?? defaultSleep;
|
|
4151
|
-
const now = options.now ?? (() =>
|
|
4418
|
+
const now$6 = options.now ?? (() => now());
|
|
4152
4419
|
const steps = {};
|
|
4153
4420
|
const emit = (event) => {
|
|
4154
4421
|
try {
|
|
4155
4422
|
onStep(event);
|
|
4156
4423
|
} catch {}
|
|
4157
4424
|
};
|
|
4158
|
-
const target = targetOf(flow, {
|
|
4425
|
+
const target = targetOf$1(flow, {
|
|
4159
4426
|
in: input,
|
|
4160
4427
|
steps
|
|
4161
4428
|
});
|
|
@@ -4218,7 +4485,7 @@ async function runFlow(options) {
|
|
|
4218
4485
|
});
|
|
4219
4486
|
},
|
|
4220
4487
|
sleep,
|
|
4221
|
-
now
|
|
4488
|
+
now: now$6
|
|
4222
4489
|
});
|
|
4223
4490
|
if (nested.status === "failed") {
|
|
4224
4491
|
const failed = nested.failedStep === void 0 ? step.id : `${step.id}.${nested.failedStep}`;
|
|
@@ -4263,7 +4530,7 @@ async function runFlow(options) {
|
|
|
4263
4530
|
runId,
|
|
4264
4531
|
workspace,
|
|
4265
4532
|
sleep,
|
|
4266
|
-
now,
|
|
4533
|
+
now: now$6,
|
|
4267
4534
|
emit
|
|
4268
4535
|
}));
|
|
4269
4536
|
}
|
|
@@ -4293,7 +4560,7 @@ async function runFlow(options) {
|
|
|
4293
4560
|
runId,
|
|
4294
4561
|
workspace,
|
|
4295
4562
|
sleep,
|
|
4296
|
-
now,
|
|
4563
|
+
now: now$6,
|
|
4297
4564
|
emit
|
|
4298
4565
|
});
|
|
4299
4566
|
emit({
|
|
@@ -4373,7 +4640,8 @@ function sessionIdOf(exec) {
|
|
|
4373
4640
|
* 建一份宿主 kit;每个挂载点一份,没有模块级状态。
|
|
4374
4641
|
* @param parts.libInstallCommand 拼「装这个共用依赖」的那条命令。
|
|
4375
4642
|
* @param parts.surface 宿主平台事实;由组合根按 profile 与宿主进程算好再交进来。
|
|
4376
|
-
* @
|
|
4643
|
+
* @param parts.seam 按 unit 绑定好的执行缝门面;模块清单没声明写入域时缺省不给。
|
|
4644
|
+
* @returns flow / sessionIdOf / sessionWorkspace / libInstallCommand / surface,有门面时带上 seam。
|
|
4377
4645
|
*/
|
|
4378
4646
|
function createHostKit(parts) {
|
|
4379
4647
|
return {
|
|
@@ -4381,7 +4649,8 @@ function createHostKit(parts) {
|
|
|
4381
4649
|
sessionIdOf,
|
|
4382
4650
|
sessionWorkspace,
|
|
4383
4651
|
libInstallCommand: parts.libInstallCommand,
|
|
4384
|
-
surface: parts.surface
|
|
4652
|
+
surface: parts.surface,
|
|
4653
|
+
...parts.seam === void 0 ? {} : { seam: parts.seam }
|
|
4385
4654
|
};
|
|
4386
4655
|
}
|
|
4387
4656
|
//#endregion
|
|
@@ -4751,37 +5020,22 @@ function createValueHandoff(options = {}) {
|
|
|
4751
5020
|
const ttlMs = options.ttlMs ?? 15e3;
|
|
4752
5021
|
const held = /* @__PURE__ */ new Map();
|
|
4753
5022
|
return {
|
|
4754
|
-
hand(entry, value, now =
|
|
5023
|
+
hand(entry, value, now$4 = now()) {
|
|
4755
5024
|
held.set(handoffId(entry), {
|
|
4756
5025
|
value,
|
|
4757
|
-
at: now
|
|
5026
|
+
at: now$4
|
|
4758
5027
|
});
|
|
4759
5028
|
},
|
|
4760
|
-
take(entry, now =
|
|
5029
|
+
take(entry, now$5 = now()) {
|
|
4761
5030
|
const id = handoffId(entry);
|
|
4762
5031
|
const hit = held.get(id);
|
|
4763
5032
|
if (hit === void 0) return void 0;
|
|
4764
5033
|
held.delete(id);
|
|
4765
|
-
return now - hit.at >= ttlMs ? void 0 : hit;
|
|
5034
|
+
return now$5 - hit.at >= ttlMs ? void 0 : hit;
|
|
4766
5035
|
}
|
|
4767
5036
|
};
|
|
4768
5037
|
}
|
|
4769
5038
|
//#endregion
|
|
4770
|
-
//#region ../shared/dist/host/data-root.js
|
|
4771
|
-
function dshHome() {
|
|
4772
|
-
const configured = process.env.DSH_HOME?.trim();
|
|
4773
|
-
if (configured !== void 0 && configured !== "") return configured;
|
|
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
5039
|
//#region ../shared/dist/host/workspace-store.js
|
|
4786
5040
|
function slug(raw, max = 40) {
|
|
4787
5041
|
const cleaned = raw.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[-.]+|[-.]+$/g, "").slice(0, max);
|
|
@@ -4823,9 +5077,6 @@ function memoryStore(dir) {
|
|
|
4823
5077
|
};
|
|
4824
5078
|
}
|
|
4825
5079
|
function fileStore(dir) {
|
|
4826
|
-
const ensureFor = (file) => {
|
|
4827
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
4828
|
-
};
|
|
4829
5080
|
return {
|
|
4830
5081
|
dir,
|
|
4831
5082
|
read: (name) => {
|
|
@@ -4837,11 +5088,7 @@ function fileStore(dir) {
|
|
|
4837
5088
|
}
|
|
4838
5089
|
},
|
|
4839
5090
|
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);
|
|
5091
|
+
writeJsonFile(inside(dir, name), value);
|
|
4845
5092
|
},
|
|
4846
5093
|
remove: (name) => {
|
|
4847
5094
|
const file = inside(dir, name);
|
|
@@ -4890,6 +5137,703 @@ function createWorkspaceStore(options) {
|
|
|
4890
5137
|
};
|
|
4891
5138
|
}
|
|
4892
5139
|
//#endregion
|
|
5140
|
+
//#region ../shared/dist/host/shell.js
|
|
5141
|
+
var DEFAULT_MAX_BYTES$3 = 8388608;
|
|
5142
|
+
var GUARD_SLACK_MS = 1e3;
|
|
5143
|
+
function stalledResult() {
|
|
5144
|
+
return {
|
|
5145
|
+
exitCode: null,
|
|
5146
|
+
timedOut: true,
|
|
5147
|
+
aborted: false,
|
|
5148
|
+
stdout: "",
|
|
5149
|
+
stderr: "",
|
|
5150
|
+
truncated: false,
|
|
5151
|
+
stalled: true
|
|
5152
|
+
};
|
|
5153
|
+
}
|
|
5154
|
+
async function settleWithin(work, limitMs) {
|
|
5155
|
+
let timer;
|
|
5156
|
+
const guard = new Promise((resolve) => {
|
|
5157
|
+
timer = setTimeout(() => resolve(null), limitMs);
|
|
5158
|
+
});
|
|
5159
|
+
try {
|
|
5160
|
+
return await Promise.race([work, guard]);
|
|
5161
|
+
} finally {
|
|
5162
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
async function runShell(shell, command, options = {}) {
|
|
5166
|
+
const { workdir, timeoutMs = GIT_TIMEOUT_MS, signal, env, sandboxPolicy } = options;
|
|
5167
|
+
const spec = shell.resolve({
|
|
5168
|
+
command,
|
|
5169
|
+
...workdir === void 0 ? {} : { workdir },
|
|
5170
|
+
timeoutMs,
|
|
5171
|
+
onExpiry: "kill",
|
|
5172
|
+
stdoutMaxBytes: DEFAULT_MAX_BYTES$3,
|
|
5173
|
+
...signal === void 0 ? {} : { signal },
|
|
5174
|
+
...env === void 0 ? {} : { env },
|
|
5175
|
+
...sandboxPolicy === void 0 ? {} : { sandboxPolicy }
|
|
5176
|
+
});
|
|
5177
|
+
return await settleWithin((async () => {
|
|
5178
|
+
const result = await (await shell.execute(spec)).result();
|
|
5179
|
+
return {
|
|
5180
|
+
exitCode: result.exitCode,
|
|
5181
|
+
timedOut: result.timedOut === true,
|
|
5182
|
+
aborted: result.aborted === true,
|
|
5183
|
+
stdout: result.stdout.text,
|
|
5184
|
+
stderr: result.stderr.text,
|
|
5185
|
+
truncated: result.stdout.truncated || result.stderr.truncated,
|
|
5186
|
+
stalled: false,
|
|
5187
|
+
...result.sandbox === void 0 ? {} : { sandbox: result.sandbox }
|
|
5188
|
+
};
|
|
5189
|
+
})(), timeoutMs + GUARD_SLACK_MS) ?? stalledResult();
|
|
5190
|
+
}
|
|
5191
|
+
//#endregion
|
|
5192
|
+
//#region ../shared/dist/host/seam/policy.js
|
|
5193
|
+
function seamModeOf(value) {
|
|
5194
|
+
const text = value?.trim();
|
|
5195
|
+
return text === "read-only" || text === "workspace-write" || text === "danger-full-access" ? text : "workspace-write";
|
|
5196
|
+
}
|
|
5197
|
+
function rank(mode) {
|
|
5198
|
+
return mode === "read-only" ? 0 : mode === "workspace-write" ? 1 : 2;
|
|
5199
|
+
}
|
|
5200
|
+
function narrowerMode(left, right) {
|
|
5201
|
+
return rank(left) <= rank(right) ? left : right;
|
|
5202
|
+
}
|
|
5203
|
+
function insideOf(root, path) {
|
|
5204
|
+
const confined = resolve(root);
|
|
5205
|
+
const target = resolve(path);
|
|
5206
|
+
if (target === confined) return true;
|
|
5207
|
+
return target.startsWith(confined.endsWith(sep) ? confined : `${confined}${sep}`);
|
|
5208
|
+
}
|
|
5209
|
+
function productPolicy(ceiling, productRoot) {
|
|
5210
|
+
return {
|
|
5211
|
+
mode: ceiling === "read-only" ? "read-only" : "workspace-write",
|
|
5212
|
+
workspaceRoot: resolve(productRoot)
|
|
5213
|
+
};
|
|
5214
|
+
}
|
|
5215
|
+
function workspacePolicy(ceiling, grant) {
|
|
5216
|
+
if (grant === void 0) throw new SeamError("workspace-unknown", "这条会话解析不到工作区:先在左栏选一个工作区再跑需要写工作区的命令");
|
|
5217
|
+
const mode = narrowerMode(ceiling, grant.mode);
|
|
5218
|
+
if (mode === "read-only") throw new SeamError("read-only", "当前会话是只读模式:要写工作区,先在权限设置里切换模式");
|
|
5219
|
+
return {
|
|
5220
|
+
mode,
|
|
5221
|
+
workspaceRoot: resolve(grant.workspaceRoot)
|
|
5222
|
+
};
|
|
5223
|
+
}
|
|
5224
|
+
function commonDirectory(paths) {
|
|
5225
|
+
const dirs = paths.map((path) => dirname(resolve(path)));
|
|
5226
|
+
let shared = dirs[0];
|
|
5227
|
+
if (shared === void 0) return void 0;
|
|
5228
|
+
for (const dir of dirs.slice(1)) while (shared !== dirname(shared) && !insideOf(shared, dir)) shared = dirname(shared);
|
|
5229
|
+
return shared;
|
|
5230
|
+
}
|
|
5231
|
+
function pathsPolicy(ceiling, paths) {
|
|
5232
|
+
if (paths.length === 0) throw new SeamError("paths-span-trees", "这次写入没有给出任何路径");
|
|
5233
|
+
if (!paths.every((path) => isAbsolute(path))) throw new SeamError("paths-span-trees", "外部路径必须是绝对路径");
|
|
5234
|
+
if (ceiling === "read-only") throw new SeamError("read-only", "当前部署是只读模式:这条命令不能写外部路径");
|
|
5235
|
+
const root = commonDirectory(paths);
|
|
5236
|
+
if (root === void 0 || root === sep && paths.length > 1) throw new SeamError("paths-span-trees", "这次写入跨了多个目录:请分开做,或把产物改到工作台目录");
|
|
5237
|
+
if (root === sep) throw new SeamError("paths-span-trees", "不能把整个文件系统声明成写入域");
|
|
5238
|
+
return {
|
|
5239
|
+
mode: "workspace-write",
|
|
5240
|
+
workspaceRoot: root
|
|
5241
|
+
};
|
|
5242
|
+
}
|
|
5243
|
+
function denialScopeOf(path, policy, productRoot, grant) {
|
|
5244
|
+
if (path === void 0) return "unknown";
|
|
5245
|
+
if (insideOf(productRoot, path)) return "product";
|
|
5246
|
+
if (grant !== void 0 && insideOf(grant.workspaceRoot, path)) return "workspace";
|
|
5247
|
+
if (insideOf(policy.workspaceRoot, path)) return policy.workspaceRoot === resolve(productRoot) ? "product" : "workspace";
|
|
5248
|
+
return "external";
|
|
5249
|
+
}
|
|
5250
|
+
function policyOf(intent, ceiling, productRoot, grant) {
|
|
5251
|
+
if (intent.kind === "product" || intent.kind === "none") return productPolicy(ceiling, productRoot);
|
|
5252
|
+
if (intent.kind === "workspace") return workspacePolicy(ceiling, grant);
|
|
5253
|
+
return pathsPolicy(ceiling, intent.paths);
|
|
5254
|
+
}
|
|
5255
|
+
//#endregion
|
|
5256
|
+
//#region ../shared/dist/host/seam/env.js
|
|
5257
|
+
/**
|
|
5258
|
+
* 产品域(运行时根)的受控 env:隔离 HOME、npm/pnpm 缓存钉在运行时根、真 npmrc/gitconfig 只读指过去。
|
|
5259
|
+
* @param input 运行时根、可选的 HOME 与真实用户目录。
|
|
5260
|
+
* @returns 产品域 env。
|
|
5261
|
+
*/
|
|
5262
|
+
function seamProductEnv(input) {
|
|
5263
|
+
return productEnvOf(input);
|
|
5264
|
+
}
|
|
5265
|
+
/**
|
|
5266
|
+
* 模块单元的受控 env:HOME 与 npm/pnpm 目录钉到单元数据目录。
|
|
5267
|
+
* @param input 运行时根、单元数据目录、可选的 TRUE 用户目录。
|
|
5268
|
+
* @returns 单元 env。
|
|
5269
|
+
*/
|
|
5270
|
+
function seamUnitEnv(input) {
|
|
5271
|
+
return unitEnvOf(input);
|
|
5272
|
+
}
|
|
5273
|
+
//#endregion
|
|
5274
|
+
//#region ../shared/dist/host/seam/denial.js
|
|
5275
|
+
var PATTERNS = [
|
|
5276
|
+
/npm (?:error|ERR!) path (.+)$/m,
|
|
5277
|
+
/(?:EPERM|EACCES):[^,\n]*, (?:open|unlink|mkdir|scandir|rename|access|stat|lstat|read|write|copyfile|rmdir|opendir|utimes|chmod|chown) '([^']+)'/i,
|
|
5278
|
+
/(?:Operation not permitted|Permission denied)[^\n]*?'([^']+)'/i,
|
|
5279
|
+
/fatal: could not create work tree dir '([^']+)'/i,
|
|
5280
|
+
/mkdir: cannot create directory '([^']+)'/i,
|
|
5281
|
+
/cannot (?:create|mkdir) (?:directory )?'([^']+)'/i,
|
|
5282
|
+
/(?:Operation not permitted|Permission denied):\s*([^\s'\n]+)/i
|
|
5283
|
+
];
|
|
5284
|
+
function firstMatch(text) {
|
|
5285
|
+
for (const pattern of PATTERNS) {
|
|
5286
|
+
const value = pattern.exec(text)?.[1]?.trim();
|
|
5287
|
+
if (value !== void 0 && value !== "") return value;
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
function deniedPathOf(stderr, stdout = "") {
|
|
5291
|
+
return firstMatch(stderr) ?? firstMatch(stdout);
|
|
5292
|
+
}
|
|
5293
|
+
function sandboxDeniedOf(facts) {
|
|
5294
|
+
return facts?.denied === true;
|
|
5295
|
+
}
|
|
5296
|
+
//#endregion
|
|
5297
|
+
//#region ../shared/dist/host/seam/host-env.js
|
|
5298
|
+
function hostEnvOf(env) {
|
|
5299
|
+
const merged = { ...process.env };
|
|
5300
|
+
for (const [key, value] of Object.entries(env ?? {})) if (value === void 0) delete merged[key];
|
|
5301
|
+
else merged[key] = value;
|
|
5302
|
+
return merged;
|
|
5303
|
+
}
|
|
5304
|
+
function definedEnvOf(env) {
|
|
5305
|
+
const out = {};
|
|
5306
|
+
for (const [key, value] of Object.entries(env ?? {})) if (value !== void 0) out[key] = value;
|
|
5307
|
+
return out;
|
|
5308
|
+
}
|
|
5309
|
+
//#endregion
|
|
5310
|
+
//#region ../shared/dist/host/seam/process.js
|
|
5311
|
+
function streamOf(readable) {
|
|
5312
|
+
if (readable === null || readable === void 0) return null;
|
|
5313
|
+
return { onData(listener) {
|
|
5314
|
+
const handler = (chunk) => listener(String(chunk));
|
|
5315
|
+
readable.on("data", handler);
|
|
5316
|
+
return () => {
|
|
5317
|
+
readable.off("data", handler);
|
|
5318
|
+
};
|
|
5319
|
+
} };
|
|
5320
|
+
}
|
|
5321
|
+
function createNodeProcessHost() {
|
|
5322
|
+
return { spawn(options) {
|
|
5323
|
+
const logFd = options.stdio === "log" && options.logFile !== void 0 ? openSync(options.logFile, "a") : void 0;
|
|
5324
|
+
const stdio = options.stdio === "pipe" ? "pipe" : logFd === void 0 ? "ignore" : [
|
|
5325
|
+
"ignore",
|
|
5326
|
+
logFd,
|
|
5327
|
+
logFd
|
|
5328
|
+
];
|
|
5329
|
+
let logClosed = false;
|
|
5330
|
+
const closeLog = () => {
|
|
5331
|
+
if (logClosed || logFd === void 0) return;
|
|
5332
|
+
logClosed = true;
|
|
5333
|
+
closeSync(logFd);
|
|
5334
|
+
};
|
|
5335
|
+
let child;
|
|
5336
|
+
try {
|
|
5337
|
+
child = execa(options.file, [...options.args], {
|
|
5338
|
+
...options.cwd === void 0 ? {} : { cwd: options.cwd },
|
|
5339
|
+
env: { ...options.env },
|
|
5340
|
+
detached: options.detached,
|
|
5341
|
+
stdio,
|
|
5342
|
+
windowsHide: options.windowsHide,
|
|
5343
|
+
reject: false,
|
|
5344
|
+
buffer: false
|
|
5345
|
+
});
|
|
5346
|
+
} catch (error) {
|
|
5347
|
+
closeLog();
|
|
5348
|
+
throw error;
|
|
5349
|
+
}
|
|
5350
|
+
if (logFd !== void 0) {
|
|
5351
|
+
child.nodeChildProcess.once("spawn", closeLog);
|
|
5352
|
+
child.nodeChildProcess.once("error", closeLog);
|
|
5353
|
+
}
|
|
5354
|
+
const stdin = child.stdin;
|
|
5355
|
+
return {
|
|
5356
|
+
pid: child.pid,
|
|
5357
|
+
stdin: stdin === null || stdin === void 0 ? null : { write: (chunk) => stdin.write(chunk) },
|
|
5358
|
+
stdout: streamOf(child.stdout),
|
|
5359
|
+
stderr: streamOf(child.stderr),
|
|
5360
|
+
onExit(listener) {
|
|
5361
|
+
const handler = (code, signal) => listener({
|
|
5362
|
+
code,
|
|
5363
|
+
signal
|
|
5364
|
+
});
|
|
5365
|
+
child.nodeChildProcess.on("exit", handler);
|
|
5366
|
+
return () => {
|
|
5367
|
+
child.nodeChildProcess.off("exit", handler);
|
|
5368
|
+
};
|
|
5369
|
+
},
|
|
5370
|
+
onError(listener) {
|
|
5371
|
+
child.nodeChildProcess.on("error", listener);
|
|
5372
|
+
return () => {
|
|
5373
|
+
child.nodeChildProcess.off("error", listener);
|
|
5374
|
+
};
|
|
5375
|
+
},
|
|
5376
|
+
unref() {
|
|
5377
|
+
child.nodeChildProcess.unref();
|
|
5378
|
+
},
|
|
5379
|
+
kill(signal) {
|
|
5380
|
+
child.kill(signal);
|
|
5381
|
+
}
|
|
5382
|
+
};
|
|
5383
|
+
} };
|
|
5384
|
+
}
|
|
5385
|
+
//#endregion
|
|
5386
|
+
//#region ../shared/dist/host/seam/run.js
|
|
5387
|
+
var DENIAL_MARKER = /\[sandbox: file access denied under [a-z-]+ mode\]/i;
|
|
5388
|
+
var DEFAULT_MAX_BYTES$2 = 8388608;
|
|
5389
|
+
function scopeText(scope) {
|
|
5390
|
+
if (scope === "product") return "工作台自己的目录";
|
|
5391
|
+
if (scope === "workspace") return "会话工作区";
|
|
5392
|
+
if (scope === "external") return "工作区之外的路径";
|
|
5393
|
+
return "没认出来的路径";
|
|
5394
|
+
}
|
|
5395
|
+
function denialMessageOf(scope, path, output) {
|
|
5396
|
+
const where = path === void 0 ? "" : `:${path}`;
|
|
5397
|
+
const marker = DENIAL_MARKER.test(output) ? "" : "(输出里没有沙箱标记,按拒绝处理)";
|
|
5398
|
+
const next = scope === "external" ? "要写工作区外的路径得先让用户点头:把目标改到工作区,或让工具补上审批后再试" : "这是工作台自己的声明问题,不是用户的配置问题:把命令、域与路径一起记下来交给维护者";
|
|
5399
|
+
return `被沙箱挡住了(${scopeText(scope)}${where})${marker}。${next}`;
|
|
5400
|
+
}
|
|
5401
|
+
function settle$1(result, denial) {
|
|
5402
|
+
return {
|
|
5403
|
+
exitCode: result.exitCode,
|
|
5404
|
+
timedOut: result.timedOut,
|
|
5405
|
+
aborted: result.aborted,
|
|
5406
|
+
stdout: result.stdout,
|
|
5407
|
+
stderr: result.stderr,
|
|
5408
|
+
truncated: result.truncated,
|
|
5409
|
+
stalled: result.stalled,
|
|
5410
|
+
...denial === void 0 ? {} : { denial }
|
|
5411
|
+
};
|
|
5412
|
+
}
|
|
5413
|
+
function targetOf(request) {
|
|
5414
|
+
const hasCommand = request.command !== void 0;
|
|
5415
|
+
const hasArgv = request.argv !== void 0;
|
|
5416
|
+
if (hasArgv && !hasCommand) {
|
|
5417
|
+
const argv = request.argv ?? [];
|
|
5418
|
+
if (argv.length === 0) throw new SeamError("scope-not-declared", "argv 是空的:至少给一个可执行文件");
|
|
5419
|
+
return { argv };
|
|
5420
|
+
}
|
|
5421
|
+
if (hasCommand && !hasArgv) return { line: request.command ?? "" };
|
|
5422
|
+
throw new SeamError("scope-not-declared", "这条执行请求要么给 argv,要么给一条命令行,不能两个都给或都不给");
|
|
5423
|
+
}
|
|
5424
|
+
function displayOf(target) {
|
|
5425
|
+
return "argv" in target ? target.argv.map((one) => quoteShellArg(one)).join(" ") : target.line;
|
|
5426
|
+
}
|
|
5427
|
+
function shellInvocationOf(shellKind, line) {
|
|
5428
|
+
return shellKind === "pwsh" ? {
|
|
5429
|
+
file: "pwsh",
|
|
5430
|
+
args: [
|
|
5431
|
+
"-NoProfile",
|
|
5432
|
+
"-NonInteractive",
|
|
5433
|
+
"-Command",
|
|
5434
|
+
line
|
|
5435
|
+
]
|
|
5436
|
+
} : {
|
|
5437
|
+
file: "bash",
|
|
5438
|
+
args: ["-c", line]
|
|
5439
|
+
};
|
|
5440
|
+
}
|
|
5441
|
+
function invocationOf(target, shellKind) {
|
|
5442
|
+
if ("argv" in target) {
|
|
5443
|
+
const [file, ...args] = target.argv;
|
|
5444
|
+
if (file === void 0) throw new SeamError("scope-not-declared", "argv 是空的:至少给一个可执行文件");
|
|
5445
|
+
return {
|
|
5446
|
+
file,
|
|
5447
|
+
args
|
|
5448
|
+
};
|
|
5449
|
+
}
|
|
5450
|
+
return shellInvocationOf(shellKind ?? shellKindOf(), target.line);
|
|
5451
|
+
}
|
|
5452
|
+
function sandboxedLineOf(target, shellKind) {
|
|
5453
|
+
if ("argv" in target) return target.argv.map((one) => quoteShellArg(one)).join(" ");
|
|
5454
|
+
if (shellKind === void 0) return target.line;
|
|
5455
|
+
const invocation = shellInvocationOf(shellKind, target.line);
|
|
5456
|
+
return [invocation.file, ...invocation.args].map((one) => quoteShellArg(one)).join(" ");
|
|
5457
|
+
}
|
|
5458
|
+
function createSeam(ports) {
|
|
5459
|
+
const runtimeRoot = ports.runtimeRoot ?? workbenchRoot();
|
|
5460
|
+
const ceiling = ports.ceiling ?? seamModeOf(process.env["DSH_PERMISSION_MODE"]);
|
|
5461
|
+
const allowHost = ports.allowHost === true;
|
|
5462
|
+
const processes = ports.processes ?? createNodeProcessHost();
|
|
5463
|
+
const limit = ports.factLimit ?? 80;
|
|
5464
|
+
const facts = [];
|
|
5465
|
+
function record(fact) {
|
|
5466
|
+
facts.push(fact);
|
|
5467
|
+
if (facts.length > limit) facts.splice(0, facts.length - limit);
|
|
5468
|
+
}
|
|
5469
|
+
function grantFor(session) {
|
|
5470
|
+
if (ports.grantOf === void 0 || session === void 0 || session === null) return void 0;
|
|
5471
|
+
try {
|
|
5472
|
+
return ports.grantOf(session);
|
|
5473
|
+
} catch {
|
|
5474
|
+
return;
|
|
5475
|
+
}
|
|
5476
|
+
}
|
|
5477
|
+
function requireHost(intent) {
|
|
5478
|
+
if (intent.kind !== "host") throw new SeamError("scope-not-declared", "这条调用要走宿主进程执行,但写入域不是 host");
|
|
5479
|
+
if (!allowHost) throw new SeamError("scope-not-declared", "宿主域只给宿主侧接线开出来;模块与普通调用点拿不到");
|
|
5480
|
+
}
|
|
5481
|
+
async function attempt(request, policy, line) {
|
|
5482
|
+
return runShell(ports.shell, line, {
|
|
5483
|
+
...request.workdir === void 0 ? {} : { workdir: request.workdir },
|
|
5484
|
+
...request.timeoutMs === void 0 ? {} : { timeoutMs: request.timeoutMs },
|
|
5485
|
+
...request.signal === void 0 ? {} : { signal: request.signal },
|
|
5486
|
+
env: {
|
|
5487
|
+
...seamProductEnv({
|
|
5488
|
+
runtimeRoot,
|
|
5489
|
+
realHome: ports.realHome
|
|
5490
|
+
}),
|
|
5491
|
+
...definedEnvOf(request.env)
|
|
5492
|
+
},
|
|
5493
|
+
sandboxPolicy: policy
|
|
5494
|
+
});
|
|
5495
|
+
}
|
|
5496
|
+
function healPolicyOf(intent, scope, granted) {
|
|
5497
|
+
if (scope === "product" && intent.kind !== "product") return productPolicy(ceiling, runtimeRoot);
|
|
5498
|
+
if (scope === "workspace" && intent.kind === "workspace") {
|
|
5499
|
+
if (granted === void 0) return void 0;
|
|
5500
|
+
try {
|
|
5501
|
+
return workspacePolicy(ceiling, granted);
|
|
5502
|
+
} catch {
|
|
5503
|
+
return;
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
}
|
|
5507
|
+
async function runHost(request, target) {
|
|
5508
|
+
const invocation = invocationOf(target, request.shellKind);
|
|
5509
|
+
const child = processes.spawn({
|
|
5510
|
+
file: invocation.file,
|
|
5511
|
+
args: invocation.args,
|
|
5512
|
+
env: hostEnvOf(request.env),
|
|
5513
|
+
...request.workdir === void 0 ? {} : { cwd: request.workdir },
|
|
5514
|
+
detached: false,
|
|
5515
|
+
stdio: "pipe",
|
|
5516
|
+
windowsHide: true
|
|
5517
|
+
});
|
|
5518
|
+
const state = {
|
|
5519
|
+
stdout: "",
|
|
5520
|
+
stderr: "",
|
|
5521
|
+
truncated: false
|
|
5522
|
+
};
|
|
5523
|
+
const append = (which, chunk) => {
|
|
5524
|
+
const total = state.stdout.length + state.stderr.length;
|
|
5525
|
+
const room = (request.maxBytes ?? DEFAULT_MAX_BYTES$2) - total;
|
|
5526
|
+
if (room <= 0) {
|
|
5527
|
+
state.truncated = true;
|
|
5528
|
+
return;
|
|
5529
|
+
}
|
|
5530
|
+
const text = chunk.length > room ? chunk.slice(0, room) : chunk;
|
|
5531
|
+
state[which] += text;
|
|
5532
|
+
if (chunk.length > room) state.truncated = true;
|
|
5533
|
+
};
|
|
5534
|
+
child.stdout?.onData((chunk) => append("stdout", chunk));
|
|
5535
|
+
child.stderr?.onData((chunk) => append("stderr", chunk));
|
|
5536
|
+
let timer;
|
|
5537
|
+
let timedOut = false;
|
|
5538
|
+
if (request.timeoutMs !== void 0) timer = setTimeout(() => {
|
|
5539
|
+
timedOut = true;
|
|
5540
|
+
child.kill("SIGKILL");
|
|
5541
|
+
}, request.timeoutMs);
|
|
5542
|
+
const exit = await new Promise((resolve) => {
|
|
5543
|
+
child.onExit((event) => resolve(event));
|
|
5544
|
+
child.onError((error) => {
|
|
5545
|
+
state.stderr = `${state.stderr}${String(error)}`;
|
|
5546
|
+
resolve({
|
|
5547
|
+
code: null,
|
|
5548
|
+
signal: null
|
|
5549
|
+
});
|
|
5550
|
+
});
|
|
5551
|
+
});
|
|
5552
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
5553
|
+
return {
|
|
5554
|
+
exitCode: exit.code,
|
|
5555
|
+
timedOut,
|
|
5556
|
+
aborted: false,
|
|
5557
|
+
stdout: state.stdout,
|
|
5558
|
+
stderr: state.stderr,
|
|
5559
|
+
truncated: state.truncated,
|
|
5560
|
+
stalled: false
|
|
5561
|
+
};
|
|
5562
|
+
}
|
|
5563
|
+
function spawnHost(request) {
|
|
5564
|
+
const invocation = invocationOf(request.target, void 0);
|
|
5565
|
+
const logFile = request.logFile ?? join(runtimeRoot, "logs", `${request.label ?? "spawn"}-${String(now())}.log`);
|
|
5566
|
+
mkdirSync(dirname(logFile), { recursive: true });
|
|
5567
|
+
const child = processes.spawn({
|
|
5568
|
+
file: invocation.file,
|
|
5569
|
+
args: invocation.args,
|
|
5570
|
+
env: hostEnvOf(request.env),
|
|
5571
|
+
...request.cwd === void 0 ? {} : { cwd: request.cwd },
|
|
5572
|
+
detached: true,
|
|
5573
|
+
stdio: "log",
|
|
5574
|
+
logFile,
|
|
5575
|
+
windowsHide: true
|
|
5576
|
+
});
|
|
5577
|
+
child.unref();
|
|
5578
|
+
record({
|
|
5579
|
+
at: now(),
|
|
5580
|
+
verb: "spawn",
|
|
5581
|
+
command: displayOf(request.target),
|
|
5582
|
+
intent: "host",
|
|
5583
|
+
mode: "host",
|
|
5584
|
+
workspaceRoot: "",
|
|
5585
|
+
exitCode: null,
|
|
5586
|
+
timedOut: false,
|
|
5587
|
+
stalled: false
|
|
5588
|
+
});
|
|
5589
|
+
const display = displayOf(request.target);
|
|
5590
|
+
const errorListeners = /* @__PURE__ */ new Set();
|
|
5591
|
+
const exitWaiters = [];
|
|
5592
|
+
let settled;
|
|
5593
|
+
let failure;
|
|
5594
|
+
const settleExit = (event) => {
|
|
5595
|
+
if (settled !== void 0) return;
|
|
5596
|
+
settled = event;
|
|
5597
|
+
for (const waiter of exitWaiters.splice(0)) waiter(event);
|
|
5598
|
+
};
|
|
5599
|
+
child.onExit((event) => settleExit(event));
|
|
5600
|
+
child.onError((error) => {
|
|
5601
|
+
failure = error;
|
|
5602
|
+
settleExit({
|
|
5603
|
+
code: null,
|
|
5604
|
+
signal: null
|
|
5605
|
+
});
|
|
5606
|
+
if (errorListeners.size === 0) {
|
|
5607
|
+
ports.logger?.warn?.(`[dsh-workbench] 起进程没跑起来(${display}):${String(error)}`);
|
|
5608
|
+
return;
|
|
5609
|
+
}
|
|
5610
|
+
for (const listener of errorListeners) listener(error);
|
|
5611
|
+
});
|
|
5612
|
+
return {
|
|
5613
|
+
pid: child.pid,
|
|
5614
|
+
logFile,
|
|
5615
|
+
exit: () => settled === void 0 ? new Promise((resolve) => exitWaiters.push(resolve)) : Promise.resolve(settled),
|
|
5616
|
+
onError(listener) {
|
|
5617
|
+
if (failure !== void 0) {
|
|
5618
|
+
listener(failure);
|
|
5619
|
+
return () => {};
|
|
5620
|
+
}
|
|
5621
|
+
errorListeners.add(listener);
|
|
5622
|
+
return () => errorListeners.delete(listener);
|
|
5623
|
+
},
|
|
5624
|
+
kill: (signal) => child.kill(signal ?? "SIGTERM")
|
|
5625
|
+
};
|
|
5626
|
+
}
|
|
5627
|
+
function openHost(request) {
|
|
5628
|
+
const invocation = invocationOf(request.target, void 0);
|
|
5629
|
+
const child = processes.spawn({
|
|
5630
|
+
file: invocation.file,
|
|
5631
|
+
args: invocation.args,
|
|
5632
|
+
env: hostEnvOf(request.env),
|
|
5633
|
+
...request.cwd === void 0 ? {} : { cwd: request.cwd },
|
|
5634
|
+
detached: false,
|
|
5635
|
+
stdio: "pipe",
|
|
5636
|
+
windowsHide: true
|
|
5637
|
+
});
|
|
5638
|
+
record({
|
|
5639
|
+
at: now(),
|
|
5640
|
+
verb: "open",
|
|
5641
|
+
command: displayOf(request.target),
|
|
5642
|
+
intent: "host",
|
|
5643
|
+
mode: "host",
|
|
5644
|
+
workspaceRoot: "",
|
|
5645
|
+
exitCode: null,
|
|
5646
|
+
timedOut: false,
|
|
5647
|
+
stalled: false
|
|
5648
|
+
});
|
|
5649
|
+
const display = displayOf(request.target);
|
|
5650
|
+
const errorListeners = /* @__PURE__ */ new Set();
|
|
5651
|
+
const exitListeners = /* @__PURE__ */ new Set();
|
|
5652
|
+
let failure;
|
|
5653
|
+
let settled;
|
|
5654
|
+
const settleExit = (event) => {
|
|
5655
|
+
if (settled !== void 0) return;
|
|
5656
|
+
settled = event;
|
|
5657
|
+
for (const listener of exitListeners) listener(event);
|
|
5658
|
+
};
|
|
5659
|
+
child.onExit((event) => settleExit(event));
|
|
5660
|
+
child.onError((error) => {
|
|
5661
|
+
failure = error;
|
|
5662
|
+
settleExit({
|
|
5663
|
+
code: null,
|
|
5664
|
+
signal: null
|
|
5665
|
+
});
|
|
5666
|
+
if (errorListeners.size === 0) {
|
|
5667
|
+
ports.logger?.warn?.(`[dsh-workbench] 双向通道起不来(${display}):${String(error)}`);
|
|
5668
|
+
return;
|
|
5669
|
+
}
|
|
5670
|
+
for (const listener of errorListeners) listener(error);
|
|
5671
|
+
});
|
|
5672
|
+
return {
|
|
5673
|
+
pid: child.pid,
|
|
5674
|
+
write: (chunk) => child.stdin?.write(chunk),
|
|
5675
|
+
onData(listener) {
|
|
5676
|
+
const offOut = child.stdout?.onData(listener) ?? (() => {});
|
|
5677
|
+
const offErr = child.stderr?.onData(listener) ?? (() => {});
|
|
5678
|
+
return () => {
|
|
5679
|
+
offOut();
|
|
5680
|
+
offErr();
|
|
5681
|
+
};
|
|
5682
|
+
},
|
|
5683
|
+
onExit(listener) {
|
|
5684
|
+
if (settled !== void 0) {
|
|
5685
|
+
listener(settled);
|
|
5686
|
+
return () => {};
|
|
5687
|
+
}
|
|
5688
|
+
exitListeners.add(listener);
|
|
5689
|
+
return () => exitListeners.delete(listener);
|
|
5690
|
+
},
|
|
5691
|
+
onError(listener) {
|
|
5692
|
+
if (failure !== void 0) {
|
|
5693
|
+
listener(failure);
|
|
5694
|
+
return () => {};
|
|
5695
|
+
}
|
|
5696
|
+
errorListeners.add(listener);
|
|
5697
|
+
return () => errorListeners.delete(listener);
|
|
5698
|
+
},
|
|
5699
|
+
kill: (signal) => child.kill(signal ?? "SIGTERM")
|
|
5700
|
+
};
|
|
5701
|
+
}
|
|
5702
|
+
return {
|
|
5703
|
+
async run(request) {
|
|
5704
|
+
const target = targetOf(request);
|
|
5705
|
+
if (request.intent.kind === "host") {
|
|
5706
|
+
requireHost(request.intent);
|
|
5707
|
+
const started = now();
|
|
5708
|
+
const result = await runHost(request, target);
|
|
5709
|
+
record({
|
|
5710
|
+
at: started,
|
|
5711
|
+
verb: "run",
|
|
5712
|
+
command: displayOf(target),
|
|
5713
|
+
intent: "host",
|
|
5714
|
+
mode: "host",
|
|
5715
|
+
workspaceRoot: "",
|
|
5716
|
+
exitCode: result.exitCode,
|
|
5717
|
+
timedOut: result.timedOut,
|
|
5718
|
+
stalled: result.stalled
|
|
5719
|
+
});
|
|
5720
|
+
return settle$1(result);
|
|
5721
|
+
}
|
|
5722
|
+
const granted = grantFor(request.session);
|
|
5723
|
+
const intent = request.intent;
|
|
5724
|
+
const policy = policyOf(intent, ceiling, runtimeRoot, granted);
|
|
5725
|
+
const line = sandboxedLineOf(target, request.shellKind);
|
|
5726
|
+
let result = await attempt(request, policy, line);
|
|
5727
|
+
if (!sandboxDeniedOf(result.sandbox)) {
|
|
5728
|
+
record({
|
|
5729
|
+
at: now(),
|
|
5730
|
+
verb: "run",
|
|
5731
|
+
command: line,
|
|
5732
|
+
intent: intent.kind,
|
|
5733
|
+
mode: policy.mode,
|
|
5734
|
+
workspaceRoot: policy.workspaceRoot,
|
|
5735
|
+
exitCode: result.exitCode,
|
|
5736
|
+
timedOut: result.timedOut,
|
|
5737
|
+
stalled: result.stalled
|
|
5738
|
+
});
|
|
5739
|
+
return settle$1(result);
|
|
5740
|
+
}
|
|
5741
|
+
const path = deniedPathOf(result.stderr, result.stdout);
|
|
5742
|
+
const scope = denialScopeOf(path, policy, runtimeRoot, granted);
|
|
5743
|
+
const healedPolicy = request.retry === "safe" ? healPolicyOf(intent, scope, granted) : void 0;
|
|
5744
|
+
let healed = false;
|
|
5745
|
+
if (healedPolicy !== void 0) {
|
|
5746
|
+
const retried = await attempt(request, healedPolicy, line).catch(() => void 0);
|
|
5747
|
+
if (retried !== void 0 && !sandboxDeniedOf(retried.sandbox)) {
|
|
5748
|
+
result = retried;
|
|
5749
|
+
healed = true;
|
|
5750
|
+
}
|
|
5751
|
+
}
|
|
5752
|
+
const denial = {
|
|
5753
|
+
...path === void 0 ? {} : { path },
|
|
5754
|
+
scope,
|
|
5755
|
+
...healed ? { healed: true } : {}
|
|
5756
|
+
};
|
|
5757
|
+
record({
|
|
5758
|
+
at: now(),
|
|
5759
|
+
verb: "run",
|
|
5760
|
+
command: line,
|
|
5761
|
+
intent: intent.kind,
|
|
5762
|
+
mode: policy.mode,
|
|
5763
|
+
workspaceRoot: policy.workspaceRoot,
|
|
5764
|
+
exitCode: result.exitCode,
|
|
5765
|
+
timedOut: result.timedOut,
|
|
5766
|
+
stalled: result.stalled,
|
|
5767
|
+
denial
|
|
5768
|
+
});
|
|
5769
|
+
if (healed) return settle$1(result, denial);
|
|
5770
|
+
if (request.bestEffort === true) {
|
|
5771
|
+
ports.logger?.warn?.(`[dsh-workbench] ${denialMessageOf(scope, path, `${result.stderr}\n${result.stdout}`)}`);
|
|
5772
|
+
return settle$1(result, denial);
|
|
5773
|
+
}
|
|
5774
|
+
throw new SeamError("sandbox-denied", denialMessageOf(scope, path, `${result.stderr}\n${result.stdout}`), {
|
|
5775
|
+
...path === void 0 ? {} : { path },
|
|
5776
|
+
scope
|
|
5777
|
+
});
|
|
5778
|
+
},
|
|
5779
|
+
spawn(request) {
|
|
5780
|
+
requireHost(request.intent);
|
|
5781
|
+
return spawnHost(request);
|
|
5782
|
+
},
|
|
5783
|
+
open(request) {
|
|
5784
|
+
requireHost(request.intent);
|
|
5785
|
+
return openHost(request);
|
|
5786
|
+
},
|
|
5787
|
+
facts: () => facts
|
|
5788
|
+
};
|
|
5789
|
+
}
|
|
5790
|
+
//#endregion
|
|
5791
|
+
//#region ../shared/dist/host/seam/unit-seam.js
|
|
5792
|
+
function unitDataRoot(runtimeRoot, unit) {
|
|
5793
|
+
return join(runtimeRoot, "units", unit);
|
|
5794
|
+
}
|
|
5795
|
+
function createUnitSeam(ports) {
|
|
5796
|
+
const unitRoot = unitDataRoot(ports.runtimeRoot, ports.unit);
|
|
5797
|
+
mkdirSync(unitRoot, { recursive: true });
|
|
5798
|
+
const declared = new Set(ports.declared ?? []);
|
|
5799
|
+
const env = seamUnitEnv({
|
|
5800
|
+
runtimeRoot: ports.runtimeRoot,
|
|
5801
|
+
unitRoot,
|
|
5802
|
+
realHome: ports.realHome
|
|
5803
|
+
});
|
|
5804
|
+
return { async run(request) {
|
|
5805
|
+
const common = {
|
|
5806
|
+
command: request.command,
|
|
5807
|
+
...request.workdir === void 0 ? {} : { workdir: request.workdir },
|
|
5808
|
+
...request.timeoutMs === void 0 ? {} : { timeoutMs: request.timeoutMs },
|
|
5809
|
+
...request.signal === void 0 ? {} : { signal: request.signal },
|
|
5810
|
+
...request.retry === void 0 ? {} : { retry: request.retry }
|
|
5811
|
+
};
|
|
5812
|
+
if (request.scope === "unit") return ports.seam.run({
|
|
5813
|
+
...common,
|
|
5814
|
+
intent: { kind: "product" },
|
|
5815
|
+
env: {
|
|
5816
|
+
...env,
|
|
5817
|
+
...request.env ?? {}
|
|
5818
|
+
}
|
|
5819
|
+
});
|
|
5820
|
+
if (!declared.has("workspace")) throw new SeamError("scope-not-declared", `${ports.unit} 的清单没有声明 host.writes 里的 workspace:要写当前工作区,先在清单里声明`);
|
|
5821
|
+
if (request.session === void 0 || request.session === null) throw new SeamError("workspace-unknown", `${ports.unit} 请求写工作区,但这次调用没有会话`);
|
|
5822
|
+
return ports.seam.run({
|
|
5823
|
+
...common,
|
|
5824
|
+
intent: { kind: "workspace" },
|
|
5825
|
+
session: request.session,
|
|
5826
|
+
env: { ...request.env ?? {} }
|
|
5827
|
+
});
|
|
5828
|
+
} };
|
|
5829
|
+
}
|
|
5830
|
+
//#endregion
|
|
5831
|
+
//#region ../shared/dist/host/seam/service.js
|
|
5832
|
+
var WORKBENCH_SEAM = "workbenchSeam";
|
|
5833
|
+
function seamOf(ctx) {
|
|
5834
|
+
return ctx.get(WORKBENCH_SEAM, false);
|
|
5835
|
+
}
|
|
5836
|
+
//#endregion
|
|
4893
5837
|
//#region ../shared/dist/host/install-failure.js
|
|
4894
5838
|
var TARGET_FLAGS = [
|
|
4895
5839
|
"--prefix",
|
|
@@ -5316,123 +6260,40 @@ function buildSupplyIndex(declarations, options = {}) {
|
|
|
5316
6260
|
const detail = `${need.kind === "extension" ? "插件" : "共用库"} ${need.name} 来自不同的包:${seen.package} / ${entry.package}`;
|
|
5317
6261
|
entry.conflicted = true;
|
|
5318
6262
|
seen.conflicted = true;
|
|
5319
|
-
conflicts.push({
|
|
5320
|
-
kind: "name-mismatch",
|
|
5321
|
-
key: entry.key,
|
|
5322
|
-
names: [need.name],
|
|
5323
|
-
declarers: [.../* @__PURE__ */ new Set([...seen.declarers, ...entry.declarers])],
|
|
5324
|
-
detail
|
|
5325
|
-
});
|
|
5326
|
-
} else if (seen === void 0) byName.set(nameKey, entry);
|
|
5327
|
-
}
|
|
5328
|
-
const deduped = /* @__PURE__ */ new Map();
|
|
5329
|
-
for (const conflict of conflicts) deduped.set(`${conflict.kind}:${conflict.key}`, conflict);
|
|
5330
|
-
const frozenEntries = /* @__PURE__ */ new Map();
|
|
5331
|
-
for (const [key, entry] of entries) frozenEntries.set(key, {
|
|
5332
|
-
key: entry.key,
|
|
5333
|
-
kind: entry.kind,
|
|
5334
|
-
names: [...entry.names],
|
|
5335
|
-
package: entry.package,
|
|
5336
|
-
...entry.runtime === void 0 ? {} : { runtime: entry.runtime },
|
|
5337
|
-
sources: entry.sources.map((source) => ({ ...source })),
|
|
5338
|
-
declarers: [...entry.declarers],
|
|
5339
|
-
commands: [...entry.commands],
|
|
5340
|
-
conflicted: entry.conflicted
|
|
5341
|
-
});
|
|
5342
|
-
const entryOf = (kind, name) => byName.get(`${kind}:${name}`);
|
|
5343
|
-
return {
|
|
5344
|
-
entries: frozenEntries,
|
|
5345
|
-
conflicts: [...deduped.values()],
|
|
5346
|
-
problems: [...deduped.values()].map((conflict) => conflict.detail),
|
|
5347
|
-
entryOf,
|
|
5348
|
-
sourcesOf: (kind, name) => {
|
|
5349
|
-
const entry = entryOf(kind, name);
|
|
5350
|
-
return entry === void 0 || entry.conflicted ? [] : entry.sources;
|
|
5351
|
-
}
|
|
5352
|
-
};
|
|
5353
|
-
}
|
|
5354
|
-
//#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;
|
|
6263
|
+
conflicts.push({
|
|
6264
|
+
kind: "name-mismatch",
|
|
6265
|
+
key: entry.key,
|
|
6266
|
+
names: [need.name],
|
|
6267
|
+
declarers: [.../* @__PURE__ */ new Set([...seen.declarers, ...entry.declarers])],
|
|
6268
|
+
detail
|
|
6269
|
+
});
|
|
6270
|
+
} else if (seen === void 0) byName.set(nameKey, entry);
|
|
5420
6271
|
}
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
const
|
|
5424
|
-
const
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
6272
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
6273
|
+
for (const conflict of conflicts) deduped.set(`${conflict.kind}:${conflict.key}`, conflict);
|
|
6274
|
+
const frozenEntries = /* @__PURE__ */ new Map();
|
|
6275
|
+
for (const [key, entry] of entries) frozenEntries.set(key, {
|
|
6276
|
+
key: entry.key,
|
|
6277
|
+
kind: entry.kind,
|
|
6278
|
+
names: [...entry.names],
|
|
6279
|
+
package: entry.package,
|
|
6280
|
+
...entry.runtime === void 0 ? {} : { runtime: entry.runtime },
|
|
6281
|
+
sources: entry.sources.map((source) => ({ ...source })),
|
|
6282
|
+
declarers: [...entry.declarers],
|
|
6283
|
+
commands: [...entry.commands],
|
|
6284
|
+
conflicted: entry.conflicted
|
|
6285
|
+
});
|
|
6286
|
+
const entryOf = (kind, name) => byName.get(`${kind}:${name}`);
|
|
6287
|
+
return {
|
|
6288
|
+
entries: frozenEntries,
|
|
6289
|
+
conflicts: [...deduped.values()],
|
|
6290
|
+
problems: [...deduped.values()].map((conflict) => conflict.detail),
|
|
6291
|
+
entryOf,
|
|
6292
|
+
sourcesOf: (kind, name) => {
|
|
6293
|
+
const entry = entryOf(kind, name);
|
|
6294
|
+
return entry === void 0 || entry.conflicted ? [] : entry.sources;
|
|
5433
6295
|
}
|
|
5434
|
-
|
|
5435
|
-
}
|
|
6296
|
+
};
|
|
5436
6297
|
}
|
|
5437
6298
|
//#endregion
|
|
5438
6299
|
//#region ../shared/dist/host/adapter-set.js
|
|
@@ -5492,7 +6353,6 @@ var FULL_METHODS = [
|
|
|
5492
6353
|
["runtime", "runners"],
|
|
5493
6354
|
["runtime", "health"],
|
|
5494
6355
|
["runtime", "seed"],
|
|
5495
|
-
["runtime", "warnUnwritableRoot"],
|
|
5496
6356
|
["plugins", "declarationLookup"],
|
|
5497
6357
|
["plugins", "createUnits"],
|
|
5498
6358
|
["actions", "configSyncHandler"],
|
|
@@ -6297,8 +7157,8 @@ var SUBSECTION = /^###[ \t]/;
|
|
|
6297
7157
|
function changelogVersion(value) {
|
|
6298
7158
|
return String(value ?? "").trim().replace(/^v/, "");
|
|
6299
7159
|
}
|
|
6300
|
-
function changelogDate(now =
|
|
6301
|
-
return dayjs(now).format("YYYY-MM-DD");
|
|
7160
|
+
function changelogDate(now$3 = new Date(now())) {
|
|
7161
|
+
return dayjs(now$3).format("YYYY-MM-DD");
|
|
6302
7162
|
}
|
|
6303
7163
|
function normalizeChangelogText(text) {
|
|
6304
7164
|
return String(text ?? "").replace(/\r\n?/g, "\n");
|
|
@@ -6458,6 +7318,82 @@ function normalizeWhitespace(rows) {
|
|
|
6458
7318
|
return out.join("\n").replace(/\n+$/, "");
|
|
6459
7319
|
}
|
|
6460
7320
|
//#endregion
|
|
7321
|
+
//#region src/shared/api/host/workbench-seam.ts
|
|
7322
|
+
function provideWorkbenchSeam(ctx) {
|
|
7323
|
+
const shell = ctx.get("shell");
|
|
7324
|
+
if (shell === void 0) return void 0;
|
|
7325
|
+
const runtimeRoot = workbenchRoot();
|
|
7326
|
+
const seam = createSeam({
|
|
7327
|
+
shell,
|
|
7328
|
+
runtimeRoot,
|
|
7329
|
+
realHome: homedir(),
|
|
7330
|
+
allowHost: true,
|
|
7331
|
+
logger: ctx.logger,
|
|
7332
|
+
grantOf: (session) => {
|
|
7333
|
+
const resolved = ctx.get("sandboxPolicy")?.resolve?.({ session });
|
|
7334
|
+
if (resolved === void 0 || resolved.mode === void 0 || resolved.workspaceRoot === void 0) return;
|
|
7335
|
+
return {
|
|
7336
|
+
mode: seamModeOf(resolved.mode),
|
|
7337
|
+
workspaceRoot: resolved.workspaceRoot
|
|
7338
|
+
};
|
|
7339
|
+
}
|
|
7340
|
+
});
|
|
7341
|
+
ctx.provide(WORKBENCH_SEAM, seam);
|
|
7342
|
+
ctx.logger?.info?.(`[dsh-workbench] 执行缝:产品域 ${runtimeRoot}`);
|
|
7343
|
+
return seam;
|
|
7344
|
+
}
|
|
7345
|
+
//#endregion
|
|
7346
|
+
//#region src/shared/api/host/git-exec.ts
|
|
7347
|
+
function gitNoPromptProcessEnv(parent = process.env, platform = process.platform) {
|
|
7348
|
+
return promptlessProcessEnvOf(parent, platform);
|
|
7349
|
+
}
|
|
7350
|
+
function withoutAskpassNoise(text) {
|
|
7351
|
+
return text.split("\n").filter((line) => !/unable to read askpass response/i.test(line)).join("\n").trim();
|
|
7352
|
+
}
|
|
7353
|
+
//#endregion
|
|
7354
|
+
//#region src/shared/api/host/git-process.ts
|
|
7355
|
+
var gitSeam;
|
|
7356
|
+
function promptlessEnv() {
|
|
7357
|
+
return gitNoPromptProcessEnv();
|
|
7358
|
+
}
|
|
7359
|
+
function provideGitProcessSeam(seam) {
|
|
7360
|
+
gitSeam = seam;
|
|
7361
|
+
}
|
|
7362
|
+
function createGitRunner(seam) {
|
|
7363
|
+
return async (args, options = {}) => {
|
|
7364
|
+
const outcome = await seam.run({
|
|
7365
|
+
argv: ["git", ...args],
|
|
7366
|
+
intent: { kind: "host" },
|
|
7367
|
+
env: promptlessEnv(),
|
|
7368
|
+
...options.cwd === void 0 ? {} : { workdir: options.cwd },
|
|
7369
|
+
...options.timeoutMs === void 0 ? {} : { timeoutMs: options.timeoutMs }
|
|
7370
|
+
});
|
|
7371
|
+
return {
|
|
7372
|
+
exitCode: outcome.exitCode ?? null,
|
|
7373
|
+
stdout: outcome.stdout,
|
|
7374
|
+
stderr: outcome.stderr,
|
|
7375
|
+
timedOut: outcome.timedOut
|
|
7376
|
+
};
|
|
7377
|
+
};
|
|
7378
|
+
}
|
|
7379
|
+
var runGitProcess = async (args, options = {}) => {
|
|
7380
|
+
if (gitSeam === void 0) throw new Error("git 命令还没接上执行缝(宿主域)—— 这个进程里没有可用的 shell 服务。");
|
|
7381
|
+
return createGitRunner(gitSeam)(args, options);
|
|
7382
|
+
};
|
|
7383
|
+
//#endregion
|
|
7384
|
+
//#region src/shared/api/host/shipped-adapter-set.ts
|
|
7385
|
+
function shippedAdapterSetSpec(moduleUrl) {
|
|
7386
|
+
const spec = JSON.parse(readFileSync(new URL("../../package.json", moduleUrl), "utf8")).workbench?.adapterSet;
|
|
7387
|
+
if (typeof spec !== "string" || spec.trim() === "") throw new Error("package.json 没写 workbench.adapterSet:外壳不知道该装哪份适配器集合");
|
|
7388
|
+
return spec.trim();
|
|
7389
|
+
}
|
|
7390
|
+
async function shippedAdapterSet(ctx) {
|
|
7391
|
+
const spec = shippedAdapterSetSpec(import.meta.url);
|
|
7392
|
+
const factory = (await import(spec)).createAdapterSet;
|
|
7393
|
+
if (typeof factory !== "function") throw new Error(`随包的适配器集合 ${spec} 没导出 createAdapterSet(ctx)`);
|
|
7394
|
+
return await factory(ctx);
|
|
7395
|
+
}
|
|
7396
|
+
//#endregion
|
|
6461
7397
|
//#region ../shared/dist/contract/protocol/dsh-plugins.js
|
|
6462
7398
|
var DSH_PLUGINS_ROUTE = "dsh-plugins";
|
|
6463
7399
|
//#endregion
|
|
@@ -6481,13 +7417,11 @@ function isFollowUpDismissed(runtimeRoot, id) {
|
|
|
6481
7417
|
return Object.prototype.hasOwnProperty.call(readDismissed(runtimeRoot), id);
|
|
6482
7418
|
}
|
|
6483
7419
|
function dismissFollowUp(runtimeRoot, id) {
|
|
6484
|
-
const file = fileOf$1(runtimeRoot);
|
|
6485
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
6486
7420
|
const dismissed = {
|
|
6487
7421
|
...readDismissed(runtimeRoot),
|
|
6488
|
-
[id]: { at: (
|
|
7422
|
+
[id]: { at: nowIso() }
|
|
6489
7423
|
};
|
|
6490
|
-
|
|
7424
|
+
writeJsonFile(fileOf$1(runtimeRoot), { dismissed });
|
|
6491
7425
|
}
|
|
6492
7426
|
//#endregion
|
|
6493
7427
|
//#region src/shared/api/host/migration-follow-up.ts
|
|
@@ -6609,7 +7543,7 @@ function titleOf(raw) {
|
|
|
6609
7543
|
}
|
|
6610
7544
|
async function latestIfReleaseReady(supply, input) {
|
|
6611
7545
|
const missing = await supply.missingReleaseParts({
|
|
6612
|
-
|
|
7546
|
+
seam: input.seam,
|
|
6613
7547
|
name: input.pkg,
|
|
6614
7548
|
registry: input.registry,
|
|
6615
7549
|
version: input.version,
|
|
@@ -6630,18 +7564,18 @@ async function workbenchVersion(supply, input) {
|
|
|
6630
7564
|
if (read === void 0) return void 0;
|
|
6631
7565
|
const spec = dependencyReader(input.dir)(PLUGIN_ID);
|
|
6632
7566
|
const registry = read.registry ?? input.fallback;
|
|
6633
|
-
const
|
|
6634
|
-
const queried = read.local === true || !input.check ||
|
|
6635
|
-
|
|
7567
|
+
const seam = input.seam;
|
|
7568
|
+
const queried = read.local === true || !input.check || seam === void 0 || registry === void 0 ? void 0 : await supply.latestVersion({
|
|
7569
|
+
seam,
|
|
6636
7570
|
name: read.pkg,
|
|
6637
7571
|
registry,
|
|
6638
7572
|
force: input.force
|
|
6639
7573
|
});
|
|
6640
|
-
const latest = queried === void 0 ||
|
|
7574
|
+
const latest = queried === void 0 || seam === void 0 || registry === void 0 || !supply.isNewerVersion(queried, read.version) ? void 0 : await latestIfReleaseReady(supply, {
|
|
6641
7575
|
pkg: read.pkg,
|
|
6642
7576
|
version: queried,
|
|
6643
7577
|
registry,
|
|
6644
|
-
|
|
7578
|
+
seam,
|
|
6645
7579
|
force: input.force,
|
|
6646
7580
|
logger: input.logger
|
|
6647
7581
|
});
|
|
@@ -6656,7 +7590,7 @@ async function workbenchVersion(supply, input) {
|
|
|
6656
7590
|
...registry === void 0 || read.local === true ? {} : { registry }
|
|
6657
7591
|
};
|
|
6658
7592
|
}
|
|
6659
|
-
function moduleVersionsHandler(ctx,
|
|
7593
|
+
function moduleVersionsHandler(ctx, seam, running, selfUpdate) {
|
|
6660
7594
|
return async (search) => {
|
|
6661
7595
|
const supply = supplyPortOf(ctx);
|
|
6662
7596
|
const manager = supply.readBundleManager(ctx);
|
|
@@ -6676,8 +7610,8 @@ function moduleVersionsHandler(ctx, shell, running, selfUpdate) {
|
|
|
6676
7610
|
const fromRegistry = module.fromRegistry !== false;
|
|
6677
7611
|
const registry = fromRegistry ? module.registry ?? fallback : void 0;
|
|
6678
7612
|
const spec = dependencyOf(module.pkg);
|
|
6679
|
-
const latest = local || !fromRegistry || !check ||
|
|
6680
|
-
|
|
7613
|
+
const latest = local || !fromRegistry || !check || seam === void 0 || registry === void 0 ? void 0 : await supply.latestVersion({
|
|
7614
|
+
seam,
|
|
6681
7615
|
name: module.pkg,
|
|
6682
7616
|
registry,
|
|
6683
7617
|
force
|
|
@@ -6686,8 +7620,8 @@ function moduleVersionsHandler(ctx, shell, running, selfUpdate) {
|
|
|
6686
7620
|
const declared = raw === void 0 ? [] : declarationProblemsOf(module.pkg, raw);
|
|
6687
7621
|
const title = titleOf(raw);
|
|
6688
7622
|
const refused = refusals.get(module.unit) ?? (declared.length === 0 ? void 0 : declared);
|
|
6689
|
-
const latestRaw = latest === void 0 || latest === module.version ||
|
|
6690
|
-
|
|
7623
|
+
const latestRaw = latest === void 0 || latest === module.version || seam === void 0 || registry === void 0 ? void 0 : await supply.registryDeclarationOf({
|
|
7624
|
+
seam,
|
|
6691
7625
|
name: module.pkg,
|
|
6692
7626
|
registry,
|
|
6693
7627
|
force
|
|
@@ -6727,7 +7661,7 @@ function moduleVersionsHandler(ctx, shell, running, selfUpdate) {
|
|
|
6727
7661
|
});
|
|
6728
7662
|
const workbench = await workbenchVersion(supply, {
|
|
6729
7663
|
dir,
|
|
6730
|
-
|
|
7664
|
+
seam,
|
|
6731
7665
|
fallback,
|
|
6732
7666
|
force,
|
|
6733
7667
|
check,
|
|
@@ -6755,23 +7689,23 @@ async function declarationRefusal(supply, spec, inspection, ctx, manager) {
|
|
|
6755
7689
|
if (inspection.status !== "accepted" || inspection.name === void 0) return void 0;
|
|
6756
7690
|
if (inspection.kind === "path") return refusedInspection(supply.declarationRefusalOf(inspection.name, supply.pathDeclarationOf(spec)));
|
|
6757
7691
|
if (inspection.kind !== "registry") return void 0;
|
|
6758
|
-
const
|
|
6759
|
-
if (
|
|
7692
|
+
const seam = seamOf(ctx);
|
|
7693
|
+
if (seam === void 0) return void 0;
|
|
6760
7694
|
const registry = typeof inspection.registry === "string" ? inspection.registry : (await manager.registries())?.resolved ?? void 0;
|
|
6761
7695
|
if (registry === void 0) return void 0;
|
|
6762
7696
|
const raw = await supply.registryDeclarationOf({
|
|
6763
|
-
|
|
7697
|
+
seam,
|
|
6764
7698
|
name: inspection.name,
|
|
6765
7699
|
registry
|
|
6766
7700
|
});
|
|
6767
7701
|
return refusedInspection(supply.declarationRefusalOf(inspection.name, raw));
|
|
6768
7702
|
}
|
|
6769
7703
|
async function packageOverride(supply, spec, inspection, ctx, manager, asked) {
|
|
6770
|
-
const
|
|
7704
|
+
const seam = seamOf(ctx);
|
|
6771
7705
|
const registry = (inspection.status === "accepted" && typeof inspection.registry === "string" ? inspection.registry : void 0) ?? (typeof asked === "string" ? asked : (await manager.registries())?.resolved ?? void 0);
|
|
6772
7706
|
const route = await supply.packageRouteOf({
|
|
6773
7707
|
spec,
|
|
6774
|
-
...
|
|
7708
|
+
...seam === void 0 ? {} : { seam },
|
|
6775
7709
|
...registry === void 0 ? {} : { registry }
|
|
6776
7710
|
});
|
|
6777
7711
|
if (route !== void 0 && route.kind === "runtime-plugin") {
|
|
@@ -6826,57 +7760,6 @@ function bundleInspectHandler(ctx) {
|
|
|
6826
7760
|
};
|
|
6827
7761
|
}
|
|
6828
7762
|
//#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)
|
|
6848
|
-
};
|
|
6849
|
-
if (platform === "win32") {
|
|
6850
|
-
delete env.GIT_ASKPASS;
|
|
6851
|
-
delete env.SSH_ASKPASS;
|
|
6852
|
-
}
|
|
6853
|
-
return env;
|
|
6854
|
-
}
|
|
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
|
-
//#endregion
|
|
6880
7763
|
//#region src/shared/api/host/git-probe.ts
|
|
6881
7764
|
var PROBE_TIMEOUT_MS = 1e4;
|
|
6882
7765
|
var AUTH_TEXT = /authentication failed|could not read (username|password)|permission denied|terminal prompts disabled|host key verification failed|repository not found|no such identity|access denied|unauthorized|forbidden/;
|
|
@@ -6910,10 +7793,10 @@ var PROBE_CACHE_MS = 6e4;
|
|
|
6910
7793
|
var probeCache = /* @__PURE__ */ new Map();
|
|
6911
7794
|
async function probeGitAddressCached(url, options = {}) {
|
|
6912
7795
|
const hit = probeCache.get(url);
|
|
6913
|
-
if (hit !== void 0 &&
|
|
7796
|
+
if (hit !== void 0 && now() - hit.at < PROBE_CACHE_MS) return hit.payload;
|
|
6914
7797
|
const payload = await probeGitAddress(url, options);
|
|
6915
7798
|
probeCache.set(url, {
|
|
6916
|
-
at:
|
|
7799
|
+
at: now(),
|
|
6917
7800
|
payload
|
|
6918
7801
|
});
|
|
6919
7802
|
return payload;
|
|
@@ -6947,18 +7830,15 @@ function createHostRoutes(input) {
|
|
|
6947
7830
|
[BUNDLE_REGISTRIES_ROUTE]: bundleRegistriesHandler(ctx),
|
|
6948
7831
|
[BUNDLE_INSPECT_ROUTE]: bundleInspectHandler(ctx),
|
|
6949
7832
|
[MARKET_GIT_PROBE_ROUTE]: gitProbeHandler(),
|
|
6950
|
-
[MODULE_VERSIONS_ROUTE]: moduleVersionsHandler(ctx, ctx
|
|
7833
|
+
[MODULE_VERSIONS_ROUTE]: moduleVersionsHandler(ctx, seamOf(ctx), running, () => profile.selfUpdateView(dshHome(), basename(profile.dirOf(ctx) ?? "")))
|
|
6951
7834
|
};
|
|
6952
7835
|
}
|
|
6953
|
-
var WORKSHOP_RELATIVE = "workbench/diy";
|
|
6954
7836
|
var WORKSHOP_TITLE = "工作台DIY";
|
|
6955
7837
|
//#endregion
|
|
6956
7838
|
//#region src/shared/api/host/diy.ts
|
|
6957
|
-
function workshopPathOf(
|
|
6958
|
-
const homePath = ctx.get("dshHomePath");
|
|
6959
|
-
if (typeof homePath !== "function") return null;
|
|
7839
|
+
function workshopPathOf(_ctx) {
|
|
6960
7840
|
try {
|
|
6961
|
-
return
|
|
7841
|
+
return join(workbenchRoot(), "diy");
|
|
6962
7842
|
} catch {
|
|
6963
7843
|
return null;
|
|
6964
7844
|
}
|
|
@@ -7135,7 +8015,7 @@ var panelsUnit = {
|
|
|
7135
8015
|
}))],
|
|
7136
8016
|
host: {
|
|
7137
8017
|
inject: [],
|
|
7138
|
-
optional: ["tools", "
|
|
8018
|
+
optional: ["tools", "workbenchSeam"]
|
|
7139
8019
|
}
|
|
7140
8020
|
};
|
|
7141
8021
|
//#endregion
|
|
@@ -8015,8 +8895,10 @@ function toolchainBinOf(root) {
|
|
|
8015
8895
|
function queryCommandOf(bin, only) {
|
|
8016
8896
|
return `${quoteShellArg(bin)} check --only ${only} --json`;
|
|
8017
8897
|
}
|
|
8018
|
-
async function runSurfaceQuery(
|
|
8019
|
-
const result = await
|
|
8898
|
+
async function runSurfaceQuery(seam, bin, only, root, fallbackBin) {
|
|
8899
|
+
const result = await seam.run({
|
|
8900
|
+
command: queryCommandOf(bin, only),
|
|
8901
|
+
intent: noWrite(),
|
|
8020
8902
|
workdir: root,
|
|
8021
8903
|
timeoutMs: SURFACE_TIMEOUT_MS
|
|
8022
8904
|
});
|
|
@@ -8024,7 +8906,9 @@ async function runSurfaceQuery(shell, bin, only, root, fallbackBin) {
|
|
|
8024
8906
|
if (parsed !== null) return withoutProblems(parsed);
|
|
8025
8907
|
const retry = fallbackBin === void 0 || fallbackBin === bin ? void 0 : fallbackBin;
|
|
8026
8908
|
if (retry !== void 0) {
|
|
8027
|
-
const secondParsed = jsonOf$1((await
|
|
8909
|
+
const secondParsed = jsonOf$1((await seam.run({
|
|
8910
|
+
command: queryCommandOf(retry, only),
|
|
8911
|
+
intent: noWrite(),
|
|
8028
8912
|
workdir: root,
|
|
8029
8913
|
timeoutMs: SURFACE_TIMEOUT_MS
|
|
8030
8914
|
})).stdout);
|
|
@@ -8790,11 +9674,11 @@ function componentsTool(ctx) {
|
|
|
8790
9674
|
})).find((one) => one.bin !== null) ?? null;
|
|
8791
9675
|
const bin = local?.bin ?? shellBin;
|
|
8792
9676
|
if (bin === null) throw new Error(`这个工作区(和它各模块)里还没有 dsh-wb-module(${join(root, "node_modules")} 下没有)—— 在它根目录跑一次 pnpm install 就有了;workbench_diy_status 会告诉你卡在哪一步`);
|
|
8793
|
-
const
|
|
8794
|
-
if (
|
|
9677
|
+
const seam = seamOf(ctx);
|
|
9678
|
+
if (seam === void 0) throw new Error("这台机器没给 shell 服务,读不了组件清单");
|
|
8795
9679
|
const workdir = local?.dir ?? shellRoot ?? root;
|
|
8796
9680
|
const fallbackBin = local !== null && shellBin !== null && shellBin !== bin ? shellBin : void 0;
|
|
8797
|
-
const [primitives, kit] = await Promise.all([wantsPrimitives(scope) ? runSurfaceQuery(
|
|
9681
|
+
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
9682
|
const query = {
|
|
8799
9683
|
...primitives === void 0 ? {} : { primitives },
|
|
8800
9684
|
...kit === void 0 ? {} : { kit }
|
|
@@ -8825,12 +9709,10 @@ function registerComponentsTool(ctx, tools) {
|
|
|
8825
9709
|
}
|
|
8826
9710
|
//#endregion
|
|
8827
9711
|
//#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 服务 —— 落不了截图与录制(取证目录由它给)");
|
|
9712
|
+
function artifactsDirOf(root) {
|
|
8831
9713
|
const name = basename(root).replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "workspace";
|
|
8832
9714
|
const hash = createHash("sha1").update(root).digest("hex").slice(0, 8);
|
|
8833
|
-
return
|
|
9715
|
+
return join(workbenchRoot(), "artifacts", `${name}-${hash}`);
|
|
8834
9716
|
}
|
|
8835
9717
|
function ensureDirOf(dir) {
|
|
8836
9718
|
mkdirSync(dir, { recursive: true });
|
|
@@ -8877,8 +9759,12 @@ var LOOK_TIMEOUT_MS = 3e4;
|
|
|
8877
9759
|
function plainLine(command, argv) {
|
|
8878
9760
|
return [command, ...argv].map(quoteShellArg).join(" ");
|
|
8879
9761
|
}
|
|
8880
|
-
async function runBrowserStep(
|
|
8881
|
-
const result = await
|
|
9762
|
+
async function runBrowserStep(seam, command, argv, timeoutMs = LOOK_TIMEOUT_MS, line = plainLine, intent = productWrite()) {
|
|
9763
|
+
const result = await seam.run({
|
|
9764
|
+
command: line(command, argv),
|
|
9765
|
+
intent,
|
|
9766
|
+
timeoutMs
|
|
9767
|
+
});
|
|
8882
9768
|
return {
|
|
8883
9769
|
ok: result.exitCode === 0 && !result.timedOut,
|
|
8884
9770
|
stdout: result.stdout.trim(),
|
|
@@ -8902,8 +9788,8 @@ function jsonOf(raw) {
|
|
|
8902
9788
|
function textOf$2(value) {
|
|
8903
9789
|
return typeof value === "string" ? value.trim() : "";
|
|
8904
9790
|
}
|
|
8905
|
-
async function bindCurrentTab(
|
|
8906
|
-
const step = await runBrowserStep(
|
|
9791
|
+
async function bindCurrentTab(seam, command, session, timeoutMs, line) {
|
|
9792
|
+
const step = await runBrowserStep(seam, command, [
|
|
8907
9793
|
"browser",
|
|
8908
9794
|
session,
|
|
8909
9795
|
"bind",
|
|
@@ -8934,8 +9820,8 @@ async function bindCurrentTab(shell, command, session, timeoutMs, line) {
|
|
|
8934
9820
|
reason: ""
|
|
8935
9821
|
};
|
|
8936
9822
|
}
|
|
8937
|
-
async function workbenchPageOf(
|
|
8938
|
-
const parsed = jsonOf((await runBrowserStep(
|
|
9823
|
+
async function workbenchPageOf(seam, command, session) {
|
|
9824
|
+
const parsed = jsonOf((await runBrowserStep(seam, command, [
|
|
8939
9825
|
"browser",
|
|
8940
9826
|
session,
|
|
8941
9827
|
"eval",
|
|
@@ -8963,22 +9849,22 @@ async function workbenchPageOf(shell, command, session) {
|
|
|
8963
9849
|
shown: parsed["shown"] === true
|
|
8964
9850
|
};
|
|
8965
9851
|
}
|
|
8966
|
-
async function readyPageOf(
|
|
8967
|
-
const already = await workbenchPageOf(
|
|
9852
|
+
async function readyPageOf(seam, command, session) {
|
|
9853
|
+
const already = await workbenchPageOf(seam, command, session);
|
|
8968
9854
|
if (already.ok) return {
|
|
8969
9855
|
ok: true,
|
|
8970
9856
|
bound: false,
|
|
8971
9857
|
page: already,
|
|
8972
9858
|
reason: ""
|
|
8973
9859
|
};
|
|
8974
|
-
const bound = await bindCurrentTab(
|
|
9860
|
+
const bound = await bindCurrentTab(seam, command, session);
|
|
8975
9861
|
if (!bound.ok) return {
|
|
8976
9862
|
ok: false,
|
|
8977
9863
|
bound: false,
|
|
8978
9864
|
page: already,
|
|
8979
9865
|
reason: bound.reason
|
|
8980
9866
|
};
|
|
8981
|
-
const after = await workbenchPageOf(
|
|
9867
|
+
const after = await workbenchPageOf(seam, command, session);
|
|
8982
9868
|
return {
|
|
8983
9869
|
ok: after.ok,
|
|
8984
9870
|
bound: true,
|
|
@@ -9033,8 +9919,8 @@ async function pageAt(ctx, exec, raw, options = {}) {
|
|
|
9033
9919
|
const pool = units.map((one) => one.unit);
|
|
9034
9920
|
const wanted = asked === "" && pool.length === 1 ? pool[0] ?? "" : asked;
|
|
9035
9921
|
if (wanted === "" && options.requireUnit !== false) throw new Error(`看一眼要一个 unit(装过的:${pool.join(", ") || "(空)"})`);
|
|
9036
|
-
const
|
|
9037
|
-
if (
|
|
9922
|
+
const seam = seamOf(ctx);
|
|
9923
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,动不了浏览器");
|
|
9038
9924
|
const held = ctx.get("workbenchDiyDebug")?.live(Date.now()) ?? null;
|
|
9039
9925
|
const sessionId = sessionIdOf(exec);
|
|
9040
9926
|
if (options.requirePact !== false) {
|
|
@@ -9044,7 +9930,7 @@ async function pageAt(ctx, exec, raw, options = {}) {
|
|
|
9044
9930
|
}
|
|
9045
9931
|
const command = cliCommandOf(ctx);
|
|
9046
9932
|
const session = asText(args["session"]) || "workbench";
|
|
9047
|
-
const handshake = await readyPageOf(
|
|
9933
|
+
const handshake = await readyPageOf(seam, command, session);
|
|
9048
9934
|
if (!handshake.ok && options.requireWorkbench !== false && !hostSurface().capabilities.pageDebug) throw new Error(desktopPageText(handshake.page));
|
|
9049
9935
|
if (!handshake.ok && !handshake.bound) throw new Error(`连不上浏览器:${handshake.reason}。先让用户把工作台那一页切到前台(只有第一次要这样);还不行就跑一次运行时自检(doctor)看扩展连没连上。`);
|
|
9050
9936
|
if (!handshake.ok && options.requireWorkbench !== false) {
|
|
@@ -9057,10 +9943,10 @@ async function pageAt(ctx, exec, raw, options = {}) {
|
|
|
9057
9943
|
if (options.requireBoard !== false && (board === "other" || board === "hidden")) throw new Error(boardGateText(situation, wanted, handshake.page, title));
|
|
9058
9944
|
return {
|
|
9059
9945
|
root,
|
|
9060
|
-
artifactsDir: artifactsDirOf(
|
|
9946
|
+
artifactsDir: artifactsDirOf(root),
|
|
9061
9947
|
unit: wanted,
|
|
9062
9948
|
args,
|
|
9063
|
-
|
|
9949
|
+
seam,
|
|
9064
9950
|
command,
|
|
9065
9951
|
session,
|
|
9066
9952
|
...surfaceOf(ctx)?.background === void 0 ? {} : { line: surfaceOf(ctx)?.background },
|
|
@@ -9220,8 +10106,8 @@ function registerDebugTools(ctx, tools, store) {
|
|
|
9220
10106
|
//#endregion
|
|
9221
10107
|
//#region src/modules/panels/model/diy-browser-act.ts
|
|
9222
10108
|
var ACT_TIMEOUT_MS = 15e3;
|
|
9223
|
-
async function actBrowser(
|
|
9224
|
-
const step = await runBrowserStep(
|
|
10109
|
+
async function actBrowser(seam, command, session, argv, timeoutMs = ACT_TIMEOUT_MS, line) {
|
|
10110
|
+
const step = await runBrowserStep(seam, command, [
|
|
9225
10111
|
"browser",
|
|
9226
10112
|
session,
|
|
9227
10113
|
...argv,
|
|
@@ -9244,10 +10130,10 @@ function actErrorOf(step) {
|
|
|
9244
10130
|
return step.stderr || step.stdout || "运行时报了个错";
|
|
9245
10131
|
}
|
|
9246
10132
|
var SWEEP_TIMEOUT_MS = 4e3;
|
|
9247
|
-
async function stopNetworkExact(
|
|
10133
|
+
async function stopNetworkExact(seam, file, timeoutMs = SWEEP_TIMEOUT_MS) {
|
|
9248
10134
|
const pidFile = quoteShellArg(`${file}.pid`);
|
|
9249
10135
|
const reaperFile = quoteShellArg(`${file}.reaper`);
|
|
9250
|
-
|
|
10136
|
+
const line = [
|
|
9251
10137
|
`p="$(cat ${pidFile} 2>/dev/null)"`,
|
|
9252
10138
|
`r="$(cat ${reaperFile} 2>/dev/null)"`,
|
|
9253
10139
|
`[ -n "$p" ] && pkill -P "$p" 2>/dev/null`,
|
|
@@ -9256,7 +10142,12 @@ async function stopNetworkExact(shell, file, timeoutMs = SWEEP_TIMEOUT_MS) {
|
|
|
9256
10142
|
`[ -n "$r" ] && kill "$r" 2>/dev/null`,
|
|
9257
10143
|
`if [ "$(cat ${pidFile} 2>/dev/null)" = "$p" ]; then rm -f ${pidFile} ${reaperFile}; fi`,
|
|
9258
10144
|
"true"
|
|
9259
|
-
].join("; ")
|
|
10145
|
+
].join("; ");
|
|
10146
|
+
return (await seam.run({
|
|
10147
|
+
command: line,
|
|
10148
|
+
intent: productWrite(),
|
|
10149
|
+
timeoutMs
|
|
10150
|
+
})).exitCode === 0;
|
|
9260
10151
|
}
|
|
9261
10152
|
//#endregion
|
|
9262
10153
|
//#region src/modules/panels/model/diy-logs.ts
|
|
@@ -9379,7 +10270,7 @@ function registerLogTools(ctx, tools) {
|
|
|
9379
10270
|
});
|
|
9380
10271
|
return {
|
|
9381
10272
|
page,
|
|
9382
|
-
step: await actBrowser(page.
|
|
10273
|
+
step: await actBrowser(page.seam, page.command, page.session, ["eval", script])
|
|
9383
10274
|
};
|
|
9384
10275
|
}
|
|
9385
10276
|
const watchTool = {
|
|
@@ -9632,7 +10523,7 @@ function cropPngFile(input, output, rect) {
|
|
|
9632
10523
|
reason: cropped.reason
|
|
9633
10524
|
};
|
|
9634
10525
|
try {
|
|
9635
|
-
|
|
10526
|
+
writeBytesFile(output, cropped.bytes);
|
|
9636
10527
|
} catch (error) {
|
|
9637
10528
|
return {
|
|
9638
10529
|
ok: false,
|
|
@@ -9647,6 +10538,44 @@ function cropPngFile(input, output, rect) {
|
|
|
9647
10538
|
};
|
|
9648
10539
|
}
|
|
9649
10540
|
//#endregion
|
|
10541
|
+
//#region src/modules/panels/model/diy-browser-clip.ts
|
|
10542
|
+
function finite(value) {
|
|
10543
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
10544
|
+
}
|
|
10545
|
+
function clipOf(value) {
|
|
10546
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
10547
|
+
const raw = value;
|
|
10548
|
+
const x = finite(raw["x"]);
|
|
10549
|
+
const y = finite(raw["y"]);
|
|
10550
|
+
const width = finite(raw["width"]);
|
|
10551
|
+
const height = finite(raw["height"]);
|
|
10552
|
+
if (x === void 0 || y === void 0 || width === void 0 || height === void 0) return void 0;
|
|
10553
|
+
if (x < 0 || y < 0 || width <= 0 || height <= 0) return void 0;
|
|
10554
|
+
return {
|
|
10555
|
+
x,
|
|
10556
|
+
y,
|
|
10557
|
+
width,
|
|
10558
|
+
height
|
|
10559
|
+
};
|
|
10560
|
+
}
|
|
10561
|
+
function deviceClipRect(clip, dpr) {
|
|
10562
|
+
const scale = Number.isFinite(dpr) && dpr > 0 ? dpr : 1;
|
|
10563
|
+
return {
|
|
10564
|
+
x: Math.max(0, Math.round(clip.x * scale)),
|
|
10565
|
+
y: Math.max(0, Math.round(clip.y * scale)),
|
|
10566
|
+
width: Math.max(1, Math.round(clip.width * scale)),
|
|
10567
|
+
height: Math.max(1, Math.round(clip.height * scale))
|
|
10568
|
+
};
|
|
10569
|
+
}
|
|
10570
|
+
function clipNote(clip, viewport) {
|
|
10571
|
+
if (viewport.width <= 0 || viewport.height <= 0) return "没读到视口尺寸(那条量 dpr / innerWidth 的 eval 没回来)—— 给的是整屏图,裁不了";
|
|
10572
|
+
if (clip.x + clip.width <= viewport.width && clip.y + clip.height <= viewport.height && viewport.width > 0) return "";
|
|
10573
|
+
return `要裁的那块 ${String(clip.x)},${String(clip.y)} ${String(clip.width)}×${String(clip.height)} 不在视口里(视口 ${String(viewport.width)}×${String(viewport.height)})—— 给的是整屏图`;
|
|
10574
|
+
}
|
|
10575
|
+
function defaultShotPath(dir, unit, stamp) {
|
|
10576
|
+
return join(dir, `${unit === "" ? "page" : unit}-${String(stamp)}.png`);
|
|
10577
|
+
}
|
|
10578
|
+
//#endregion
|
|
9650
10579
|
//#region src/modules/panels/model/diy-schema.ts
|
|
9651
10580
|
var UNIT_PROP = {
|
|
9652
10581
|
type: "string",
|
|
@@ -9718,42 +10647,6 @@ function argvOf(row, raw, unit) {
|
|
|
9718
10647
|
if (row.css.kind === "flag") return prefixValue(tokens, row.css.flag, card);
|
|
9719
10648
|
return prefixPositional(tokens, row.css.at, card, row.css.onlyWhenFirst);
|
|
9720
10649
|
}
|
|
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
10650
|
var BROWSER_TOOLS = [
|
|
9758
10651
|
{
|
|
9759
10652
|
tool: DIY_STATE_TOOL,
|
|
@@ -9934,7 +10827,7 @@ function parametersOf(row, command) {
|
|
|
9934
10827
|
};
|
|
9935
10828
|
}
|
|
9936
10829
|
async function viewportOf(page) {
|
|
9937
|
-
const parsed = jsonOf((await runBrowserStep(page.
|
|
10830
|
+
const parsed = jsonOf((await runBrowserStep(page.seam, page.command, [
|
|
9938
10831
|
"browser",
|
|
9939
10832
|
page.session,
|
|
9940
10833
|
"eval",
|
|
@@ -9960,8 +10853,8 @@ function hostOf$1(url) {
|
|
|
9960
10853
|
return "";
|
|
9961
10854
|
}
|
|
9962
10855
|
}
|
|
9963
|
-
async function onSiteOnce(
|
|
9964
|
-
const step = await runBrowserStep(
|
|
10856
|
+
async function onSiteOnce(seam, command, site, line) {
|
|
10857
|
+
const step = await runBrowserStep(seam, command, [
|
|
9965
10858
|
"browser",
|
|
9966
10859
|
BROWSER_READ_SESSION,
|
|
9967
10860
|
"get",
|
|
@@ -9971,7 +10864,7 @@ async function onSiteOnce(shell, command, site, line) {
|
|
|
9971
10864
|
], LOOK_TIMEOUT_MS, line);
|
|
9972
10865
|
const want = hostOf$1(site);
|
|
9973
10866
|
if (want === "" || hostOf$1(step.stdout.trim()) === want) return false;
|
|
9974
|
-
await runBrowserStep(
|
|
10867
|
+
await runBrowserStep(seam, command, [
|
|
9975
10868
|
"browser",
|
|
9976
10869
|
BROWSER_READ_SESSION,
|
|
9977
10870
|
"open",
|
|
@@ -9994,15 +10887,15 @@ function readRefusalOf(row, raw) {
|
|
|
9994
10887
|
async function runReadTool(row, ctx, raw, site) {
|
|
9995
10888
|
const refused = readRefusalOf(row, raw);
|
|
9996
10889
|
if (refused !== "") throw new Error(refused);
|
|
9997
|
-
const
|
|
9998
|
-
if (
|
|
10890
|
+
const seam = seamOf(ctx);
|
|
10891
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,驱动不了浏览器");
|
|
9999
10892
|
const command = cliCommandOf(ctx);
|
|
10000
10893
|
const requested = asNumber(raw["timeoutMs"]);
|
|
10001
10894
|
const timeoutMs = requested !== void 0 && requested > 0 ? requested : DEFAULT_TIMEOUT_MS$4;
|
|
10002
10895
|
const argv = stringsOf$1(raw["args"]);
|
|
10003
10896
|
const line = surfaceOf(ctx)?.background;
|
|
10004
|
-
const opened = await onSiteOnce(
|
|
10005
|
-
const step = await runBrowserStep(
|
|
10897
|
+
const opened = await onSiteOnce(seam, command, site, line);
|
|
10898
|
+
const step = await runBrowserStep(seam, command, [
|
|
10006
10899
|
"browser",
|
|
10007
10900
|
BROWSER_READ_SESSION,
|
|
10008
10901
|
...row.verb,
|
|
@@ -10037,22 +10930,26 @@ async function runBrowserTool(row, ctx, raw, exec) {
|
|
|
10037
10930
|
const clipAsked = row.clip === true && page.args["clip"] !== void 0;
|
|
10038
10931
|
let file = "";
|
|
10039
10932
|
let finalArgv = argv;
|
|
10933
|
+
let intent = productWrite();
|
|
10040
10934
|
if (row.clip === true) {
|
|
10041
10935
|
const given = argv[0] !== void 0 && !argv[0].startsWith("-") ? argv[0] : "";
|
|
10042
10936
|
if (given === "") {
|
|
10043
10937
|
file = defaultShotPath(page.artifactsDir, page.unit, Date.now());
|
|
10044
10938
|
finalArgv = [file, ...argv];
|
|
10045
10939
|
ensureDirOf(dirname(file));
|
|
10046
|
-
} else
|
|
10940
|
+
} else {
|
|
10941
|
+
file = given;
|
|
10942
|
+
intent = pathsWrite([file]);
|
|
10943
|
+
}
|
|
10047
10944
|
}
|
|
10048
|
-
const step = await runBrowserStep(page.
|
|
10945
|
+
const step = await runBrowserStep(page.seam, page.command, [
|
|
10049
10946
|
"browser",
|
|
10050
10947
|
page.session,
|
|
10051
10948
|
...row.verb,
|
|
10052
10949
|
...finalArgv,
|
|
10053
10950
|
"--window",
|
|
10054
10951
|
"background"
|
|
10055
|
-
], timeoutMs, page.line);
|
|
10952
|
+
], timeoutMs, page.line, intent);
|
|
10056
10953
|
const shot = {};
|
|
10057
10954
|
if (row.clip === true) {
|
|
10058
10955
|
shot["file"] = step.ok ? file : "";
|
|
@@ -10138,11 +11035,15 @@ function specTool(ctx) {
|
|
|
10138
11035
|
const args = asRecord$1(raw);
|
|
10139
11036
|
const argv = specArgvOf(asText(args["command"]));
|
|
10140
11037
|
if (argv.length === 0) throw new Error("给一条命令,site 与名字两个词 —— 例 `inventory publish`(`inventory/publish` 也认)。");
|
|
10141
|
-
const
|
|
10142
|
-
if (
|
|
11038
|
+
const seam = seamOf(ctx);
|
|
11039
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,问不了命令的形状");
|
|
10143
11040
|
const asked = asNumber(args["timeoutMs"]);
|
|
10144
11041
|
const timeoutMs = asked !== void 0 && asked > 0 ? asked : SPEC_TIMEOUT_MS;
|
|
10145
|
-
const result = await
|
|
11042
|
+
const result = await seam.run({
|
|
11043
|
+
command: (surfaceOf(ctx)?.background ?? plainLine)(cliCommandOf(ctx), argv),
|
|
11044
|
+
intent: productWrite(),
|
|
11045
|
+
timeoutMs
|
|
11046
|
+
});
|
|
10146
11047
|
const spec = jsonOf(result.stdout);
|
|
10147
11048
|
return plainJson({
|
|
10148
11049
|
ok: result.exitCode === 0 && !result.timedOut,
|
|
@@ -10160,8 +11061,139 @@ function registerSpecTool(ctx, tools) {
|
|
|
10160
11061
|
return [tools.register(specTool(ctx))];
|
|
10161
11062
|
}
|
|
10162
11063
|
//#endregion
|
|
11064
|
+
//#region src/shared/api/host/diy-release-staging.ts
|
|
11065
|
+
var STAGING_ROOT = "dsh-wb-release";
|
|
11066
|
+
function stagingDirOf(unit) {
|
|
11067
|
+
return join(tmpdir(), STAGING_ROOT, unit.replace(/[^A-Za-z0-9._-]+/g, "-"));
|
|
11068
|
+
}
|
|
11069
|
+
function journalFileOf(staging, unit) {
|
|
11070
|
+
return join(staging, "releases", `${unit}.json`);
|
|
11071
|
+
}
|
|
11072
|
+
function prepareStaging(unit, resume) {
|
|
11073
|
+
const staging = stagingDirOf(unit);
|
|
11074
|
+
if (!resume) rmSync(staging, {
|
|
11075
|
+
recursive: true,
|
|
11076
|
+
force: true
|
|
11077
|
+
});
|
|
11078
|
+
mkdirSync(staging, { recursive: true });
|
|
11079
|
+
return staging;
|
|
11080
|
+
}
|
|
11081
|
+
function cleanupStaging(unit) {
|
|
11082
|
+
rmSync(stagingDirOf(unit), {
|
|
11083
|
+
recursive: true,
|
|
11084
|
+
force: true
|
|
11085
|
+
});
|
|
11086
|
+
}
|
|
11087
|
+
function adoptTarballFile(file, unit) {
|
|
11088
|
+
if (file === "" || !existsSync(file)) return void 0;
|
|
11089
|
+
const dest = join(workbenchRoot(), "artifacts", unit, basename(file));
|
|
11090
|
+
if (dest === file) return dest;
|
|
11091
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
11092
|
+
copyFileSync(file, dest);
|
|
11093
|
+
rmSync(file, { force: true });
|
|
11094
|
+
return dest;
|
|
11095
|
+
}
|
|
11096
|
+
function mirrorJournal(staging, unit) {
|
|
11097
|
+
const from = journalFileOf(staging, unit);
|
|
11098
|
+
if (!existsSync(from)) return;
|
|
11099
|
+
const to = join(workbenchRoot(), "releases", `${unit}.json`);
|
|
11100
|
+
mkdirSync(dirname(to), { recursive: true });
|
|
11101
|
+
copyFileSync(from, to);
|
|
11102
|
+
}
|
|
11103
|
+
//#endregion
|
|
10163
11104
|
//#region src/modules/panels/model/diy-publish.ts
|
|
10164
11105
|
var PUBLISH_TIMEOUT_MS = 18e5;
|
|
11106
|
+
function adoptTarball(payload, unit) {
|
|
11107
|
+
const file = asText(payload["file"]);
|
|
11108
|
+
const dest = adoptTarballFile(file, unit);
|
|
11109
|
+
if (dest === void 0) return payload;
|
|
11110
|
+
const install = asText(payload["install"]);
|
|
11111
|
+
return {
|
|
11112
|
+
...payload,
|
|
11113
|
+
file: dest,
|
|
11114
|
+
...install === "" ? {} : { install: install.split(file).join(dest) }
|
|
11115
|
+
};
|
|
11116
|
+
}
|
|
11117
|
+
function needsUserPayload(payload) {
|
|
11118
|
+
const needs = asRecord$1(payload["needsUser"]);
|
|
11119
|
+
const stepText = asText(needs["text"]);
|
|
11120
|
+
const command = asText(needs["command"]);
|
|
11121
|
+
return plainJson({
|
|
11122
|
+
...payload,
|
|
11123
|
+
text: [
|
|
11124
|
+
`还差一步:${stepText}`,
|
|
11125
|
+
...command === "" ? [] : [`命令:${command}`],
|
|
11126
|
+
"做完跟我说「好了」,我接着发。"
|
|
11127
|
+
].join("\n")
|
|
11128
|
+
});
|
|
11129
|
+
}
|
|
11130
|
+
function resumablePayload(payload, unit) {
|
|
11131
|
+
const resumeInfo = asRecord$1(payload["resume"]);
|
|
11132
|
+
return plainJson({
|
|
11133
|
+
...payload,
|
|
11134
|
+
text: [`发布中断在第 ${asText(resumeInfo["phase"])} 步:${asText(payload["text"])}`, `再调一次 workbench_diy_publish(unit=${unit}, resume=true)就能接着发。`].join("\n")
|
|
11135
|
+
});
|
|
11136
|
+
}
|
|
11137
|
+
function outcomeOf$1(result, unit) {
|
|
11138
|
+
const payload = jsonOf(result.stdout);
|
|
11139
|
+
if (result.exitCode === 3 && payload !== void 0) return { payload: needsUserPayload(payload) };
|
|
11140
|
+
if (result.exitCode === 4 && payload !== void 0) return { payload: resumablePayload(payload, unit) };
|
|
11141
|
+
if (result.exitCode !== 0 || payload === void 0) {
|
|
11142
|
+
const tail = `${result.stderr}\n${result.stdout}`.trim().split("\n").slice(-6).join(" | ");
|
|
11143
|
+
return { error: /* @__PURE__ */ new Error(`发布没走通(exit ${String(result.exitCode)})${tail === "" ? "" : `:${tail}`}`) };
|
|
11144
|
+
}
|
|
11145
|
+
return { payload };
|
|
11146
|
+
}
|
|
11147
|
+
function successPayloadOf(payload, found, options) {
|
|
11148
|
+
const name = asText(payload["name"]);
|
|
11149
|
+
const released = asText(payload["version"]);
|
|
11150
|
+
const registry = asText(payload["registry"]);
|
|
11151
|
+
const file = asText(payload["file"]);
|
|
11152
|
+
const install = asText(payload["install"]);
|
|
11153
|
+
const git = asRecord$1(payload["git"]);
|
|
11154
|
+
const targetOfPayload = asText(payload["target"]);
|
|
11155
|
+
const text = options.verify ? [
|
|
11156
|
+
`已核过 ${name}@${released}`,
|
|
11157
|
+
`安装:${install}`,
|
|
11158
|
+
"没有重新发布。"
|
|
11159
|
+
].join("\n") : payload["refresh"] === true ? [payload["refreshed"] === true ? `已刷新 ${name} 的市集元数据(版本 ${released} 不变)` : `${name} 的市集元数据本来就是这样,没动`, `市场索引:${asText(asRecord$1(payload["market"])["url"])}`].join("\n") : targetOfPayload === "tarball" ? [
|
|
11160
|
+
`安装包在 ${file}`,
|
|
11161
|
+
`本机装:${install}`,
|
|
11162
|
+
"文件可以从对话里带走,发给同事。"
|
|
11163
|
+
].join("\n") : targetOfPayload === "git" ? [
|
|
11164
|
+
`已推到 ${asText(git["url"])}(tag ${asText(git["tag"])})`,
|
|
11165
|
+
`安装:${install}`,
|
|
11166
|
+
"已核过 tag 里的产物。"
|
|
11167
|
+
].join("\n") : targetOfPayload === "market" ? [
|
|
11168
|
+
`已发到市场索引 ${asText(asRecord$1(payload["market"])["url"])}`,
|
|
11169
|
+
`安装:${install}`,
|
|
11170
|
+
"已从索引仓库侧核过产物。"
|
|
11171
|
+
].join("\n") : targetOfPayload === "local" ? "没发,留在本机。" : [
|
|
11172
|
+
`已发布 ${name}@${released} → ${registry}`,
|
|
11173
|
+
`同事安装:${install}`,
|
|
11174
|
+
"包内容已经从源上核过(dist/host.js / client.js / cordis.patch.yml 都在)。"
|
|
11175
|
+
].join("\n");
|
|
11176
|
+
const changelog = asRecord$1(payload["changelog"]);
|
|
11177
|
+
const changelogLine = asText(changelog["action"]) === "written" ? `更新日志:已写进 ${CHANGELOG_FILE} 的 ${asText(changelog["version"])}` : asText(changelog["action"]) === "kept" ? `更新日志:这一版已有(${asText(changelog["version"])}),没动` : "";
|
|
11178
|
+
const labelList = Array.isArray(payload["labels"]) ? payload["labels"].map((one) => asText(one)).filter((one) => one !== "") : [];
|
|
11179
|
+
const tail = [changelogLine, labelList.length === 0 || targetOfPayload !== "market" && payload["refresh"] !== true ? "" : `标签:${labelList.join(" · ")}`].filter((one) => one !== "").join("\n");
|
|
11180
|
+
return plainJson({
|
|
11181
|
+
...payload,
|
|
11182
|
+
unit: found.unit,
|
|
11183
|
+
text: tail === "" ? text : `${text}\n${tail}`
|
|
11184
|
+
});
|
|
11185
|
+
}
|
|
11186
|
+
function withPhasePayloads(first, second) {
|
|
11187
|
+
const merged = {
|
|
11188
|
+
...first,
|
|
11189
|
+
...second
|
|
11190
|
+
};
|
|
11191
|
+
delete merged["stop"];
|
|
11192
|
+
if (merged["changelog"] === void 0 && first["changelog"] !== void 0) merged["changelog"] = first["changelog"];
|
|
11193
|
+
if (merged["changes"] === void 0 && first["changes"] !== void 0) merged["changes"] = first["changes"];
|
|
11194
|
+
if (merged["labels"] === void 0 && first["labels"] !== void 0) merged["labels"] = first["labels"];
|
|
11195
|
+
return merged;
|
|
11196
|
+
}
|
|
10165
11197
|
function registerPublishTool(ctx, tools, deps) {
|
|
10166
11198
|
const publishTool = {
|
|
10167
11199
|
name: DIY_PUBLISH_TOOL,
|
|
@@ -10235,20 +11267,22 @@ function registerPublishTool(ctx, tools, deps) {
|
|
|
10235
11267
|
async execute(raw, exec) {
|
|
10236
11268
|
const { root } = await workspaceOf(ctx, exec);
|
|
10237
11269
|
const found = deps.resolve(root, raw);
|
|
10238
|
-
const
|
|
10239
|
-
if (
|
|
11270
|
+
const seam = seamOf(ctx);
|
|
11271
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,发布跑不起来");
|
|
10240
11272
|
const bin = toolchainBinOf(root) ?? toolchainBinOf(found.dir);
|
|
10241
11273
|
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
|
|
11274
|
+
const args = asRecord$1(raw);
|
|
11275
|
+
const target = asText(args["target"]);
|
|
11276
|
+
const source = asText(args["source"]);
|
|
11277
|
+
const version = asText(args["version"]);
|
|
11278
|
+
const license = asText(args["license"]);
|
|
11279
|
+
const changelogNote = asText(args["changelogNote"]);
|
|
11280
|
+
const changelogType = asText(args["changelogType"]);
|
|
11281
|
+
const labels = labelsOf(args["labels"]);
|
|
11282
|
+
let author = asText(args["author"]);
|
|
11283
|
+
const verify = args["verify"] === true;
|
|
11284
|
+
const resume = args["resume"] === true;
|
|
11285
|
+
const refresh = args["refresh"] === true;
|
|
10252
11286
|
if (!verify && target !== "local" && author === "" && (deps.previewOf(found.unit)?.author ?? "") === "") author = await deps.inferredAuthorOf(found.unit) ?? "";
|
|
10253
11287
|
const argv = [
|
|
10254
11288
|
bin,
|
|
@@ -10268,70 +11302,43 @@ function registerPublishTool(ctx, tools, deps) {
|
|
|
10268
11302
|
if (changelogNote !== "") argv.push("--changelog-note", changelogNote);
|
|
10269
11303
|
if (changelogType !== "") argv.push("--changelog-type", changelogType);
|
|
10270
11304
|
for (const label of labels) argv.push("--label", label);
|
|
10271
|
-
if (
|
|
10272
|
-
const
|
|
11305
|
+
if (refresh) argv.push("--refresh");
|
|
11306
|
+
const run = async (extra, request) => seam.run({
|
|
11307
|
+
command: [...argv, ...extra].map((one) => quoteShellArg(one)).join(" "),
|
|
11308
|
+
intent: request.intent,
|
|
11309
|
+
...request.session === void 0 ? {} : { session: request.session },
|
|
11310
|
+
...request.env === void 0 ? {} : { env: request.env },
|
|
10273
11311
|
workdir: root,
|
|
10274
11312
|
timeoutMs: PUBLISH_TIMEOUT_MS
|
|
10275
11313
|
});
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
|
|
10280
|
-
const command = asText(needs["command"]);
|
|
10281
|
-
return plainJson({
|
|
10282
|
-
...payload,
|
|
10283
|
-
text: [
|
|
10284
|
-
`还差一步:${stepText}`,
|
|
10285
|
-
...command === "" ? [] : [`命令:${command}`],
|
|
10286
|
-
"做完跟我说「好了」,我接着发。"
|
|
10287
|
-
].join("\n")
|
|
10288
|
-
});
|
|
10289
|
-
}
|
|
10290
|
-
if (result.exitCode === 4 && payload !== void 0) {
|
|
10291
|
-
const resumeInfo = asRecord$1(payload["resume"]);
|
|
10292
|
-
return plainJson({
|
|
10293
|
-
...payload,
|
|
10294
|
-
text: [`发布中断在第 ${asText(resumeInfo["phase"])} 步:${asText(payload["text"])}`, `再调一次 workbench_diy_publish(unit=${found.unit}, resume=true)就能接着发。`].join("\n")
|
|
10295
|
-
});
|
|
11314
|
+
if (verify || refresh) {
|
|
11315
|
+
const outcome = outcomeOf$1(await run([], { intent: productWrite() }), found.unit);
|
|
11316
|
+
if ("error" in outcome) throw outcome.error;
|
|
11317
|
+
return plainJson(successPayloadOf(outcome.payload, found, { verify }));
|
|
10296
11318
|
}
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
11319
|
+
const staging = prepareStaging(found.unit, resume);
|
|
11320
|
+
const env = { DSH_WB_RELEASE_HOME: staging };
|
|
11321
|
+
const session = exec.agent?.session;
|
|
11322
|
+
const firstOutcome = outcomeOf$1(await run(["--workspace-only"], {
|
|
11323
|
+
intent: workspaceWrite(),
|
|
11324
|
+
...session === void 0 ? {} : { session },
|
|
11325
|
+
env
|
|
11326
|
+
}), found.unit);
|
|
11327
|
+
mirrorJournal(staging, found.unit);
|
|
11328
|
+
if ("error" in firstOutcome) throw firstOutcome.error;
|
|
11329
|
+
const first = firstOutcome.payload;
|
|
11330
|
+
if (first["stop"] !== "workspace-only" || asText(first["target"]) === "local") {
|
|
11331
|
+
cleanupStaging(found.unit);
|
|
11332
|
+
return plainJson(first);
|
|
10300
11333
|
}
|
|
10301
|
-
const
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
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
|
-
});
|
|
11334
|
+
const indexOutcome = outcomeOf$1(await run(["--index-only"], {
|
|
11335
|
+
intent: productWrite(),
|
|
11336
|
+
env
|
|
11337
|
+
}), found.unit);
|
|
11338
|
+
mirrorJournal(staging, found.unit);
|
|
11339
|
+
if ("error" in indexOutcome) throw indexOutcome.error;
|
|
11340
|
+
cleanupStaging(found.unit);
|
|
11341
|
+
return plainJson(successPayloadOf(adoptTarball(withPhasePayloads(first, indexOutcome.payload), found.unit), found, { verify: false }));
|
|
10335
11342
|
}
|
|
10336
11343
|
};
|
|
10337
11344
|
return tools.register(publishTool);
|
|
@@ -11339,14 +12346,14 @@ function changelogOf(dir, version, note, kind) {
|
|
|
11339
12346
|
//#endregion
|
|
11340
12347
|
//#region src/modules/panels/model/diy-observers.ts
|
|
11341
12348
|
async function stopObservers(ctx, workspace, session = BROWSER_SCREEN_SESSION) {
|
|
11342
|
-
const
|
|
11343
|
-
if (workspace === null || workspace === void 0 || workspace === "" ||
|
|
12349
|
+
const seam = seamOf(ctx);
|
|
12350
|
+
if (workspace === null || workspace === void 0 || workspace === "" || seam === void 0) return {
|
|
11344
12351
|
net: false,
|
|
11345
12352
|
logs: false
|
|
11346
12353
|
};
|
|
11347
|
-
const file = join(artifactsDirOf(
|
|
12354
|
+
const file = join(artifactsDirOf(workspace), `net-${session}.jsonl`);
|
|
11348
12355
|
const command = cliCommandOf(ctx);
|
|
11349
|
-
const [net, step] = await Promise.all([stopNetworkExact(
|
|
12356
|
+
const [net, step] = await Promise.all([stopNetworkExact(seam, file), actBrowser(seam, command, session, ["eval", LOGS_STOP_SCRIPT], SWEEP_TIMEOUT_MS)]);
|
|
11350
12357
|
return {
|
|
11351
12358
|
net,
|
|
11352
12359
|
logs: typeof step.payload?.["unhooked"] === "number"
|
|
@@ -11498,7 +12505,7 @@ var REFUSAL$1 = {
|
|
|
11498
12505
|
"not-owner": "这场调试不是这个会话的"
|
|
11499
12506
|
};
|
|
11500
12507
|
function debugActResource(deps) {
|
|
11501
|
-
const now = () => deps.now?.() ??
|
|
12508
|
+
const now$2 = () => deps.now?.() ?? now();
|
|
11502
12509
|
const settled = (intent, delivered, summary, state) => ({
|
|
11503
12510
|
ok: true,
|
|
11504
12511
|
intent,
|
|
@@ -11519,7 +12526,7 @@ function debugActResource(deps) {
|
|
|
11519
12526
|
ok: false,
|
|
11520
12527
|
text: "现在没有调试态 —— 没什么可暂停 / 继续 / 结束的"
|
|
11521
12528
|
};
|
|
11522
|
-
const at = now();
|
|
12529
|
+
const at = now$2();
|
|
11523
12530
|
const held = store.live(at);
|
|
11524
12531
|
if (held === null) return {
|
|
11525
12532
|
ok: false,
|
|
@@ -11532,8 +12539,8 @@ function debugActResource(deps) {
|
|
|
11532
12539
|
};
|
|
11533
12540
|
const notice = deps.notice?.(intent, move.state) ?? debugNotice(intent, move.state);
|
|
11534
12541
|
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())));
|
|
12542
|
+
if (intent !== "stop") return settled(intent, delivered, notice.summary, store.live(now$2()));
|
|
12543
|
+
return (store.settle?.() ?? Promise.resolve()).then(() => settled(intent, delivered, notice.summary, store.live(now$2())));
|
|
11537
12544
|
}
|
|
11538
12545
|
};
|
|
11539
12546
|
}
|
|
@@ -12192,7 +13199,7 @@ async function takeShotOf(input, held, n) {
|
|
|
12192
13199
|
const path = shotFileOf(input.captureFile, held.unit, n);
|
|
12193
13200
|
const at = Date.now();
|
|
12194
13201
|
try {
|
|
12195
|
-
const step = await actBrowser(input.
|
|
13202
|
+
const step = await actBrowser(input.seam, input.command, BROWSER_RECORD_SESSION, ["screenshot", path]);
|
|
12196
13203
|
if (step.ok) input.store.shotTaken(input.sessionId, {
|
|
12197
13204
|
n,
|
|
12198
13205
|
at,
|
|
@@ -12214,7 +13221,7 @@ async function takeShotOf(input, held, n) {
|
|
|
12214
13221
|
}, Date.now());
|
|
12215
13222
|
}
|
|
12216
13223
|
}
|
|
12217
|
-
async function startRecordCapture(
|
|
13224
|
+
async function startRecordCapture(seam, command, session, file, makeLine = plainLine) {
|
|
12218
13225
|
const bin = command === "" ? "runtime" : command;
|
|
12219
13226
|
const arm = makeLine(bin, [
|
|
12220
13227
|
"browser",
|
|
@@ -12234,7 +13241,7 @@ async function startRecordCapture(shell, command, session, file, makeLine = plai
|
|
|
12234
13241
|
"--window",
|
|
12235
13242
|
"background"
|
|
12236
13243
|
]);
|
|
12237
|
-
|
|
13244
|
+
const line = [
|
|
12238
13245
|
`f=${quoteShellArg(file)}`,
|
|
12239
13246
|
`r=${quoteShellArg(`${file}.round`)}`,
|
|
12240
13247
|
": > \"$f\"",
|
|
@@ -12242,11 +13249,16 @@ async function startRecordCapture(shell, command, session, file, makeLine = plai
|
|
|
12242
13249
|
`( ${arm} >> "$f" 2>/dev/null & p=$!; sleep ${String(CAPTURE_ARM_S)}; kill "$p" 2>/dev/null )`,
|
|
12243
13250
|
`( while kill -0 ${HOST_PID} 2>/dev/null; do if ${snap} > "$r" 2>/dev/null; then printf ok > "$f.first"; else printf fail > "$f.first"; fi; if [ -s "$r" ]; then if command -v node >/dev/null 2>&1; then tr -d '\\n' < "$r" | node -e ${quoteShellArg(DROP_STATIC_JS)} ${quoteShellArg(STATIC_URL_PATTERN)} >> "$f"; else tr -d '\\n' < "$r" >> "$f"; printf '\\n' >> "$f"; fi; fi; rm -f "$r"; sleep ${String(CAPTURE_POLL_S)}; done & echo $! > "$f.pid" ) & echo $! > "$f.reaper"`,
|
|
12244
13251
|
"kill -0 \"$(cat \"$f.pid\" 2>/dev/null)\" 2>/dev/null"
|
|
12245
|
-
].join("; ")
|
|
13252
|
+
].join("; ");
|
|
13253
|
+
return (await seam.run({
|
|
13254
|
+
command: line,
|
|
13255
|
+
intent: productWrite(),
|
|
13256
|
+
timeoutMs: START_TIMEOUT_MS
|
|
13257
|
+
})).exitCode === 0;
|
|
12246
13258
|
}
|
|
12247
13259
|
async function releaseLeftoversOf(input) {
|
|
12248
13260
|
await releaseQuietly({
|
|
12249
|
-
|
|
13261
|
+
seam: input.seam,
|
|
12250
13262
|
command: input.command,
|
|
12251
13263
|
session: BROWSER_RECORD_SESSION,
|
|
12252
13264
|
url: input.url,
|
|
@@ -12255,14 +13267,14 @@ async function releaseLeftoversOf(input) {
|
|
|
12255
13267
|
}
|
|
12256
13268
|
async function stopCaptureQuiet(input) {
|
|
12257
13269
|
try {
|
|
12258
|
-
return await stopNetworkExact(input.
|
|
13270
|
+
return await stopNetworkExact(input.seam, input.captureFile);
|
|
12259
13271
|
} catch {
|
|
12260
13272
|
return false;
|
|
12261
13273
|
}
|
|
12262
13274
|
}
|
|
12263
13275
|
async function closeRecordTabQuiet(input) {
|
|
12264
13276
|
try {
|
|
12265
|
-
return await closeRecordTab(input.
|
|
13277
|
+
return await closeRecordTab(input.seam, input.command, BROWSER_RECORD_SESSION, input.url);
|
|
12266
13278
|
} catch {
|
|
12267
13279
|
return false;
|
|
12268
13280
|
}
|
|
@@ -12290,7 +13302,7 @@ async function watchFinishStepOf(input) {
|
|
|
12290
13302
|
let page;
|
|
12291
13303
|
const started = Date.now();
|
|
12292
13304
|
try {
|
|
12293
|
-
page = recordPageOf((await actBrowser(input.
|
|
13305
|
+
page = recordPageOf((await actBrowser(input.seam, input.command, BROWSER_RECORD_SESSION, ["eval", RECORD_READ_SCRIPT])).payload);
|
|
12294
13306
|
} catch {
|
|
12295
13307
|
return {
|
|
12296
13308
|
stop: false,
|
|
@@ -12360,26 +13372,38 @@ function watchRecordFinish(input) {
|
|
|
12360
13372
|
};
|
|
12361
13373
|
setTimeout(tick, 0);
|
|
12362
13374
|
}
|
|
12363
|
-
async function captureLinesOf(
|
|
12364
|
-
const step = await
|
|
13375
|
+
async function captureLinesOf(seam, file, timeoutMs = START_TIMEOUT_MS) {
|
|
13376
|
+
const step = await seam.run({
|
|
13377
|
+
command: `wc -l < ${quoteShellArg(file)} 2>/dev/null || echo 0`,
|
|
13378
|
+
intent: productWrite(),
|
|
13379
|
+
timeoutMs
|
|
13380
|
+
});
|
|
12365
13381
|
const value = Number.parseInt(step.stdout.trim() === "" ? "0" : step.stdout.trim(), 10);
|
|
12366
13382
|
return Number.isFinite(value) ? value : 0;
|
|
12367
13383
|
}
|
|
12368
|
-
async function captureRoundOf(
|
|
12369
|
-
const value = (await
|
|
13384
|
+
async function captureRoundOf(seam, file, timeoutMs = START_TIMEOUT_MS) {
|
|
13385
|
+
const value = (await seam.run({
|
|
13386
|
+
command: `cat ${quoteShellArg(`${file}.first`)} 2>/dev/null || true`,
|
|
13387
|
+
intent: productWrite(),
|
|
13388
|
+
timeoutMs
|
|
13389
|
+
})).stdout.trim();
|
|
12370
13390
|
return value === "ok" || value === "fail" ? value : "";
|
|
12371
13391
|
}
|
|
12372
|
-
async function captureTextOf(
|
|
12373
|
-
const step = await
|
|
13392
|
+
async function captureTextOf(seam, file, timeoutMs = START_TIMEOUT_MS) {
|
|
13393
|
+
const step = await seam.run({
|
|
13394
|
+
command: `tail -n ${String(CAPTURE_TAIL_LINES)} ${quoteShellArg(file)} 2>/dev/null || true`,
|
|
13395
|
+
intent: productWrite(),
|
|
13396
|
+
timeoutMs
|
|
13397
|
+
});
|
|
12374
13398
|
return {
|
|
12375
13399
|
text: step.stdout,
|
|
12376
13400
|
truncated: step.truncated
|
|
12377
13401
|
};
|
|
12378
13402
|
}
|
|
12379
|
-
async function captureReadOf(
|
|
12380
|
-
const totalLines = await captureLinesOf(
|
|
12381
|
-
const read = await captureTextOf(
|
|
12382
|
-
const round = await captureRoundOf(
|
|
13403
|
+
async function captureReadOf(seam, file, timeoutMs = START_TIMEOUT_MS) {
|
|
13404
|
+
const totalLines = await captureLinesOf(seam, file, timeoutMs);
|
|
13405
|
+
const read = await captureTextOf(seam, file, timeoutMs);
|
|
13406
|
+
const round = await captureRoundOf(seam, file, timeoutMs);
|
|
12383
13407
|
return {
|
|
12384
13408
|
text: read.text,
|
|
12385
13409
|
totalLines,
|
|
@@ -12388,14 +13412,24 @@ async function captureReadOf(shell, file, timeoutMs = START_TIMEOUT_MS) {
|
|
|
12388
13412
|
round
|
|
12389
13413
|
};
|
|
12390
13414
|
}
|
|
12391
|
-
async function keepCaptureOf(
|
|
12392
|
-
|
|
13415
|
+
async function keepCaptureOf(seam, file, timeoutMs = START_TIMEOUT_MS) {
|
|
13416
|
+
const line = `[ -f ${quoteShellArg(file)} ] && mv ${quoteShellArg(file)} ${quoteShellArg(lastCaptureFileOf(file))} && [ -s ${quoteShellArg(lastCaptureFileOf(file))} ]`;
|
|
13417
|
+
return (await seam.run({
|
|
13418
|
+
command: line,
|
|
13419
|
+
intent: productWrite(),
|
|
13420
|
+
timeoutMs
|
|
13421
|
+
})).exitCode === 0;
|
|
12393
13422
|
}
|
|
12394
|
-
async function captureLinesWithKeyOf(
|
|
13423
|
+
async function captureLinesWithKeyOf(seam, file, key, timeoutMs = START_TIMEOUT_MS) {
|
|
12395
13424
|
const quoted = quoteShellArg(file);
|
|
12396
13425
|
const compact = quoteShellArg(`"key":"${key}"`);
|
|
12397
13426
|
const spaced = quoteShellArg(`"key": "${key}"`);
|
|
12398
|
-
|
|
13427
|
+
const line = [`[ -f ${quoted} ] || exit 0`, `grep -m 20 -F ${compact} ${quoted} 2>/dev/null || grep -m 20 -F ${spaced} ${quoted} 2>/dev/null || true`].join("; ");
|
|
13428
|
+
return (await seam.run({
|
|
13429
|
+
command: line,
|
|
13430
|
+
intent: productWrite(),
|
|
13431
|
+
timeoutMs
|
|
13432
|
+
})).stdout;
|
|
12399
13433
|
}
|
|
12400
13434
|
function bundleOf(capture, page, state) {
|
|
12401
13435
|
const entries = captureEntriesOf(capture.text);
|
|
@@ -12505,32 +13539,32 @@ function tabsOf(raw) {
|
|
|
12505
13539
|
return null;
|
|
12506
13540
|
}
|
|
12507
13541
|
}
|
|
12508
|
-
async function closeRecordTab(
|
|
12509
|
-
const pages = tabsOf((await actBrowser(
|
|
13542
|
+
async function closeRecordTab(seam, command, session, url) {
|
|
13543
|
+
const pages = tabsOf((await actBrowser(seam, command, session, ["tab", "list"])).stdout);
|
|
12510
13544
|
if (pages !== null && pages.length === 0) return true;
|
|
12511
13545
|
const pick = pages?.find((one) => typeof one["url"] === "string" && one["url"] === url) ?? pages?.[0];
|
|
12512
13546
|
const page = pick === void 0 ? "" : typeof pick["page"] === "string" ? pick["page"] : "";
|
|
12513
13547
|
if (page !== "") {
|
|
12514
|
-
if ((await actBrowser(
|
|
13548
|
+
if ((await actBrowser(seam, command, session, [
|
|
12515
13549
|
"tab",
|
|
12516
13550
|
"close",
|
|
12517
13551
|
page
|
|
12518
13552
|
])).ok) {
|
|
12519
|
-
for (const one of blanksOf(pages, page)) await actBrowser(
|
|
13553
|
+
for (const one of blanksOf(pages, page)) await actBrowser(seam, command, session, [
|
|
12520
13554
|
"tab",
|
|
12521
13555
|
"close",
|
|
12522
13556
|
one
|
|
12523
13557
|
]);
|
|
12524
|
-
await actBrowser(
|
|
13558
|
+
await actBrowser(seam, command, session, ["close"]);
|
|
12525
13559
|
return true;
|
|
12526
13560
|
}
|
|
12527
13561
|
}
|
|
12528
|
-
return (await actBrowser(
|
|
13562
|
+
return (await actBrowser(seam, command, session, ["close"])).ok;
|
|
12529
13563
|
}
|
|
12530
13564
|
async function releaseRecord(input) {
|
|
12531
|
-
const capture = await stopNetworkExact(input.
|
|
13565
|
+
const capture = await stopNetworkExact(input.seam, input.captureFile);
|
|
12532
13566
|
return {
|
|
12533
|
-
tab: await closeRecordTab(input.
|
|
13567
|
+
tab: await closeRecordTab(input.seam, input.command, input.session, input.url),
|
|
12534
13568
|
capture
|
|
12535
13569
|
};
|
|
12536
13570
|
}
|
|
@@ -12552,12 +13586,12 @@ function samePageOf(here, want) {
|
|
|
12552
13586
|
return here !== "" && norm(here) === norm(want);
|
|
12553
13587
|
}
|
|
12554
13588
|
async function whereAtOf(input) {
|
|
12555
|
-
const step = await actBrowser(input.
|
|
13589
|
+
const step = await actBrowser(input.seam, input.command, input.session, ["eval", PAGE_HREF_SCRIPT]);
|
|
12556
13590
|
const href = step.payload === void 0 ? "" : step.payload["href"];
|
|
12557
13591
|
return typeof href === "string" ? href : "";
|
|
12558
13592
|
}
|
|
12559
13593
|
async function settleOf(input) {
|
|
12560
|
-
await runBrowserStep(input.
|
|
13594
|
+
await runBrowserStep(input.seam, input.command, [
|
|
12561
13595
|
"browser",
|
|
12562
13596
|
input.session,
|
|
12563
13597
|
"wait",
|
|
@@ -12567,7 +13601,7 @@ async function settleOf(input) {
|
|
|
12567
13601
|
}
|
|
12568
13602
|
async function openRecordPage(input) {
|
|
12569
13603
|
await releaseQuietly(input);
|
|
12570
|
-
const opened = await runBrowserStep(input.
|
|
13604
|
+
const opened = await runBrowserStep(input.seam, input.command, [
|
|
12571
13605
|
"browser",
|
|
12572
13606
|
input.session,
|
|
12573
13607
|
"tab",
|
|
@@ -12584,7 +13618,7 @@ async function openRecordPage(input) {
|
|
|
12584
13618
|
await settleOf(input);
|
|
12585
13619
|
let at = await whereAtOf(input);
|
|
12586
13620
|
if (!samePageOf(at, input.url)) {
|
|
12587
|
-
await runBrowserStep(input.
|
|
13621
|
+
await runBrowserStep(input.seam, input.command, [
|
|
12588
13622
|
"browser",
|
|
12589
13623
|
input.session,
|
|
12590
13624
|
"open",
|
|
@@ -12652,7 +13686,7 @@ function registerRecordTools(ctx, store) {
|
|
|
12652
13686
|
output: OUTPUT$3,
|
|
12653
13687
|
async execute(raw, exec) {
|
|
12654
13688
|
const { root } = await workspaceOf(ctx, exec);
|
|
12655
|
-
const artifactsDir = artifactsDirOf(
|
|
13689
|
+
const artifactsDir = artifactsDirOf(root);
|
|
12656
13690
|
const args = asRecord$1(raw);
|
|
12657
13691
|
const asked = asText(args["unit"]);
|
|
12658
13692
|
const { units } = unitsOf(ctx, root);
|
|
@@ -12670,8 +13704,8 @@ function registerRecordTools(ctx, store) {
|
|
|
12670
13704
|
method,
|
|
12671
13705
|
urlContains
|
|
12672
13706
|
};
|
|
12673
|
-
const
|
|
12674
|
-
if (
|
|
13707
|
+
const seam = seamOf(ctx);
|
|
13708
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,开不了录制窗");
|
|
12675
13709
|
const command = cliCommandOf(ctx);
|
|
12676
13710
|
const sessionId = sessionIdOf(exec) ?? "";
|
|
12677
13711
|
const held = store.live(Date.now());
|
|
@@ -12690,21 +13724,21 @@ function registerRecordTools(ctx, store) {
|
|
|
12690
13724
|
}), held);
|
|
12691
13725
|
try {
|
|
12692
13726
|
const opened = await openRecordPage({
|
|
12693
|
-
|
|
13727
|
+
seam,
|
|
12694
13728
|
command,
|
|
12695
13729
|
session: BROWSER_RECORD_SESSION,
|
|
12696
13730
|
url,
|
|
12697
13731
|
captureFile
|
|
12698
13732
|
});
|
|
12699
13733
|
if (!opened.ok) throw new Error(opened.why);
|
|
12700
|
-
const armed = await actBrowser(
|
|
13734
|
+
const armed = await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_ARM_SCRIPT]);
|
|
12701
13735
|
const page = recordPageOf(armed.payload);
|
|
12702
13736
|
if (!armed.ok || !page.ok) throw new Error(`浮条没装上:${armed.stderr || armed.stdout || "那一页还在加载"} —— 等用户那一页站稳了再试。`);
|
|
12703
|
-
await stopNetworkExact(
|
|
12704
|
-
const capture = await startRecordCapture(
|
|
13737
|
+
await stopNetworkExact(seam, captureFile);
|
|
13738
|
+
const capture = await startRecordCapture(seam, command, BROWSER_RECORD_SESSION, captureFile);
|
|
12705
13739
|
watchRecordFinish({
|
|
12706
13740
|
ctx,
|
|
12707
|
-
|
|
13741
|
+
seam,
|
|
12708
13742
|
command,
|
|
12709
13743
|
sessionId,
|
|
12710
13744
|
store,
|
|
@@ -12727,7 +13761,7 @@ function registerRecordTools(ctx, store) {
|
|
|
12727
13761
|
});
|
|
12728
13762
|
} catch (error) {
|
|
12729
13763
|
await releaseQuietly({
|
|
12730
|
-
|
|
13764
|
+
seam,
|
|
12731
13765
|
command,
|
|
12732
13766
|
session: BROWSER_RECORD_SESSION,
|
|
12733
13767
|
url,
|
|
@@ -12751,8 +13785,8 @@ function registerRecordTools(ctx, store) {
|
|
|
12751
13785
|
},
|
|
12752
13786
|
output: OUTPUT$3,
|
|
12753
13787
|
async execute(raw, exec) {
|
|
12754
|
-
const
|
|
12755
|
-
if (
|
|
13788
|
+
const seam = seamOf(ctx);
|
|
13789
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,读不了录制");
|
|
12756
13790
|
const sessionId = sessionIdOf(exec) ?? "";
|
|
12757
13791
|
const held = store.live(Date.now());
|
|
12758
13792
|
if (held === null) throw new Error(recordMissingText());
|
|
@@ -12777,14 +13811,14 @@ function registerRecordTools(ctx, store) {
|
|
|
12777
13811
|
let stepOk = true;
|
|
12778
13812
|
let page = recordPageOf(void 0);
|
|
12779
13813
|
if (!over) {
|
|
12780
|
-
await actBrowser(
|
|
12781
|
-
let step = await actBrowser(
|
|
13814
|
+
await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_ARM_SCRIPT]);
|
|
13815
|
+
let step = await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_READ_SCRIPT]);
|
|
12782
13816
|
page = recordPageOf(step.payload);
|
|
12783
13817
|
stepOk = step.ok;
|
|
12784
13818
|
sync(page);
|
|
12785
13819
|
while (budget > 0 && page.phase !== "finished" && Date.now() < deadline) {
|
|
12786
13820
|
await pauseFor(Math.min(POLL_MS, Math.max(0, deadline - Date.now())));
|
|
12787
|
-
step = await actBrowser(
|
|
13821
|
+
step = await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_READ_SCRIPT]);
|
|
12788
13822
|
const next = recordPageOf(step.payload);
|
|
12789
13823
|
if (next.ok) page = next;
|
|
12790
13824
|
sync(page);
|
|
@@ -12798,7 +13832,7 @@ function registerRecordTools(ctx, store) {
|
|
|
12798
13832
|
}
|
|
12799
13833
|
const state = store.live(Date.now()) ?? held;
|
|
12800
13834
|
const ledger = state.shots;
|
|
12801
|
-
const capture = await captureReadOf(
|
|
13835
|
+
const capture = await captureReadOf(seam, state.captureFile);
|
|
12802
13836
|
const bundle = bundleOf(capture, page, state);
|
|
12803
13837
|
const hookCaught = page.calls.length;
|
|
12804
13838
|
const nav = page.href !== "" && page.here !== "" && page.href !== page.here;
|
|
@@ -12882,9 +13916,9 @@ function registerRecordTools(ctx, store) {
|
|
|
12882
13916
|
output: OUTPUT$3,
|
|
12883
13917
|
async execute(raw, exec) {
|
|
12884
13918
|
const { root } = await workspaceOf(ctx, exec);
|
|
12885
|
-
const artifactsDir = artifactsDirOf(
|
|
12886
|
-
const
|
|
12887
|
-
if (
|
|
13919
|
+
const artifactsDir = artifactsDirOf(root);
|
|
13920
|
+
const seam = seamOf(ctx);
|
|
13921
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,取不了详情");
|
|
12888
13922
|
const args = asRecord$1(raw);
|
|
12889
13923
|
const unit = asText(args["unit"]);
|
|
12890
13924
|
const askedKey = asText(args["key"]).trim();
|
|
@@ -12908,8 +13942,8 @@ function registerRecordTools(ctx, store) {
|
|
|
12908
13942
|
if (key === "") {
|
|
12909
13943
|
if (index === null) throw new Error("给一个 key(清单里那一条的)或 index(清单里的序号)。");
|
|
12910
13944
|
if (held === null) throw new Error("收工之后只能用 key —— index 是\"那一场清单里\"的序号,这一场已经清掉了。");
|
|
12911
|
-
page = recordPageOf((await actBrowser(
|
|
12912
|
-
const bundle = bundleOf(await captureReadOf(
|
|
13945
|
+
page = recordPageOf((await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_READ_SCRIPT])).payload);
|
|
13946
|
+
const bundle = bundleOf(await captureReadOf(seam, file), page, held);
|
|
12913
13947
|
const row = bundle.rows[index];
|
|
12914
13948
|
if (row === void 0) throw new Error(`这一段清单里没有第 ${String(index)} 条(一共 ${String(bundle.rows.length)} 条)—— 先 ${DIY_RECORD_READ_TOOL} 拿一份新的清单。`);
|
|
12915
13949
|
key = row.key;
|
|
@@ -12919,11 +13953,11 @@ function registerRecordTools(ctx, store) {
|
|
|
12919
13953
|
if (only !== null) return detailOfCall(only, maxBytes);
|
|
12920
13954
|
}
|
|
12921
13955
|
}
|
|
12922
|
-
const candidates = pickAllOf(captureEntriesOf(await captureLinesWithKeyOf(
|
|
13956
|
+
const candidates = pickAllOf(captureEntriesOf(await captureLinesWithKeyOf(seam, file, key)), key);
|
|
12923
13957
|
const fromCapture = pickOf(candidates, key, at);
|
|
12924
13958
|
if (fromCapture !== null) return detailOfEntry(fromCapture, maxBytes, candidates.length);
|
|
12925
13959
|
const over = held === null || held.phase === "finished";
|
|
12926
|
-
const livePage = page !== null || over ? page : recordPageOf((await actBrowser(
|
|
13960
|
+
const livePage = page !== null || over ? page : recordPageOf((await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_READ_SCRIPT])).payload);
|
|
12927
13961
|
const call = livePage === null ? null : hookCallOf(livePage, key);
|
|
12928
13962
|
if (call !== null) return detailOfCall(call, maxBytes);
|
|
12929
13963
|
return plainJson({
|
|
@@ -12963,8 +13997,8 @@ function registerRecordTools(ctx, store) {
|
|
|
12963
13997
|
},
|
|
12964
13998
|
output: OUTPUT$3,
|
|
12965
13999
|
async execute(_raw, exec) {
|
|
12966
|
-
const
|
|
12967
|
-
if (
|
|
14000
|
+
const seam = seamOf(ctx);
|
|
14001
|
+
if (seam === void 0) throw new Error("这台机器上没有 shell 服务,收不了工");
|
|
12968
14002
|
const sessionId = sessionIdOf(exec) ?? "";
|
|
12969
14003
|
const held = store.live(Date.now());
|
|
12970
14004
|
if (held === null) throw new Error(recordMissingText());
|
|
@@ -12973,14 +14007,14 @@ function registerRecordTools(ctx, store) {
|
|
|
12973
14007
|
code: "not-owner"
|
|
12974
14008
|
}, held);
|
|
12975
14009
|
const command = cliCommandOf(ctx);
|
|
12976
|
-
await actBrowser(
|
|
12977
|
-
const page = recordPageOf((await actBrowser(
|
|
12978
|
-
const bundle = bundleOf(await captureReadOf(
|
|
14010
|
+
await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_ARM_SCRIPT]);
|
|
14011
|
+
const page = recordPageOf((await actBrowser(seam, command, BROWSER_RECORD_SESSION, ["eval", RECORD_STOP_SCRIPT])).payload);
|
|
14012
|
+
const bundle = bundleOf(await captureReadOf(seam, held.captureFile), page, held);
|
|
12979
14013
|
const hookCaught = page.calls.length;
|
|
12980
14014
|
const noBody = bodyMissingOf(bundle.merged);
|
|
12981
|
-
const kept = await keepCaptureOf(
|
|
14015
|
+
const kept = await keepCaptureOf(seam, held.captureFile);
|
|
12982
14016
|
const cleaned = await releaseQuietly({
|
|
12983
|
-
|
|
14017
|
+
seam,
|
|
12984
14018
|
command,
|
|
12985
14019
|
session: BROWSER_RECORD_SESSION,
|
|
12986
14020
|
url: held.url,
|
|
@@ -13277,11 +14311,11 @@ async function settle(deps, spec, options, change) {
|
|
|
13277
14311
|
};
|
|
13278
14312
|
}
|
|
13279
14313
|
async function installRouted(deps) {
|
|
13280
|
-
const { spec, options, registry,
|
|
14314
|
+
const { spec, options, registry, seam } = deps;
|
|
13281
14315
|
const supply = deps.supply ?? requireSupply();
|
|
13282
14316
|
const route = await supply.packageRouteOf({
|
|
13283
14317
|
spec,
|
|
13284
|
-
...
|
|
14318
|
+
...seam === void 0 ? {} : { seam },
|
|
13285
14319
|
...registry === void 0 ? {} : { registry }
|
|
13286
14320
|
});
|
|
13287
14321
|
if (route === void 0 || route.kind === "dsh-bundle") return installDshBundle(deps, spec, options);
|
|
@@ -13491,15 +14525,15 @@ async function updateModuleBundle(unit, deps) {
|
|
|
13491
14525
|
pkg
|
|
13492
14526
|
};
|
|
13493
14527
|
const registry = read.kind === "module" ? read.registry : void 0;
|
|
13494
|
-
const latest = deps.
|
|
13495
|
-
|
|
14528
|
+
const latest = deps.seam === void 0 || registry === void 0 ? void 0 : await supply.latestVersion({
|
|
14529
|
+
seam: deps.seam,
|
|
13496
14530
|
name: pkg,
|
|
13497
14531
|
registry,
|
|
13498
14532
|
force: true
|
|
13499
14533
|
});
|
|
13500
|
-
if (latest !== void 0 && deps.
|
|
14534
|
+
if (latest !== void 0 && deps.seam !== void 0 && registry !== void 0) {
|
|
13501
14535
|
const declared = await supply.registryDeclarationOf({
|
|
13502
|
-
|
|
14536
|
+
seam: deps.seam,
|
|
13503
14537
|
name: pkg,
|
|
13504
14538
|
registry,
|
|
13505
14539
|
force: true
|
|
@@ -13701,17 +14735,17 @@ async function updateWorkbenchOnce(deps) {
|
|
|
13701
14735
|
const read = deps.workbench?.(target);
|
|
13702
14736
|
if (read === void 0) throw new Error(`读不到 profile 里装的 ${target} —— 没法判断装的是哪一份,更新做不了。`);
|
|
13703
14737
|
if (read.local === true) throw new Error("这份工作台是从源码目录装进来的(开发副本),不从源上更新。");
|
|
13704
|
-
const
|
|
14738
|
+
const seam = deps.seam;
|
|
13705
14739
|
const registry = read.registry ?? await deps.profileRegistry?.();
|
|
13706
|
-
const askedLatest =
|
|
13707
|
-
|
|
14740
|
+
const askedLatest = seam === void 0 || registry === void 0 ? void 0 : await supply.latestVersion({
|
|
14741
|
+
seam,
|
|
13708
14742
|
name: read.pkg,
|
|
13709
14743
|
registry,
|
|
13710
14744
|
force: true
|
|
13711
14745
|
});
|
|
13712
|
-
if (
|
|
14746
|
+
if (seam !== void 0 && registry !== void 0 && askedLatest !== void 0) {
|
|
13713
14747
|
const missing = await supply.missingReleaseParts({
|
|
13714
|
-
|
|
14748
|
+
seam,
|
|
13715
14749
|
name: read.pkg,
|
|
13716
14750
|
registry,
|
|
13717
14751
|
version: askedLatest,
|
|
@@ -13774,25 +14808,29 @@ async function migrateWorkbench(input) {
|
|
|
13774
14808
|
}
|
|
13775
14809
|
//#endregion
|
|
13776
14810
|
//#region src/modules/panels/model/market-follow-up.ts
|
|
14811
|
+
var MIGRATION_TIMEOUT_MS = 12e4;
|
|
13777
14812
|
function createWorkbenchFollowUps(deps) {
|
|
13778
14813
|
const runMigration = deps.runMigration ?? (async (input) => {
|
|
13779
|
-
const
|
|
13780
|
-
|
|
13781
|
-
|
|
13782
|
-
|
|
13783
|
-
|
|
14814
|
+
const seam = deps.seam;
|
|
14815
|
+
if (seam === void 0) throw new Error("没有执行缝(shell 服务不可用),迁移跑不起来。");
|
|
14816
|
+
const outcome = await seam.run({
|
|
14817
|
+
argv: [
|
|
14818
|
+
process.execPath,
|
|
14819
|
+
input.script,
|
|
14820
|
+
"--migrate",
|
|
14821
|
+
"--force"
|
|
14822
|
+
],
|
|
14823
|
+
intent: { kind: "host" },
|
|
13784
14824
|
env: {
|
|
13785
|
-
...process.env,
|
|
13786
14825
|
ELECTRON_RUN_AS_NODE: "1",
|
|
13787
14826
|
...input.env
|
|
13788
14827
|
},
|
|
13789
|
-
|
|
13790
|
-
timeout: 12e4
|
|
14828
|
+
timeoutMs: MIGRATION_TIMEOUT_MS
|
|
13791
14829
|
});
|
|
13792
14830
|
return {
|
|
13793
|
-
exitCode:
|
|
13794
|
-
stdout:
|
|
13795
|
-
stderr:
|
|
14831
|
+
exitCode: outcome.exitCode ?? 1,
|
|
14832
|
+
stdout: outcome.stdout,
|
|
14833
|
+
stderr: outcome.stderr
|
|
13796
14834
|
};
|
|
13797
14835
|
});
|
|
13798
14836
|
function homeAndProfile() {
|
|
@@ -13849,7 +14887,7 @@ function pluginSweptText(pkg) {
|
|
|
13849
14887
|
return `清掉了 ${pkg}:先前要它的都放手了,也没别人在用,文件跟着删了。`;
|
|
13850
14888
|
}
|
|
13851
14889
|
function createPluginActions(options) {
|
|
13852
|
-
const { ledger,
|
|
14890
|
+
const { ledger, seam, command, runAll, logger } = options;
|
|
13853
14891
|
const supply = options.supply ?? requireSupply();
|
|
13854
14892
|
const pluginFilesOf = () => {
|
|
13855
14893
|
const dir = supply.vendorPluginsDir?.();
|
|
@@ -13911,16 +14949,16 @@ function createPluginActions(options) {
|
|
|
13911
14949
|
};
|
|
13912
14950
|
}
|
|
13913
14951
|
async function removeFiles(plugin) {
|
|
13914
|
-
if (
|
|
14952
|
+
if (seam === void 0) throw new Error("没有执行缝(shell 服务不可用),删不掉插件文件。");
|
|
13915
14953
|
await runAll([{ line: `${quoteShellArg(command)} plugin uninstall ${quoteShellArg(plugin)}` }]);
|
|
13916
14954
|
}
|
|
13917
14955
|
return {
|
|
13918
14956
|
async update(pkg) {
|
|
13919
14957
|
const { entry } = requireHeld(pkg);
|
|
13920
14958
|
if (entry.local === true || entry.registry === void 0) throw new Error(`「${entry.package}」是从本地装的,没有地方查新版。`);
|
|
13921
|
-
if (
|
|
14959
|
+
if (seam === void 0) throw new Error("没有执行缝(shell 服务不可用),更新做不了。");
|
|
13922
14960
|
const latest = await supply.latestVersion({
|
|
13923
|
-
|
|
14961
|
+
seam,
|
|
13924
14962
|
name: entry.package,
|
|
13925
14963
|
registry: entry.registry,
|
|
13926
14964
|
force: true
|
|
@@ -14060,7 +15098,7 @@ function restartLogLine(change) {
|
|
|
14060
15098
|
//#endregion
|
|
14061
15099
|
//#region src/modules/panels/model/market.ts
|
|
14062
15100
|
function createMarketService(options) {
|
|
14063
|
-
const { manifests, manifestsNow, probe,
|
|
15101
|
+
const { manifests, manifestsNow, probe, seam, logger, supply: supplyOption, onInstalled, onUninstalled, isReleased, bundles, installed, modules, workbench, registries, profileDir, ledger, references, dshHome, profileName } = options;
|
|
14064
15102
|
const supply = supplyOption ?? requireSupply();
|
|
14065
15103
|
const command = options.command;
|
|
14066
15104
|
const catalog = () => manifestsNow?.() ?? manifests;
|
|
@@ -14098,13 +15136,17 @@ function createMarketService(options) {
|
|
|
14098
15136
|
};
|
|
14099
15137
|
}
|
|
14100
15138
|
async function run(line, optional = false) {
|
|
14101
|
-
if (
|
|
15139
|
+
if (seam === void 0) throw new Error("没有执行缝(shell 服务不可用),装 / 卸做不了。");
|
|
14102
15140
|
const blocked = installWriteProblem({
|
|
14103
15141
|
command: line,
|
|
14104
15142
|
fallback: supply.modules.isolatedRoot()
|
|
14105
15143
|
});
|
|
14106
15144
|
if (blocked !== void 0) throw installError(blocked);
|
|
14107
|
-
const result = await
|
|
15145
|
+
const result = await seam.run({
|
|
15146
|
+
command: line,
|
|
15147
|
+
intent: productWrite(),
|
|
15148
|
+
timeoutMs: INSTALL_TIMEOUT_MS
|
|
15149
|
+
});
|
|
14108
15150
|
if (result.exitCode !== 0 && !optional) throw installError(diagnoseInstallFailure({
|
|
14109
15151
|
command: line,
|
|
14110
15152
|
exitCode: result.exitCode,
|
|
@@ -14214,7 +15256,7 @@ function createMarketService(options) {
|
|
|
14214
15256
|
registryFix,
|
|
14215
15257
|
...logger === void 0 ? {} : { logger }
|
|
14216
15258
|
}),
|
|
14217
|
-
...
|
|
15259
|
+
...seam === void 0 ? {} : { seam },
|
|
14218
15260
|
...installed === void 0 ? {} : { installed },
|
|
14219
15261
|
...ledger === void 0 ? {} : { ledger },
|
|
14220
15262
|
...logger === void 0 ? {} : { logger }
|
|
@@ -14222,7 +15264,7 @@ function createMarketService(options) {
|
|
|
14222
15264
|
}
|
|
14223
15265
|
const plugins = createPluginActions({
|
|
14224
15266
|
...ledger === void 0 ? {} : { ledger },
|
|
14225
|
-
...
|
|
15267
|
+
...seam === void 0 ? {} : { seam },
|
|
14226
15268
|
command,
|
|
14227
15269
|
runAll,
|
|
14228
15270
|
claimedBy: (plugin) => pluginUsersOf(plugin),
|
|
@@ -14377,7 +15419,7 @@ function createMarketService(options) {
|
|
|
14377
15419
|
supply,
|
|
14378
15420
|
modules,
|
|
14379
15421
|
installed,
|
|
14380
|
-
|
|
15422
|
+
seam,
|
|
14381
15423
|
manager,
|
|
14382
15424
|
...profile === void 0 ? {} : {
|
|
14383
15425
|
profile,
|
|
@@ -14399,7 +15441,7 @@ function createMarketService(options) {
|
|
|
14399
15441
|
supply,
|
|
14400
15442
|
asked: pkg,
|
|
14401
15443
|
...workbench === void 0 ? {} : { workbench },
|
|
14402
|
-
...
|
|
15444
|
+
...seam === void 0 ? {} : { seam },
|
|
14403
15445
|
...installed === void 0 ? {} : { installed },
|
|
14404
15446
|
...dshHome === void 0 ? {} : { dshHome },
|
|
14405
15447
|
...profileName === void 0 ? {} : { profileName },
|
|
@@ -14428,6 +15470,7 @@ function createMarketService(options) {
|
|
|
14428
15470
|
dshHome,
|
|
14429
15471
|
profileName,
|
|
14430
15472
|
workbench,
|
|
15473
|
+
...seam === void 0 ? {} : { seam },
|
|
14431
15474
|
...options.runMigration === void 0 ? {} : { runMigration: options.runMigration }
|
|
14432
15475
|
}),
|
|
14433
15476
|
updateDependency: updateDependencyRow$1
|
|
@@ -14436,18 +15479,11 @@ function createMarketService(options) {
|
|
|
14436
15479
|
}
|
|
14437
15480
|
//#endregion
|
|
14438
15481
|
//#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
15482
|
function explainProblem(problem, profilePath) {
|
|
14446
15483
|
if (problem.kind !== "sandbox" || profilePath === void 0 || profilePath === "") return problem;
|
|
14447
|
-
const file = join(profilePath, "cordis.patch.yml");
|
|
14448
15484
|
return {
|
|
14449
15485
|
...problem,
|
|
14450
|
-
detail:
|
|
15486
|
+
detail: `被系统拦住了:工作台按 $DSH_HOME 声明的可写根没盖住它的缓存目录,这条命令被沙箱挡下了。\n这是工作台自己的问题,不用改 profile —— 把这一屏和 $DSH_HOME 报给工作台维护者,或先在工作台外面手动加这个源(${profilePath})。`
|
|
14451
15487
|
};
|
|
14452
15488
|
}
|
|
14453
15489
|
var DEFAULT_PEER_TTL_MS = 6e5;
|
|
@@ -14507,7 +15543,7 @@ function createMarketDiscover(options) {
|
|
|
14507
15543
|
const git = version.source;
|
|
14508
15544
|
if (git.type !== "git") return void 0;
|
|
14509
15545
|
if (normalizedRepo(git.url) !== normalizedRepo(source.url)) return void 0;
|
|
14510
|
-
if (options.
|
|
15546
|
+
if (options.seam === void 0) return void 0;
|
|
14511
15547
|
const key = `${git.commit}|${git.path ?? ""}`;
|
|
14512
15548
|
const cached = peerCache.get(key);
|
|
14513
15549
|
if (cached !== void 0 && now() - cached.at < peerTtlMs) return cached.peer;
|
|
@@ -14515,7 +15551,12 @@ function createMarketDiscover(options) {
|
|
|
14515
15551
|
const directory = (git.path ?? "").replace(/^\/+|\/+$/g, "");
|
|
14516
15552
|
const file = directory === "" ? "package.json" : `${directory}/package.json`;
|
|
14517
15553
|
try {
|
|
14518
|
-
const result = await
|
|
15554
|
+
const result = await options.seam.run({
|
|
15555
|
+
command: `git -C ${quoteShellArg(repo)} show ${git.commit}:${quoteShellArg(file)}`,
|
|
15556
|
+
intent: noWrite(),
|
|
15557
|
+
retry: "safe",
|
|
15558
|
+
timeoutMs
|
|
15559
|
+
});
|
|
14519
15560
|
if (result.exitCode !== 0) throw new Error(result.stderr.trim());
|
|
14520
15561
|
const peer = JSON.parse(result.stdout).peerDependencies?.[CORE_PEER];
|
|
14521
15562
|
peerCache.set(key, {
|
|
@@ -14909,7 +15950,7 @@ function repoOf(root, source) {
|
|
|
14909
15950
|
return join(directoryOf(root, source), "repo.git");
|
|
14910
15951
|
}
|
|
14911
15952
|
function writeMeta(path, meta) {
|
|
14912
|
-
return
|
|
15953
|
+
return writeJsonFileAsync(path, meta);
|
|
14913
15954
|
}
|
|
14914
15955
|
function readMeta(path) {
|
|
14915
15956
|
try {
|
|
@@ -15045,7 +16086,7 @@ function createMarketIndexReader(options) {
|
|
|
15045
16086
|
};
|
|
15046
16087
|
const fetchedAt = now();
|
|
15047
16088
|
mkdirSync(directoryOf(options.root, source), { recursive: true });
|
|
15048
|
-
await
|
|
16089
|
+
await writeTextFileAsync(indexFileOf(options.root, source), text);
|
|
15049
16090
|
await writeMeta(metaFileOf(options.root, source), { fetchedAt });
|
|
15050
16091
|
return {
|
|
15051
16092
|
ok: true,
|
|
@@ -15196,7 +16237,7 @@ function createMarketIndexReader(options) {
|
|
|
15196
16237
|
const entries = rows.filter((one) => one.type === "blob" && one.file !== path && one.file.startsWith(`${path}/`) && one.file.endsWith(".json"));
|
|
15197
16238
|
const finish = async (index, text) => {
|
|
15198
16239
|
const fetchedAt = now();
|
|
15199
|
-
await
|
|
16240
|
+
await writeTextFileAsync(indexFileOf(options.root, source), text);
|
|
15200
16241
|
await writeMeta(metaFileOf(options.root, source), {
|
|
15201
16242
|
fetchedAt,
|
|
15202
16243
|
commit
|
|
@@ -15472,7 +16513,7 @@ function readPayload(path, logger) {
|
|
|
15472
16513
|
}
|
|
15473
16514
|
async function writePayload(path, payload) {
|
|
15474
16515
|
mkdirSync(dirname(path), { recursive: true });
|
|
15475
|
-
await
|
|
16516
|
+
await writeJsonFileAsync(path, payload);
|
|
15476
16517
|
}
|
|
15477
16518
|
function upsert(sources, source) {
|
|
15478
16519
|
const found = sources.findIndex((item) => item.id === source.id);
|
|
@@ -15660,9 +16701,7 @@ function createMarketSourceInitializer(options) {
|
|
|
15660
16701
|
const migrated = marketEntryFilesOfLegacy(readFileSync(legacyFile, "utf8"), indexPath);
|
|
15661
16702
|
if (migrated !== void 0) {
|
|
15662
16703
|
for (const file of migrated) {
|
|
15663
|
-
|
|
15664
|
-
mkdirSync(dirname(target), { recursive: true });
|
|
15665
|
-
writeFileSync(target, file.text);
|
|
16704
|
+
writeTextFile(join(workdir, ...file.path.split("/")), file.text);
|
|
15666
16705
|
written.push(file.path);
|
|
15667
16706
|
}
|
|
15668
16707
|
rmSync(legacyFile);
|
|
@@ -15671,12 +16710,12 @@ function createMarketSourceInitializer(options) {
|
|
|
15671
16710
|
}
|
|
15672
16711
|
if (!existsSync(indexMarker)) {
|
|
15673
16712
|
mkdirSync(indexDir, { recursive: true });
|
|
15674
|
-
|
|
16713
|
+
writeTextFile(indexMarker, MARKET_ENTRIES_README_LINES.join("\n"));
|
|
15675
16714
|
written.push(`${indexPath}/README.md`);
|
|
15676
16715
|
}
|
|
15677
16716
|
const readmeFile = join(workdir, "README.md");
|
|
15678
16717
|
if (!existsSync(readmeFile)) {
|
|
15679
|
-
|
|
16718
|
+
writeTextFile(readmeFile, MARKET_INDEX_README_LINES.join("\n"));
|
|
15680
16719
|
written.push("README.md");
|
|
15681
16720
|
}
|
|
15682
16721
|
}
|
|
@@ -16710,12 +17749,12 @@ var INSTALLED_PLUGINS_ROUTE = "panels/plugins";
|
|
|
16710
17749
|
//#endregion
|
|
16711
17750
|
//#region src/modules/panels/model/plugin-section.ts
|
|
16712
17751
|
async function installedPlugins(options) {
|
|
16713
|
-
const { ledger,
|
|
17752
|
+
const { ledger, seam, force = false, check = true } = options;
|
|
16714
17753
|
const supply = options.supply ?? requireSupply();
|
|
16715
17754
|
const rows = [];
|
|
16716
17755
|
for (const entry of ledger.list()) {
|
|
16717
|
-
const release =
|
|
16718
|
-
|
|
17756
|
+
const release = seam === void 0 || entry.local === true || entry.registry === void 0 ? void 0 : await supply.probeRelease({
|
|
17757
|
+
seam,
|
|
16719
17758
|
package: entry.package,
|
|
16720
17759
|
registry: entry.registry,
|
|
16721
17760
|
force,
|
|
@@ -16742,7 +17781,7 @@ function pluginRoutes(options) {
|
|
|
16742
17781
|
ok: true,
|
|
16743
17782
|
plugins: await installedPlugins({
|
|
16744
17783
|
ledger: options.ledger,
|
|
16745
|
-
...options.
|
|
17784
|
+
...options.seam === void 0 ? {} : { seam: options.seam },
|
|
16746
17785
|
...options.supply === void 0 ? {} : { supply: options.supply },
|
|
16747
17786
|
force: param(search, "force") === "1",
|
|
16748
17787
|
check: param(search, "check") !== "0"
|
|
@@ -17010,13 +18049,13 @@ var OWNED = [{
|
|
|
17010
18049
|
const record = {
|
|
17011
18050
|
...createRecordStore(),
|
|
17012
18051
|
release: (state) => {
|
|
17013
|
-
const
|
|
17014
|
-
if (
|
|
18052
|
+
const seamNow = seamOf(ctx);
|
|
18053
|
+
if (seamNow === void 0) return Promise.resolve({
|
|
17015
18054
|
tab: false,
|
|
17016
18055
|
capture: false
|
|
17017
18056
|
});
|
|
17018
18057
|
return releaseQuietly({
|
|
17019
|
-
|
|
18058
|
+
seam: seamNow,
|
|
17020
18059
|
command: cliCommandOf(ctx),
|
|
17021
18060
|
session: BROWSER_RECORD_SESSION,
|
|
17022
18061
|
url: state.url,
|
|
@@ -17026,7 +18065,7 @@ var OWNED = [{
|
|
|
17026
18065
|
};
|
|
17027
18066
|
ctx.provide(DIY_RECORD_SERVICE, record);
|
|
17028
18067
|
const profileDir = supply.profileDirOf(ctx);
|
|
17029
|
-
const
|
|
18068
|
+
const seam = seamOf(ctx);
|
|
17030
18069
|
const moduleManifests = () => {
|
|
17031
18070
|
const list = manifests();
|
|
17032
18071
|
if (installable === void 0) return list.filter((manifest) => supplyNeedsOf(manifest).length > 0);
|
|
@@ -17038,7 +18077,7 @@ var OWNED = [{
|
|
|
17038
18077
|
manifests: moduleManifests(),
|
|
17039
18078
|
manifestsNow: moduleManifests,
|
|
17040
18079
|
...probe === void 0 ? {} : { probe },
|
|
17041
|
-
|
|
18080
|
+
seam,
|
|
17042
18081
|
command: cliCommandOf(ctx),
|
|
17043
18082
|
supply,
|
|
17044
18083
|
bundles,
|
|
@@ -17099,7 +18138,7 @@ var OWNED = [{
|
|
|
17099
18138
|
return;
|
|
17100
18139
|
}
|
|
17101
18140
|
},
|
|
17102
|
-
|
|
18141
|
+
seam,
|
|
17103
18142
|
...logger === void 0 ? {} : { logger }
|
|
17104
18143
|
});
|
|
17105
18144
|
return {
|
|
@@ -17108,7 +18147,7 @@ var OWNED = [{
|
|
|
17108
18147
|
...pluginRoutes({
|
|
17109
18148
|
ledger,
|
|
17110
18149
|
supply,
|
|
17111
|
-
...
|
|
18150
|
+
...seam === void 0 ? {} : { seam }
|
|
17112
18151
|
}),
|
|
17113
18152
|
...diyDebugRoutes(debug),
|
|
17114
18153
|
...diyRecordRoutes(record),
|
|
@@ -17393,6 +18432,17 @@ function declaredEnvOf(manifest) {
|
|
|
17393
18432
|
for (const need of manifest.needs ?? []) if (need.kind === "env") out[need.name] = process.env[need.name];
|
|
17394
18433
|
return out;
|
|
17395
18434
|
}
|
|
18435
|
+
function unitSeamOf(seam, unit, manifest) {
|
|
18436
|
+
const declared = manifest.host.writes ?? [];
|
|
18437
|
+
if (seam === void 0 || declared.length === 0) return void 0;
|
|
18438
|
+
return createUnitSeam({
|
|
18439
|
+
seam,
|
|
18440
|
+
unit,
|
|
18441
|
+
runtimeRoot: workbenchRoot(),
|
|
18442
|
+
declared,
|
|
18443
|
+
realHome: homedir()
|
|
18444
|
+
});
|
|
18445
|
+
}
|
|
17396
18446
|
function mountUnits(units, deps) {
|
|
17397
18447
|
const store = deps.store ?? createWorkspaceStore({ root: null });
|
|
17398
18448
|
const missingData = async () => ({
|
|
@@ -17447,6 +18497,7 @@ function mountUnits(units, deps) {
|
|
|
17447
18497
|
});
|
|
17448
18498
|
let result;
|
|
17449
18499
|
try {
|
|
18500
|
+
const seam = unitSeamOf(deps.seam, unit, host.manifest);
|
|
17450
18501
|
result = host.face.mount({
|
|
17451
18502
|
manifest: host.manifest,
|
|
17452
18503
|
ctx: mountCtx,
|
|
@@ -17456,6 +18507,7 @@ function mountUnits(units, deps) {
|
|
|
17456
18507
|
...deps.installable === void 0 ? {} : { installable: deps.installable },
|
|
17457
18508
|
data,
|
|
17458
18509
|
...hostKit,
|
|
18510
|
+
...seam === void 0 ? {} : { seam },
|
|
17459
18511
|
env: declaredEnvOf(host.manifest),
|
|
17460
18512
|
...deps.libs === void 0 ? {} : { libs: deps.libs },
|
|
17461
18513
|
...deps.git === void 0 ? {} : { git: deps.git },
|
|
@@ -17608,7 +18660,7 @@ async function releaseHeld(store, held) {
|
|
|
17608
18660
|
}
|
|
17609
18661
|
}
|
|
17610
18662
|
function recordActResource(deps) {
|
|
17611
|
-
const now = () => deps.now?.() ??
|
|
18663
|
+
const now$1 = () => deps.now?.() ?? now();
|
|
17612
18664
|
return {
|
|
17613
18665
|
resource: "act",
|
|
17614
18666
|
async handle(payload) {
|
|
@@ -17618,7 +18670,7 @@ function recordActResource(deps) {
|
|
|
17618
18670
|
text: "不认识的意图(只认 peek / halt / stop)"
|
|
17619
18671
|
};
|
|
17620
18672
|
const store = deps.store();
|
|
17621
|
-
const at = now();
|
|
18673
|
+
const at = now$1();
|
|
17622
18674
|
const held = store?.live(at) ?? null;
|
|
17623
18675
|
if (store === void 0 || held === null) return {
|
|
17624
18676
|
ok: false,
|
|
@@ -17630,7 +18682,7 @@ function recordActResource(deps) {
|
|
|
17630
18682
|
intent,
|
|
17631
18683
|
delivered: deliverDebugNotice(deps.agents(), held.sessionId, notice.message, NOTICE_MODE[intent]),
|
|
17632
18684
|
summary: notice.summary,
|
|
17633
|
-
state: store.live(now())
|
|
18685
|
+
state: store.live(now$1())
|
|
17634
18686
|
};
|
|
17635
18687
|
const cleaned = await releaseHeld(store, held);
|
|
17636
18688
|
const move = store.stop(held.sessionId, at);
|
|
@@ -17641,7 +18693,7 @@ function recordActResource(deps) {
|
|
|
17641
18693
|
summary: notice.summary,
|
|
17642
18694
|
cleaned,
|
|
17643
18695
|
refused: move.ok ? "" : move.code,
|
|
17644
|
-
state: store.live(now())
|
|
18696
|
+
state: store.live(now$1())
|
|
17645
18697
|
};
|
|
17646
18698
|
}
|
|
17647
18699
|
};
|
|
@@ -18295,18 +19347,9 @@ function notifyLongTaskStop(agents, unit, change) {
|
|
|
18295
19347
|
//#region src/app/host.ts
|
|
18296
19348
|
var name = "dsh-workbench";
|
|
18297
19349
|
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
19350
|
async function apply(ctx, config) {
|
|
19351
|
+
const seam = provideWorkbenchSeam(ctx);
|
|
19352
|
+
if (seam !== void 0) provideGitProcessSeam(seam);
|
|
18310
19353
|
const facts = createFactLog();
|
|
18311
19354
|
const values = createValueHandoff();
|
|
18312
19355
|
const panelState = createPanelStateStore();
|
|
@@ -18317,13 +19360,11 @@ async function apply(ctx, config) {
|
|
|
18317
19360
|
corePackageUrl: import.meta.url,
|
|
18318
19361
|
log: (message) => warn(ctx, message)
|
|
18319
19362
|
});
|
|
18320
|
-
adapters.runtime.warnUnwritableRoot(ctx);
|
|
18321
19363
|
if (loaded.ok) ctx.logger?.info?.(`[dsh-workbench] 适配器集合:${adapters.id}@${adapters.version}(${loaded.spec})`);
|
|
18322
19364
|
else if (loaded.failure !== "not-configured") ctx.logger?.warn?.(`[dsh-workbench] 适配器集合没生效(${loaded.failure}):${loaded.detail};先用默认集合`);
|
|
18323
19365
|
ctx.provide(WORKBENCH_ADAPTERS, adapters);
|
|
18324
19366
|
const runtimeCommands = adapters.runtime.commands(ctx);
|
|
18325
19367
|
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
19368
|
const routes = createHostRoutes({
|
|
18328
19369
|
ctx,
|
|
18329
19370
|
facts,
|
|
@@ -18411,11 +19452,11 @@ async function apply(ctx, config) {
|
|
|
18411
19452
|
});
|
|
18412
19453
|
const releasedOf = (unit) => ctx.get("workbenchPanels")?.isReleased(unit) ?? false;
|
|
18413
19454
|
const probeOne = async (unit, force = false) => {
|
|
18414
|
-
if (
|
|
19455
|
+
if (seam === void 0) return void 0;
|
|
18415
19456
|
const manifest = manifestOf(unit);
|
|
18416
19457
|
if (manifest === void 0 || !hasEnvNeeds(manifest)) return void 0;
|
|
18417
19458
|
const common = {
|
|
18418
|
-
|
|
19459
|
+
seam,
|
|
18419
19460
|
runtimeCommands,
|
|
18420
19461
|
force
|
|
18421
19462
|
};
|
|
@@ -18435,7 +19476,7 @@ async function apply(ctx, config) {
|
|
|
18435
19476
|
deniedTools: (context) => deniedNow(context),
|
|
18436
19477
|
logger: ctx.logger
|
|
18437
19478
|
});
|
|
18438
|
-
const pluginTools =
|
|
19479
|
+
const pluginTools = seam === void 0 ? void 0 : adapters.plugins.createUnits({
|
|
18439
19480
|
registry: units,
|
|
18440
19481
|
rows: () => ctx.get("workbenchPanels")?.listPlugins(null) ?? [],
|
|
18441
19482
|
declared: (plugin) => adapters.probe.declaredPluginCommands(profileDir).get(plugin) ?? /* @__PURE__ */ new Set(),
|
|
@@ -18510,6 +19551,7 @@ async function apply(ctx, config) {
|
|
|
18510
19551
|
probe: (unit, options) => probeOne(unit, options?.force ?? false),
|
|
18511
19552
|
values,
|
|
18512
19553
|
surface,
|
|
19554
|
+
...seam === void 0 ? {} : { seam },
|
|
18513
19555
|
longTasks: {
|
|
18514
19556
|
scope: process.env["DSH_HOME"] ?? "",
|
|
18515
19557
|
bump: (names) => facts.bump([...names, LONG_TASKS_ROUTE]),
|
|
@@ -18593,12 +19635,12 @@ async function apply(ctx, config) {
|
|
|
18593
19635
|
}
|
|
18594
19636
|
rebindPost();
|
|
18595
19637
|
}
|
|
18596
|
-
if (
|
|
19638
|
+
if (seam === void 0) ctx.logger?.warn?.("[dsh-workbench] 没有 shell 服务:需要执行缝的套件会降级");
|
|
18597
19639
|
else {
|
|
18598
19640
|
adapters.probe.registerPreflight({
|
|
18599
19641
|
routes,
|
|
18600
19642
|
ctx,
|
|
18601
|
-
|
|
19643
|
+
seam,
|
|
18602
19644
|
runtimeCommands,
|
|
18603
19645
|
runtimeCapabilities: capabilities,
|
|
18604
19646
|
...runtimeCheck === void 0 ? {} : { runtimeCheck: () => runtimeCheck(ctx) },
|
|
@@ -18608,7 +19650,7 @@ async function apply(ctx, config) {
|
|
|
18608
19650
|
routes.capability = capabilityHandler(async (unit, force) => {
|
|
18609
19651
|
if (unit === void 0) {
|
|
18610
19652
|
const common = {
|
|
18611
|
-
|
|
19653
|
+
seam,
|
|
18612
19654
|
runtimeCommands,
|
|
18613
19655
|
force
|
|
18614
19656
|
};
|