@tapi-dev/sdk 0.1.27 → 0.1.29
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 +3 -4
- package/dist/cli.js +89 -30
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
|
@@ -114,10 +114,9 @@ export declare function validateStudioManifest(input: unknown): StudioReleaseMan
|
|
|
114
114
|
export declare function validateServiceReleaseManifest(input: unknown): ServiceReleaseManifest;
|
|
115
115
|
export declare function compareVersions(left: string, right: string): number;
|
|
116
116
|
export declare function memoizeStudioInstallToken(options: StudioCliOptions, installToken?: string): string;
|
|
117
|
-
export declare function
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
reason?: string;
|
|
117
|
+
export declare function closeExistingPortableStudioServersForFreshLaunch(stopProcesses?: () => Promise<number>): Promise<{
|
|
118
|
+
stopped: number;
|
|
119
|
+
recordsRemoved: number;
|
|
121
120
|
}>;
|
|
122
121
|
export declare function findPortablePlaywrightBrowsersDir(exePath: string): string | undefined;
|
|
123
122
|
export declare function waitForHttpOk(url: string, timeoutMs?: number, intervalMs?: number, fetchImpl?: FetchLike): Promise<void>;
|
package/dist/cli.js
CHANGED
|
@@ -2123,13 +2123,10 @@ async function openStudio(options) {
|
|
|
2123
2123
|
throw new Error("--download-only is only supported with `tapi studio install`.");
|
|
2124
2124
|
}
|
|
2125
2125
|
await ensureServiceReadyForStudio(options);
|
|
2126
|
+
await closeExistingPortableStudioServersForFreshLaunch();
|
|
2126
2127
|
if (!options.exePath) {
|
|
2127
2128
|
const portable = await ensurePortableStudioServer(options);
|
|
2128
2129
|
if (portable) {
|
|
2129
|
-
const existing = await tryOpenExistingPortableStudioServer(options, portable.manifest);
|
|
2130
|
-
if (existing.opened) {
|
|
2131
|
-
return 0;
|
|
2132
|
-
}
|
|
2133
2130
|
await launchPortableStudioServer(portable.exePath, options, portable.manifest);
|
|
2134
2131
|
return 0;
|
|
2135
2132
|
}
|
|
@@ -2164,33 +2161,16 @@ function findStudioExecutable(options) {
|
|
|
2164
2161
|
}
|
|
2165
2162
|
return getStudioExecutableCandidates().find((candidate) => existsSync(candidate));
|
|
2166
2163
|
}
|
|
2167
|
-
export async function
|
|
2168
|
-
const
|
|
2169
|
-
|
|
2170
|
-
|
|
2164
|
+
export async function closeExistingPortableStudioServersForFreshLaunch(stopProcesses = stopPortableStudioServerProcesses) {
|
|
2165
|
+
const stopped = await stopProcesses();
|
|
2166
|
+
const recordsRemoved = await clearStudioInstanceRecords();
|
|
2167
|
+
if (stopped > 0) {
|
|
2168
|
+
await delay(500);
|
|
2171
2169
|
}
|
|
2172
|
-
if (
|
|
2173
|
-
|
|
2174
|
-
}
|
|
2175
|
-
try {
|
|
2176
|
-
const identityResponse = await fetchWithTimeout(`${record.baseUrl.replace(/\/+$/, "")}/api/studio/control/identity`, { headers: { Accept: "application/json" } }, 1_000, fetchImpl);
|
|
2177
|
-
if (!identityResponse.ok) {
|
|
2178
|
-
await unlinkIfExists(studioInstanceRecordPathForOptions(options));
|
|
2179
|
-
return { opened: false, reason: `identity_http_${identityResponse.status}` };
|
|
2180
|
-
}
|
|
2181
|
-
const identity = await readJsonBody(identityResponse);
|
|
2182
|
-
if (!studioInstanceIdentityMatches(identity, options)) {
|
|
2183
|
-
return { opened: false, reason: "identity_mismatch" };
|
|
2184
|
-
}
|
|
2185
|
-
const launchUrl = studioLaunchUrl(record.baseUrl, options);
|
|
2186
|
-
openBrowserImpl(launchUrl);
|
|
2187
|
-
console.log(`Opened existing Tapi Studio ${record.manifestVersion || manifest.version}: ${launchUrl}`);
|
|
2188
|
-
return { opened: true, baseUrl: record.baseUrl };
|
|
2189
|
-
}
|
|
2190
|
-
catch (error) {
|
|
2191
|
-
await unlinkIfExists(studioInstanceRecordPathForOptions(options));
|
|
2192
|
-
return { opened: false, reason: formatError(error) };
|
|
2170
|
+
if (stopped > 0 || recordsRemoved > 0) {
|
|
2171
|
+
console.log(`Closed existing Tapi Studio servers before fresh launch: stopped=${stopped}, records=${recordsRemoved}`);
|
|
2193
2172
|
}
|
|
2173
|
+
return { stopped, recordsRemoved };
|
|
2194
2174
|
}
|
|
2195
2175
|
async function ensurePortableStudioServer(options) {
|
|
2196
2176
|
const event = await createCliWideEvent("tapi_cli.studio_server_ensure", {
|
|
@@ -2470,6 +2450,53 @@ foreach ($pidValue in $owners.Keys) {
|
|
|
2470
2450
|
return 0;
|
|
2471
2451
|
}
|
|
2472
2452
|
}
|
|
2453
|
+
async function stopPortableStudioServerProcesses() {
|
|
2454
|
+
if (process.platform !== "win32") {
|
|
2455
|
+
return 0;
|
|
2456
|
+
}
|
|
2457
|
+
const script = `
|
|
2458
|
+
$ErrorActionPreference = 'SilentlyContinue'
|
|
2459
|
+
$currentPid = $PID
|
|
2460
|
+
$processes = @(Get-CimInstance Win32_Process | Where-Object {
|
|
2461
|
+
$pidValue = [int]$_.ProcessId
|
|
2462
|
+
if ($pidValue -le 0 -or $pidValue -eq $currentPid) {
|
|
2463
|
+
$false
|
|
2464
|
+
} else {
|
|
2465
|
+
$path = [string]$_.ExecutablePath
|
|
2466
|
+
$cmd = [string]$_.CommandLine
|
|
2467
|
+
$name = [string]$_.Name
|
|
2468
|
+
(
|
|
2469
|
+
$name -ieq 'tapi-studio-server.exe' -or
|
|
2470
|
+
$path -match '\\\\Tapi\\\\Studio\\\\server\\\\releases\\\\' -or
|
|
2471
|
+
$cmd -match 'tapi-studio-server' -or
|
|
2472
|
+
$cmd -match 'scripts[\\\\/]studio\\.py'
|
|
2473
|
+
)
|
|
2474
|
+
}
|
|
2475
|
+
})
|
|
2476
|
+
$stopped = 0
|
|
2477
|
+
foreach ($process in $processes) {
|
|
2478
|
+
try {
|
|
2479
|
+
Stop-Process -Id ([int]$process.ProcessId) -Force -ErrorAction Stop
|
|
2480
|
+
$stopped += 1
|
|
2481
|
+
} catch {}
|
|
2482
|
+
}
|
|
2483
|
+
[pscustomobject]@{ stopped = $stopped } | ConvertTo-Json -Compress
|
|
2484
|
+
`;
|
|
2485
|
+
try {
|
|
2486
|
+
const output = await runProcessCapture("powershell.exe", [
|
|
2487
|
+
"-NoProfile",
|
|
2488
|
+
"-ExecutionPolicy",
|
|
2489
|
+
"Bypass",
|
|
2490
|
+
"-Command",
|
|
2491
|
+
script,
|
|
2492
|
+
]);
|
|
2493
|
+
const payload = JSON.parse(output.trim() || "{}");
|
|
2494
|
+
return typeof payload.stopped === "number" ? payload.stopped : 0;
|
|
2495
|
+
}
|
|
2496
|
+
catch {
|
|
2497
|
+
return 0;
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2473
2500
|
async function runDoctor(options) {
|
|
2474
2501
|
console.log(`Tapi SDK: ${sdkVersion}`);
|
|
2475
2502
|
console.log(`Node: ${process.version}`);
|
|
@@ -2775,7 +2802,39 @@ export function studioInstanceRecordPathForOptions(options) {
|
|
|
2775
2802
|
}))
|
|
2776
2803
|
.digest("hex")
|
|
2777
2804
|
.slice(0, 24);
|
|
2778
|
-
return join(
|
|
2805
|
+
return join(studioInstanceRecordsDir(), `${key}.json`);
|
|
2806
|
+
}
|
|
2807
|
+
function studioInstanceRecordsDir() {
|
|
2808
|
+
return join(getDefaultTapiDataDir(), "Studio", "instances");
|
|
2809
|
+
}
|
|
2810
|
+
async function clearStudioInstanceRecords() {
|
|
2811
|
+
let entries;
|
|
2812
|
+
try {
|
|
2813
|
+
entries = await readdir(studioInstanceRecordsDir(), { withFileTypes: true });
|
|
2814
|
+
}
|
|
2815
|
+
catch (error) {
|
|
2816
|
+
if (error.code === "ENOENT") {
|
|
2817
|
+
return 0;
|
|
2818
|
+
}
|
|
2819
|
+
throw error;
|
|
2820
|
+
}
|
|
2821
|
+
let removed = 0;
|
|
2822
|
+
for (const entry of entries) {
|
|
2823
|
+
if (!entry.isFile() || !entry.name.toLowerCase().endsWith(".json")) {
|
|
2824
|
+
continue;
|
|
2825
|
+
}
|
|
2826
|
+
const path = join(studioInstanceRecordsDir(), entry.name);
|
|
2827
|
+
try {
|
|
2828
|
+
await unlink(path);
|
|
2829
|
+
removed += 1;
|
|
2830
|
+
}
|
|
2831
|
+
catch (error) {
|
|
2832
|
+
if (error.code !== "ENOENT") {
|
|
2833
|
+
throw error;
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
return removed;
|
|
2779
2838
|
}
|
|
2780
2839
|
async function writeStudioInstanceRecord(options, record) {
|
|
2781
2840
|
await writeJsonFile(studioInstanceRecordPathForOptions(options), record);
|