@tapi-dev/sdk 0.1.31 → 0.1.33

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.d.ts CHANGED
@@ -85,6 +85,7 @@ export declare function trySelectDevSessionInOpenStudio(session: TapiDevSession,
85
85
  baseUrl?: string;
86
86
  reason?: string;
87
87
  }>;
88
+ export declare function servicePowerShell(action: string): string;
88
89
  export declare function installedServiceVersionFromPathName(pathName: string): string | undefined;
89
90
  export declare function serviceNeedsInstall(status: ServiceStatus, manifest: ServiceReleaseManifest): boolean;
90
91
  export declare function getDefaultStudioCacheDir(): string;
package/dist/cli.js CHANGED
@@ -28,6 +28,8 @@ const DOWNLOAD_PROGRESS_LOG_BYTES = 50 * 1024 * 1024;
28
28
  const MAX_WIDE_EVENT_PROCESS_ROOTS = 5;
29
29
  const MAX_NONCURRENT_MANAGED_DOWNLOADS = 0;
30
30
  const MAX_NONCURRENT_MANAGED_RELEASES = 0;
31
+ const WINDOWS_SERVICE_STATUS_TIMEOUT_MS = 8_000;
32
+ const WINDOWS_SERVICE_COMMAND_TIMEOUT_MS = 30_000;
31
33
  const MANAGED_RELEASE_MARKER = ".tapi-managed-release";
32
34
  const MIN_EXTRACTION_REQUIRED_BYTES = 1024 * 1024 * 1024;
33
35
  const EXTRACTION_REQUIRED_MULTIPLIER = 3;
@@ -1351,7 +1353,7 @@ async function runServiceCommand(action, args = []) {
1351
1353
  return repairService(parseStudioOptions([]));
1352
1354
  }
1353
1355
  const command = servicePowerShell(normalized);
1354
- const output = await withCliSpinner(`Running tapi-service ${normalized}`, () => runProcessCapture("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command]));
1356
+ const output = await withCliSpinner(`Running tapi-service ${normalized}`, () => runProcessCapture("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command], { timeoutMs: WINDOWS_SERVICE_COMMAND_TIMEOUT_MS }));
1355
1357
  if (output.trim()) {
1356
1358
  console.log(output.trim());
1357
1359
  }
@@ -1369,9 +1371,9 @@ async function repairService(options) {
1369
1371
  }
1370
1372
  return runServiceCommand("restart");
1371
1373
  }
1372
- function servicePowerShell(action) {
1374
+ export function servicePowerShell(action) {
1373
1375
  const serviceName = "tapi-service";
1374
- const status = `$svc = Get-Service -Name '${serviceName}' -ErrorAction SilentlyContinue; if ($null -eq $svc) { [pscustomobject]@{ installed = $false; name = '${serviceName}'; status = 'not_installed'; pathName = '' } | ConvertTo-Json -Compress; exit 0 }; $cim = Get-CimInstance Win32_Service -Filter "Name='${serviceName}'" -ErrorAction SilentlyContinue; $pathName = if ($null -ne $cim) { [string]$cim.PathName } else { '' }; [pscustomobject]@{ installed = $true; name = $svc.Name; status = $svc.Status.ToString(); pathName = $pathName } | ConvertTo-Json -Compress`;
1376
+ const status = `$svc = Get-Service -Name '${serviceName}' -ErrorAction SilentlyContinue; if ($null -eq $svc) { [pscustomobject]@{ installed = $false; name = '${serviceName}'; status = 'not_installed'; pathName = '' } | ConvertTo-Json -Compress; exit 0 }; $pathName = ''; try { $imagePath = (Get-ItemProperty -LiteralPath 'HKLM:\\SYSTEM\\CurrentControlSet\\Services\\${serviceName}' -Name ImagePath -ErrorAction SilentlyContinue).ImagePath; if ($null -ne $imagePath) { $pathName = [string]$imagePath } } catch {}; [pscustomobject]@{ installed = $true; name = $svc.Name; status = $svc.Status.ToString(); pathName = $pathName } | ConvertTo-Json -Compress`;
1375
1377
  if (action === "status") {
1376
1378
  return status;
1377
1379
  }
@@ -1393,7 +1395,7 @@ async function getServiceStatus() {
1393
1395
  "Bypass",
1394
1396
  "-Command",
1395
1397
  servicePowerShell("status"),
1396
- ]);
1398
+ ], { timeoutMs: WINDOWS_SERVICE_STATUS_TIMEOUT_MS });
1397
1399
  try {
1398
1400
  const payload = JSON.parse(output.trim());
1399
1401
  return {
@@ -1436,7 +1438,7 @@ async function ensureServiceReadyForStudio(options) {
1436
1438
  "Bypass",
1437
1439
  "-Command",
1438
1440
  servicePowerShell("start"),
1439
- ]));
1441
+ ], { timeoutMs: WINDOWS_SERVICE_COMMAND_TIMEOUT_MS }));
1440
1442
  if (output.trim()) {
1441
1443
  console.log(output.trim());
1442
1444
  }
@@ -2434,54 +2436,9 @@ async function isPortAvailable(port) {
2434
2436
  });
2435
2437
  }
2436
2438
  async function reapPortableStudioServersBlockingPorts(preferred, count) {
2437
- if (process.platform !== "win32") {
2438
- return 0;
2439
- }
2440
- const start = Math.max(1, Math.min(65535, Math.trunc(preferred)));
2441
- const end = Math.max(start, Math.min(65535, start + Math.max(1, Math.trunc(count)) - 1));
2442
- const script = `
2443
- $ErrorActionPreference = 'SilentlyContinue'
2444
- $ports = ${start}..${end}
2445
- $connections = @(Get-NetTCPConnection -State Listen | Where-Object { $ports -contains $_.LocalPort })
2446
- $owners = @{}
2447
- foreach ($conn in $connections) {
2448
- $pidValue = [int]$conn.OwningProcess
2449
- if ($pidValue -le 0 -or $owners.ContainsKey($pidValue)) { continue }
2450
- $proc = Get-CimInstance Win32_Process -Filter "ProcessId=$pidValue"
2451
- if ($null -eq $proc) { continue }
2452
- $path = [string]$proc.ExecutablePath
2453
- $cmd = [string]$proc.CommandLine
2454
- $isPortableStudio = (
2455
- $path -match '\\\\Tapi\\\\Studio\\\\server\\\\releases\\\\' -or
2456
- $cmd -match 'tapi-studio-server'
2457
- )
2458
- if ($isPortableStudio) {
2459
- $owners[$pidValue] = $true
2460
- }
2461
- }
2462
- $stopped = 0
2463
- foreach ($pidValue in $owners.Keys) {
2464
- try {
2465
- Stop-Process -Id $pidValue -Force -ErrorAction Stop
2466
- $stopped += 1
2467
- } catch {}
2468
- }
2469
- [pscustomobject]@{ stopped = $stopped } | ConvertTo-Json -Compress
2470
- `;
2471
- try {
2472
- const output = await runProcessCapture("powershell.exe", [
2473
- "-NoProfile",
2474
- "-ExecutionPolicy",
2475
- "Bypass",
2476
- "-Command",
2477
- script,
2478
- ]);
2479
- const payload = JSON.parse(output.trim() || "{}");
2480
- return typeof payload.stopped === "number" ? payload.stopped : 0;
2481
- }
2482
- catch {
2483
- return 0;
2484
- }
2439
+ void preferred;
2440
+ void count;
2441
+ return 0;
2485
2442
  }
2486
2443
  async function stopPortableStudioServerProcesses() {
2487
2444
  if (process.platform !== "win32") {
@@ -2490,26 +2447,24 @@ async function stopPortableStudioServerProcesses() {
2490
2447
  const script = `
2491
2448
  $ErrorActionPreference = 'SilentlyContinue'
2492
2449
  $currentPid = $PID
2493
- $processes = @(Get-CimInstance Win32_Process | Where-Object {
2494
- $pidValue = [int]$_.ProcessId
2450
+ $processes = @(Get-Process -ErrorAction SilentlyContinue | Where-Object {
2451
+ $pidValue = [int]$_.Id
2495
2452
  if ($pidValue -le 0 -or $pidValue -eq $currentPid) {
2496
2453
  $false
2497
2454
  } else {
2498
- $path = [string]$_.ExecutablePath
2499
- $cmd = [string]$_.CommandLine
2500
- $name = [string]$_.Name
2455
+ $path = ''
2456
+ try { $path = [string]$_.Path } catch {}
2457
+ $name = [string]$_.ProcessName
2501
2458
  (
2502
- $name -ieq 'tapi-studio-server.exe' -or
2503
- $path -match '\\\\Tapi\\\\Studio\\\\server\\\\releases\\\\' -or
2504
- $cmd -match 'tapi-studio-server' -or
2505
- $cmd -match 'scripts[\\\\/]studio\\.py'
2459
+ $name -ieq 'tapi-studio-server' -or
2460
+ $path -match '\\\\Tapi\\\\Studio\\\\server\\\\releases\\\\'
2506
2461
  )
2507
2462
  }
2508
2463
  })
2509
2464
  $stopped = 0
2510
2465
  foreach ($process in $processes) {
2511
2466
  try {
2512
- Stop-Process -Id ([int]$process.ProcessId) -Force -ErrorAction Stop
2467
+ Stop-Process -Id ([int]$process.Id) -Force -ErrorAction Stop
2513
2468
  $stopped += 1
2514
2469
  } catch {}
2515
2470
  }
@@ -2522,7 +2477,7 @@ foreach ($process in $processes) {
2522
2477
  "Bypass",
2523
2478
  "-Command",
2524
2479
  script,
2525
- ]);
2480
+ ], { timeoutMs: WINDOWS_SERVICE_STATUS_TIMEOUT_MS });
2526
2481
  const payload = JSON.parse(output.trim() || "{}");
2527
2482
  return typeof payload.stopped === "number" ? payload.stopped : 0;
2528
2483
  }
@@ -3086,7 +3041,7 @@ async function runProcess(command, args) {
3086
3041
  });
3087
3042
  });
3088
3043
  }
3089
- async function runProcessCapture(command, args) {
3044
+ async function runProcessCapture(command, args, options = {}) {
3090
3045
  return await new Promise((resolvePromise, rejectPromise) => {
3091
3046
  const child = spawn(command, args, {
3092
3047
  stdio: ["ignore", "pipe", "pipe"],
@@ -3094,21 +3049,47 @@ async function runProcessCapture(command, args) {
3094
3049
  });
3095
3050
  let stdout = "";
3096
3051
  let stderr = "";
3052
+ let timedOut = false;
3053
+ let settled = false;
3054
+ const timeoutMs = Math.max(0, options.timeoutMs ?? 0);
3055
+ const timeout = timeoutMs > 0
3056
+ ? setTimeout(() => {
3057
+ timedOut = true;
3058
+ child.kill();
3059
+ }, timeoutMs)
3060
+ : undefined;
3061
+ timeout?.unref?.();
3062
+ const finish = (callback) => {
3063
+ if (settled) {
3064
+ return;
3065
+ }
3066
+ settled = true;
3067
+ if (timeout) {
3068
+ clearTimeout(timeout);
3069
+ }
3070
+ callback();
3071
+ };
3097
3072
  child.stdout.on("data", (chunk) => {
3098
3073
  stdout += String(chunk);
3099
3074
  });
3100
3075
  child.stderr.on("data", (chunk) => {
3101
3076
  stderr += String(chunk);
3102
3077
  });
3103
- child.once("error", rejectPromise);
3078
+ child.once("error", (error) => finish(() => rejectPromise(error)));
3104
3079
  child.once("exit", (code) => {
3105
- const err = stderr.trim();
3106
- if (code === 0) {
3107
- resolvePromise(stdout);
3108
- }
3109
- else {
3110
- rejectPromise(new Error(err || `Process exited with code ${code ?? "unknown"}.`));
3111
- }
3080
+ finish(() => {
3081
+ const err = stderr.trim();
3082
+ if (timedOut) {
3083
+ rejectPromise(new Error(`Process timed out after ${timeoutMs} ms: ${command} ${args.join(" ")}`));
3084
+ return;
3085
+ }
3086
+ if (code === 0) {
3087
+ resolvePromise(stdout);
3088
+ }
3089
+ else {
3090
+ rejectPromise(new Error(err || `Process exited with code ${code ?? "unknown"}.`));
3091
+ }
3092
+ });
3112
3093
  });
3113
3094
  });
3114
3095
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tapi-dev/sdk",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",