@dsh-workbench/core 1.1.9 → 1.1.10

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.
@@ -8,8 +8,8 @@ import { createHash, randomUUID } from "node:crypto";
8
8
  import { isDeepStrictEqual } from "node:util";
9
9
  import { createRequire } from "node:module";
10
10
  import writeFileAtomic from "write-file-atomic";
11
- import { deflateSync, inflateSync } from "node:zlib";
12
11
  import { execa } from "execa";
12
+ import { deflateSync, inflateSync } from "node:zlib";
13
13
  //#region \0rolldown/runtime.js
14
14
  var __create = Object.create;
15
15
  var __defProp = Object.defineProperty;
@@ -842,7 +842,7 @@ function refusalReason() {
842
842
  }
843
843
  //#endregion
844
844
  //#region ../shared/dist/host/shell.js
845
- var DEFAULT_MAX_BYTES$2 = 8388608;
845
+ var DEFAULT_MAX_BYTES$3 = 8388608;
846
846
  async function runShell(shell, command, options = {}) {
847
847
  const { workdir, timeoutMs = GIT_TIMEOUT_MS, signal } = options;
848
848
  const spec = shell.resolve({
@@ -850,7 +850,7 @@ async function runShell(shell, command, options = {}) {
850
850
  ...workdir === void 0 ? {} : { workdir },
851
851
  timeoutMs,
852
852
  onExpiry: "kill",
853
- stdoutMaxBytes: DEFAULT_MAX_BYTES$2,
853
+ stdoutMaxBytes: DEFAULT_MAX_BYTES$3,
854
854
  ...signal === void 0 ? {} : { signal }
855
855
  });
856
856
  const result = await (await shell.execute(spec)).result();
@@ -4597,7 +4597,7 @@ function approvalNoteOf(call) {
4597
4597
  if (line === "") return void 0;
4598
4598
  return line.length > 40 ? `${line.slice(0, 40)}…` : line;
4599
4599
  }
4600
- function messageOf$2(error) {
4600
+ function messageOf$3(error) {
4601
4601
  return error instanceof Error ? error.message : String(error);
4602
4602
  }
4603
4603
  function compose(detail, verdict, note) {
@@ -4622,7 +4622,7 @@ async function approvalFor(call, context = {}) {
4622
4622
  } catch (error) {
4623
4623
  return {
4624
4624
  kind: "deny",
4625
- reason: `问"要不要审批"的时候套件的策略抛了错,已经停下:${messageOf$2(error)}`
4625
+ reason: `问"要不要审批"的时候套件的策略抛了错,已经停下:${messageOf$3(error)}`
4626
4626
  };
4627
4627
  }
4628
4628
  if (verdict?.kind === "read" || verdict?.kind === "allow") return void 0;
@@ -4640,7 +4640,7 @@ async function approvalFor(call, context = {}) {
4640
4640
  } catch (error) {
4641
4641
  return {
4642
4642
  kind: "deny",
4643
- reason: `拿不到"这次会发生什么",已经停下:${messageOf$2(error)}`
4643
+ reason: `拿不到"这次会发生什么",已经停下:${messageOf$3(error)}`
4644
4644
  };
4645
4645
  }
4646
4646
  if (facts !== void 0 && "blocked" in facts) return {
@@ -6754,21 +6754,42 @@ function bundleInspectHandler(ctx) {
6754
6754
  }
6755
6755
  //#endregion
6756
6756
  //#region src/shared/api/host/git-exec.ts
6757
- var NO_PROMPT_ENV = [
6758
- "GIT_TERMINAL_PROMPT=0",
6759
- "GIT_ASKPASS=/usr/bin/false",
6760
- "SSH_ASKPASS=/usr/bin/false",
6761
- "SSH_ASKPASS_REQUIRE=never",
6762
- "GCM_INTERACTIVE=never",
6763
- "GIT_SSH_COMMAND=ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new"
6764
- ];
6765
- function gitNoPromptPrefix() {
6766
- return [quoteShellArg("env"), ...NO_PROMPT_ENV.map((one) => quoteShellArg(one))].join(" ");
6767
- }
6757
+ var GIT_NO_PROMPT_ENV = {
6758
+ GIT_TERMINAL_PROMPT: "0",
6759
+ GIT_ASKPASS: "/usr/bin/false",
6760
+ SSH_ASKPASS: "/usr/bin/false",
6761
+ SSH_ASKPASS_REQUIRE: "never",
6762
+ GCM_INTERACTIVE: "never",
6763
+ GIT_SSH_COMMAND: "ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new"
6764
+ };
6768
6765
  function withoutAskpassNoise(text) {
6769
6766
  return text.split("\n").filter((line) => !/unable to read askpass response/i.test(line)).join("\n").trim();
6770
6767
  }
6771
6768
  //#endregion
6769
+ //#region src/shared/api/host/git-process.ts
6770
+ var DEFAULT_MAX_BYTES$2 = 8388608;
6771
+ var runGitProcess = async (args, options = {}) => {
6772
+ const result = await execa("git", [...args], {
6773
+ ...options.cwd === void 0 ? {} : { cwd: options.cwd },
6774
+ env: {
6775
+ ...process.env,
6776
+ ...GIT_NO_PROMPT_ENV
6777
+ },
6778
+ stdin: "ignore",
6779
+ reject: false,
6780
+ ...options.timeoutMs === void 0 ? {} : { timeout: options.timeoutMs },
6781
+ killSignal: "SIGKILL",
6782
+ maxBuffer: options.maxBytes ?? DEFAULT_MAX_BYTES$2,
6783
+ stripFinalNewline: false
6784
+ });
6785
+ return {
6786
+ exitCode: result.exitCode ?? null,
6787
+ stdout: result.stdout ?? "",
6788
+ stderr: result.stderr ?? "",
6789
+ timedOut: result.timedOut === true
6790
+ };
6791
+ };
6792
+ //#endregion
6772
6793
  //#region src/shared/api/host/git-probe.ts
6773
6794
  var PROBE_TIMEOUT_MS = 1e4;
6774
6795
  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/;
@@ -6776,17 +6797,15 @@ function classifyGitProbe(stderr, timedOut) {
6776
6797
  if (timedOut) return "unreachable";
6777
6798
  return AUTH_TEXT.test(stderr.toLowerCase()) ? "auth" : "unreachable";
6778
6799
  }
6779
- async function probeGitAddress(shell, url, options = {}) {
6780
- const line = [
6781
- gitNoPromptPrefix(),
6782
- quoteShellArg("git"),
6783
- quoteShellArg("ls-remote"),
6784
- quoteShellArg(url),
6785
- quoteShellArg("HEAD")
6786
- ].join(" ");
6800
+ async function probeGitAddress(url, options = {}) {
6801
+ const runGit = options.runGit ?? runGitProcess;
6787
6802
  let result;
6788
6803
  try {
6789
- result = await runShell(shell, line, { timeoutMs: options.timeoutMs ?? PROBE_TIMEOUT_MS });
6804
+ result = await runGit([
6805
+ "ls-remote",
6806
+ url,
6807
+ "HEAD"
6808
+ ], { timeoutMs: options.timeoutMs ?? PROBE_TIMEOUT_MS });
6790
6809
  } catch (error) {
6791
6810
  return {
6792
6811
  status: "unreachable",
@@ -6802,10 +6821,10 @@ async function probeGitAddress(shell, url, options = {}) {
6802
6821
  }
6803
6822
  var PROBE_CACHE_MS = 6e4;
6804
6823
  var probeCache = /* @__PURE__ */ new Map();
6805
- async function probeGitAddressCached(shell, url, options = {}) {
6824
+ async function probeGitAddressCached(url, options = {}) {
6806
6825
  const hit = probeCache.get(url);
6807
6826
  if (hit !== void 0 && Date.now() - hit.at < PROBE_CACHE_MS) return hit.payload;
6808
- const payload = await probeGitAddress(shell, url, options);
6827
+ const payload = await probeGitAddress(url, options);
6809
6828
  probeCache.set(url, {
6810
6829
  at: Date.now(),
6811
6830
  payload
@@ -6814,7 +6833,7 @@ async function probeGitAddressCached(shell, url, options = {}) {
6814
6833
  }
6815
6834
  //#endregion
6816
6835
  //#region src/shared/api/host/git-routes.ts
6817
- function gitProbeHandler(ctx) {
6836
+ function gitProbeHandler(options = {}) {
6818
6837
  return async (search) => {
6819
6838
  const url = param(search, "url");
6820
6839
  if (url === void 0) return {
@@ -6824,17 +6843,9 @@ function gitProbeHandler(ctx) {
6824
6843
  message: "要探哪个地址?缺 url。"
6825
6844
  }
6826
6845
  };
6827
- const shell = ctx.get("shell");
6828
- if (shell === void 0) return {
6829
- ok: false,
6830
- error: {
6831
- code: "workbench/no-shell",
6832
- message: "这台机器上没有执行缝,探不了 git 地址"
6833
- }
6834
- };
6835
6846
  return {
6836
6847
  ok: true,
6837
- ...await probeGitAddress(shell, url)
6848
+ ...await probeGitAddress(url, options)
6838
6849
  };
6839
6850
  };
6840
6851
  }
@@ -6848,7 +6859,7 @@ function createHostRoutes(input) {
6848
6859
  [DSH_PLUGINS_ROUTE]: profile.dshPlugins(ctx),
6849
6860
  [BUNDLE_REGISTRIES_ROUTE]: bundleRegistriesHandler(ctx),
6850
6861
  [BUNDLE_INSPECT_ROUTE]: bundleInspectHandler(ctx),
6851
- [MARKET_GIT_PROBE_ROUTE]: gitProbeHandler(ctx),
6862
+ [MARKET_GIT_PROBE_ROUTE]: gitProbeHandler(),
6852
6863
  [MODULE_VERSIONS_ROUTE]: moduleVersionsHandler(ctx, ctx.get("shell"), running, () => profile.selfUpdateView(dshHome(), basename(profile.dirOf(ctx) ?? "")))
6853
6864
  };
6854
6865
  }
@@ -11096,24 +11107,34 @@ function siblingAuthorOf(root, unit) {
11096
11107
  }
11097
11108
  return authors.size === 1 ? [...authors][0] : void 0;
11098
11109
  }
11099
- async function localGitIdentityOf(shell, dir) {
11100
- if (shell === void 0) return void 0;
11101
- const read = async (argv) => {
11102
- const result = await runShell(shell, `git -C ${quoteShellArg(dir)} ${argv}`, { workdir: dir });
11103
- return result.exitCode === 0 ? result.stdout.trim() : "";
11110
+ async function localGitIdentityOf(dir, runGit = runGitProcess) {
11111
+ const read = async (args) => {
11112
+ try {
11113
+ const result = await runGit(args, { cwd: dir });
11114
+ return result.exitCode === 0 ? result.stdout.trim() : "";
11115
+ } catch {
11116
+ return "";
11117
+ }
11104
11118
  };
11105
- const name = await read(`config ${quoteShellArg("user.name")}`);
11119
+ const name = await read(["config", "user.name"]);
11106
11120
  if (name === "") return void 0;
11107
- const email = await read(`config ${quoteShellArg("user.email")}`);
11121
+ const email = await read(["config", "user.email"]);
11108
11122
  return email === "" ? name : `${name} <${email}>`;
11109
11123
  }
11110
- async function lastCommitAuthorOf(shell, dir) {
11111
- if (shell === void 0) return void 0;
11112
- const result = await runShell(shell, `git -C ${quoteShellArg(dir)} log -1 --format=${quoteShellArg("%an <%ae>")}`, { workdir: dir });
11113
- const author = result.exitCode === 0 ? result.stdout.trim() : "";
11114
- return author === "" ? void 0 : author;
11124
+ async function lastCommitAuthorOf(dir, runGit = runGitProcess) {
11125
+ try {
11126
+ const result = await runGit([
11127
+ "log",
11128
+ "-1",
11129
+ "--format=%an <%ae>"
11130
+ ], { cwd: dir });
11131
+ const author = result.exitCode === 0 ? result.stdout.trim() : "";
11132
+ return author === "" ? void 0 : author;
11133
+ } catch {
11134
+ return;
11135
+ }
11115
11136
  }
11116
- async function inferredAuthorOf(root, unit, shell) {
11137
+ async function inferredAuthorOf(root, unit, runGit = runGitProcess) {
11117
11138
  const fact = moduleFactOf(root, unit);
11118
11139
  if (fact === null) return void 0;
11119
11140
  if (fact.author !== "") return fact.author;
@@ -11124,9 +11145,9 @@ async function inferredAuthorOf(root, unit, shell) {
11124
11145
  }
11125
11146
  const sibling = root === null || root === void 0 ? void 0 : siblingAuthorOf(root, unit);
11126
11147
  if (sibling !== void 0) return sibling;
11127
- const local = await localGitIdentityOf(shell, fact.dir);
11148
+ const local = await localGitIdentityOf(fact.dir, runGit);
11128
11149
  if (local !== void 0) return local;
11129
- return lastCommitAuthorOf(shell, fact.dir);
11150
+ return lastCommitAuthorOf(fact.dir, runGit);
11130
11151
  }
11131
11152
  function publishPreviewOf(root, unit, options = {}) {
11132
11153
  if (root === null || root === void 0 || unit === "") return null;
@@ -14325,6 +14346,20 @@ function createMarketService(options) {
14325
14346
  }
14326
14347
  //#endregion
14327
14348
  //#region src/modules/panels/model/market-discover.ts
14349
+ var SANDBOX_BLOCK = [
14350
+ "- id: sandbox-policy",
14351
+ " config:",
14352
+ " mode: !!js process.env.DSH_PERMISSION_MODE ?? 'workspace-write'",
14353
+ " workspaceRoot: !!js dshHomePath('workbench')"
14354
+ ].join("\n");
14355
+ function explainProblem(problem, profilePath) {
14356
+ if (problem.kind !== "sandbox" || profilePath === void 0 || profilePath === "") return problem;
14357
+ const file = join(profilePath, "cordis.patch.yml");
14358
+ return {
14359
+ ...problem,
14360
+ detail: `被系统拦住了:这个 profile 的可写根盖不住工作台的缓存目录。\n退出 dsh 后,把下面这段加进 ${file},再启动:\n${SANDBOX_BLOCK}`
14361
+ };
14362
+ }
14328
14363
  var DEFAULT_PEER_TTL_MS = 6e5;
14329
14364
  var DEFAULT_TIMEOUT_MS$3 = 15e3;
14330
14365
  var CORE_PEER = "@dsh-workbench/core";
@@ -14476,12 +14511,12 @@ function createMarketDiscover(options) {
14476
14511
  sources: rows,
14477
14512
  selected: source.id,
14478
14513
  entries: [],
14479
- problem: read.problem
14514
+ problem: explainProblem(read.problem, options.profilePath)
14480
14515
  };
14481
14516
  return {
14482
14517
  ...await payloadOf(source, read.cached.index, rows, source.id, filter, read.cached.fetchedAt, read.cached.commit),
14483
14518
  stale: true,
14484
- problem: read.problem
14519
+ problem: explainProblem(read.problem, options.profilePath)
14485
14520
  };
14486
14521
  }
14487
14522
  return payloadOf(source, read.index, rows, source.id, filter, read.fetchedAt, read.commit);
@@ -14784,7 +14819,7 @@ function publishPreviewRoutes(workshop, inferredAuthorOf = async () => void 0) {
14784
14819
  var DEFAULT_TTL_MS = 3e5;
14785
14820
  var DEFAULT_TIMEOUT_MS$2 = 3e4;
14786
14821
  var DEFAULT_MAX_BYTES$1 = 4194304;
14787
- function messageOf$1(error) {
14822
+ function messageOf$2(error) {
14788
14823
  return error instanceof Error ? error.message : String(error);
14789
14824
  }
14790
14825
  function directoryOf(root, source) {
@@ -14853,6 +14888,10 @@ function classifyGitError(stderr, timedOut) {
14853
14888
  detail: "git 拉取超时"
14854
14889
  };
14855
14890
  const text = stderr.toLowerCase();
14891
+ if (text.includes("operation not permitted") || /\beperm\b/.test(text)) return {
14892
+ kind: "sandbox",
14893
+ detail: "被系统拦住了:这个 profile 的可写根盖不住工作台的缓存目录"
14894
+ };
14856
14895
  if (/couldn'?t find remote ref\s+["']?head/.test(text) || /could not find remote ref\s+["']?head/.test(text)) return {
14857
14896
  kind: "git-empty",
14858
14897
  detail: "仓库还没有任何提交"
@@ -14880,6 +14919,7 @@ function createMarketIndexReader(options) {
14880
14919
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS$2;
14881
14920
  const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES$1;
14882
14921
  const fetchImpl = options.fetchImpl ?? globalThis.fetch;
14922
+ const runGit = options.runGit ?? runGitProcess;
14883
14923
  const inFlight = /* @__PURE__ */ new Map();
14884
14924
  async function readHttp(source) {
14885
14925
  const bad = httpProblemOf(source.url);
@@ -14902,7 +14942,7 @@ function createMarketIndexReader(options) {
14902
14942
  detail: `拉索引超过 ${String(Math.round(timeoutMs / 1e3))} 秒`
14903
14943
  } : {
14904
14944
  kind: "http-network",
14905
- detail: messageOf$1(error)
14945
+ detail: messageOf$2(error)
14906
14946
  }
14907
14947
  };
14908
14948
  }
@@ -14939,17 +14979,13 @@ function createMarketIndexReader(options) {
14939
14979
  fetchedAt
14940
14980
  };
14941
14981
  }
14942
- async function run(line) {
14943
- const shell = options.shell;
14944
- if (shell === void 0) return {
14945
- ok: false,
14946
- problem: {
14947
- kind: "git-missing",
14948
- detail: "没有执行缝,读不了 git 索引"
14949
- }
14950
- };
14982
+ async function run(args, cwd) {
14951
14983
  try {
14952
- const result = await runShell(shell, `${gitNoPromptPrefix()} ${line}`, { timeoutMs });
14984
+ const result = await runGit(args, {
14985
+ ...cwd === void 0 ? {} : { cwd },
14986
+ timeoutMs,
14987
+ maxBytes
14988
+ });
14953
14989
  if (result.exitCode !== 0) return {
14954
14990
  ok: false,
14955
14991
  problem: classifyGitError(result.stderr, result.timedOut),
@@ -14960,7 +14996,7 @@ function createMarketIndexReader(options) {
14960
14996
  stdout: result.stdout
14961
14997
  };
14962
14998
  } catch (error) {
14963
- const text = messageOf$1(error);
14999
+ const text = messageOf$2(error);
14964
15000
  if (/not found|no such file|ENOENT/i.test(text)) return {
14965
15001
  ok: false,
14966
15002
  problem: {
@@ -14978,7 +15014,11 @@ function createMarketIndexReader(options) {
14978
15014
  }
14979
15015
  }
14980
15016
  async function branchOf(repo) {
14981
- const result = await run(`git -C ${quoteShellArg(repo)} symbolic-ref --short HEAD`);
15017
+ const result = await run([
15018
+ "symbolic-ref",
15019
+ "--short",
15020
+ "HEAD"
15021
+ ], repo);
14982
15022
  const branch = result.ok ? result.stdout.trim() : "";
14983
15023
  return branch === "" ? void 0 : branch;
14984
15024
  }
@@ -14999,20 +15039,21 @@ function createMarketIndexReader(options) {
14999
15039
  ok: false,
15000
15040
  problem: bad
15001
15041
  };
15002
- if (options.shell === void 0) return {
15003
- ok: false,
15004
- problem: {
15005
- kind: "git-missing",
15006
- detail: "没有 git 执行缝"
15007
- }
15008
- };
15009
15042
  const repo = repoOf(options.root, source);
15010
15043
  const path = ((source.path ?? "market").replace(/^\/+/, "") || "market").replace(/\/+$/, "");
15011
15044
  const ref = (source.ref ?? "").trim();
15012
15045
  let revision = "FETCH_HEAD";
15013
15046
  mkdirSync(directoryOf(options.root, source), { recursive: true });
15014
15047
  if (!existsSync(repo)) {
15015
- const cloned = await run(`git clone --bare --depth 1 ${ref === "" ? "" : `--branch ${quoteShellArg(ref)} `}${quoteShellArg(source.url)} ${quoteShellArg(repo)}`);
15048
+ const cloned = await run([
15049
+ "clone",
15050
+ "--bare",
15051
+ "--depth",
15052
+ "1",
15053
+ ...ref === "" ? [] : ["--branch", ref],
15054
+ source.url,
15055
+ repo
15056
+ ]);
15016
15057
  if (!cloned.ok) {
15017
15058
  rmSync(repo, {
15018
15059
  recursive: true,
@@ -15022,17 +15063,27 @@ function createMarketIndexReader(options) {
15022
15063
  }
15023
15064
  revision = "HEAD";
15024
15065
  } else {
15025
- const fetched = await run(`git -C ${quoteShellArg(repo)} fetch --depth 1 origin ${quoteShellArg(ref === "" ? "HEAD" : ref)}`);
15066
+ const fetched = await run([
15067
+ "fetch",
15068
+ "--depth",
15069
+ "1",
15070
+ "origin",
15071
+ ref === "" ? "HEAD" : ref
15072
+ ], repo);
15026
15073
  if (!fetched.ok) return withBranch(fetched, repo);
15027
15074
  }
15028
- if (!(await run(`git -C ${quoteShellArg(repo)} rev-parse --verify HEAD`)).ok) return withBranch({
15075
+ if (!(await run([
15076
+ "rev-parse",
15077
+ "--verify",
15078
+ "HEAD"
15079
+ ], repo)).ok) return withBranch({
15029
15080
  ok: false,
15030
15081
  problem: {
15031
15082
  kind: "git-empty",
15032
15083
  detail: "仓库还没有任何提交"
15033
15084
  }
15034
15085
  }, repo);
15035
- const head = await run(`git -C ${quoteShellArg(repo)} rev-parse ${revision}`);
15086
+ const head = await run(["rev-parse", revision], repo);
15036
15087
  if (!head.ok) return head;
15037
15088
  const commit = head.stdout.trim();
15038
15089
  if (!/^[0-9a-f]{40}$/.test(commit)) return {
@@ -15042,7 +15093,13 @@ function createMarketIndexReader(options) {
15042
15093
  detail: `读到的 commit 不是 40 位:${commit}`
15043
15094
  }
15044
15095
  };
15045
- const listed = await run(`git -C ${quoteShellArg(repo)} ls-tree -r ${quoteShellArg(commit)} -- ${quoteShellArg(path)}`);
15096
+ const listed = await run([
15097
+ "ls-tree",
15098
+ "-r",
15099
+ commit,
15100
+ "--",
15101
+ path
15102
+ ], repo);
15046
15103
  if (!listed.ok) {
15047
15104
  if (listed.problem.kind === "git-file") return withBranch({
15048
15105
  ok: false,
@@ -15078,7 +15135,7 @@ function createMarketIndexReader(options) {
15078
15135
  };
15079
15136
  };
15080
15137
  const readLegacy = async (file) => {
15081
- const shown = await run(`git -C ${quoteShellArg(repo)} show ${quoteShellArg(commit)}:${quoteShellArg(file)}`);
15138
+ const shown = await run(["show", `${commit}:${file}`], repo);
15082
15139
  if (!shown.ok) return void 0;
15083
15140
  const parsed = parseMarketIndexJson(shown.stdout);
15084
15141
  if (!parsed.ok) return {
@@ -15102,7 +15159,7 @@ function createMarketIndexReader(options) {
15102
15159
  for (;;) {
15103
15160
  const next = queue.shift();
15104
15161
  if (next === void 0 || failed !== void 0) return;
15105
- const shown = await run(`git -C ${quoteShellArg(repo)} show ${quoteShellArg(commit)}:${quoteShellArg(next.file)}`);
15162
+ const shown = await run(["show", `${commit}:${next.file}`], repo);
15106
15163
  if (!shown.ok) {
15107
15164
  failed = shown;
15108
15165
  return;
@@ -15155,7 +15212,7 @@ function createMarketIndexReader(options) {
15155
15212
  ok: false,
15156
15213
  problem: {
15157
15214
  kind: "unknown",
15158
- detail: messageOf$1(error)
15215
+ detail: messageOf$2(error)
15159
15216
  }
15160
15217
  })).then((result) => {
15161
15218
  if (result.ok) return result;
@@ -15184,7 +15241,7 @@ var FILES = {
15184
15241
  var DEFAULT_TIMEOUT_MS$1 = 15e3;
15185
15242
  var DEFAULT_MAX_BYTES = 204800;
15186
15243
  var DEFAULT_REGISTRY = "https://registry.npmjs.org";
15187
- function messageOf(error) {
15244
+ function messageOf$1(error) {
15188
15245
  return error instanceof Error ? error.message : String(error);
15189
15246
  }
15190
15247
  function clipped(text, maxBytes) {
@@ -15199,6 +15256,7 @@ function createMarketDocReader(options) {
15199
15256
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS$1;
15200
15257
  const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
15201
15258
  const fetchImpl = options.fetchImpl ?? globalThis.fetch;
15259
+ const runGit = options.runGit ?? runGitProcess;
15202
15260
  const cache = /* @__PURE__ */ new Map();
15203
15261
  function fail(kind, version, reason, detail) {
15204
15262
  return {
@@ -15216,22 +15274,40 @@ function createMarketDocReader(options) {
15216
15274
  async function readGit(source, target, kind) {
15217
15275
  const git = target.source;
15218
15276
  if (git.type !== "git") return fail(kind, target.version, "missing");
15219
- const shell = options.shell;
15220
- if (shell === void 0) return fail(kind, target.version, "unavailable", "没有 git 执行缝");
15221
15277
  const key = `${git.commit}|${git.path ?? ""}|${kind}`;
15222
15278
  const cached = cache.get(key);
15223
15279
  if (cached !== void 0) return cached;
15224
15280
  const directory = (git.path ?? "").replace(/^\/+|\/+$/g, "");
15225
15281
  const file = directory === "" ? FILES[kind] : `${directory}/${FILES[kind]}`;
15226
15282
  const repo = join(options.root, "market-index", marketSourceKey(source), "repo.git");
15227
- const show = () => runShell(shell, `git -C ${quoteShellArg(repo)} show ${git.commit}:${quoteShellArg(file)}`, { timeoutMs });
15283
+ const show = () => runGit(["show", `${git.commit}:${file}`], {
15284
+ cwd: repo,
15285
+ timeoutMs,
15286
+ maxBytes
15287
+ });
15228
15288
  const hasCommit = async () => {
15229
- return (await runShell(shell, `git -C ${quoteShellArg(repo)} cat-file -e ${quoteShellArg(`${git.commit}^{commit}`)}`, { timeoutMs })).exitCode === 0;
15289
+ return (await runGit([
15290
+ "cat-file",
15291
+ "-e",
15292
+ `${git.commit}^{commit}`
15293
+ ], {
15294
+ cwd: repo,
15295
+ timeoutMs
15296
+ })).exitCode === 0;
15230
15297
  };
15231
15298
  try {
15232
15299
  let result = await show();
15233
15300
  if (result.exitCode !== 0 && !await hasCommit()) {
15234
- if ((await runShell(shell, `git -C ${quoteShellArg(repo)} fetch --depth 1 origin ${git.commit}`, { timeoutMs })).exitCode === 0) result = await show();
15301
+ if ((await runGit([
15302
+ "fetch",
15303
+ "--depth",
15304
+ "1",
15305
+ "origin",
15306
+ git.commit
15307
+ ], {
15308
+ cwd: repo,
15309
+ timeoutMs
15310
+ })).exitCode === 0) result = await show();
15235
15311
  }
15236
15312
  if (result.exitCode !== 0) {
15237
15313
  const text = result.stderr.trim();
@@ -15244,8 +15320,8 @@ function createMarketDocReader(options) {
15244
15320
  text: clipped(result.stdout, maxBytes)
15245
15321
  });
15246
15322
  } catch (error) {
15247
- options.logger?.warn?.(`[dsh-workbench] 读 ${file} 失败:${messageOf(error)}`);
15248
- return fail(kind, target.version, "unavailable", messageOf(error));
15323
+ options.logger?.warn?.(`[dsh-workbench] 读 ${file} 失败:${messageOf$1(error)}`);
15324
+ return fail(kind, target.version, "unavailable", messageOf$1(error));
15249
15325
  }
15250
15326
  }
15251
15327
  async function readNpm(target, kind) {
@@ -15271,8 +15347,8 @@ function createMarketDocReader(options) {
15271
15347
  text: clipped(text, maxBytes)
15272
15348
  });
15273
15349
  } catch (error) {
15274
- options.logger?.warn?.(`[dsh-workbench] 读 ${target.name} 的说明失败:${messageOf(error)}`);
15275
- return fail(kind, target.version, "unavailable", messageOf(error));
15350
+ options.logger?.warn?.(`[dsh-workbench] 读 ${target.name} 的说明失败:${messageOf$1(error)}`);
15351
+ return fail(kind, target.version, "unavailable", messageOf$1(error));
15276
15352
  }
15277
15353
  }
15278
15354
  return { read: (source, target, kind) => target.source.type === "git" ? readGit(source, target, kind) : readNpm(target, kind) };
@@ -15391,8 +15467,17 @@ var CACHE_FILES = [
15391
15467
  "index.json",
15392
15468
  "meta.json"
15393
15469
  ];
15470
+ function messageOf(error) {
15471
+ return error instanceof Error ? error.message : String(error);
15472
+ }
15473
+ var SANDBOX_TEXT = "被系统拦住了:这个 profile 的可写根盖不住工作台的缓存目录";
15474
+ function sandboxBlocked(text) {
15475
+ const lower = text.toLowerCase();
15476
+ return lower.includes("operation not permitted") || /\beperm\b/.test(lower);
15477
+ }
15394
15478
  function createMarketSourceInitializer(options) {
15395
15479
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
15480
+ const runGit = options.runGit ?? runGitProcess;
15396
15481
  function forget(source, next) {
15397
15482
  const base = join(options.root, "market-index");
15398
15483
  const oldKey = marketSourceKey(source);
@@ -15415,22 +15500,22 @@ function createMarketSourceInitializer(options) {
15415
15500
  });
15416
15501
  }
15417
15502
  return async (request) => {
15418
- const shell = options.shell;
15419
- if (shell === void 0) throw new Error("这台机器上没有 shell 服务,初始化不了索引仓库");
15420
15503
  const source = options.store.read().sources.find((one) => one.id === request.id);
15421
15504
  if (source === void 0) throw new Error("这个索引源已经不在了 —— 刷新后再试");
15422
15505
  if (source.kind !== "git") throw new Error("只有 git 仓库能初始化成市场索引");
15423
15506
  if (!gitUrlOk(source.url)) throw new Error(`这个 git 地址不合格:${source.url}`);
15424
15507
  const asked = (request.ref ?? "").trim();
15425
15508
  if (asked !== "" && !marketBranchOk(asked)) throw new Error(`分支名不合格:${asked}`);
15426
- const git = (argv, workdir) => runShell(shell, [
15427
- "git",
15428
- argv[0],
15429
- ...argv.slice(1).map((one) => quoteShellArg(one))
15430
- ].join(" "), {
15431
- ...workdir === void 0 ? {} : { workdir },
15432
- timeoutMs
15433
- });
15509
+ const git = async (args, workdir) => {
15510
+ try {
15511
+ return await runGit(args, {
15512
+ ...workdir === void 0 ? {} : { cwd: workdir },
15513
+ timeoutMs
15514
+ });
15515
+ } catch (error) {
15516
+ throw new Error(`git 跑不起来:${messageOf(error)}`, { cause: error });
15517
+ }
15518
+ };
15434
15519
  const workdir = join(options.root, "market-index", marketSourceKey(source), "worktree");
15435
15520
  mkdirSync(dirname(workdir), { recursive: true });
15436
15521
  if (!existsSync(join(workdir, ".git"))) {
@@ -15444,6 +15529,7 @@ function createMarketSourceInitializer(options) {
15444
15529
  recursive: true,
15445
15530
  force: true
15446
15531
  });
15532
+ if (sandboxBlocked(cloned.stderr)) throw new Error(SANDBOX_TEXT);
15447
15533
  throw new Error(`索引仓库克隆不下来:${tailLines(cloned.stderr || cloned.stdout, 4)}`);
15448
15534
  }
15449
15535
  }
@@ -16912,24 +16998,22 @@ var OWNED = [{
16912
16998
  });
16913
16999
  const marketIndex = createMarketIndexReader({
16914
17000
  root: marketRoot,
16915
- shell,
16916
17001
  ...logger === void 0 ? {} : { logger }
16917
17002
  });
16918
17003
  const marketDocs = createMarketDocReader({
16919
17004
  root: marketRoot,
16920
- shell,
16921
17005
  ...logger === void 0 ? {} : { logger }
16922
17006
  });
16923
17007
  const marketInit = createMarketSourceInitializer({
16924
17008
  root: marketRoot,
16925
- store: marketSources,
16926
- ...shell === void 0 ? {} : { shell }
17009
+ store: marketSources
16927
17010
  });
16928
17011
  const discover = createMarketDiscover({
16929
17012
  store: marketSources,
16930
17013
  reader: marketIndex,
16931
17014
  docs: marketDocs,
16932
17015
  modules: () => supply.profileModules(profileDir),
17016
+ ...profileDir === null ? {} : { profilePath: profileDir },
16933
17017
  defaultRegistry: async () => {
16934
17018
  try {
16935
17019
  return (await bundles?.registries())?.resolved ?? void 0;
@@ -16959,9 +17043,9 @@ var OWNED = [{
16959
17043
  dependencyOf: dependencyReader(profileDir),
16960
17044
  isNewer: supply.isNewerVersion,
16961
17045
  preferred: (host) => marketSources.read().transports?.[host],
16962
- ...shell === void 0 ? {} : { probe: (url) => probeGitAddressCached(shell, url, { timeoutMs: 4e3 }).then((one) => one.status) }
17046
+ probe: (url) => probeGitAddressCached(url, { timeoutMs: 4e3 }).then((one) => one.status)
16963
17047
  }) })),
16964
- ...publishPreviewRoutes(workshopPathOf(ctx), (unit) => inferredAuthorOf(workshopPathOf(ctx), unit, shell))
17048
+ ...publishPreviewRoutes(workshopPathOf(ctx), (unit) => inferredAuthorOf(workshopPathOf(ctx), unit))
16965
17049
  },
16966
17050
  registerTools: () => [
16967
17051
  ...registerPanelTools(ctx, panels, values),
@@ -16970,7 +17054,7 @@ var OWNED = [{
16970
17054
  init: marketInit,
16971
17055
  readOnly: (id) => markSourceReadOnly(marketSources, id)
16972
17056
  }),
16973
- ...registerDiyTools(ctx, market, panels, debug, (unit) => publishPreviewOf(workshopPathOf(ctx), unit), (unit) => inferredAuthorOf(workshopPathOf(ctx), unit, shell)),
17057
+ ...registerDiyTools(ctx, market, panels, debug, (unit) => publishPreviewOf(workshopPathOf(ctx), unit), (unit) => inferredAuthorOf(workshopPathOf(ctx), unit)),
16974
17058
  ...registerRecordTools(ctx, record),
16975
17059
  ...registerLongTaskTools(ctx, longTaskRuns)
16976
17060
  ],
@@ -3,6 +3,7 @@ import type { PreflightBridgeIssue, PreflightGuidance, PreflightSnapshot, Prefli
3
3
  import type { RuntimeProfile } from '@dsh-workbench/shared';
4
4
  export declare function failedStepText(guidance: PreflightGuidance | undefined, t: Translate): string;
5
5
  export declare function stepDetailText(step: PreflightStep, snapshot: PreflightSnapshot, t: Translate): string;
6
+ export declare function gateHintOf(snapshot: PreflightSnapshot, t: Translate): string;
6
7
  export declare function showsRawDetails(snapshot: PreflightSnapshot): boolean;
7
8
  export declare function showsRawAll(snapshot: PreflightSnapshot, panelShown: boolean): boolean;
8
9
  export interface BridgeAdvice {
@@ -1,5 +1,6 @@
1
1
  import 'dayjs/locale/zh-cn';
2
2
  import type { DiscoverEntry, DiscoverSourceRow, DiscoverVersionRow, MarketSourceConfig } from '../../../kernel/contract/market-index';
3
+ export declare function localeNow(): string;
3
4
  export type DiscoverSort = 'default' | 'published';
4
5
  export declare const DISCOVER_PAGE_SIZE = 50;
5
6
  export interface DiscoverFilterState {
@@ -10,7 +10,7 @@ export interface WorkspaceSectionPlan<T> {
10
10
  readonly items: readonly T[];
11
11
  }
12
12
  export interface WorkspaceSectionOptions<T> {
13
- readonly rankOf?: ((item: T) => number) | undefined;
13
+ readonly rankOf?: ((item: T) => number | null) | undefined;
14
14
  }
15
15
  export declare function workspaceSections<T extends {
16
16
  readonly path: string;