@ganglion/xacpx 0.20.0 → 0.20.1

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.
@@ -2102,9 +2102,9 @@ function decodeTarget(value) {
2102
2102
  return null;
2103
2103
  if (parseCanonicalFileTime(item.creationDate) === null)
2104
2104
  return null;
2105
- if (item.commandLine !== undefined && typeof item.commandLine !== "string")
2105
+ if (item.commandLine !== undefined && item.commandLine !== null && typeof item.commandLine !== "string")
2106
2106
  return null;
2107
- if (item.executablePath !== undefined && typeof item.executablePath !== "string")
2107
+ if (item.executablePath !== undefined && item.executablePath !== null && typeof item.executablePath !== "string")
2108
2108
  return null;
2109
2109
  return {
2110
2110
  pid: Number(item.pid),
@@ -2228,8 +2228,9 @@ async function terminateWindowsResidual(target, options = {}) {
2228
2228
  }
2229
2229
  async function runPowerShellWorker(request, deadlineMs) {
2230
2230
  return await new Promise((resolve2, reject) => {
2231
- const child = spawn("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", "-"], {
2232
- stdio: ["pipe", "pipe", "pipe"],
2231
+ const encodedScript = Buffer.from(WINDOWS_TREE_WORKER_SCRIPT, "utf16le").toString("base64");
2232
+ const child = spawn("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", encodedScript], {
2233
+ stdio: ["ignore", "pipe", "pipe"],
2233
2234
  windowsHide: true,
2234
2235
  env: {
2235
2236
  ...process.env,
@@ -2266,7 +2267,6 @@ async function runPowerShellWorker(request, deadlineMs) {
2266
2267
  });
2267
2268
  child.once("error", (error) => finish(error));
2268
2269
  child.once("close", (code) => code === 0 ? finish() : finish(new Error(`Windows process worker failed (${code}): ${stderr}`)));
2269
- child.stdin.end(WINDOWS_TREE_WORKER_SCRIPT);
2270
2270
  });
2271
2271
  }
2272
2272
  var UUID, OUTCOMES, WINDOWS_TREE_WORKER_SCRIPT;
@@ -2375,7 +2375,14 @@ if($request.action -eq 'terminate-one-cim'){
2375
2375
  if(!$check.ok){Write-Output (@{outcome=$check.status} | ConvertTo-Json -Compress);exit 0}
2376
2376
  try {
2377
2377
  if(![XacpxNativeProcess]::Alive($check.handle)){$status='already-exited'}
2378
- elseif(![XacpxNativeProcess]::Kill($check.handle)){$status=if([XacpxNativeProcess]::LastError() -eq 5){'access-denied'}else{'query-failed'}}
2378
+ elseif(![XacpxNativeProcess]::Kill($check.handle)){
2379
+ $code=[XacpxNativeProcess]::LastError()
2380
+ # Ancestor kills can cascade through Job objects (libuv/Node children
2381
+ # die with their parent), so a failed kill on an already-dying process
2382
+ # is a confirmed exit, not a denial.
2383
+ if([XacpxNativeProcess]::WaitDead($check.handle)){$status='already-exited'}
2384
+ else{$status=if($code -eq 5){'access-denied'}else{'query-failed'}}
2385
+ }
2379
2386
  else{$status=if([XacpxNativeProcess]::WaitDead($check.handle)){'killed'}else{'kill-requested-unconfirmed'}}
2380
2387
  Write-Output (@{outcome=$status} | ConvertTo-Json -Compress);exit 0
2381
2388
  } finally {[XacpxNativeProcess]::Close($check.handle)}
@@ -2430,7 +2437,15 @@ try {
2430
2437
  foreach($node in $nodes){
2431
2438
  $h=$handles[$node.pid]
2432
2439
  if(![XacpxNativeProcess]::Alive($h)){[void]$outcomes.Add((Outcome $node 'already-exited'));continue}
2433
- if(![XacpxNativeProcess]::Kill($h)){$status=if([XacpxNativeProcess]::LastError() -eq 5){'access-denied'}else{'query-failed'};[void]$outcomes.Add((Outcome $node $status));continue}
2440
+ if(![XacpxNativeProcess]::Kill($h)){
2441
+ $code=[XacpxNativeProcess]::LastError()
2442
+ # Ancestor kills can cascade through Job objects (libuv/Node children
2443
+ # die with their parent), so a failed kill on an already-dying process
2444
+ # is a confirmed exit, not a denial.
2445
+ if([XacpxNativeProcess]::WaitDead($h)){$status='already-exited'}
2446
+ else{$status=if($code -eq 5){'access-denied'}else{'query-failed'}}
2447
+ [void]$outcomes.Add((Outcome $node $status));continue
2448
+ }
2434
2449
  $status=if([XacpxNativeProcess]::WaitDead($h)){'killed'}else{'kill-requested-unconfirmed'}
2435
2450
  [void]$outcomes.Add((Outcome $node $status))
2436
2451
  }
@@ -7210,13 +7225,14 @@ async function verifyAcpInitialize(command, args, options = {}) {
7210
7225
  let stdoutBuffer = "";
7211
7226
  let settled = false;
7212
7227
  let timer;
7213
- let windowsTarget;
7214
- if (process.platform === "win32" && child.pid) {
7228
+ const windowsProbe = (async () => {
7229
+ if (process.platform !== "win32" || !child.pid)
7230
+ return;
7215
7231
  const identity = await probeWindowsProcessIdentity(child.pid);
7216
- if (identity.status === "found") {
7217
- windowsTarget = { pid: child.pid, creationDate: identity.identity.creationDate, executablePath: identity.identity.executablePath };
7218
- }
7219
- }
7232
+ if (identity.status !== "found")
7233
+ return;
7234
+ return { pid: child.pid, creationDate: identity.identity.creationDate, executablePath: identity.identity.executablePath };
7235
+ })();
7220
7236
  try {
7221
7237
  await new Promise((resolve2, reject) => {
7222
7238
  const finish = (error) => {
@@ -7301,6 +7317,7 @@ async function verifyAcpInitialize(command, args, options = {}) {
7301
7317
  child.stdin.destroy();
7302
7318
  child.stdout.destroy();
7303
7319
  child.stderr.destroy();
7320
+ const windowsTarget = await windowsProbe;
7304
7321
  if (process.platform !== "win32" || windowsTarget) {
7305
7322
  await terminateProcessTree(windowsTarget ?? child.pid ?? 0, { detachedProcessGroup: detached });
7306
7323
  }
package/dist/cli.js CHANGED
@@ -4816,9 +4816,9 @@ function decodeTarget(value) {
4816
4816
  return null;
4817
4817
  if (parseCanonicalFileTime(item.creationDate) === null)
4818
4818
  return null;
4819
- if (item.commandLine !== undefined && typeof item.commandLine !== "string")
4819
+ if (item.commandLine !== undefined && item.commandLine !== null && typeof item.commandLine !== "string")
4820
4820
  return null;
4821
- if (item.executablePath !== undefined && typeof item.executablePath !== "string")
4821
+ if (item.executablePath !== undefined && item.executablePath !== null && typeof item.executablePath !== "string")
4822
4822
  return null;
4823
4823
  return {
4824
4824
  pid: Number(item.pid),
@@ -4942,8 +4942,9 @@ async function terminateWindowsResidual(target, options = {}) {
4942
4942
  }
4943
4943
  async function runPowerShellWorker(request, deadlineMs) {
4944
4944
  return await new Promise((resolve2, reject) => {
4945
- const child = spawn2("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", "-"], {
4946
- stdio: ["pipe", "pipe", "pipe"],
4945
+ const encodedScript = Buffer.from(WINDOWS_TREE_WORKER_SCRIPT, "utf16le").toString("base64");
4946
+ const child = spawn2("powershell.exe", ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", encodedScript], {
4947
+ stdio: ["ignore", "pipe", "pipe"],
4947
4948
  windowsHide: true,
4948
4949
  env: {
4949
4950
  ...process.env,
@@ -4980,7 +4981,6 @@ async function runPowerShellWorker(request, deadlineMs) {
4980
4981
  });
4981
4982
  child.once("error", (error) => finish(error));
4982
4983
  child.once("close", (code) => code === 0 ? finish() : finish(new Error(`Windows process worker failed (${code}): ${stderr}`)));
4983
- child.stdin.end(WINDOWS_TREE_WORKER_SCRIPT);
4984
4984
  });
4985
4985
  }
4986
4986
  var UUID, OUTCOMES, WINDOWS_TREE_WORKER_SCRIPT;
@@ -5089,7 +5089,14 @@ if($request.action -eq 'terminate-one-cim'){
5089
5089
  if(!$check.ok){Write-Output (@{outcome=$check.status} | ConvertTo-Json -Compress);exit 0}
5090
5090
  try {
5091
5091
  if(![XacpxNativeProcess]::Alive($check.handle)){$status='already-exited'}
5092
- elseif(![XacpxNativeProcess]::Kill($check.handle)){$status=if([XacpxNativeProcess]::LastError() -eq 5){'access-denied'}else{'query-failed'}}
5092
+ elseif(![XacpxNativeProcess]::Kill($check.handle)){
5093
+ $code=[XacpxNativeProcess]::LastError()
5094
+ # Ancestor kills can cascade through Job objects (libuv/Node children
5095
+ # die with their parent), so a failed kill on an already-dying process
5096
+ # is a confirmed exit, not a denial.
5097
+ if([XacpxNativeProcess]::WaitDead($check.handle)){$status='already-exited'}
5098
+ else{$status=if($code -eq 5){'access-denied'}else{'query-failed'}}
5099
+ }
5093
5100
  else{$status=if([XacpxNativeProcess]::WaitDead($check.handle)){'killed'}else{'kill-requested-unconfirmed'}}
5094
5101
  Write-Output (@{outcome=$status} | ConvertTo-Json -Compress);exit 0
5095
5102
  } finally {[XacpxNativeProcess]::Close($check.handle)}
@@ -5144,7 +5151,15 @@ try {
5144
5151
  foreach($node in $nodes){
5145
5152
  $h=$handles[$node.pid]
5146
5153
  if(![XacpxNativeProcess]::Alive($h)){[void]$outcomes.Add((Outcome $node 'already-exited'));continue}
5147
- if(![XacpxNativeProcess]::Kill($h)){$status=if([XacpxNativeProcess]::LastError() -eq 5){'access-denied'}else{'query-failed'};[void]$outcomes.Add((Outcome $node $status));continue}
5154
+ if(![XacpxNativeProcess]::Kill($h)){
5155
+ $code=[XacpxNativeProcess]::LastError()
5156
+ # Ancestor kills can cascade through Job objects (libuv/Node children
5157
+ # die with their parent), so a failed kill on an already-dying process
5158
+ # is a confirmed exit, not a denial.
5159
+ if([XacpxNativeProcess]::WaitDead($h)){$status='already-exited'}
5160
+ else{$status=if($code -eq 5){'access-denied'}else{'query-failed'}}
5161
+ [void]$outcomes.Add((Outcome $node $status));continue
5162
+ }
5148
5163
  $status=if([XacpxNativeProcess]::WaitDead($h)){'killed'}else{'kill-requested-unconfirmed'}
5149
5164
  [void]$outcomes.Add((Outcome $node $status))
5150
5165
  }
@@ -5222,13 +5237,14 @@ async function verifyAcpInitialize(command, args, options = {}) {
5222
5237
  let stdoutBuffer = "";
5223
5238
  let settled = false;
5224
5239
  let timer;
5225
- let windowsTarget;
5226
- if (process.platform === "win32" && child.pid) {
5240
+ const windowsProbe = (async () => {
5241
+ if (process.platform !== "win32" || !child.pid)
5242
+ return;
5227
5243
  const identity = await probeWindowsProcessIdentity(child.pid);
5228
- if (identity.status === "found") {
5229
- windowsTarget = { pid: child.pid, creationDate: identity.identity.creationDate, executablePath: identity.identity.executablePath };
5230
- }
5231
- }
5244
+ if (identity.status !== "found")
5245
+ return;
5246
+ return { pid: child.pid, creationDate: identity.identity.creationDate, executablePath: identity.identity.executablePath };
5247
+ })();
5232
5248
  try {
5233
5249
  await new Promise((resolve2, reject) => {
5234
5250
  const finish = (error) => {
@@ -5313,6 +5329,7 @@ async function verifyAcpInitialize(command, args, options = {}) {
5313
5329
  child.stdin.destroy();
5314
5330
  child.stdout.destroy();
5315
5331
  child.stderr.destroy();
5332
+ const windowsTarget = await windowsProbe;
5316
5333
  if (process.platform !== "win32" || windowsTarget) {
5317
5334
  await terminateProcessTree(windowsTarget ?? child.pid ?? 0, { detachedProcessGroup: detached });
5318
5335
  }
@@ -56,3 +56,4 @@ export declare function queryWindowsProcessIdentity(pid: number, options?: Windo
56
56
  export declare function probeWindowsProcessIdentity(pid: number, options?: WindowsProcessWorkerOptions): Promise<WindowsProbeStatus>;
57
57
  export declare function snapshotWindowsProcessesByToken(token: string, options?: WindowsProcessWorkerOptions): Promise<WindowsTokenProcess[] | null>;
58
58
  export declare function terminateWindowsResidual(target: BatchTarget, options?: WindowsProcessWorkerOptions): Promise<KillOutcome>;
59
+ export declare const WINDOWS_TREE_WORKER_SCRIPT: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ganglion/xacpx",
3
- "version": "0.20.0",
3
+ "version": "0.20.1",
4
4
  "description": "随时随地通过聊天频道(微信 / 飞书 / 元宝等)远程控制 `acpx` 上的 Claude Code、Codex 等 Agents。",
5
5
  "keywords": [
6
6
  "acpx",