@michengai/dsh-automation 0.1.55 → 0.1.59

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/lib/index.js CHANGED
@@ -1655,8 +1655,8 @@ function emoji() {
1655
1655
  }
1656
1656
  var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
1657
1657
  var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
1658
- var mac = (delimiter) => {
1659
- const escapedDelim = escapeRegex(delimiter ?? ":");
1658
+ var mac = (delimiter2) => {
1659
+ const escapedDelim = escapeRegex(delimiter2 ?? ":");
1660
1660
  return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
1661
1661
  };
1662
1662
  var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
@@ -23336,7 +23336,7 @@ import { spawn } from "node:child_process";
23336
23336
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
23337
23337
  import { readFile } from "node:fs/promises";
23338
23338
  import { homedir as homedir2 } from "node:os";
23339
- import { basename, dirname, isAbsolute, resolve } from "node:path";
23339
+ import { basename, delimiter, dirname, isAbsolute, resolve } from "node:path";
23340
23340
  import { fileURLToPath } from "node:url";
23341
23341
  var PLUGIN_UPDATE_HEADER = "x-michengai-plugin-update";
23342
23342
  var PLUGIN_UPDATE_IPC = "apply-plugin-updates";
@@ -23353,9 +23353,16 @@ function isTrustedUpdateRequest(request) {
23353
23353
  if (!isLoopbackAddress(request.socket?.remoteAddress)) return false;
23354
23354
  const site = header(request, "sec-fetch-site");
23355
23355
  if (site !== void 0 && site !== "same-origin") return false;
23356
- const origin = header(request, "origin");
23357
23356
  const host = header(request, "host");
23358
- if (origin === void 0 || host === void 0) return false;
23357
+ if (host === void 0 || host === "") return false;
23358
+ const origin = header(request, "origin");
23359
+ if (origin === void 0 || origin === "") {
23360
+ try {
23361
+ return isLoopbackAddress(new URL(`http://${host}`).hostname);
23362
+ } catch {
23363
+ return false;
23364
+ }
23365
+ }
23359
23366
  try {
23360
23367
  const url2 = new URL(origin);
23361
23368
  return (url2.protocol === "http:" || url2.protocol === "https:") && isLoopbackAddress(url2.hostname) && url2.host === host;
@@ -23380,14 +23387,14 @@ function isDshCliEntry(entry, manifest, packageRoot) {
23380
23387
  const bin = typeof value.bin === "string" ? value.bin : typeof value.bin === "object" && value.bin !== null ? value.bin.dsh : void 0;
23381
23388
  return typeof bin === "string" && bin !== "" && !isAbsolute(bin) && resolve(packageRoot, bin) === resolve(entry);
23382
23389
  }
23383
- function cliEntry() {
23384
- const value = process.argv[1];
23390
+ function cliEntry(argv = process.argv, cwd = process.cwd(), exists = existsSync2) {
23391
+ const value = argv[1];
23385
23392
  if (value === void 0 || value === "") return void 0;
23386
- const entry = value.startsWith("file:") ? fileURLToPath(value) : resolve(process.cwd(), value);
23387
- if (!existsSync2(entry)) return void 0;
23393
+ const entry = value.startsWith("file:") ? fileURLToPath(value) : resolve(cwd, value);
23394
+ if (!exists(entry)) return void 0;
23388
23395
  for (let directory = dirname(entry); ; ) {
23389
23396
  const manifestPath = resolve(directory, "package.json");
23390
- if (existsSync2(manifestPath)) {
23397
+ if (exists(manifestPath)) {
23391
23398
  try {
23392
23399
  if (isDshCliEntry(entry, JSON.parse(readFileSync2(manifestPath, "utf8")), directory)) return entry;
23393
23400
  } catch {
@@ -23398,21 +23405,75 @@ function cliEntry() {
23398
23405
  directory = parent;
23399
23406
  }
23400
23407
  }
23401
- function runtime(ctx) {
23402
- const profiles = ctx.get?.("desktopProfiles");
23403
- const desktopPnpm = ctx.get?.("desktopPnpm");
23408
+ function optionalService(ctx, name2) {
23409
+ try {
23410
+ return typeof ctx.get === "function" ? ctx.get(name2) : void 0;
23411
+ } catch {
23412
+ return void 0;
23413
+ }
23414
+ }
23415
+ function isOfficialDesktopHostEntry(entry) {
23416
+ return entry !== void 0 && entry !== "" && entry.replaceAll("\\", "/").includes("/dsh-desktop-host/");
23417
+ }
23418
+ function stringEnvironment(value) {
23419
+ if (value === null || typeof value !== "object") return void 0;
23420
+ const env = {};
23421
+ for (const [key, item] of Object.entries(value)) if (typeof item === "string") env[key] = item;
23422
+ return Object.keys(env).length === 0 ? void 0 : env;
23423
+ }
23424
+ function packageManagerFrom(value) {
23425
+ if (value === null || typeof value !== "object") return void 0;
23426
+ const item = value;
23427
+ if (typeof item.command !== "string" || item.command === "") return void 0;
23428
+ if (!Array.isArray(item.args) || !item.args.every((arg) => typeof arg === "string")) return void 0;
23429
+ const env = stringEnvironment(item.env);
23430
+ return { command: item.command, args: item.args, ...env === void 0 ? {} : { env } };
23431
+ }
23432
+ function packageManagerFromArgv(argv, exists, execPath, pathEnv) {
23433
+ if (!isOfficialDesktopHostEntry(argv[1])) return void 0;
23434
+ const pnpm = argv[5];
23435
+ if (typeof pnpm !== "string" || !isAbsolute(pnpm) || !exists(pnpm)) return void 0;
23436
+ const bin = argv[6];
23437
+ const path = typeof bin === "string" && bin !== "" ? `${bin}${delimiter}${pathEnv ?? ""}` : pathEnv;
23438
+ return { command: execPath, args: ["--expose-internals", pnpm], env: { ELECTRON_RUN_AS_NODE: "1", ...typeof path === "string" && path !== "" ? { PATH: path } : {} } };
23439
+ }
23440
+ function pluginManagerFrom(value) {
23441
+ if (value === null || typeof value !== "object" || typeof value.installBundle !== "function") return void 0;
23442
+ return value;
23443
+ }
23444
+ function resolveUpdateRuntime(ctx, options = {}) {
23445
+ const argv = options.argv ?? process.argv;
23446
+ const env = options.env ?? process.env;
23447
+ const cwd = options.cwd ?? process.cwd();
23448
+ const home = options.homeDir ?? homedir2();
23449
+ const exists = options.exists ?? existsSync2;
23450
+ const execPath = options.execPath ?? process.execPath;
23451
+ const profiles = optionalService(ctx, "desktopProfiles");
23452
+ const desktopPnpm = optionalService(ctx, "desktopPnpm");
23404
23453
  if (profiles?.current !== void 0) {
23405
23454
  const current = profiles.current;
23406
- if (!validProfileName(current.name) || typeof current.dir !== "string" || !isAbsolute(current.dir)) {
23407
- throw new Error("\u5F53\u524D Desktop Profile \u4FE1\u606F\u65E0\u6548\uFF0C\u8BF7\u91CD\u542F\u540E\u91CD\u8BD5\u3002");
23408
- }
23409
- return { profileName: current.name, profileDir: resolve(current.dir), ...typeof desktopPnpm?.runPlugin === "function" ? { desktopPnpm } : {} };
23410
- }
23411
- const profileDir = resolve(process.env.DSH_PROFILE_DIR ?? resolve(homedir2(), ".dsh", "profiles", "web"));
23412
- const selected = profileNameFromArgv(process.argv);
23413
- const profileName = validProfileName(selected) ? selected : validProfileName(basename(profileDir)) ? basename(profileDir) : "web";
23414
- const entry = cliEntry();
23415
- return { profileName, profileDir, ...entry === void 0 ? {} : { cliEntry: entry } };
23455
+ if (!validProfileName(current?.name) || typeof current.dir !== "string" || !isAbsolute(current.dir)) throw new Error("\u5F53\u524D Desktop Profile \u4FE1\u606F\u65E0\u6548\uFF0C\u8BF7\u91CD\u542F\u540E\u91CD\u8BD5\u3002");
23456
+ const runnable = typeof desktopPnpm?.runPlugin === "function";
23457
+ return { profileName: current.name, profileDir: resolve(current.dir), officialDesktop: false, canAutoUpdate: runnable, ...runnable ? { desktopPnpm } : {} };
23458
+ }
23459
+ const launched = optionalService(ctx, "profileContext");
23460
+ const official = isOfficialDesktopHostEntry(argv[1]) || launched?.name === "desktop";
23461
+ const launchedDir = typeof launched?.dir === "string" && isAbsolute(launched.dir) ? resolve(launched.dir) : void 0;
23462
+ const projectDir = official && typeof argv[3] === "string" && isAbsolute(argv[3]) ? resolve(argv[3]) : void 0;
23463
+ const cwdProfile = official && exists(resolve(cwd, "package.json")) ? resolve(cwd) : void 0;
23464
+ const profileDir = launchedDir ?? projectDir ?? cwdProfile ?? resolve(env.DSH_PROFILE_DIR ?? resolve(home, ".dsh", "profiles", "web"));
23465
+ const selected = profileNameFromArgv(argv);
23466
+ const profileName = validProfileName(launched?.name) ? launched.name : official ? "desktop" : validProfileName(selected) ? selected : validProfileName(basename(profileDir)) ? basename(profileDir) : "web";
23467
+ const packageManager = official ? packageManagerFrom(launched?.packageManager) ?? packageManagerFromArgv(argv, exists, execPath, env.PATH) : void 0;
23468
+ const pluginManager = official && packageManager === void 0 ? pluginManagerFrom(optionalService(ctx, "pluginManager")) : void 0;
23469
+ const entry = official ? void 0 : cliEntry(argv, cwd, exists);
23470
+ return { profileName, profileDir, officialDesktop: official, canAutoUpdate: packageManager !== void 0 || pluginManager !== void 0 || entry !== void 0, ...packageManager === void 0 ? {} : { packageManager }, ...pluginManager === void 0 ? {} : { pluginManager }, ...entry === void 0 ? {} : { cliEntry: entry } };
23471
+ }
23472
+ function shouldNotifyParent(target, send = process.send) {
23473
+ return target.officialDesktop !== true && target.desktopPnpm === void 0 && target.packageManager === void 0 && typeof send === "function";
23474
+ }
23475
+ function runtime(ctx) {
23476
+ return resolveUpdateRuntime(ctx);
23416
23477
  }
23417
23478
  function parseSemver(value) {
23418
23479
  const match2 = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value);
@@ -23478,7 +23539,7 @@ async function status(options, target) {
23478
23539
  latestCheckFailed: latest === void 0,
23479
23540
  updateAvailable: latest !== void 0 && isNewerVersion(current, latest),
23480
23541
  profileName: target.profileName,
23481
- canAutoUpdate: target.desktopPnpm !== void 0 || target.cliEntry !== void 0
23542
+ canAutoUpdate: target.canAutoUpdate
23482
23543
  };
23483
23544
  }
23484
23545
  async function runCliInstall(target, packageSpec) {
@@ -23513,10 +23574,50 @@ async function runCliInstall(target, packageSpec) {
23513
23574
  });
23514
23575
  }
23515
23576
  async function install(target, packageSpec) {
23516
- if (target.desktopPnpm === void 0) return runCliInstall(target, packageSpec);
23517
- const handle = target.desktopPnpm.runPlugin(["add", "--config.minimumReleaseAge=0", packageSpec, "--registry=https://registry.npmjs.org/"], target.profileDir);
23518
- const result = await handle.done;
23519
- if (result.exitCode !== 0) throw new Error(`\u66F4\u65B0\u8FDB\u7A0B\u9000\u51FA\u7801 ${String(result.exitCode)}\u3002`);
23577
+ if (target.desktopPnpm !== void 0) {
23578
+ const handle = target.desktopPnpm.runPlugin(["add", "--config.minimumReleaseAge=0", packageSpec, "--registry=https://registry.npmjs.org/"], target.profileDir);
23579
+ const result = await handle.done;
23580
+ if (result.exitCode !== 0) throw new Error(`\u66F4\u65B0\u8FDB\u7A0B\u9000\u51FA\u7801 ${String(result.exitCode)}\u3002`);
23581
+ return;
23582
+ }
23583
+ if (target.packageManager !== void 0) {
23584
+ const manager = target.packageManager;
23585
+ await new Promise((resolvePromise, reject) => {
23586
+ const child = spawn(manager.command, [...manager.args, "add", "--config.minimumReleaseAge=0", packageSpec, "--registry=https://registry.npmjs.org/"], {
23587
+ cwd: target.profileDir,
23588
+ windowsHide: true,
23589
+ stdio: ["ignore", "pipe", "pipe"],
23590
+ env: { ...process.env, ...manager.env, NO_COLOR: "1" }
23591
+ });
23592
+ let detail = "";
23593
+ child.stdout?.on("data", (chunk) => {
23594
+ detail = (detail + String(chunk)).slice(-4e3);
23595
+ });
23596
+ child.stderr?.on("data", (chunk) => {
23597
+ detail = (detail + String(chunk)).slice(-4e3);
23598
+ });
23599
+ const timer = setTimeout(() => {
23600
+ child.kill();
23601
+ reject(new Error("\u66F4\u65B0\u8D85\u65F6\uFF0C\u8BF7\u6539\u7528\u624B\u5DE5\u66F4\u65B0\u3002"));
23602
+ }, 10 * 6e4);
23603
+ child.once("error", (error51) => {
23604
+ clearTimeout(timer);
23605
+ reject(error51);
23606
+ });
23607
+ child.once("exit", (code) => {
23608
+ clearTimeout(timer);
23609
+ if (code === 0) resolvePromise();
23610
+ else reject(new Error(detail.trim() || `\u66F4\u65B0\u8FDB\u7A0B\u9000\u51FA\u7801 ${String(code)}`));
23611
+ });
23612
+ });
23613
+ return;
23614
+ }
23615
+ if (target.pluginManager !== void 0) {
23616
+ const result = await target.pluginManager.installBundle(packageSpec);
23617
+ if (result.application === "applied" || result.application === "restart-required" || result.application === "overridden") return;
23618
+ throw new Error(result.error?.message || result.packageResult?.output || "\u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B\u670D\u52A1\u7AEF\u65E5\u5FD7\u3002");
23619
+ }
23620
+ return runCliInstall(target, packageSpec);
23520
23621
  }
23521
23622
  function json3(response, statusCode, value) {
23522
23623
  response.writeHead(statusCode, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" });
@@ -23569,9 +23670,9 @@ function registerPluginUpdater(ctx, options) {
23569
23670
  } finally {
23570
23671
  installing = false;
23571
23672
  }
23572
- const notifyParent = target.desktopPnpm === void 0 && typeof process.send === "function";
23673
+ const notifyParent = shouldNotifyParent(target);
23573
23674
  const autoReload = target.desktopPnpm !== void 0 || notifyParent;
23574
- json3(response, 200, { ...before, updatedVersion: before.latestVersion, restartRequired: true, autoReload });
23675
+ json3(response, 200, { ...before, updatedVersion: before.latestVersion, restartRequired: true, autoReload, ...target.officialDesktop ? { restartDesktop: true } : {} });
23575
23676
  if (notifyParent) setTimeout(() => {
23576
23677
  process.send?.(PLUGIN_UPDATE_IPC);
23577
23678
  }, 150).unref?.();
@@ -9,13 +9,60 @@ type HostRequest = {
9
9
  remoteAddress?: string;
10
10
  };
11
11
  };
12
+ type DesktopPnpmHandle = {
13
+ done: Promise<{
14
+ exitCode: number | null;
15
+ signal: NodeJS.Signals | null;
16
+ }>;
17
+ cancel(): void;
18
+ };
19
+ type DesktopPnpm = {
20
+ runPlugin(args: readonly string[], invokingDir: string, signal?: AbortSignal): DesktopPnpmHandle;
21
+ };
22
+ type PackageManagerLaunch = {
23
+ command: string;
24
+ args: readonly string[];
25
+ env?: NodeJS.ProcessEnv;
26
+ };
27
+ type PluginManagerInstall = {
28
+ installBundle(spec: string): Promise<{
29
+ application?: string;
30
+ error?: {
31
+ message?: string;
32
+ };
33
+ packageResult?: {
34
+ output?: string;
35
+ };
36
+ }>;
37
+ };
12
38
  export type PluginUpdaterOptions = {
13
39
  readonly endpoint: string;
14
40
  readonly packageName: string;
15
41
  readonly manifestUrl: URL;
16
42
  };
43
+ type Runtime = {
44
+ profileName: string;
45
+ profileDir: string;
46
+ desktopPnpm?: DesktopPnpm;
47
+ cliEntry?: string;
48
+ packageManager?: PackageManagerLaunch;
49
+ pluginManager?: PluginManagerInstall;
50
+ officialDesktop: boolean;
51
+ canAutoUpdate: boolean;
52
+ };
17
53
  export declare function isTrustedUpdateRequest(request: HostRequest): boolean;
18
54
  export declare function isDshCliEntry(entry: string, manifest: unknown, packageRoot: string): boolean;
55
+ export declare function resolveUpdateRuntime(ctx: {
56
+ get?: (name: string) => unknown;
57
+ }, options?: {
58
+ argv?: readonly string[];
59
+ env?: NodeJS.ProcessEnv;
60
+ cwd?: string;
61
+ homeDir?: string;
62
+ exists?: (path: string) => boolean;
63
+ execPath?: string;
64
+ }): Runtime;
65
+ export declare function shouldNotifyParent(target: Runtime, send?: unknown): boolean;
19
66
  export declare function isNewerVersion(currentValue: string, candidateValue: string): boolean;
20
67
  export declare function registerPluginUpdater(ctx: Context, options: PluginUpdaterOptions): () => void;
21
68
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-automation",
3
- "version": "0.1.55",
3
+ "version": "0.1.59",
4
4
  "description": "DSH Automation — 在独立 DSH Session 中按计划执行和管理编码任务 · Schedule and manage coding tasks in isolated DSH sessions",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.22.0",