indusagi 0.13.8 → 0.13.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.
@@ -263,7 +263,7 @@ var ToolRegistry = class {
263
263
 
264
264
  // src/capabilities/backends/node-backends.ts
265
265
  import { promises as fsp } from "node:fs";
266
- import { spawn as childSpawn } from "node:child_process";
266
+ import { spawn as childSpawn, spawnSync } from "node:child_process";
267
267
  var NodeFs = class {
268
268
  async readFile(path, encoding) {
269
269
  if (encoding === void 0) {
@@ -323,6 +323,21 @@ function joinChunks(chunks) {
323
323
  function asSignal(signal) {
324
324
  return signal;
325
325
  }
326
+ function killProcessTree(child, signal = "SIGTERM") {
327
+ if (child.pid === void 0) return;
328
+ if (process.platform === "win32") {
329
+ spawnSync("taskkill", ["/F", "/T", "/PID", String(child.pid)], {
330
+ stdio: "ignore",
331
+ windowsHide: true
332
+ });
333
+ return;
334
+ }
335
+ try {
336
+ process.kill(-child.pid, signal);
337
+ } catch {
338
+ child.kill(signal);
339
+ }
340
+ }
326
341
  var NodeShell = class {
327
342
  exec(command, options) {
328
343
  if (typeof command !== "string" || command.length === 0) {
@@ -333,21 +348,30 @@ var NodeShell = class {
333
348
  return new Promise((resolve, reject) => {
334
349
  const child = childSpawn(command, {
335
350
  shell: true,
351
+ detached: process.platform !== "win32",
336
352
  cwd: options?.cwd,
337
- env: options?.env ? { ...process.env, ...options.env } : process.env,
338
- signal: options?.signal
353
+ env: options?.env ? { ...process.env, ...options.env } : process.env
339
354
  });
340
355
  const out = [];
341
356
  const err = [];
342
357
  let settled = false;
343
358
  let timedOut = false;
359
+ let aborted = false;
344
360
  let timer;
361
+ const abortSignal = options?.signal;
362
+ const onAbort = () => {
363
+ aborted = true;
364
+ killProcessTree(child);
365
+ };
345
366
  const finish = (fn) => {
346
367
  if (settled) return;
347
368
  settled = true;
348
369
  if (timer !== void 0) clearTimeout(timer);
370
+ abortSignal?.removeEventListener("abort", onAbort);
349
371
  fn();
350
372
  };
373
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
374
+ if (abortSignal?.aborted) onAbort();
351
375
  child.stdout?.on("data", (chunk) => out.push(chunk));
352
376
  child.stderr?.on("data", (chunk) => err.push(chunk));
353
377
  if (options?.input !== void 0 && child.stdin) {
@@ -360,7 +384,7 @@ var NodeShell = class {
360
384
  if (limit !== void 0 && limit > 0) {
361
385
  timer = setTimeout(() => {
362
386
  timedOut = true;
363
- child.kill("SIGTERM");
387
+ killProcessTree(child);
364
388
  }, limit);
365
389
  }
366
390
  child.on("error", (cause) => {
@@ -376,6 +400,10 @@ var NodeShell = class {
376
400
  );
377
401
  return;
378
402
  }
403
+ if (aborted) {
404
+ reject(new Error("The command was aborted and stopped."));
405
+ return;
406
+ }
379
407
  resolve({
380
408
  stdout: joinChunks(out),
381
409
  stderr: joinChunks(err),
@@ -393,11 +421,11 @@ var NodeShell = class {
393
421
  }
394
422
  const child = childSpawn(command, {
395
423
  shell: true,
424
+ detached: process.platform !== "win32",
396
425
  cwd: options?.cwd,
397
- env: options?.env ? { ...process.env, ...options.env } : process.env,
398
- signal: options?.signal
426
+ env: options?.env ? { ...process.env, ...options.env } : process.env
399
427
  });
400
- return new NodeShellHandle(child);
428
+ return new NodeShellHandle(child, options?.signal);
401
429
  }
402
430
  };
403
431
  var NodeShellHandle = class {
@@ -406,27 +434,33 @@ var NodeShellHandle = class {
406
434
  exited = false;
407
435
  exitCode = null;
408
436
  exitSignal = null;
409
- constructor(child) {
437
+ abortSignal;
438
+ onAbort = () => this.kill();
439
+ constructor(child, abortSignal) {
410
440
  this.child = child;
441
+ this.abortSignal = abortSignal;
411
442
  child.once(
412
443
  "close",
413
444
  (code, signal) => {
414
445
  this.exited = true;
415
446
  this.exitCode = code;
416
447
  this.exitSignal = asSignal(signal);
448
+ this.abortSignal?.removeEventListener("abort", this.onAbort);
417
449
  for (const listener of [...this.listeners]) {
418
450
  listener(this.exitCode, this.exitSignal);
419
451
  }
420
452
  this.listeners.clear();
421
453
  }
422
454
  );
455
+ this.abortSignal?.addEventListener("abort", this.onAbort, { once: true });
456
+ if (this.abortSignal?.aborted) this.onAbort();
423
457
  }
424
458
  get pid() {
425
459
  return this.child.pid;
426
460
  }
427
461
  kill(signal) {
428
462
  if (this.exited) return;
429
- this.child.kill(signal ?? "SIGTERM");
463
+ killProcessTree(this.child, signal);
430
464
  }
431
465
  onExit(listener) {
432
466
  if (this.exited) {
@@ -1860,9 +1894,6 @@ function readInput2(raw) {
1860
1894
  }
1861
1895
  return { action, command, id };
1862
1896
  }
1863
- function shellQuote(value) {
1864
- return `'${value.replace(/'/g, `'\\''`)}'`;
1865
- }
1866
1897
  async function drainCapture(ctx, path, alreadyRead) {
1867
1898
  let full;
1868
1899
  try {
@@ -1898,7 +1929,7 @@ async function startJob(ctx, command) {
1898
1929
  await ctx.fs.mkdir(scratchDir, { recursive: true });
1899
1930
  await ctx.fs.writeFile(outPath, "", "utf8");
1900
1931
  await ctx.fs.writeFile(errPath, "", "utf8");
1901
- const wrapped = `( ${command} ) >${shellQuote(outPath)} 2>${shellQuote(errPath)}`;
1932
+ const wrapped = `( ${command} ) >${SCRATCH_DIR}/${id}.out 2>${SCRATCH_DIR}/${id}.err`;
1902
1933
  const handle = ctx.shell.spawn(wrapped, { cwd: ctx.cwd });
1903
1934
  const entry = {
1904
1935
  id,
@@ -1978,7 +2009,7 @@ async function pollJob(ctx, id) {
1978
2009
  hadNewOutput: out.fresh.length > 0 || err.fresh.length > 0
1979
2010
  });
1980
2011
  }
1981
- function stopJob(id) {
2012
+ async function stopJob(id) {
1982
2013
  const entry = table.remove(id);
1983
2014
  if (entry === void 0) {
1984
2015
  return asText2(
@@ -1986,14 +2017,24 @@ function stopJob(id) {
1986
2017
  true
1987
2018
  );
1988
2019
  }
1989
- entry.unsubscribe();
1990
2020
  const wasRunning = entry.state === "running";
1991
2021
  if (wasRunning) {
1992
- try {
1993
- entry.handle.kill();
1994
- } catch {
1995
- }
2022
+ await new Promise((resolve) => {
2023
+ let unsubscribeExit = () => {
2024
+ };
2025
+ unsubscribeExit = entry.handle.onExit(() => {
2026
+ unsubscribeExit();
2027
+ resolve();
2028
+ });
2029
+ try {
2030
+ entry.handle.kill();
2031
+ } catch {
2032
+ unsubscribeExit();
2033
+ resolve();
2034
+ }
2035
+ });
1996
2036
  }
2037
+ entry.unsubscribe();
1997
2038
  return asJson({
1998
2039
  action: "stop",
1999
2040
  id: entry.id,
package/dist/cli.js CHANGED
@@ -5035,7 +5035,7 @@ var ToolRegistry = class {
5035
5035
 
5036
5036
  // src/capabilities/backends/node-backends.ts
5037
5037
  import { promises as fsp } from "node:fs";
5038
- import { spawn as childSpawn } from "node:child_process";
5038
+ import { spawn as childSpawn, spawnSync } from "node:child_process";
5039
5039
  var NodeFs = class {
5040
5040
  async readFile(path2, encoding) {
5041
5041
  if (encoding === void 0) {
@@ -5095,6 +5095,21 @@ function joinChunks(chunks) {
5095
5095
  function asSignal(signal) {
5096
5096
  return signal;
5097
5097
  }
5098
+ function killProcessTree(child, signal = "SIGTERM") {
5099
+ if (child.pid === void 0) return;
5100
+ if (process.platform === "win32") {
5101
+ spawnSync("taskkill", ["/F", "/T", "/PID", String(child.pid)], {
5102
+ stdio: "ignore",
5103
+ windowsHide: true
5104
+ });
5105
+ return;
5106
+ }
5107
+ try {
5108
+ process.kill(-child.pid, signal);
5109
+ } catch {
5110
+ child.kill(signal);
5111
+ }
5112
+ }
5098
5113
  var NodeShell = class {
5099
5114
  exec(command, options) {
5100
5115
  if (typeof command !== "string" || command.length === 0) {
@@ -5105,21 +5120,30 @@ var NodeShell = class {
5105
5120
  return new Promise((resolve, reject) => {
5106
5121
  const child = childSpawn(command, {
5107
5122
  shell: true,
5123
+ detached: process.platform !== "win32",
5108
5124
  cwd: options?.cwd,
5109
- env: options?.env ? { ...process.env, ...options.env } : process.env,
5110
- signal: options?.signal
5125
+ env: options?.env ? { ...process.env, ...options.env } : process.env
5111
5126
  });
5112
5127
  const out = [];
5113
5128
  const err = [];
5114
5129
  let settled2 = false;
5115
5130
  let timedOut = false;
5131
+ let aborted = false;
5116
5132
  let timer;
5133
+ const abortSignal = options?.signal;
5134
+ const onAbort = () => {
5135
+ aborted = true;
5136
+ killProcessTree(child);
5137
+ };
5117
5138
  const finish = (fn) => {
5118
5139
  if (settled2) return;
5119
5140
  settled2 = true;
5120
5141
  if (timer !== void 0) clearTimeout(timer);
5142
+ abortSignal?.removeEventListener("abort", onAbort);
5121
5143
  fn();
5122
5144
  };
5145
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
5146
+ if (abortSignal?.aborted) onAbort();
5123
5147
  child.stdout?.on("data", (chunk) => out.push(chunk));
5124
5148
  child.stderr?.on("data", (chunk) => err.push(chunk));
5125
5149
  if (options?.input !== void 0 && child.stdin) {
@@ -5132,7 +5156,7 @@ var NodeShell = class {
5132
5156
  if (limit !== void 0 && limit > 0) {
5133
5157
  timer = setTimeout(() => {
5134
5158
  timedOut = true;
5135
- child.kill("SIGTERM");
5159
+ killProcessTree(child);
5136
5160
  }, limit);
5137
5161
  }
5138
5162
  child.on("error", (cause) => {
@@ -5148,6 +5172,10 @@ var NodeShell = class {
5148
5172
  );
5149
5173
  return;
5150
5174
  }
5175
+ if (aborted) {
5176
+ reject(new Error("The command was aborted and stopped."));
5177
+ return;
5178
+ }
5151
5179
  resolve({
5152
5180
  stdout: joinChunks(out),
5153
5181
  stderr: joinChunks(err),
@@ -5165,11 +5193,11 @@ var NodeShell = class {
5165
5193
  }
5166
5194
  const child = childSpawn(command, {
5167
5195
  shell: true,
5196
+ detached: process.platform !== "win32",
5168
5197
  cwd: options?.cwd,
5169
- env: options?.env ? { ...process.env, ...options.env } : process.env,
5170
- signal: options?.signal
5198
+ env: options?.env ? { ...process.env, ...options.env } : process.env
5171
5199
  });
5172
- return new NodeShellHandle(child);
5200
+ return new NodeShellHandle(child, options?.signal);
5173
5201
  }
5174
5202
  };
5175
5203
  var NodeShellHandle = class {
@@ -5178,27 +5206,33 @@ var NodeShellHandle = class {
5178
5206
  exited = false;
5179
5207
  exitCode = null;
5180
5208
  exitSignal = null;
5181
- constructor(child) {
5209
+ abortSignal;
5210
+ onAbort = () => this.kill();
5211
+ constructor(child, abortSignal) {
5182
5212
  this.child = child;
5213
+ this.abortSignal = abortSignal;
5183
5214
  child.once(
5184
5215
  "close",
5185
5216
  (code, signal) => {
5186
5217
  this.exited = true;
5187
5218
  this.exitCode = code;
5188
5219
  this.exitSignal = asSignal(signal);
5220
+ this.abortSignal?.removeEventListener("abort", this.onAbort);
5189
5221
  for (const listener of [...this.listeners]) {
5190
5222
  listener(this.exitCode, this.exitSignal);
5191
5223
  }
5192
5224
  this.listeners.clear();
5193
5225
  }
5194
5226
  );
5227
+ this.abortSignal?.addEventListener("abort", this.onAbort, { once: true });
5228
+ if (this.abortSignal?.aborted) this.onAbort();
5195
5229
  }
5196
5230
  get pid() {
5197
5231
  return this.child.pid;
5198
5232
  }
5199
5233
  kill(signal) {
5200
5234
  if (this.exited) return;
5201
- this.child.kill(signal ?? "SIGTERM");
5235
+ killProcessTree(this.child, signal);
5202
5236
  }
5203
5237
  onExit(listener) {
5204
5238
  if (this.exited) {
@@ -6629,9 +6663,6 @@ function readInput2(raw) {
6629
6663
  }
6630
6664
  return { action, command, id };
6631
6665
  }
6632
- function shellQuote(value) {
6633
- return `'${value.replace(/'/g, `'\\''`)}'`;
6634
- }
6635
6666
  async function drainCapture(ctx, path2, alreadyRead) {
6636
6667
  let full;
6637
6668
  try {
@@ -6667,7 +6698,7 @@ async function startJob(ctx, command) {
6667
6698
  await ctx.fs.mkdir(scratchDir, { recursive: true });
6668
6699
  await ctx.fs.writeFile(outPath, "", "utf8");
6669
6700
  await ctx.fs.writeFile(errPath, "", "utf8");
6670
- const wrapped = `( ${command} ) >${shellQuote(outPath)} 2>${shellQuote(errPath)}`;
6701
+ const wrapped = `( ${command} ) >${SCRATCH_DIR}/${id}.out 2>${SCRATCH_DIR}/${id}.err`;
6671
6702
  const handle = ctx.shell.spawn(wrapped, { cwd: ctx.cwd });
6672
6703
  const entry = {
6673
6704
  id,
@@ -6747,7 +6778,7 @@ async function pollJob(ctx, id) {
6747
6778
  hadNewOutput: out.fresh.length > 0 || err.fresh.length > 0
6748
6779
  });
6749
6780
  }
6750
- function stopJob(id) {
6781
+ async function stopJob(id) {
6751
6782
  const entry = table.remove(id);
6752
6783
  if (entry === void 0) {
6753
6784
  return asText2(
@@ -6755,14 +6786,24 @@ function stopJob(id) {
6755
6786
  true
6756
6787
  );
6757
6788
  }
6758
- entry.unsubscribe();
6759
6789
  const wasRunning = entry.state === "running";
6760
6790
  if (wasRunning) {
6761
- try {
6762
- entry.handle.kill();
6763
- } catch {
6764
- }
6791
+ await new Promise((resolve) => {
6792
+ let unsubscribeExit = () => {
6793
+ };
6794
+ unsubscribeExit = entry.handle.onExit(() => {
6795
+ unsubscribeExit();
6796
+ resolve();
6797
+ });
6798
+ try {
6799
+ entry.handle.kill();
6800
+ } catch {
6801
+ unsubscribeExit();
6802
+ resolve();
6803
+ }
6804
+ });
6765
6805
  }
6806
+ entry.unsubscribe();
6766
6807
  return asJson({
6767
6808
  action: "stop",
6768
6809
  id: entry.id,
@@ -263,7 +263,7 @@ var ToolRegistry = class {
263
263
 
264
264
  // src/capabilities/backends/node-backends.ts
265
265
  import { promises as fsp } from "node:fs";
266
- import { spawn as childSpawn } from "node:child_process";
266
+ import { spawn as childSpawn, spawnSync } from "node:child_process";
267
267
  var NodeFs = class {
268
268
  async readFile(path, encoding) {
269
269
  if (encoding === void 0) {
@@ -323,6 +323,21 @@ function joinChunks(chunks) {
323
323
  function asSignal(signal) {
324
324
  return signal;
325
325
  }
326
+ function killProcessTree(child, signal = "SIGTERM") {
327
+ if (child.pid === void 0) return;
328
+ if (process.platform === "win32") {
329
+ spawnSync("taskkill", ["/F", "/T", "/PID", String(child.pid)], {
330
+ stdio: "ignore",
331
+ windowsHide: true
332
+ });
333
+ return;
334
+ }
335
+ try {
336
+ process.kill(-child.pid, signal);
337
+ } catch {
338
+ child.kill(signal);
339
+ }
340
+ }
326
341
  var NodeShell = class {
327
342
  exec(command, options) {
328
343
  if (typeof command !== "string" || command.length === 0) {
@@ -333,21 +348,30 @@ var NodeShell = class {
333
348
  return new Promise((resolve, reject) => {
334
349
  const child = childSpawn(command, {
335
350
  shell: true,
351
+ detached: process.platform !== "win32",
336
352
  cwd: options?.cwd,
337
- env: options?.env ? { ...process.env, ...options.env } : process.env,
338
- signal: options?.signal
353
+ env: options?.env ? { ...process.env, ...options.env } : process.env
339
354
  });
340
355
  const out = [];
341
356
  const err = [];
342
357
  let settled = false;
343
358
  let timedOut = false;
359
+ let aborted = false;
344
360
  let timer;
361
+ const abortSignal = options?.signal;
362
+ const onAbort = () => {
363
+ aborted = true;
364
+ killProcessTree(child);
365
+ };
345
366
  const finish = (fn) => {
346
367
  if (settled) return;
347
368
  settled = true;
348
369
  if (timer !== void 0) clearTimeout(timer);
370
+ abortSignal?.removeEventListener("abort", onAbort);
349
371
  fn();
350
372
  };
373
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
374
+ if (abortSignal?.aborted) onAbort();
351
375
  child.stdout?.on("data", (chunk) => out.push(chunk));
352
376
  child.stderr?.on("data", (chunk) => err.push(chunk));
353
377
  if (options?.input !== void 0 && child.stdin) {
@@ -360,7 +384,7 @@ var NodeShell = class {
360
384
  if (limit !== void 0 && limit > 0) {
361
385
  timer = setTimeout(() => {
362
386
  timedOut = true;
363
- child.kill("SIGTERM");
387
+ killProcessTree(child);
364
388
  }, limit);
365
389
  }
366
390
  child.on("error", (cause) => {
@@ -376,6 +400,10 @@ var NodeShell = class {
376
400
  );
377
401
  return;
378
402
  }
403
+ if (aborted) {
404
+ reject(new Error("The command was aborted and stopped."));
405
+ return;
406
+ }
379
407
  resolve({
380
408
  stdout: joinChunks(out),
381
409
  stderr: joinChunks(err),
@@ -393,11 +421,11 @@ var NodeShell = class {
393
421
  }
394
422
  const child = childSpawn(command, {
395
423
  shell: true,
424
+ detached: process.platform !== "win32",
396
425
  cwd: options?.cwd,
397
- env: options?.env ? { ...process.env, ...options.env } : process.env,
398
- signal: options?.signal
426
+ env: options?.env ? { ...process.env, ...options.env } : process.env
399
427
  });
400
- return new NodeShellHandle(child);
428
+ return new NodeShellHandle(child, options?.signal);
401
429
  }
402
430
  };
403
431
  var NodeShellHandle = class {
@@ -406,27 +434,33 @@ var NodeShellHandle = class {
406
434
  exited = false;
407
435
  exitCode = null;
408
436
  exitSignal = null;
409
- constructor(child) {
437
+ abortSignal;
438
+ onAbort = () => this.kill();
439
+ constructor(child, abortSignal) {
410
440
  this.child = child;
441
+ this.abortSignal = abortSignal;
411
442
  child.once(
412
443
  "close",
413
444
  (code, signal) => {
414
445
  this.exited = true;
415
446
  this.exitCode = code;
416
447
  this.exitSignal = asSignal(signal);
448
+ this.abortSignal?.removeEventListener("abort", this.onAbort);
417
449
  for (const listener of [...this.listeners]) {
418
450
  listener(this.exitCode, this.exitSignal);
419
451
  }
420
452
  this.listeners.clear();
421
453
  }
422
454
  );
455
+ this.abortSignal?.addEventListener("abort", this.onAbort, { once: true });
456
+ if (this.abortSignal?.aborted) this.onAbort();
423
457
  }
424
458
  get pid() {
425
459
  return this.child.pid;
426
460
  }
427
461
  kill(signal) {
428
462
  if (this.exited) return;
429
- this.child.kill(signal ?? "SIGTERM");
463
+ killProcessTree(this.child, signal);
430
464
  }
431
465
  onExit(listener) {
432
466
  if (this.exited) {
@@ -1841,9 +1875,6 @@ function readInput2(raw) {
1841
1875
  }
1842
1876
  return { action, command, id };
1843
1877
  }
1844
- function shellQuote(value) {
1845
- return `'${value.replace(/'/g, `'\\''`)}'`;
1846
- }
1847
1878
  async function drainCapture(ctx, path, alreadyRead) {
1848
1879
  let full;
1849
1880
  try {
@@ -1879,7 +1910,7 @@ async function startJob(ctx, command) {
1879
1910
  await ctx.fs.mkdir(scratchDir, { recursive: true });
1880
1911
  await ctx.fs.writeFile(outPath, "", "utf8");
1881
1912
  await ctx.fs.writeFile(errPath, "", "utf8");
1882
- const wrapped = `( ${command} ) >${shellQuote(outPath)} 2>${shellQuote(errPath)}`;
1913
+ const wrapped = `( ${command} ) >${SCRATCH_DIR}/${id}.out 2>${SCRATCH_DIR}/${id}.err`;
1883
1914
  const handle = ctx.shell.spawn(wrapped, { cwd: ctx.cwd });
1884
1915
  const entry = {
1885
1916
  id,
@@ -1959,7 +1990,7 @@ async function pollJob(ctx, id) {
1959
1990
  hadNewOutput: out.fresh.length > 0 || err.fresh.length > 0
1960
1991
  });
1961
1992
  }
1962
- function stopJob(id) {
1993
+ async function stopJob(id) {
1963
1994
  const entry = table.remove(id);
1964
1995
  if (entry === void 0) {
1965
1996
  return asText2(
@@ -1967,14 +1998,24 @@ function stopJob(id) {
1967
1998
  true
1968
1999
  );
1969
2000
  }
1970
- entry.unsubscribe();
1971
2001
  const wasRunning = entry.state === "running";
1972
2002
  if (wasRunning) {
1973
- try {
1974
- entry.handle.kill();
1975
- } catch {
1976
- }
2003
+ await new Promise((resolve) => {
2004
+ let unsubscribeExit = () => {
2005
+ };
2006
+ unsubscribeExit = entry.handle.onExit(() => {
2007
+ unsubscribeExit();
2008
+ resolve();
2009
+ });
2010
+ try {
2011
+ entry.handle.kill();
2012
+ } catch {
2013
+ unsubscribeExit();
2014
+ resolve();
2015
+ }
2016
+ });
1977
2017
  }
2018
+ entry.unsubscribe();
1978
2019
  return asJson({
1979
2020
  action: "stop",
1980
2021
  id: entry.id,