@tapi-dev/sdk 0.1.39 → 0.1.44

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
@@ -3,7 +3,7 @@ import { spawn } from "node:child_process";
3
3
  import { createServer } from "node:http";
4
4
  import { createServer as createNetServer } from "node:net";
5
5
  import { createHash, randomUUID } from "node:crypto";
6
- import { createReadStream, createWriteStream, existsSync, readFileSync, readdirSync, realpathSync } from "node:fs";
6
+ import { createReadStream, createWriteStream, existsSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
7
7
  import { mkdir, readFile, readdir, rename, rm, stat, statfs, unlink, writeFile } from "node:fs/promises";
8
8
  import { homedir } from "node:os";
9
9
  import { basename, dirname, join, resolve } from "node:path";
@@ -16,7 +16,8 @@ import { select } from "@inquirer/prompts";
16
16
  import { Presets, SingleBar } from "cli-progress";
17
17
  import pc from "yoctocolors";
18
18
  import { TapiClient } from "./index.js";
19
- import { loadWorkspace, normalizeProjectValue, writeWorkspaceConfig, } from "./workspace.js";
19
+ import { normalizeProjectValue, writeWorkspaceConfig, } from "./workspace.js";
20
+ import { openStudioDev } from "./studio-dev.js";
20
21
  const DEFAULT_DOWNLOADS_BASE_URL = "https://d4xaf52nfwiok.cloudfront.net";
21
22
  const DEFAULT_STUDIO_API_BASE_URL = "https://determined-motivation-production.up.railway.app";
22
23
  const DEFAULT_STUDIO_AUTH_HTML_URL = "https://rsarlong-1f92fd.gitlab.io/auth.html";
@@ -240,12 +241,6 @@ export async function runCli(argv = process.argv.slice(2)) {
240
241
  return addTappService(rest.slice(1));
241
242
  }
242
243
  }
243
- if (subcommand === "queue") {
244
- const queueCommand = rest[0] || "";
245
- if (queueCommand === "add") {
246
- return addTappQueue(rest.slice(1));
247
- }
248
- }
249
244
  console.error(`Unknown Tapp command: ${[subcommand, ...rest].filter(Boolean).join(" ")}`);
250
245
  printTappHelp();
251
246
  return 1;
@@ -270,6 +265,9 @@ export async function runCli(argv = process.argv.slice(2)) {
270
265
  if (subcommand === "setup") {
271
266
  return runRunnerSetup(rest);
272
267
  }
268
+ if (subcommand === "gui") {
269
+ return openRunnerGui(rest);
270
+ }
273
271
  if (subcommand === "slot") {
274
272
  const slotCommand = rest[0] || "";
275
273
  if (slotCommand === "set") {
@@ -328,6 +326,9 @@ export async function runCli(argv = process.argv.slice(2)) {
328
326
  printStudioHelp();
329
327
  return 0;
330
328
  }
329
+ if (subcommand === "dev") {
330
+ return openStudioDev(rest);
331
+ }
331
332
  const options = parseStudioOptions(rest);
332
333
  if (subcommand === "install") {
333
334
  return installStudio(options);
@@ -344,8 +345,6 @@ export async function runCli(argv = process.argv.slice(2)) {
344
345
  }
345
346
  export function parseStudioOptions(args) {
346
347
  const raw = {};
347
- let workspaceSearchRoot;
348
- let skipWorkspace = false;
349
348
  let projectIdSource;
350
349
  let tappSelectionMode = "auto";
351
350
  for (let index = 0; index < args.length; index += 1) {
@@ -382,16 +381,13 @@ export function parseStudioOptions(args) {
382
381
  continue;
383
382
  }
384
383
  if (arg === "--workspace") {
385
- workspaceSearchRoot = requireOptionValue(args, ++index, "--workspace");
386
- continue;
384
+ throw new Error("--workspace is retired. Use `tapi studio <tapp>` or `--tapp <id>`.");
387
385
  }
388
386
  if (arg.startsWith("--workspace=")) {
389
- workspaceSearchRoot = arg.slice("--workspace=".length);
390
- continue;
387
+ throw new Error("--workspace is retired. Use `tapi studio <tapp>` or `--tapp <id>`.");
391
388
  }
392
389
  if (arg === "--no-workspace") {
393
- skipWorkspace = true;
394
- continue;
390
+ throw new Error("--no-workspace is retired. Studio no longer reads local workspace configs.");
395
391
  }
396
392
  if (arg === "--tapp") {
397
393
  raw.projectId = normalizeProjectValue(requireOptionValue(args, ++index, "--tapp"), "tapp");
@@ -482,13 +478,11 @@ export function parseStudioOptions(args) {
482
478
  }
483
479
  throw new Error(`Unknown option: ${arg}`);
484
480
  }
485
- const workspace = skipWorkspace ? undefined : loadWorkspace(workspaceSearchRoot ?? process.cwd());
486
481
  const channel = raw.channel ?? parseChannel(envString("TAPI_STUDIO_CHANNEL") ?? DEFAULT_CHANNEL);
487
482
  const apiBaseUrl = normalizeHttpUrl(raw.apiBaseUrl
488
483
  ?? envString("TAPI_STUDIO_API_BASE_URL")
489
484
  ?? envString("TAPI_BASE_URL")
490
485
  ?? envString("TAPI_STUDIO_SERVER_URL")
491
- ?? workspace?.apiBaseUrl
492
486
  ?? DEFAULT_STUDIO_API_BASE_URL, "Studio API base URL");
493
487
  const downloadsBaseUrl = normalizeHttpUrl(envString("TAPI_DOWNLOADS_BASE_URL") ?? DEFAULT_DOWNLOADS_BASE_URL, "TAPI_DOWNLOADS_BASE_URL");
494
488
  const explicitManifestUrl = raw.manifestUrlOverride ?? envString("TAPI_STUDIO_MANIFEST_URL");
@@ -496,11 +490,11 @@ export function parseStudioOptions(args) {
496
490
  ? normalizeHttpUrl(explicitManifestUrl, "Studio manifest URL")
497
491
  : `${downloadsBaseUrl.replace(/\/+$/, "")}/studio/channels/${channel}/latest.json`;
498
492
  const envProjectId = envString("TAPI_PROJECT_ID");
499
- const projectId = raw.projectId ?? envProjectId ?? workspace?.projectId;
493
+ const projectId = raw.projectId ?? envProjectId;
500
494
  if (!projectIdSource && projectId) {
501
- projectIdSource = raw.projectId ? "option" : envProjectId ? "env" : "workspace";
495
+ projectIdSource = raw.projectId ? "option" : "env";
502
496
  }
503
- const projectSlug = raw.projectSlug ?? envString("TAPI_PROJECT_SLUG") ?? workspace?.projectSlug;
497
+ const projectSlug = raw.projectSlug ?? envString("TAPI_PROJECT_SLUG");
504
498
  const envInstallToken = envString("TAPI_STUDIO_INSTALL_TOKEN");
505
499
  const installToken = raw.installToken ?? envInstallToken;
506
500
  return {
@@ -515,20 +509,17 @@ export function parseStudioOptions(args) {
515
509
  exePath: raw.exePath ?? envString("TAPI_STUDIO_EXE"),
516
510
  installToken,
517
511
  installTokenSource: raw.installToken ? "option" : installToken ? "env" : undefined,
518
- workspace,
519
- workspaceRoot: workspace?.root,
520
512
  projectId,
521
513
  projectSlug,
522
514
  projectIdSource,
523
- workspaceMode: Boolean(projectId || workspace),
515
+ workspaceMode: Boolean(projectId),
524
516
  tappSelectionMode,
525
517
  };
526
518
  }
527
519
  function parseServiceOptions(args) {
528
520
  let operation = "";
529
- const workspace = loadWorkspace();
530
- let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
531
- let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
521
+ let apiBaseUrl = envString("TAPI_BASE_URL") || DEFAULT_STUDIO_API_BASE_URL;
522
+ let projectId = envString("TAPI_PROJECT_ID") || "";
532
523
  for (let index = 0; index < args.length; index += 1) {
533
524
  const arg = args[index];
534
525
  if (!arg)
@@ -566,7 +557,7 @@ function parseServiceOptions(args) {
566
557
  throw new Error("services describe requires a service run like schwab.place_order.");
567
558
  }
568
559
  if (!projectId) {
569
- throw new Error("services describe requires --project, TAPI_PROJECT_ID, or .tapi/project.json.");
560
+ throw new Error("services describe requires --project or TAPI_PROJECT_ID.");
570
561
  }
571
562
  return {
572
563
  operation,
@@ -596,10 +587,9 @@ async function describeServiceOperation(args) {
596
587
  }
597
588
  }
598
589
  function parseApiWorkspaceOptions(args) {
599
- const workspace = loadWorkspace();
600
- let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
601
- let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
602
- let catalogPath = workspace?.config.services?.catalog || ".tapi/services/catalog.json";
590
+ let apiBaseUrl = envString("TAPI_BASE_URL") || DEFAULT_STUDIO_API_BASE_URL;
591
+ let projectId = envString("TAPI_PROJECT_ID") || "";
592
+ let catalogPath = "tapi-services.catalog.json";
603
593
  for (let index = 0; index < args.length; index += 1) {
604
594
  const arg = args[index];
605
595
  if (!arg)
@@ -635,22 +625,18 @@ function parseApiWorkspaceOptions(args) {
635
625
  throw new Error(`Unknown services option: ${arg}`);
636
626
  }
637
627
  if (!projectId) {
638
- throw new Error("services command requires --project, TAPI_PROJECT_ID, or .tapi/project.json.");
628
+ throw new Error("services command requires --project or TAPI_PROJECT_ID.");
639
629
  }
640
- const root = workspace?.root || process.cwd();
641
630
  return {
642
631
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
643
632
  projectId,
644
- catalogPath: resolve(root, catalogPath),
633
+ catalogPath: resolve(process.cwd(), catalogPath),
645
634
  };
646
635
  }
647
636
  async function syncServiceCatalog(args) {
648
637
  try {
649
- const options = await withSdkAuth(parseApiWorkspaceOptions(args));
650
- const catalog = await withCliSpinner("Fetching Tapi service-run catalog", () => fetchApiCatalog(options));
651
- await writeJsonFile(options.catalogPath, catalog);
652
- console.log(`Synced Tapi service-run catalog: ${options.catalogPath}`);
653
- return 0;
638
+ void args;
639
+ throw new Error("services sync is retired. Service-run contracts are server-backed; use `tapi services describe <service.call>`.");
654
640
  }
655
641
  catch (error) {
656
642
  console.error(formatError(error));
@@ -658,8 +644,7 @@ async function syncServiceCatalog(args) {
658
644
  }
659
645
  }
660
646
  function parseTappServiceAddOptions(args) {
661
- const workspace = loadWorkspace();
662
- let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
647
+ let apiBaseUrl = envString("TAPI_BASE_URL") || DEFAULT_STUDIO_API_BASE_URL;
663
648
  let tappId = "";
664
649
  let serviceCall = "";
665
650
  let serviceMapId = "";
@@ -740,9 +725,8 @@ function parseTappServiceAddOptions(args) {
740
725
  };
741
726
  }
742
727
  function parseTappCreateOptions(args) {
743
- const workspace = loadWorkspace();
744
- let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
745
- let tappId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
728
+ let apiBaseUrl = envString("TAPI_BASE_URL") || DEFAULT_STUDIO_API_BASE_URL;
729
+ let tappId = envString("TAPI_PROJECT_ID") || "";
746
730
  let name = "";
747
731
  for (let index = 0; index < args.length; index += 1) {
748
732
  const arg = args[index];
@@ -862,9 +846,7 @@ async function postTappServiceAdd(options) {
862
846
  return body ?? {};
863
847
  }
864
848
  function parseQueueCreateOptions(args) {
865
- const workspace = loadWorkspace();
866
- let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
867
- let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
849
+ let apiBaseUrl = envString("TAPI_BASE_URL") || DEFAULT_STUDIO_API_BASE_URL;
868
850
  let displayName = "";
869
851
  let maxSlots = 8;
870
852
  for (let index = 0; index < args.length; index += 1) {
@@ -883,14 +865,6 @@ function parseQueueCreateOptions(args) {
883
865
  apiBaseUrl = arg.slice("--server=".length);
884
866
  continue;
885
867
  }
886
- if (arg === "--project") {
887
- projectId = normalizeProjectValue(requireOptionValue(args, ++index, "--project"), "project");
888
- continue;
889
- }
890
- if (arg.startsWith("--project=")) {
891
- projectId = normalizeProjectValue(arg.slice("--project=".length), "project");
892
- continue;
893
- }
894
868
  if (arg === "--max-slots") {
895
869
  maxSlots = parsePositiveInteger(requireOptionValue(args, ++index, "--max-slots"), "--max-slots");
896
870
  continue;
@@ -910,61 +884,12 @@ function parseQueueCreateOptions(args) {
910
884
  }
911
885
  return {
912
886
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
913
- projectId,
914
887
  displayName,
915
888
  maxSlots,
916
889
  };
917
890
  }
918
- function parseTappQueueAddOptions(args) {
919
- const workspace = loadWorkspace();
920
- let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
921
- let tappId = "";
922
- let queueId = "";
923
- for (let index = 0; index < args.length; index += 1) {
924
- const arg = args[index];
925
- if (!arg)
926
- continue;
927
- if (arg === "--api-base-url" || arg === "--server") {
928
- apiBaseUrl = requireOptionValue(args, ++index, arg);
929
- continue;
930
- }
931
- if (arg.startsWith("--api-base-url=")) {
932
- apiBaseUrl = arg.slice("--api-base-url=".length);
933
- continue;
934
- }
935
- if (arg.startsWith("--server=")) {
936
- apiBaseUrl = arg.slice("--server=".length);
937
- continue;
938
- }
939
- if (arg.startsWith("--")) {
940
- throw new Error(`Unknown tapp queue add option: ${arg}`);
941
- }
942
- if (!tappId) {
943
- tappId = normalizeProjectValue(arg, "tapp");
944
- continue;
945
- }
946
- if (!queueId) {
947
- queueId = arg.trim();
948
- continue;
949
- }
950
- throw new Error(`Unexpected tapp queue add argument: ${arg}`);
951
- }
952
- if (!tappId) {
953
- throw new Error("tapp queue add requires a Tapp id.");
954
- }
955
- if (!queueId) {
956
- throw new Error("tapp queue add requires a queue id.");
957
- }
958
- return {
959
- apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
960
- tappId,
961
- queueId,
962
- };
963
- }
964
891
  function parseRunnerSlotSetOptions(args) {
965
- const workspace = loadWorkspace();
966
- let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
967
- let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
892
+ let apiBaseUrl = envString("TAPI_BASE_URL") || DEFAULT_STUDIO_API_BASE_URL;
968
893
  let runnerId = "";
969
894
  let slotIndex = 0;
970
895
  let queueId = "";
@@ -984,14 +909,6 @@ function parseRunnerSlotSetOptions(args) {
984
909
  apiBaseUrl = arg.slice("--server=".length);
985
910
  continue;
986
911
  }
987
- if (arg === "--project") {
988
- projectId = normalizeProjectValue(requireOptionValue(args, ++index, "--project"), "project");
989
- continue;
990
- }
991
- if (arg.startsWith("--project=")) {
992
- projectId = normalizeProjectValue(arg.slice("--project=".length), "project");
993
- continue;
994
- }
995
912
  if (arg.startsWith("--")) {
996
913
  throw new Error(`Unknown runner slot set option: ${arg}`);
997
914
  }
@@ -1009,9 +926,6 @@ function parseRunnerSlotSetOptions(args) {
1009
926
  }
1010
927
  throw new Error(`Unexpected runner slot set argument: ${arg}`);
1011
928
  }
1012
- if (!projectId) {
1013
- throw new Error("runner slot set requires --project or TAPI_PROJECT_ID.");
1014
- }
1015
929
  if (!runnerId) {
1016
930
  throw new Error("runner slot set requires a runner id.");
1017
931
  }
@@ -1023,7 +937,6 @@ function parseRunnerSlotSetOptions(args) {
1023
937
  }
1024
938
  return {
1025
939
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
1026
- projectId,
1027
940
  runnerId,
1028
941
  slotIndex,
1029
942
  queueId,
@@ -1041,18 +954,6 @@ async function createQueue(args) {
1041
954
  return 1;
1042
955
  }
1043
956
  }
1044
- async function addTappQueue(args) {
1045
- try {
1046
- const options = await withSdkAuth(parseTappQueueAddOptions(args));
1047
- const result = await withCliSpinner(`Attaching Tapi queue ${options.queueId} to ${options.tappId}`, () => postTappQueueAdd(options));
1048
- console.log(JSON.stringify(result, null, 2));
1049
- return 0;
1050
- }
1051
- catch (error) {
1052
- console.error(formatError(error));
1053
- return 1;
1054
- }
1055
- }
1056
957
  async function setRunnerSlot(args) {
1057
958
  try {
1058
959
  const options = await withSdkAuth(parseRunnerSlotSetOptions(args));
@@ -1066,7 +967,7 @@ async function setRunnerSlot(args) {
1066
967
  }
1067
968
  }
1068
969
  async function postQueueCreate(options) {
1069
- const headers = sdkJsonHeaders(options.authToken, options.projectId);
970
+ const headers = sdkJsonHeaders(options.authToken);
1070
971
  const response = await fetch(`${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/queues`, {
1071
972
  method: "POST",
1072
973
  headers,
@@ -1082,23 +983,10 @@ async function postQueueCreate(options) {
1082
983
  }
1083
984
  return body ?? {};
1084
985
  }
1085
- async function postTappQueueAdd(options) {
1086
- const response = await fetch(`${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/tapps/${encodeURIComponent(options.tappId)}/queues`, {
1087
- method: "POST",
1088
- headers: sdkJsonHeaders(options.authToken, options.tappId),
1089
- body: JSON.stringify({ queueId: options.queueId }),
1090
- });
1091
- const body = await readJsonBody(response);
1092
- if (!response.ok) {
1093
- const detail = body?.detail ?? body?.error ?? body ?? `HTTP ${response.status}`;
1094
- throw new Error(typeof detail === "string" ? detail : JSON.stringify(detail));
1095
- }
1096
- return body ?? {};
1097
- }
1098
986
  async function putRunnerSlot(options) {
1099
987
  const response = await fetch(`${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/runners/${encodeURIComponent(options.runnerId)}/slots/${encodeURIComponent(String(options.slotIndex))}`, {
1100
988
  method: "PUT",
1101
- headers: sdkJsonHeaders(options.authToken, options.projectId),
989
+ headers: sdkJsonHeaders(options.authToken),
1102
990
  body: JSON.stringify({ queueId: options.queueId }),
1103
991
  });
1104
992
  const body = await readJsonBody(response);
@@ -1109,9 +997,7 @@ async function putRunnerSlot(options) {
1109
997
  return body ?? {};
1110
998
  }
1111
999
  export function parseRunnerSetupOptions(args) {
1112
- const workspace = loadWorkspace();
1113
- let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
1114
- let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
1000
+ let apiBaseUrl = envString("TAPI_BASE_URL") || DEFAULT_STUDIO_API_BASE_URL;
1115
1001
  let runnerId = envString("TAPI_RUNNER_ID") || "";
1116
1002
  let port = Number(envString("TAPI_RUNNER_SETUP_PORT") || "17687");
1117
1003
  let openBrowser = true;
@@ -1131,14 +1017,6 @@ export function parseRunnerSetupOptions(args) {
1131
1017
  apiBaseUrl = arg.slice("--server=".length);
1132
1018
  continue;
1133
1019
  }
1134
- if (arg === "--project") {
1135
- projectId = normalizeProjectValue(requireOptionValue(args, ++index, "--project"), "project");
1136
- continue;
1137
- }
1138
- if (arg.startsWith("--project=")) {
1139
- projectId = normalizeProjectValue(arg.slice("--project=".length), "project");
1140
- continue;
1141
- }
1142
1020
  if (arg === "--runner-id") {
1143
1021
  runnerId = requireOptionValue(args, ++index, "--runner-id").trim();
1144
1022
  continue;
@@ -1168,9 +1046,6 @@ export function parseRunnerSetupOptions(args) {
1168
1046
  }
1169
1047
  throw new Error(`Unexpected runner setup argument: ${arg}`);
1170
1048
  }
1171
- if (!projectId) {
1172
- throw new Error("runner setup requires --project or TAPI_PROJECT_ID.");
1173
- }
1174
1049
  if (!runnerId) {
1175
1050
  throw new Error("runner setup requires --runner-id or TAPI_RUNNER_ID.");
1176
1051
  }
@@ -1179,7 +1054,6 @@ export function parseRunnerSetupOptions(args) {
1179
1054
  }
1180
1055
  return {
1181
1056
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
1182
- projectId,
1183
1057
  runnerId,
1184
1058
  port,
1185
1059
  openBrowser,
@@ -1203,6 +1077,78 @@ async function runRunnerSetup(args) {
1203
1077
  return 1;
1204
1078
  }
1205
1079
  }
1080
+ async function openRunnerGui(args) {
1081
+ try {
1082
+ if (hasHelpFlag(args)) {
1083
+ printRunnerHelp();
1084
+ return 0;
1085
+ }
1086
+ if (args.some((arg) => arg && arg.startsWith("-"))) {
1087
+ throw new Error(`Unknown runner gui option: ${args.find((arg) => arg.startsWith("-"))}`);
1088
+ }
1089
+ const executable = findInstalledRunnerGui();
1090
+ if (!executable) {
1091
+ throw new Error("Tapi Runner GUI is not installed. Run `tapi service install --channel pilot`, then retry.");
1092
+ }
1093
+ const child = spawn(executable, [], {
1094
+ detached: true,
1095
+ stdio: "ignore",
1096
+ windowsHide: false,
1097
+ });
1098
+ child.unref();
1099
+ console.log(`Opened Tapi Runner: ${executable}`);
1100
+ return 0;
1101
+ }
1102
+ catch (error) {
1103
+ console.error(formatError(error));
1104
+ return 1;
1105
+ }
1106
+ }
1107
+ function findInstalledRunnerGui() {
1108
+ const explicit = envString("TAPI_RUNNER_GUI_PATH");
1109
+ if (explicit && existsSync(explicit)) {
1110
+ return explicit;
1111
+ }
1112
+ const candidates = [];
1113
+ const localCandidate = join(getDefaultTapiDataDir(), "Service", "releases");
1114
+ const programData = envString("ProgramData") || "C:\\ProgramData";
1115
+ const roots = [
1116
+ join(programData, "Tapi", "Service", "releases"),
1117
+ localCandidate,
1118
+ resolve(process.cwd(), "build", "v2-processes", "runtime"),
1119
+ ];
1120
+ for (const root of roots) {
1121
+ if (!existsSync(root))
1122
+ continue;
1123
+ const direct = join(root, "tapi-runner-gui.exe");
1124
+ if (existsSync(direct)) {
1125
+ candidates.push(direct);
1126
+ }
1127
+ let entries = [];
1128
+ try {
1129
+ entries = readdirSync(root);
1130
+ }
1131
+ catch {
1132
+ continue;
1133
+ }
1134
+ for (const entry of entries) {
1135
+ const nested = join(root, String(entry), "tapi-runner-gui.exe");
1136
+ if (existsSync(nested)) {
1137
+ candidates.push(nested);
1138
+ }
1139
+ }
1140
+ }
1141
+ const unique = Array.from(new Set(candidates));
1142
+ unique.sort((left, right) => {
1143
+ try {
1144
+ return statSync(right).mtimeMs - statSync(left).mtimeMs;
1145
+ }
1146
+ catch {
1147
+ return 0;
1148
+ }
1149
+ });
1150
+ return unique[0] || "";
1151
+ }
1206
1152
  export async function startRunnerSetupServer(options, fetchImpl = fetch) {
1207
1153
  const server = createServer((request, response) => {
1208
1154
  void handleRunnerSetupRequest(request, response, options, fetchImpl);
@@ -1225,9 +1171,9 @@ export async function executeRunnerSetupAction(options, action, fetchImpl = fetc
1225
1171
  if (!queueId) {
1226
1172
  queue = await sdkJsonRequest(fetchImpl, `${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/queues`, {
1227
1173
  method: "POST",
1228
- headers: sdkJsonHeaders(options.authToken, options.projectId),
1174
+ headers: sdkJsonHeaders(options.authToken),
1229
1175
  body: JSON.stringify({
1230
- displayName: String(action.displayName || `${options.projectId} queue`).trim(),
1176
+ displayName: String(action.displayName || `${options.runnerId} queue`).trim(),
1231
1177
  maxSlots,
1232
1178
  }),
1233
1179
  });
@@ -1236,24 +1182,18 @@ export async function executeRunnerSetupAction(options, action, fetchImpl = fetc
1236
1182
  if (!queueId) {
1237
1183
  throw new Error("Runner setup needs an existing queue id or a created queue response with queueId.");
1238
1184
  }
1239
- const attachment = await sdkJsonRequest(fetchImpl, `${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/tapps/${encodeURIComponent(options.projectId)}/queues`, {
1240
- method: "POST",
1241
- headers: sdkJsonHeaders(options.authToken, options.projectId),
1242
- body: JSON.stringify({ queueId }),
1243
- });
1244
1185
  const slots = [];
1245
1186
  for (let offset = 0; offset < slotCount; offset += 1) {
1246
1187
  const slotIndex = slotStart + offset;
1247
1188
  slots.push(await sdkJsonRequest(fetchImpl, `${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/runners/${encodeURIComponent(options.runnerId)}/slots/${encodeURIComponent(String(slotIndex))}`, {
1248
1189
  method: "PUT",
1249
- headers: sdkJsonHeaders(options.authToken, options.projectId),
1190
+ headers: sdkJsonHeaders(options.authToken),
1250
1191
  body: JSON.stringify({ queueId }),
1251
1192
  }));
1252
1193
  }
1253
1194
  return {
1254
1195
  queueId,
1255
1196
  queue: queueId && !Object.keys(queue).length ? { queueId } : queue,
1256
- attachment,
1257
1197
  slots,
1258
1198
  };
1259
1199
  }
@@ -1267,7 +1207,6 @@ async function handleRunnerSetupRequest(request, response, options, fetchImpl) {
1267
1207
  if (request.method === "GET" && url.pathname === "/api/status") {
1268
1208
  sendJson(response, 200, {
1269
1209
  apiBaseUrl: options.apiBaseUrl,
1270
- projectId: options.projectId,
1271
1210
  runnerId: options.runnerId,
1272
1211
  });
1273
1212
  return;
@@ -1285,7 +1224,7 @@ async function handleRunnerSetupRequest(request, response, options, fetchImpl) {
1285
1224
  }
1286
1225
  }
1287
1226
  function runnerSetupHtml(options) {
1288
- const displayName = `${options.projectId} queue`;
1227
+ const displayName = `${options.runnerId} queue`;
1289
1228
  return `<!doctype html>
1290
1229
  <html lang="en">
1291
1230
  <head>
@@ -1339,7 +1278,6 @@ code { word-break: break-all; }
1339
1278
  <h2>Target</h2>
1340
1279
  <dl>
1341
1280
  <dt>Server</dt><dd><code>${escapeHtml(options.apiBaseUrl)}</code></dd>
1342
- <dt>Tapp</dt><dd><code>${escapeHtml(options.projectId)}</code></dd>
1343
1281
  <dt>Runner</dt><dd><code>${escapeHtml(options.runnerId)}</code></dd>
1344
1282
  </dl>
1345
1283
  </aside>
@@ -1425,11 +1363,10 @@ function parseNonNegativeInteger(value, label) {
1425
1363
  return parsed;
1426
1364
  }
1427
1365
  function parseTriggerSyncOptions(args) {
1428
- const workspace = loadWorkspace();
1429
- let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
1430
- let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
1366
+ let apiBaseUrl = envString("TAPI_BASE_URL") || DEFAULT_STUDIO_API_BASE_URL;
1367
+ let projectId = envString("TAPI_PROJECT_ID") || "";
1431
1368
  let configPath = "";
1432
- const root = workspace?.root || process.cwd();
1369
+ const root = process.cwd();
1433
1370
  for (let index = 0; index < args.length; index += 1) {
1434
1371
  const arg = args[index];
1435
1372
  if (!arg)
@@ -1465,7 +1402,7 @@ function parseTriggerSyncOptions(args) {
1465
1402
  throw new Error(`Unknown triggers option: ${arg}`);
1466
1403
  }
1467
1404
  if (!projectId) {
1468
- throw new Error("triggers sync requires --project, TAPI_PROJECT_ID, or .tapi/project.json.");
1405
+ throw new Error("triggers sync requires --project or TAPI_PROJECT_ID.");
1469
1406
  }
1470
1407
  return {
1471
1408
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
@@ -1500,9 +1437,8 @@ async function syncServiceTriggers(args) {
1500
1437
  }
1501
1438
  }
1502
1439
  function parseSessionsOptions(args) {
1503
- const workspace = loadWorkspace();
1504
- let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
1505
- let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
1440
+ let apiBaseUrl = envString("TAPI_BASE_URL") || DEFAULT_STUDIO_API_BASE_URL;
1441
+ let projectId = envString("TAPI_PROJECT_ID") || "";
1506
1442
  let site = "";
1507
1443
  let sessionId = "";
1508
1444
  let json = false;
@@ -1558,7 +1494,7 @@ function parseSessionsOptions(args) {
1558
1494
  throw new Error(`Unexpected sessions argument: ${arg}`);
1559
1495
  }
1560
1496
  if (!projectId) {
1561
- throw new Error("sessions command requires --project, TAPI_PROJECT_ID, or .tapi/project.json.");
1497
+ throw new Error("sessions command requires --project or TAPI_PROJECT_ID.");
1562
1498
  }
1563
1499
  return {
1564
1500
  sessionId,
@@ -3026,7 +2962,7 @@ async function openStudio(options) {
3026
2962
  console.log(`Opened Tapi Studio: ${exePath}`);
3027
2963
  return 0;
3028
2964
  }
3029
- async function resolveStudioTappSelection(options) {
2965
+ export async function resolveStudioTappSelection(options) {
3030
2966
  if (options.tappSelectionMode === "none") {
3031
2967
  return options.projectId ? await rememberSelectedStudioTapp(options, options.projectId) : options;
3032
2968
  }
@@ -3040,22 +2976,9 @@ async function resolveStudioTappSelection(options) {
3040
2976
  }
3041
2977
  return await rememberSelectedStudioTapp(options, lastSelection.lastTappId);
3042
2978
  }
3043
- let tapps = [];
3044
- try {
3045
- const authenticated = await withSdkAuth({ authToken: undefined });
3046
- tapps = await fetchAvailableTappsForStudio(options, authenticated.authToken);
3047
- }
3048
- catch (error) {
3049
- if (options.projectId) {
3050
- console.warn(`Could not load Tapp list (${formatError(error)}). Opening Studio with ${options.projectId}.`);
3051
- return await rememberSelectedStudioTapp(options, options.projectId);
3052
- }
3053
- throw error;
3054
- }
2979
+ const authenticated = await withSdkAuth({ authToken: undefined });
2980
+ const tapps = await fetchAvailableTappsForStudio(options, authenticated.authToken);
3055
2981
  if (!tapps.length) {
3056
- if (options.projectId) {
3057
- return await rememberSelectedStudioTapp(options, options.projectId);
3058
- }
3059
2982
  throw new Error("No Tapps found for this account. Create one with `tapi tapp create <name>`, then run `tapi studio`.");
3060
2983
  }
3061
2984
  const preferred = preferredStudioTappId(options, lastSelection, tapps);
@@ -3106,7 +3029,6 @@ function normalizeTappList(body) {
3106
3029
  tappId,
3107
3030
  name: typeof item.name === "string" && item.name.trim() ? item.name.trim() : tappId,
3108
3031
  serviceCount: normalizeNonnegativeInteger(item.serviceCount),
3109
- queueCount: normalizeNonnegativeInteger(item.queueCount),
3110
3032
  });
3111
3033
  }
3112
3034
  return tapps;
@@ -3127,7 +3049,6 @@ function preferredStudioTappId(options, lastSelection, tapps) {
3127
3049
  function formatTappChoice(tapp) {
3128
3050
  const details = [
3129
3051
  `${tapp.serviceCount} service${tapp.serviceCount === 1 ? "" : "s"}`,
3130
- `${tapp.queueCount} queue${tapp.queueCount === 1 ? "" : "s"}`,
3131
3052
  ].join(", ");
3132
3053
  return tapp.name === tapp.tappId
3133
3054
  ? `${tapp.tappId} ${pc.dim(`(${details})`)}`
@@ -3311,7 +3232,7 @@ async function launchPortableStudioServer(exePath, options, manifest) {
3311
3232
  const launchUrl = studioLaunchUrl(url, options);
3312
3233
  let childPid = 0;
3313
3234
  await withCliSpinner("Starting Tapi Studio", async () => {
3314
- const child = spawn(exePath, [], {
3235
+ const child = spawn(exePath, studioServerLaunchArgs(), {
3315
3236
  detached: true,
3316
3237
  env,
3317
3238
  stdio: "ignore",
@@ -3330,14 +3251,15 @@ async function launchPortableStudioServer(exePath, options, manifest) {
3330
3251
  apiBaseUrl: options.apiBaseUrl,
3331
3252
  projectId: options.projectId,
3332
3253
  tappId: options.projectId,
3333
- workspaceRoot: options.workspaceRoot,
3334
- workspaceConfig: options.workspace?.configPath,
3335
3254
  manifestVersion: manifest.version,
3336
3255
  updatedAt: new Date().toISOString(),
3337
3256
  });
3338
3257
  openBrowser(launchUrl);
3339
3258
  console.log(`Opened Tapi Studio ${manifest.version}: ${launchUrl}`);
3340
3259
  }
3260
+ export function studioServerLaunchArgs() {
3261
+ return ["--no-open-browser"];
3262
+ }
3341
3263
  function studioLaunchUrl(baseUrl, options) {
3342
3264
  const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
3343
3265
  const launchQuery = options.launchUrlQuery ? options.launchUrlQuery.replace(/^\?+/, "") : "";
@@ -3496,7 +3418,6 @@ async function runDoctor(options) {
3496
3418
  console.log(`Studio channel: ${options.channel}`);
3497
3419
  console.log(`Studio manifest: ${options.manifestUrl}`);
3498
3420
  console.log(`Studio cache: ${options.cacheDir}`);
3499
- console.log(`Workspace: ${options.workspaceRoot ?? "not found"}`);
3500
3421
  console.log(`Project: ${options.projectId ?? "not set"}`);
3501
3422
  try {
3502
3423
  const manifest = await withCliSpinner("Fetching latest Tapi Studio manifest", () => fetchStudioManifest(options.manifestUrl));
@@ -3517,12 +3438,6 @@ function buildStudioLaunchEnv(options) {
3517
3438
  const env = { ...process.env };
3518
3439
  env.TAPI_STUDIO_API_BASE_URL = options.apiBaseUrl;
3519
3440
  env.TAPI_BASE_URL = env.TAPI_BASE_URL || options.apiBaseUrl;
3520
- if (options.workspaceRoot) {
3521
- env.TAPI_WORKSPACE_ROOT = options.workspaceRoot;
3522
- }
3523
- if (options.workspace?.configPath) {
3524
- env.TAPI_WORKSPACE_CONFIG = options.workspace.configPath;
3525
- }
3526
3441
  if (options.projectId) {
3527
3442
  env.TAPI_PROJECT_ID = options.projectId;
3528
3443
  }
@@ -3903,7 +3818,6 @@ export function studioInstanceRecordPathForOptions(options) {
3903
3818
  .update(JSON.stringify({
3904
3819
  apiBaseUrl: comparableHttpUrl(options.apiBaseUrl),
3905
3820
  projectId: options.projectId ?? "",
3906
- workspaceRoot: comparableFsPath(options.workspaceRoot ?? ""),
3907
3821
  }))
3908
3822
  .digest("hex")
3909
3823
  .slice(0, 24);
@@ -3965,8 +3879,6 @@ async function readStudioInstanceRecord(options) {
3965
3879
  apiBaseUrl: typeof parsed.apiBaseUrl === "string" ? parsed.apiBaseUrl : "",
3966
3880
  projectId: typeof parsed.projectId === "string" ? parsed.projectId : undefined,
3967
3881
  tappId: typeof parsed.tappId === "string" ? parsed.tappId : undefined,
3968
- workspaceRoot: typeof parsed.workspaceRoot === "string" ? parsed.workspaceRoot : undefined,
3969
- workspaceConfig: typeof parsed.workspaceConfig === "string" ? parsed.workspaceConfig : undefined,
3970
3882
  manifestVersion: typeof parsed.manifestVersion === "string" ? parsed.manifestVersion : undefined,
3971
3883
  updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : "",
3972
3884
  };
@@ -3987,11 +3899,6 @@ function studioInstanceIdentityMatches(identity, options) {
3987
3899
  if (expectedProject && actualProject && actualProject !== expectedProject) {
3988
3900
  return false;
3989
3901
  }
3990
- const expectedWorkspace = comparableFsPath(options.workspaceRoot ?? "");
3991
- const actualWorkspace = comparableFsPath(typeof identity.workspaceRoot === "string" ? identity.workspaceRoot : "");
3992
- if (expectedWorkspace && actualWorkspace && actualWorkspace !== expectedWorkspace) {
3993
- return false;
3994
- }
3995
3902
  const expectedApiBaseUrl = comparableHttpUrl(options.apiBaseUrl);
3996
3903
  const actualApiBaseUrl = comparableHttpUrl(typeof identity.apiBaseUrl === "string" ? identity.apiBaseUrl : "");
3997
3904
  if (expectedApiBaseUrl && actualApiBaseUrl && actualApiBaseUrl !== expectedApiBaseUrl) {
@@ -4002,10 +3909,6 @@ function studioInstanceIdentityMatches(identity, options) {
4002
3909
  function comparableHttpUrl(value) {
4003
3910
  return String(value || "").trim().replace(/\/+$/, "").toLowerCase();
4004
3911
  }
4005
- function comparableFsPath(value) {
4006
- const trimmed = String(value || "").trim();
4007
- return trimmed ? resolve(trimmed).toLowerCase() : "";
4008
- }
4009
3912
  function getDefaultTapiDataDir() {
4010
3913
  if (process.platform === "win32") {
4011
3914
  const localAppData = process.env.LOCALAPPDATA
@@ -4516,7 +4419,9 @@ async function isTapiManagedReleaseDir(directory) {
4516
4419
  return Array.isArray(entries) && entries.length === 0;
4517
4420
  }
4518
4421
  function looksLikeTapiServiceReleaseDir(directory) {
4519
- return (existsSync(join(directory, "tapi-service-host.exe"))
4422
+ const hasServiceHost = existsSync(join(directory, "tapi-service-host.exe"))
4423
+ || existsSync(join(directory, "tapi-service-host", "tapi-service-host.exe"));
4424
+ return (hasServiceHost
4520
4425
  && existsSync(join(directory, "install_tapi_service.ps1"))
4521
4426
  && (existsSync(join(directory, "tapi-service-worker.exe"))
4522
4427
  || existsSync(join(directory, "tapi-service-worker", "tapi-service-worker.exe"))));
@@ -4703,10 +4608,9 @@ function printHelp() {
4703
4608
 
4704
4609
  Usage:
4705
4610
  tapi login
4706
- tapi init --project PROJECT
4707
- tapi link --project PROJECT
4708
4611
  tapi studio install [--channel pilot] [--api-base-url URL]
4709
4612
  tapi studio
4613
+ tapi studio dev --tapp <tapp>
4710
4614
  tapi studio --tapp <tapp>
4711
4615
  tapi studio --select
4712
4616
  tapi studio open
@@ -4714,20 +4618,19 @@ Usage:
4714
4618
  tapi tapp create <tapp>
4715
4619
  tapi queue create [display-name]
4716
4620
  tapi tapp service add <tapp> <service.call> --service-map ID --entry ENTRY
4717
- tapi tapp queue add <tapp> <queue-id>
4718
- tapi runner setup --runner-id RUNNER --project TAPP
4621
+ tapi runner gui
4622
+ tapi runner setup --runner-id RUNNER
4719
4623
  tapi runner slot set <runner-id> <slot-index> <queue-id>
4720
4624
  tapi services describe <servicemap.run>
4721
- tapi services sync
4722
- tapi triggers sync
4625
+ tapi triggers sync
4723
4626
  tapi sessions
4724
4627
  tapi service status
4725
4628
  tapi doctor
4726
4629
 
4727
4630
  Commands:
4728
4631
  login Sign in with Firebase credentials for SDK commands
4729
- init Create .tapi/project.json for this repo
4730
- link Rebind this repo to an existing Tapi project
4632
+ init Retired; use tapp create/login/studio instead
4633
+ link Retired; use explicit Tapp commands instead
4731
4634
  studio install Download, verify, and run the Tapi Studio installer
4732
4635
  studio Open Tapi Studio for a selected Tapp
4733
4636
  studio open Open Tapi Studio for a selected Tapp
@@ -4736,12 +4639,12 @@ Commands:
4736
4639
  queue create Create a queue for service-run routing
4737
4640
  tapp service add
4738
4641
  Add a ServiceMap-backed service call to a Tapp
4739
- tapp queue add Attach a queue to a Tapp
4740
- runner setup Open the local runner queue/slot setup UI
4642
+ runner gui Open the installed desktop Runner app
4643
+ runner setup Open the legacy local browser queue/slot setup UI
4741
4644
  runner slot set Assign a queue to a runner slot
4742
4645
  services describe
4743
4646
  Print a ServiceMap service-run input/output contract
4744
- services sync Save the service-run catalog to .tapi/services
4647
+ services sync Retired; service-run contracts are server-backed
4745
4648
  triggers sync Upsert service triggers from tapi.config
4746
4649
  sessions List dev-mode API sessions and open takeover sessions
4747
4650
  service Inspect or control the local Tapi Windows service
@@ -4763,7 +4666,6 @@ function printTappHelp() {
4763
4666
  Usage:
4764
4667
  tapi tapp create <tapp> [--name NAME] [--api-base-url URL]
4765
4668
  tapi tapp service add <tapp> <service.call> --service-map ID --entry ENTRY [--api-base-url URL]
4766
- tapi tapp queue add <tapp> <queue-id> [--api-base-url URL]
4767
4669
 
4768
4670
  Options:
4769
4671
  --api-base-url <url> Tapi API base URL
@@ -4779,12 +4681,11 @@ function printQueueHelp() {
4779
4681
  console.log(`Tapi queue commands
4780
4682
 
4781
4683
  Usage:
4782
- tapi queue create [display-name] [--max-slots N] [--project TAPP] [--api-base-url URL]
4684
+ tapi queue create [display-name] [--max-slots N] [--api-base-url URL]
4783
4685
 
4784
4686
  Options:
4785
4687
  --api-base-url <url> Tapi API base URL
4786
4688
  --server <url> Alias for --api-base-url
4787
- --project <id> Optional project scope for the queue request
4788
4689
  --max-slots <n> Maximum runner slots this queue may use
4789
4690
  `);
4790
4691
  }
@@ -4792,16 +4693,20 @@ function printRunnerHelp() {
4792
4693
  console.log(`Tapi runner commands
4793
4694
 
4794
4695
  Usage:
4795
- tapi runner setup --runner-id RUNNER --project TAPP [--port PORT] [--no-open] [--api-base-url URL]
4796
- tapi runner slot set <runner-id> <slot-index> <queue-id> [--project TAPP] [--api-base-url URL]
4696
+ tapi runner gui
4697
+ tapi runner setup --runner-id RUNNER [--port PORT] [--no-open] [--api-base-url URL]
4698
+ tapi runner slot set <runner-id> <slot-index> <queue-id> [--api-base-url URL]
4797
4699
 
4798
4700
  Options:
4799
4701
  --api-base-url <url> Tapi API base URL
4800
4702
  --server <url> Alias for --api-base-url
4801
- --project <id> Tapi project id; defaults to TAPI_PROJECT_ID or .tapi/project.json
4802
4703
  --runner-id <id> Runner id for setup; defaults to TAPI_RUNNER_ID
4803
4704
  --port <n> Local setup UI port; defaults to 17687
4804
4705
  --no-open Print the setup URL without opening a browser
4706
+
4707
+ Notes:
4708
+ The normal Windows path is the Tapi Runner desktop app. The setup command is
4709
+ kept for development and automation fallback.
4805
4710
  `);
4806
4711
  }
4807
4712
  function printSessionsHelp() {
@@ -4815,7 +4720,7 @@ Usage:
4815
4720
  Options:
4816
4721
  --api-base-url <url> Tapi API base URL
4817
4722
  --server <url> Alias for --api-base-url
4818
- --project <id> Tapi project id; defaults to TAPI_PROJECT_ID or .tapi/project.json
4723
+ --project <id> Tapi project id; defaults to TAPI_PROJECT_ID
4819
4724
  --site <name> Limit sessions to one sitemap/site
4820
4725
  --json Print raw grouped session JSON
4821
4726
  --no-interactive Print the grouped list without the arrow-key picker
@@ -4826,13 +4731,12 @@ function printServicesHelp() {
4826
4731
 
4827
4732
  Usage:
4828
4733
  tapi services describe <servicemap.run> [--api-base-url URL] [--project PROJECT]
4829
- tapi services sync [--api-base-url URL] [--project PROJECT]
4734
+ tapi services sync
4830
4735
 
4831
4736
  Options:
4832
4737
  --api-base-url <url> Tapi API base URL
4833
4738
  --server <url> Alias for --api-base-url
4834
- --project <id> Tapi project id; defaults to TAPI_PROJECT_ID or .tapi/project.json
4835
- --catalog <path> Catalog JSON output path
4739
+ --project <id> Tapi project id; defaults to TAPI_PROJECT_ID
4836
4740
  `);
4837
4741
  }
4838
4742
  function printTriggersHelp() {
@@ -4842,10 +4746,10 @@ Usage:
4842
4746
  tapi triggers sync [--config FILE] [--api-base-url URL] [--project PROJECT]
4843
4747
 
4844
4748
  Options:
4845
- --config <path> Trigger config path; defaults to tapi.config.ts/js/json in the workspace root
4749
+ --config <path> Trigger config path; defaults to tapi.config.ts/js/json in the current directory
4846
4750
  --api-base-url <url> Tapi API base URL
4847
4751
  --server <url> Alias for --api-base-url
4848
- --project <id> Tapi project id; defaults to TAPI_PROJECT_ID or .tapi/project.json
4752
+ --project <id> Tapi project id; defaults to TAPI_PROJECT_ID
4849
4753
 
4850
4754
  Config:
4851
4755
  export default {
@@ -4864,8 +4768,9 @@ function printStudioHelp() {
4864
4768
  console.log(`Tapi Studio commands
4865
4769
 
4866
4770
  Usage:
4867
- tapi studio [options]
4868
- tapi studio install [options]
4771
+ tapi studio [options]
4772
+ tapi studio dev --tapp <tapp> [options]
4773
+ tapi studio install [options]
4869
4774
  tapi studio open [options]
4870
4775
  tapi studio doctor [options]
4871
4776
 
@@ -4876,9 +4781,7 @@ Options:
4876
4781
  --tapp <id> Open this Tapp directly
4877
4782
  --select Force the account Tapp picker
4878
4783
  --last Reuse the last selected Tapp
4879
- --no-select Skip the picker and use env/workspace/default context
4880
- --workspace <path> Search this directory for .tapi/project.json
4881
- --no-workspace Do not load .tapi/project.json
4784
+ --no-select Skip the picker and use explicit/env Tapp context
4882
4785
  --project <id> Alias for --tapp
4883
4786
  --project-slug <slug> Optional display slug for the bound project
4884
4787
  --install-token <tok> Preissued Studio install token (skips browser sign-in)
@@ -4891,18 +4794,16 @@ Options:
4891
4794
  `);
4892
4795
  }
4893
4796
  function printWorkspaceHelp(command) {
4894
- console.log(`Tapi workspace ${command}
4895
-
4896
- Usage:
4897
- tapi ${command} --project PROJECT [--api-base-url URL] [--root PATH] [--force]
4898
-
4899
- Options:
4900
- --project <id> Tapi project id/name to bind this repo to
4901
- --project-slug <slug> Optional display slug
4902
- --api-base-url <url> Tapi API base URL stored in .tapi/project.json
4903
- --server <url> Alias for --api-base-url
4904
- --root <path> Directory where .tapi/project.json should be written
4905
- --force Overwrite an existing workspace config
4797
+ console.log(`Tapi workspace ${command} is retired
4798
+
4799
+ Usage:
4800
+ tapi tapp create <tapp>
4801
+ tapi login
4802
+ tapi studio <tapp>
4803
+
4804
+ Options:
4805
+ --api-base-url <url> Tapi API base URL
4806
+ --server <url> Alias for --api-base-url
4906
4807
  `);
4907
4808
  }
4908
4809
  function printServiceHelp() {