@openspecui/core 2.3.0 → 2.3.3

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.
Files changed (2) hide show
  1. package/dist/index.mjs +186 -102
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1919,6 +1919,101 @@ function createFileChangeObservable(watcher) {
1919
1919
  } };
1920
1920
  }
1921
1921
 
1922
+ //#endregion
1923
+ //#region src/spawn-safe.ts
1924
+ function getSpawnErrorCode(err) {
1925
+ if (typeof err !== "object" || err === null || !("code" in err)) return;
1926
+ const code = err.code;
1927
+ return typeof code === "string" ? code : void 0;
1928
+ }
1929
+ function formatSpawnError(err) {
1930
+ const message = err instanceof Error ? err.message : String(err);
1931
+ const code = getSpawnErrorCode(err);
1932
+ return {
1933
+ code,
1934
+ message: `${message}${code ? ` (${code})` : ""}`
1935
+ };
1936
+ }
1937
+ function spawnSafe(command, args, options) {
1938
+ try {
1939
+ return {
1940
+ ok: true,
1941
+ child: spawn(command, [...args], options)
1942
+ };
1943
+ } catch (err) {
1944
+ return {
1945
+ ok: false,
1946
+ error: formatSpawnError(err)
1947
+ };
1948
+ }
1949
+ }
1950
+ function killChild(child) {
1951
+ try {
1952
+ child.kill();
1953
+ } catch {}
1954
+ }
1955
+ function runBufferedCommand(options) {
1956
+ return new Promise((resolve$1) => {
1957
+ const started = spawnSafe(options.command, options.args, {
1958
+ cwd: options.cwd,
1959
+ shell: false,
1960
+ env: options.env
1961
+ });
1962
+ if (!started.ok) {
1963
+ resolve$1({
1964
+ stdout: "",
1965
+ stderr: "",
1966
+ exitCode: null,
1967
+ timedOut: false,
1968
+ spawnError: started.error
1969
+ });
1970
+ return;
1971
+ }
1972
+ const { child } = started;
1973
+ let stdout = "";
1974
+ let stderr = "";
1975
+ let timedOut = false;
1976
+ let settled = false;
1977
+ let clearTimer = () => {};
1978
+ if (options.timeoutMs !== void 0) {
1979
+ const timer = setTimeout(() => {
1980
+ timedOut = true;
1981
+ killChild(child);
1982
+ }, options.timeoutMs);
1983
+ clearTimer = () => clearTimeout(timer);
1984
+ }
1985
+ const finish = (result) => {
1986
+ if (settled) return;
1987
+ settled = true;
1988
+ clearTimer();
1989
+ resolve$1(result);
1990
+ };
1991
+ child.stdout?.on("data", (data) => {
1992
+ stdout += data.toString();
1993
+ });
1994
+ child.stderr?.on("data", (data) => {
1995
+ stderr += data.toString();
1996
+ });
1997
+ child.on("error", (err) => {
1998
+ finish({
1999
+ stdout,
2000
+ stderr,
2001
+ exitCode: null,
2002
+ timedOut,
2003
+ spawnError: formatSpawnError(err)
2004
+ });
2005
+ });
2006
+ child.on("close", (exitCode) => {
2007
+ finish({
2008
+ stdout,
2009
+ stderr,
2010
+ exitCode,
2011
+ timedOut
2012
+ });
2013
+ });
2014
+ });
2015
+ }
2016
+
1922
2017
  //#endregion
1923
2018
  //#region src/config.ts
1924
2019
  const execAsync = promisify(exec);
@@ -2172,69 +2267,44 @@ function createCleanCliEnv(baseEnv = process.env) {
2172
2267
  }
2173
2268
  async function probeCliRunner(candidate, cwd, env) {
2174
2269
  const [cmd, ...cmdArgs] = candidate.commandParts;
2175
- return new Promise((resolve$1) => {
2176
- let stdout = "";
2177
- let stderr = "";
2178
- let timedOut = false;
2179
- const timer = setTimeout(() => {
2180
- timedOut = true;
2181
- child.kill();
2182
- }, CLI_PROBE_TIMEOUT_MS);
2183
- const child = spawn(cmd, [...cmdArgs, "--version"], {
2184
- cwd,
2185
- shell: false,
2186
- env
2187
- });
2188
- child.stdout?.on("data", (data) => {
2189
- stdout += data.toString();
2190
- });
2191
- child.stderr?.on("data", (data) => {
2192
- stderr += data.toString();
2193
- });
2194
- child.on("error", (err) => {
2195
- clearTimeout(timer);
2196
- const code = err.code;
2197
- const suffix = code ? ` (${code})` : "";
2198
- resolve$1({
2199
- source: candidate.source,
2200
- command: commandToString(candidate.commandParts),
2201
- success: false,
2202
- error: `${err.message}${suffix}`,
2203
- exitCode: null
2204
- });
2205
- });
2206
- child.on("close", (exitCode) => {
2207
- clearTimeout(timer);
2208
- if (timedOut) {
2209
- resolve$1({
2210
- source: candidate.source,
2211
- command: commandToString(candidate.commandParts),
2212
- success: false,
2213
- error: "CLI probe timed out",
2214
- exitCode
2215
- });
2216
- return;
2217
- }
2218
- if (exitCode === 0) {
2219
- const version = stdout.trim().split("\n")[0] || void 0;
2220
- resolve$1({
2221
- source: candidate.source,
2222
- command: commandToString(candidate.commandParts),
2223
- success: true,
2224
- version,
2225
- exitCode
2226
- });
2227
- return;
2228
- }
2229
- resolve$1({
2230
- source: candidate.source,
2231
- command: commandToString(candidate.commandParts),
2232
- success: false,
2233
- error: stderr.trim() || `Exit code ${exitCode ?? "null"}`,
2234
- exitCode
2235
- });
2236
- });
2270
+ const result = await runBufferedCommand({
2271
+ command: cmd,
2272
+ args: [...cmdArgs, "--version"],
2273
+ cwd,
2274
+ env,
2275
+ timeoutMs: CLI_PROBE_TIMEOUT_MS
2237
2276
  });
2277
+ if (result.timedOut) return {
2278
+ source: candidate.source,
2279
+ command: commandToString(candidate.commandParts),
2280
+ success: false,
2281
+ error: "CLI probe timed out",
2282
+ exitCode: result.exitCode
2283
+ };
2284
+ if (result.spawnError) return {
2285
+ source: candidate.source,
2286
+ command: commandToString(candidate.commandParts),
2287
+ success: false,
2288
+ error: result.spawnError.message,
2289
+ exitCode: null
2290
+ };
2291
+ if (result.exitCode === 0) {
2292
+ const version = result.stdout.trim().split("\n")[0] || void 0;
2293
+ return {
2294
+ source: candidate.source,
2295
+ command: commandToString(candidate.commandParts),
2296
+ success: true,
2297
+ version,
2298
+ exitCode: result.exitCode
2299
+ };
2300
+ }
2301
+ return {
2302
+ source: candidate.source,
2303
+ command: commandToString(candidate.commandParts),
2304
+ success: false,
2305
+ error: result.stderr.trim() || `Exit code ${result.exitCode ?? "null"}`,
2306
+ exitCode: result.exitCode
2307
+ };
2238
2308
  }
2239
2309
  async function resolveCliRunner(candidates, cwd, env) {
2240
2310
  const expandedCandidates = await expandCliRunnerCandidates(candidates, cwd, env);
@@ -2656,42 +2726,27 @@ var CliExecutor = class {
2656
2726
  async buildCommandArray(args) {
2657
2727
  return [...await this.configManager.getCliCommand(), ...args];
2658
2728
  }
2659
- runCommandOnce(fullCommand) {
2729
+ async runCommandOnce(fullCommand) {
2660
2730
  const [cmd, ...cmdArgs] = fullCommand;
2661
- return new Promise((resolve$1) => {
2662
- const child = spawn(cmd, cmdArgs, {
2663
- cwd: this.projectDir,
2664
- shell: false,
2665
- env: createCleanCliEnv()
2666
- });
2667
- let stdout = "";
2668
- let stderr = "";
2669
- child.stdout?.on("data", (data) => {
2670
- stdout += data.toString();
2671
- });
2672
- child.stderr?.on("data", (data) => {
2673
- stderr += data.toString();
2674
- });
2675
- child.on("close", (exitCode) => {
2676
- resolve$1({
2677
- success: exitCode === 0,
2678
- stdout,
2679
- stderr,
2680
- exitCode
2681
- });
2682
- });
2683
- child.on("error", (err) => {
2684
- const errorCode = err.code;
2685
- const errorMessage = err.message + (errorCode ? ` (${errorCode})` : "");
2686
- resolve$1({
2687
- success: false,
2688
- stdout,
2689
- stderr: stderr ? `${stderr}\n${errorMessage}` : errorMessage,
2690
- exitCode: null,
2691
- errorCode
2692
- });
2693
- });
2731
+ const result = await runBufferedCommand({
2732
+ command: cmd,
2733
+ args: cmdArgs,
2734
+ cwd: this.projectDir,
2735
+ env: createCleanCliEnv()
2694
2736
  });
2737
+ if (result.spawnError) return {
2738
+ success: false,
2739
+ stdout: result.stdout,
2740
+ stderr: result.stderr ? `${result.stderr}\n${result.spawnError.message}` : result.spawnError.message,
2741
+ exitCode: null,
2742
+ errorCode: result.spawnError.code
2743
+ };
2744
+ return {
2745
+ success: result.exitCode === 0,
2746
+ stdout: result.stdout,
2747
+ stderr: result.stderr,
2748
+ exitCode: result.exitCode
2749
+ };
2695
2750
  }
2696
2751
  async executeInternal(args, allowRetry) {
2697
2752
  let fullCommand;
@@ -2845,11 +2900,29 @@ var CliExecutor = class {
2845
2900
  data: fullCommand.join(" ")
2846
2901
  });
2847
2902
  const [cmd, ...cmdArgs] = fullCommand;
2848
- const child = spawn(cmd, cmdArgs, {
2903
+ const started = spawnSafe(cmd, cmdArgs, {
2849
2904
  cwd: this.projectDir,
2850
2905
  shell: false,
2851
2906
  env: createCleanCliEnv()
2852
2907
  });
2908
+ if (!started.ok) {
2909
+ const { code, message } = started.error;
2910
+ if (allowRetry && code === "ENOENT" && !cancelled) {
2911
+ this.configManager.invalidateResolvedCliRunner();
2912
+ start(false);
2913
+ return;
2914
+ }
2915
+ onEvent({
2916
+ type: "stderr",
2917
+ data: message
2918
+ });
2919
+ onEvent({
2920
+ type: "exit",
2921
+ exitCode: null
2922
+ });
2923
+ return;
2924
+ }
2925
+ const child = started.child;
2853
2926
  activeChild = child;
2854
2927
  child.stdout?.on("data", (data) => {
2855
2928
  onEvent({
@@ -2874,8 +2947,7 @@ var CliExecutor = class {
2874
2947
  child.on("error", (err) => {
2875
2948
  if (activeChild !== child) return;
2876
2949
  activeChild = null;
2877
- const code = err.code;
2878
- const message = err.message + (code ? ` (${code})` : "");
2950
+ const { code, message } = formatSpawnError(err);
2879
2951
  if (allowRetry && code === "ENOENT" && !cancelled) {
2880
2952
  this.configManager.invalidateResolvedCliRunner();
2881
2953
  start(false);
@@ -2960,11 +3032,23 @@ var CliExecutor = class {
2960
3032
  type: "command",
2961
3033
  data: command.join(" ")
2962
3034
  });
2963
- const child = spawn(cmd, cmdArgs, {
3035
+ const started = spawnSafe(cmd, cmdArgs, {
2964
3036
  cwd: this.projectDir,
2965
3037
  shell: false,
2966
3038
  env: createCleanCliEnv()
2967
3039
  });
3040
+ if (!started.ok) {
3041
+ onEvent({
3042
+ type: "stderr",
3043
+ data: started.error.message
3044
+ });
3045
+ onEvent({
3046
+ type: "exit",
3047
+ exitCode: null
3048
+ });
3049
+ return () => {};
3050
+ }
3051
+ const child = started.child;
2968
3052
  child.stdout?.on("data", (data) => {
2969
3053
  onEvent({
2970
3054
  type: "stdout",
@@ -2984,10 +3068,10 @@ var CliExecutor = class {
2984
3068
  });
2985
3069
  });
2986
3070
  child.on("error", (err) => {
2987
- const code = err.code;
3071
+ const { message } = formatSpawnError(err);
2988
3072
  onEvent({
2989
3073
  type: "stderr",
2990
- data: err.message + (code ? ` (${code})` : "")
3074
+ data: message
2991
3075
  });
2992
3076
  onEvent({
2993
3077
  type: "exit",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openspecui/core",
3
- "version": "2.3.0",
3
+ "version": "2.3.3",
4
4
  "description": "Core OpenSpec adapter and parser",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",