@tapi-dev/sdk 0.1.24 → 0.1.26

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
@@ -114,6 +114,11 @@ 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 tryOpenExistingPortableStudioServer(options: StudioCliOptions, manifest: StudioReleaseManifest, fetchImpl?: FetchLike, openBrowserImpl?: (url: string) => void): Promise<{
118
+ opened: boolean;
119
+ baseUrl?: string;
120
+ reason?: string;
121
+ }>;
117
122
  export declare function findPortablePlaywrightBrowsersDir(exePath: string): string | undefined;
118
123
  export declare function waitForHttpOk(url: string, timeoutMs?: number, intervalMs?: number, fetchImpl?: FetchLike): Promise<void>;
119
124
  export declare function chooseStudioPort(preferred?: number, options?: {
package/dist/cli.js CHANGED
@@ -2126,6 +2126,10 @@ async function openStudio(options) {
2126
2126
  if (!options.exePath) {
2127
2127
  const portable = await ensurePortableStudioServer(options);
2128
2128
  if (portable) {
2129
+ const existing = await tryOpenExistingPortableStudioServer(options, portable.manifest);
2130
+ if (existing.opened) {
2131
+ return 0;
2132
+ }
2129
2133
  await launchPortableStudioServer(portable.exePath, options, portable.manifest);
2130
2134
  return 0;
2131
2135
  }
@@ -2160,6 +2164,34 @@ function findStudioExecutable(options) {
2160
2164
  }
2161
2165
  return getStudioExecutableCandidates().find((candidate) => existsSync(candidate));
2162
2166
  }
2167
+ export async function tryOpenExistingPortableStudioServer(options, manifest, fetchImpl = fetch, openBrowserImpl = openBrowser) {
2168
+ const record = await readStudioInstanceRecord(options);
2169
+ if (!record) {
2170
+ return { opened: false, reason: "instance_record_missing" };
2171
+ }
2172
+ if ((record.manifestVersion || "") !== manifest.version) {
2173
+ return { opened: false, reason: "manifest_version_mismatch" };
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) };
2193
+ }
2194
+ }
2163
2195
  async function ensurePortableStudioServer(options) {
2164
2196
  const event = await createCliWideEvent("tapi_cli.studio_server_ensure", {
2165
2197
  sdk_version: sdkVersion,
@@ -2259,8 +2291,7 @@ async function launchPortableStudioServer(exePath, options, manifest) {
2259
2291
  env.PLAYWRIGHT_BROWSERS_PATH = playwrightBrowsersPath;
2260
2292
  }
2261
2293
  const url = `http://${host}:${port}`;
2262
- const launchQuery = options.launchUrlQuery ? options.launchUrlQuery.replace(/^\?+/, "") : "";
2263
- const launchUrl = launchQuery ? `${url}/?${launchQuery}` : url;
2294
+ const launchUrl = studioLaunchUrl(url, options);
2264
2295
  let childPid = 0;
2265
2296
  await withCliSpinner("Starting Tapi Studio", async () => {
2266
2297
  const child = spawn(exePath, [], {
@@ -2289,6 +2320,11 @@ async function launchPortableStudioServer(exePath, options, manifest) {
2289
2320
  openBrowser(launchUrl);
2290
2321
  console.log(`Opened Tapi Studio ${manifest.version}: ${launchUrl}`);
2291
2322
  }
2323
+ function studioLaunchUrl(baseUrl, options) {
2324
+ const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
2325
+ const launchQuery = options.launchUrlQuery ? options.launchUrlQuery.replace(/^\?+/, "") : "";
2326
+ return launchQuery ? `${normalizedBaseUrl}/?${launchQuery}` : normalizedBaseUrl;
2327
+ }
2292
2328
  export function findPortablePlaywrightBrowsersDir(exePath) {
2293
2329
  const exeDir = dirname(exePath);
2294
2330
  const releaseDir = basename(exeDir).toLowerCase() === "tapi-studio-server"
@@ -2444,14 +2480,16 @@ async function runDoctor(options) {
2444
2480
  console.log(`Studio cache: ${options.cacheDir}`);
2445
2481
  console.log(`Workspace: ${options.workspaceRoot ?? "not found"}`);
2446
2482
  console.log(`Project: ${options.projectId ?? "not set"}`);
2447
- const exePath = options.exePath ?? getStudioExecutableCandidates().find((candidate) => existsSync(candidate));
2448
- console.log(`Studio executable: ${exePath && existsSync(exePath) ? exePath : "not found"}`);
2449
2483
  try {
2450
2484
  const manifest = await withCliSpinner("Fetching latest Tapi Studio manifest", () => fetchStudioManifest(options.manifestUrl));
2451
2485
  ensureCompatibleManifest(manifest);
2486
+ const exePath = findInstalledStudioExecutable(options, manifest);
2487
+ console.log(`Studio executable: ${exePath ?? "not found"}`);
2452
2488
  console.log(`Latest Studio: ${manifest.version} (${manifest.platform})`);
2453
2489
  }
2454
2490
  catch (error) {
2491
+ const exePath = options.exePath ?? getStudioExecutableCandidates().find((candidate) => existsSync(candidate));
2492
+ console.log(`Studio executable: ${exePath && existsSync(exePath) ? exePath : "not found"}`);
2455
2493
  console.log(`Latest Studio: unavailable (${formatError(error)})`);
2456
2494
  return 1;
2457
2495
  }
@@ -2920,6 +2958,19 @@ function isPortableStudioServerManifest(manifest) {
2920
2958
  const kind = String(manifest.installerKind || "").toLowerCase();
2921
2959
  return kind === "portable-server" || manifest.artifactName.toLowerCase().endsWith(".zip");
2922
2960
  }
2961
+ function findInstalledStudioExecutable(options, manifest) {
2962
+ if (options.exePath) {
2963
+ return existsSync(options.exePath) ? options.exePath : undefined;
2964
+ }
2965
+ const legacyDesktopExe = getStudioExecutableCandidates().find((candidate) => existsSync(candidate));
2966
+ if (legacyDesktopExe) {
2967
+ return legacyDesktopExe;
2968
+ }
2969
+ if (!isPortableStudioServerManifest(manifest)) {
2970
+ return undefined;
2971
+ }
2972
+ return findPortableStudioServerExe(portableStudioServerReleaseDir(manifest, options.installDir), manifest);
2973
+ }
2923
2974
  function portableStudioServerReleaseDir(manifest, releasesDir = getDefaultPortableStudioServerReleasesDir()) {
2924
2975
  return join(releasesDir, safePathSegment(manifest.version));
2925
2976
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tapi-dev/sdk",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",