indusagi 0.13.8 → 0.13.9

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/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,
package/dist/index.js CHANGED
@@ -4916,7 +4916,7 @@ var ToolRegistry = class {
4916
4916
 
4917
4917
  // src/capabilities/backends/node-backends.ts
4918
4918
  import { promises as fsp } from "node:fs";
4919
- import { spawn as childSpawn } from "node:child_process";
4919
+ import { spawn as childSpawn, spawnSync } from "node:child_process";
4920
4920
  var NodeFs = class {
4921
4921
  async readFile(path2, encoding) {
4922
4922
  if (encoding === void 0) {
@@ -4976,6 +4976,21 @@ function joinChunks(chunks) {
4976
4976
  function asSignal(signal) {
4977
4977
  return signal;
4978
4978
  }
4979
+ function killProcessTree(child, signal = "SIGTERM") {
4980
+ if (child.pid === void 0) return;
4981
+ if (process.platform === "win32") {
4982
+ spawnSync("taskkill", ["/F", "/T", "/PID", String(child.pid)], {
4983
+ stdio: "ignore",
4984
+ windowsHide: true
4985
+ });
4986
+ return;
4987
+ }
4988
+ try {
4989
+ process.kill(-child.pid, signal);
4990
+ } catch {
4991
+ child.kill(signal);
4992
+ }
4993
+ }
4979
4994
  var NodeShell = class {
4980
4995
  exec(command, options) {
4981
4996
  if (typeof command !== "string" || command.length === 0) {
@@ -4986,21 +5001,30 @@ var NodeShell = class {
4986
5001
  return new Promise((resolve3, reject) => {
4987
5002
  const child = childSpawn(command, {
4988
5003
  shell: true,
5004
+ detached: process.platform !== "win32",
4989
5005
  cwd: options?.cwd,
4990
- env: options?.env ? { ...process.env, ...options.env } : process.env,
4991
- signal: options?.signal
5006
+ env: options?.env ? { ...process.env, ...options.env } : process.env
4992
5007
  });
4993
5008
  const out = [];
4994
5009
  const err = [];
4995
5010
  let settled2 = false;
4996
5011
  let timedOut = false;
5012
+ let aborted = false;
4997
5013
  let timer;
5014
+ const abortSignal = options?.signal;
5015
+ const onAbort = () => {
5016
+ aborted = true;
5017
+ killProcessTree(child);
5018
+ };
4998
5019
  const finish = (fn) => {
4999
5020
  if (settled2) return;
5000
5021
  settled2 = true;
5001
5022
  if (timer !== void 0) clearTimeout(timer);
5023
+ abortSignal?.removeEventListener("abort", onAbort);
5002
5024
  fn();
5003
5025
  };
5026
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
5027
+ if (abortSignal?.aborted) onAbort();
5004
5028
  child.stdout?.on("data", (chunk) => out.push(chunk));
5005
5029
  child.stderr?.on("data", (chunk) => err.push(chunk));
5006
5030
  if (options?.input !== void 0 && child.stdin) {
@@ -5013,7 +5037,7 @@ var NodeShell = class {
5013
5037
  if (limit !== void 0 && limit > 0) {
5014
5038
  timer = setTimeout(() => {
5015
5039
  timedOut = true;
5016
- child.kill("SIGTERM");
5040
+ killProcessTree(child);
5017
5041
  }, limit);
5018
5042
  }
5019
5043
  child.on("error", (cause) => {
@@ -5029,6 +5053,10 @@ var NodeShell = class {
5029
5053
  );
5030
5054
  return;
5031
5055
  }
5056
+ if (aborted) {
5057
+ reject(new Error("The command was aborted and stopped."));
5058
+ return;
5059
+ }
5032
5060
  resolve3({
5033
5061
  stdout: joinChunks(out),
5034
5062
  stderr: joinChunks(err),
@@ -5046,11 +5074,11 @@ var NodeShell = class {
5046
5074
  }
5047
5075
  const child = childSpawn(command, {
5048
5076
  shell: true,
5077
+ detached: process.platform !== "win32",
5049
5078
  cwd: options?.cwd,
5050
- env: options?.env ? { ...process.env, ...options.env } : process.env,
5051
- signal: options?.signal
5079
+ env: options?.env ? { ...process.env, ...options.env } : process.env
5052
5080
  });
5053
- return new NodeShellHandle(child);
5081
+ return new NodeShellHandle(child, options?.signal);
5054
5082
  }
5055
5083
  };
5056
5084
  var NodeShellHandle = class {
@@ -5059,27 +5087,33 @@ var NodeShellHandle = class {
5059
5087
  exited = false;
5060
5088
  exitCode = null;
5061
5089
  exitSignal = null;
5062
- constructor(child) {
5090
+ abortSignal;
5091
+ onAbort = () => this.kill();
5092
+ constructor(child, abortSignal) {
5063
5093
  this.child = child;
5094
+ this.abortSignal = abortSignal;
5064
5095
  child.once(
5065
5096
  "close",
5066
5097
  (code, signal) => {
5067
5098
  this.exited = true;
5068
5099
  this.exitCode = code;
5069
5100
  this.exitSignal = asSignal(signal);
5101
+ this.abortSignal?.removeEventListener("abort", this.onAbort);
5070
5102
  for (const listener of [...this.listeners]) {
5071
5103
  listener(this.exitCode, this.exitSignal);
5072
5104
  }
5073
5105
  this.listeners.clear();
5074
5106
  }
5075
5107
  );
5108
+ this.abortSignal?.addEventListener("abort", this.onAbort, { once: true });
5109
+ if (this.abortSignal?.aborted) this.onAbort();
5076
5110
  }
5077
5111
  get pid() {
5078
5112
  return this.child.pid;
5079
5113
  }
5080
5114
  kill(signal) {
5081
5115
  if (this.exited) return;
5082
- this.child.kill(signal ?? "SIGTERM");
5116
+ killProcessTree(this.child, signal);
5083
5117
  }
5084
5118
  onExit(listener) {
5085
5119
  if (this.exited) {
@@ -6513,9 +6547,6 @@ function readInput2(raw) {
6513
6547
  }
6514
6548
  return { action, command, id };
6515
6549
  }
6516
- function shellQuote(value) {
6517
- return `'${value.replace(/'/g, `'\\''`)}'`;
6518
- }
6519
6550
  async function drainCapture(ctx, path2, alreadyRead) {
6520
6551
  let full;
6521
6552
  try {
@@ -6551,7 +6582,7 @@ async function startJob(ctx, command) {
6551
6582
  await ctx.fs.mkdir(scratchDir, { recursive: true });
6552
6583
  await ctx.fs.writeFile(outPath, "", "utf8");
6553
6584
  await ctx.fs.writeFile(errPath, "", "utf8");
6554
- const wrapped = `( ${command} ) >${shellQuote(outPath)} 2>${shellQuote(errPath)}`;
6585
+ const wrapped = `( ${command} ) >${SCRATCH_DIR}/${id}.out 2>${SCRATCH_DIR}/${id}.err`;
6555
6586
  const handle = ctx.shell.spawn(wrapped, { cwd: ctx.cwd });
6556
6587
  const entry = {
6557
6588
  id,
@@ -6631,7 +6662,7 @@ async function pollJob(ctx, id) {
6631
6662
  hadNewOutput: out.fresh.length > 0 || err.fresh.length > 0
6632
6663
  });
6633
6664
  }
6634
- function stopJob(id) {
6665
+ async function stopJob(id) {
6635
6666
  const entry = table.remove(id);
6636
6667
  if (entry === void 0) {
6637
6668
  return asText2(
@@ -6639,14 +6670,24 @@ function stopJob(id) {
6639
6670
  true
6640
6671
  );
6641
6672
  }
6642
- entry.unsubscribe();
6643
6673
  const wasRunning = entry.state === "running";
6644
6674
  if (wasRunning) {
6645
- try {
6646
- entry.handle.kill();
6647
- } catch {
6648
- }
6675
+ await new Promise((resolve3) => {
6676
+ let unsubscribeExit = () => {
6677
+ };
6678
+ unsubscribeExit = entry.handle.onExit(() => {
6679
+ unsubscribeExit();
6680
+ resolve3();
6681
+ });
6682
+ try {
6683
+ entry.handle.kill();
6684
+ } catch {
6685
+ unsubscribeExit();
6686
+ resolve3();
6687
+ }
6688
+ });
6649
6689
  }
6690
+ entry.unsubscribe();
6650
6691
  return asJson({
6651
6692
  action: "stop",
6652
6693
  id: entry.id,