@tapi-dev/sdk 0.1.36 → 0.1.38

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/README.md CHANGED
@@ -52,9 +52,10 @@ The current artifact and current release are kept.
52
52
 
53
53
  Useful commands:
54
54
 
55
- ```bash
56
- tapi init --project brokerage
57
- tapi link --project brokerage
55
+ ```bash
56
+ tapi login
57
+ tapi init --project brokerage
58
+ tapi link --project brokerage
58
59
  tapi studio
59
60
  tapi service install --channel pilot
60
61
  tapi service status
@@ -80,8 +81,8 @@ ServiceMaps and service-run contracts are saved in the bound Tapi server
80
81
  project. Runtime SDK calls read the service catalog from that project.
81
82
 
82
83
  ```bash
83
- TAPI_API_KEY=tapi_project_key tapi tapp create brokerage --name "Brokerage"
84
- TAPI_API_KEY=tapi_project_key tapi tapp service add brokerage schwab.place_order \
84
+ tapi tapp create brokerage --name "Brokerage"
85
+ tapi tapp service add brokerage schwab.place_order \
85
86
  --service-map sm_123 \
86
87
  --entry place_order
87
88
  ```
@@ -102,23 +103,23 @@ page that can create or accept a queue id, attach that queue to the Tapp, and
102
103
  assign one or more slots on the runner:
103
104
 
104
105
  ```bash
105
- TAPI_API_KEY=tapi_project_key tapi runner setup \
106
+ tapi runner setup \
106
107
  --runner-id john-laptop \
107
108
  --project brokerage
108
109
  ```
109
110
 
110
111
  ## Quick Start
111
112
 
112
- Create one TAPI client in server-side app code. Use a developer API key, the
113
- Tapp id that owns the service call, and optionally the same project id from
114
- `.tapi/project.json`.
113
+ Create one TAPI client in server-side app code. Use a Firebase ID token for the
114
+ developer identity that owns and pays for the Tapp, the Tapp id that owns the
115
+ service call, and optionally the same project id from `.tapi/project.json`.
115
116
 
116
117
  ```ts
117
118
  import { TapiClient } from "@tapi-dev/sdk";
118
119
 
119
120
  export const tapi = new TapiClient({
120
121
  baseUrl: process.env.TAPI_BASE_URL!,
121
- apiKey: process.env.TAPI_API_KEY!,
122
+ authToken: process.env.TAPI_FIREBASE_ID_TOKEN!,
122
123
  tappId: process.env.TAPI_TAPP_ID!,
123
124
  projectId: process.env.TAPI_PROJECT_ID!,
124
125
  });
@@ -130,7 +131,7 @@ holding a browser open:
130
131
  ```ts
131
132
  const tapi = new TapiClient({
132
133
  baseUrl: process.env.TAPI_BASE_URL!,
133
- apiKey: process.env.TAPI_API_KEY!,
134
+ authToken: process.env.TAPI_FIREBASE_ID_TOKEN!,
134
135
  tappId: process.env.TAPI_TAPP_ID!,
135
136
  projectId: process.env.TAPI_PROJECT_ID!,
136
137
  dev: false,
@@ -144,7 +145,7 @@ production failure:
144
145
  ```ts
145
146
  const tapi = new TapiClient({
146
147
  baseUrl: process.env.TAPI_BASE_URL!,
147
- apiKey: process.env.TAPI_API_KEY!,
148
+ authToken: process.env.TAPI_FIREBASE_ID_TOKEN!,
148
149
  tappId: process.env.TAPI_TAPP_ID!,
149
150
  projectId: process.env.TAPI_PROJECT_ID!,
150
151
  dev: true,
@@ -269,8 +270,8 @@ export default {
269
270
  };
270
271
  ```
271
272
 
272
- ```bash
273
- TAPI_API_KEY=tapi_project_key tapi triggers sync
273
+ ```bash
274
+ tapi triggers sync
274
275
  ```
275
276
 
276
277
  `triggers sync` upserts by trigger name for the current `.tapi/project.json`
@@ -295,14 +296,13 @@ await tapi.triggers.disable("act_123");
295
296
  You can inspect the same input/output contract from the CLI:
296
297
 
297
298
  ```bash
298
- tapi services describe schwab.place_order \
299
- --api-base-url "$TAPI_BASE_URL" \
300
- --api-key "$TAPI_API_KEY" \
301
- --project "$TAPI_PROJECT_ID"
302
- ```
303
-
304
- Do not expose `TAPI_API_KEY` in public browser bundles. Put the SDK behind your
305
- own backend route, server action, or job worker when using secret API keys.
299
+ tapi services describe schwab.place_order \
300
+ --api-base-url "$TAPI_BASE_URL" \
301
+ --project "$TAPI_PROJECT_ID"
302
+ ```
303
+
304
+ Do not expose developer Firebase ID tokens in public browser bundles. Put the
305
+ SDK behind your own backend route, server action, or job worker.
306
306
 
307
307
  ## Runtime Profiles and Proxies
308
308
 
@@ -370,7 +370,7 @@ If your JavaScript runtime does not provide `WebSocket`, pass one:
370
370
  ```ts
371
371
  const tapi = new TapiClient({
372
372
  baseUrl: process.env.TAPI_BASE_URL!,
373
- apiKey: process.env.TAPI_API_KEY!,
373
+ authToken: process.env.TAPI_FIREBASE_ID_TOKEN!,
374
374
  tappId: process.env.TAPI_TAPP_ID!,
375
375
  webSocket: MyWebSocketImplementation,
376
376
  });
@@ -378,17 +378,16 @@ const tapi = new TapiClient({
378
378
 
379
379
  ## Configuration
380
380
 
381
- ```env
381
+ ```env
382
382
  TAPI_BASE_URL=https://your-tapi-api-host
383
- TAPI_API_KEY=tapi_project_key
383
+ TAPI_FIREBASE_ID_TOKEN=firebase_id_token_for_the_tapp_owner
384
384
  TAPI_TAPP_ID=brokerage
385
385
  TAPI_PROJECT_ID=brokerage
386
386
  ```
387
387
 
388
388
  `tappId` is required. Service calls are made for that Tapp, and the developer
389
- who owns the Tapp pays for the service delivery. `projectId` is optional when
390
- the API key is already project-scoped. If provided, the SDK sends it as the
391
- `X-Tapi-Project` header and the server rejects mismatches.
389
+ who owns the Tapp pays for the service delivery. If provided, `projectId` is
390
+ sent as the `X-Tapi-Project` header and the server rejects mismatches.
392
391
 
393
392
  ## Common Project Setup
394
393
 
@@ -406,7 +405,7 @@ import { TapiClient } from "@tapi-dev/sdk";
406
405
 
407
406
  export const tapi = new TapiClient({
408
407
  baseUrl: process.env.TAPI_BASE_URL!,
409
- apiKey: process.env.TAPI_API_KEY!,
408
+ authToken: process.env.TAPI_FIREBASE_ID_TOKEN!,
410
409
  tappId: process.env.TAPI_TAPP_ID!,
411
410
  projectId: process.env.TAPI_PROJECT_ID!,
412
411
  });
@@ -446,10 +445,10 @@ Cloud runs are requested from the SDK, but VM provisioning, service
446
445
  installation, worker leases, browser internals, and AWS cleanup stay on the
447
446
  Tapi server. The SDK is only the front door.
448
447
 
449
- Keep this code server-side. Do not put `TAPI_API_KEY` in a browser bundle and
450
- do not wire AWS or Stripe from the developer's app. The app sends its own user
451
- identity to Tapi, and Tapi handles prepaid Stripe checkout, credit accounting,
452
- AWS worker provisioning, and cleanup.
448
+ Keep this code server-side. Do not put developer Firebase ID tokens in a
449
+ browser bundle and do not wire AWS or Stripe from the developer's app. The app
450
+ sends its own user identity to Tapi, and Tapi handles prepaid Stripe checkout,
451
+ credit accounting, AWS worker provisioning, and cleanup.
453
452
 
454
453
  The minimum developer flow is:
455
454
 
@@ -523,7 +522,7 @@ low, `runCloudBatch` converts the server's HTTP `402` into a normal
523
522
  `availableBalanceCents`, and `requiredBalanceCents`. That keeps the button
524
523
  handler simple.
525
524
 
526
- Credits are scoped under the Tapi API key owner, Tapp id, and
525
+ Credits are scoped under the authenticated Firebase owner, Tapp id, and
527
526
  `user.externalUserId`. Use the same `externalUserId` for quote, balance,
528
527
  checkout, and run calls. If you omit `externalUserId` but provide `email`, Tapi
529
528
  uses the normalized email as the external user id.
package/dist/cli.d.ts CHANGED
@@ -71,7 +71,7 @@ export interface StudioCliOptions {
71
71
  }
72
72
  export interface RunnerSetupCliOptions {
73
73
  apiBaseUrl: string;
74
- apiKey: string;
74
+ authToken?: string;
75
75
  projectId: string;
76
76
  runnerId: string;
77
77
  port: number;
package/dist/cli.js CHANGED
@@ -117,6 +117,50 @@ async function withCliSpinner(message, work) {
117
117
  throw error;
118
118
  }
119
119
  }
120
+ async function withSdkAuth(options) {
121
+ if (options.authToken?.trim()) {
122
+ return { ...options, authToken: options.authToken.trim() };
123
+ }
124
+ const envAuthToken = envString("TAPI_FIREBASE_ID_TOKEN");
125
+ if (envAuthToken) {
126
+ return { ...options, authToken: envAuthToken };
127
+ }
128
+ const credentials = await requireSdkFirebaseCredentials();
129
+ return { ...options, authToken: credentials.idToken };
130
+ }
131
+ async function requireSdkFirebaseCredentials() {
132
+ const credentials = await refreshCachedFirebaseCredentials();
133
+ if (!credentials) {
134
+ throw new Error(`Tapi sign-in is required. Run \`tapi login\`, then retry.`);
135
+ }
136
+ await writeStudioAuthCache(credentials);
137
+ return credentials;
138
+ }
139
+ async function runLogin() {
140
+ try {
141
+ const credentials = await withCliSpinner("Signing in to Tapi", () => authenticateSdkUser());
142
+ console.log(`Signed in to Tapi as ${credentials.uid}.`);
143
+ return 0;
144
+ }
145
+ catch (error) {
146
+ console.error(formatError(error));
147
+ return 1;
148
+ }
149
+ }
150
+ async function authenticateSdkUser() {
151
+ const cached = await refreshCachedFirebaseCredentials();
152
+ if (cached) {
153
+ await writeStudioAuthCache(cached);
154
+ return cached;
155
+ }
156
+ const browserAuth = await browserGoogleSignIn();
157
+ if (!browserAuth.idToken) {
158
+ throw new Error(browserAuth.error || browserAuth.detail || "Browser sign-in did not return a Google ID token.");
159
+ }
160
+ const credentials = await exchangeGoogleToFirebase(browserAuth.idToken, browserAuth.accessToken);
161
+ await writeStudioAuthCache(credentials);
162
+ return credentials;
163
+ }
120
164
  export async function runCli(argv = process.argv.slice(2)) {
121
165
  const [command, subcommand, ...rest] = argv;
122
166
  if (!command || command === "help" || command === "--help" || command === "-h") {
@@ -127,6 +171,18 @@ export async function runCli(argv = process.argv.slice(2)) {
127
171
  console.log(sdkVersion);
128
172
  return 0;
129
173
  }
174
+ if (command === "login") {
175
+ if (hasHelpFlag([subcommand, ...rest])) {
176
+ printLoginHelp();
177
+ return 0;
178
+ }
179
+ if (subcommand) {
180
+ console.error(`Unexpected login argument: ${subcommand}`);
181
+ printLoginHelp();
182
+ return 1;
183
+ }
184
+ return runLogin();
185
+ }
130
186
  if (command === "doctor") {
131
187
  if (hasHelpFlag([subcommand, ...rest])) {
132
188
  printStudioHelp();
@@ -430,7 +486,6 @@ function parseServiceOptions(args) {
430
486
  let operation = "";
431
487
  const workspace = loadWorkspace();
432
488
  let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
433
- let apiKey = envString("TAPI_API_KEY") || "";
434
489
  let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
435
490
  for (let index = 0; index < args.length; index += 1) {
436
491
  const arg = args[index];
@@ -448,14 +503,6 @@ function parseServiceOptions(args) {
448
503
  apiBaseUrl = arg.slice("--server=".length);
449
504
  continue;
450
505
  }
451
- if (arg === "--api-key") {
452
- apiKey = requireOptionValue(args, ++index, "--api-key");
453
- continue;
454
- }
455
- if (arg.startsWith("--api-key=")) {
456
- apiKey = arg.slice("--api-key=".length);
457
- continue;
458
- }
459
506
  if (arg === "--project") {
460
507
  projectId = normalizeProjectValue(requireOptionValue(args, ++index, "--project"), "project");
461
508
  continue;
@@ -476,9 +523,6 @@ function parseServiceOptions(args) {
476
523
  if (!operation) {
477
524
  throw new Error("services describe requires a service run like schwab.place_order.");
478
525
  }
479
- if (!apiKey) {
480
- throw new Error("services describe requires --api-key or TAPI_API_KEY.");
481
- }
482
526
  if (!projectId) {
483
527
  throw new Error("services describe requires --project, TAPI_PROJECT_ID, or .tapi/project.json.");
484
528
  }
@@ -486,7 +530,6 @@ function parseServiceOptions(args) {
486
530
  operation,
487
531
  options: {
488
532
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
489
- apiKey,
490
533
  projectId,
491
534
  },
492
535
  };
@@ -494,11 +537,12 @@ function parseServiceOptions(args) {
494
537
  async function describeServiceOperation(args) {
495
538
  try {
496
539
  const { operation, options } = parseServiceOptions(args);
540
+ const authenticated = await withSdkAuth(options);
497
541
  const client = new TapiClient({
498
- baseUrl: options.apiBaseUrl,
499
- apiKey: options.apiKey,
500
- tappId: options.projectId,
501
- projectId: options.projectId,
542
+ baseUrl: authenticated.apiBaseUrl,
543
+ authToken: authenticated.authToken,
544
+ tappId: authenticated.projectId,
545
+ projectId: authenticated.projectId,
502
546
  });
503
547
  const description = await withCliSpinner(`Fetching Tapi service-run contract for ${operation}`, () => client.services.describe(operation));
504
548
  console.log(JSON.stringify(description, null, 2));
@@ -512,7 +556,6 @@ async function describeServiceOperation(args) {
512
556
  function parseApiWorkspaceOptions(args) {
513
557
  const workspace = loadWorkspace();
514
558
  let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
515
- let apiKey = envString("TAPI_API_KEY") || "";
516
559
  let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
517
560
  let catalogPath = workspace?.config.services?.catalog || ".tapi/services/catalog.json";
518
561
  for (let index = 0; index < args.length; index += 1) {
@@ -531,14 +574,6 @@ function parseApiWorkspaceOptions(args) {
531
574
  apiBaseUrl = arg.slice("--server=".length);
532
575
  continue;
533
576
  }
534
- if (arg === "--api-key") {
535
- apiKey = requireOptionValue(args, ++index, "--api-key");
536
- continue;
537
- }
538
- if (arg.startsWith("--api-key=")) {
539
- apiKey = arg.slice("--api-key=".length);
540
- continue;
541
- }
542
577
  if (arg === "--project") {
543
578
  projectId = normalizeProjectValue(requireOptionValue(args, ++index, "--project"), "project");
544
579
  continue;
@@ -557,23 +592,19 @@ function parseApiWorkspaceOptions(args) {
557
592
  }
558
593
  throw new Error(`Unknown services option: ${arg}`);
559
594
  }
560
- if (!apiKey) {
561
- throw new Error("services command requires --api-key or TAPI_API_KEY.");
562
- }
563
595
  if (!projectId) {
564
596
  throw new Error("services command requires --project, TAPI_PROJECT_ID, or .tapi/project.json.");
565
597
  }
566
598
  const root = workspace?.root || process.cwd();
567
599
  return {
568
600
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
569
- apiKey,
570
601
  projectId,
571
602
  catalogPath: resolve(root, catalogPath),
572
603
  };
573
604
  }
574
605
  async function syncServiceCatalog(args) {
575
606
  try {
576
- const options = parseApiWorkspaceOptions(args);
607
+ const options = await withSdkAuth(parseApiWorkspaceOptions(args));
577
608
  const catalog = await withCliSpinner("Fetching Tapi service-run catalog", () => fetchApiCatalog(options));
578
609
  await writeJsonFile(options.catalogPath, catalog);
579
610
  console.log(`Synced Tapi service-run catalog: ${options.catalogPath}`);
@@ -587,7 +618,6 @@ async function syncServiceCatalog(args) {
587
618
  function parseTappServiceAddOptions(args) {
588
619
  const workspace = loadWorkspace();
589
620
  let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
590
- let apiKey = envString("TAPI_API_KEY") || "";
591
621
  let tappId = "";
592
622
  let serviceCall = "";
593
623
  let serviceMapId = "";
@@ -609,14 +639,6 @@ function parseTappServiceAddOptions(args) {
609
639
  apiBaseUrl = arg.slice("--server=".length);
610
640
  continue;
611
641
  }
612
- if (arg === "--api-key") {
613
- apiKey = requireOptionValue(args, ++index, "--api-key");
614
- continue;
615
- }
616
- if (arg.startsWith("--api-key=")) {
617
- apiKey = arg.slice("--api-key=".length);
618
- continue;
619
- }
620
642
  if (arg === "--service-map") {
621
643
  serviceMapId = requireOptionValue(args, ++index, "--service-map");
622
644
  continue;
@@ -654,9 +676,6 @@ function parseTappServiceAddOptions(args) {
654
676
  }
655
677
  throw new Error(`Unexpected tapp service add argument: ${arg}`);
656
678
  }
657
- if (!apiKey) {
658
- throw new Error("tapp service add requires --api-key or TAPI_API_KEY.");
659
- }
660
679
  if (!tappId) {
661
680
  throw new Error("tapp service add requires a Tapp id.");
662
681
  }
@@ -671,7 +690,6 @@ function parseTappServiceAddOptions(args) {
671
690
  }
672
691
  return {
673
692
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
674
- apiKey,
675
693
  tappId,
676
694
  serviceCall,
677
695
  serviceMapId,
@@ -682,7 +700,6 @@ function parseTappServiceAddOptions(args) {
682
700
  function parseTappCreateOptions(args) {
683
701
  const workspace = loadWorkspace();
684
702
  let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
685
- let apiKey = envString("TAPI_API_KEY") || "";
686
703
  let tappId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
687
704
  let name = "";
688
705
  for (let index = 0; index < args.length; index += 1) {
@@ -701,14 +718,6 @@ function parseTappCreateOptions(args) {
701
718
  apiBaseUrl = arg.slice("--server=".length);
702
719
  continue;
703
720
  }
704
- if (arg === "--api-key") {
705
- apiKey = requireOptionValue(args, ++index, "--api-key");
706
- continue;
707
- }
708
- if (arg.startsWith("--api-key=")) {
709
- apiKey = arg.slice("--api-key=".length);
710
- continue;
711
- }
712
721
  if (arg === "--tapp-id" || arg === "--project") {
713
722
  tappId = normalizeProjectValue(requireOptionValue(args, ++index, arg), "tapp");
714
723
  continue;
@@ -741,9 +750,6 @@ function parseTappCreateOptions(args) {
741
750
  }
742
751
  throw new Error(`Unexpected tapp create argument: ${arg}`);
743
752
  }
744
- if (!apiKey) {
745
- throw new Error("tapp create requires --api-key or TAPI_API_KEY.");
746
- }
747
753
  if (!tappId) {
748
754
  throw new Error("tapp create requires a Tapp id or name.");
749
755
  }
@@ -752,14 +758,13 @@ function parseTappCreateOptions(args) {
752
758
  }
753
759
  return {
754
760
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
755
- apiKey,
756
761
  tappId,
757
762
  name,
758
763
  };
759
764
  }
760
765
  async function createTapp(args) {
761
766
  try {
762
- const options = parseTappCreateOptions(args);
767
+ const options = await withSdkAuth(parseTappCreateOptions(args));
763
768
  const tapp = await withCliSpinner(`Creating Tapi Tapp ${options.tappId}`, () => postTappCreate(options));
764
769
  console.log(JSON.stringify(tapp, null, 2));
765
770
  return 0;
@@ -772,11 +777,7 @@ async function createTapp(args) {
772
777
  async function postTappCreate(options, fetchImpl = fetch) {
773
778
  const response = await fetchImpl(`${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/tapps`, {
774
779
  method: "POST",
775
- headers: {
776
- "Content-Type": "application/json",
777
- "X-API-Key": options.apiKey,
778
- "X-Tapi-Project": options.tappId,
779
- },
780
+ headers: sdkJsonHeaders(options.authToken, options.tappId),
780
781
  body: JSON.stringify({
781
782
  tappId: options.tappId,
782
783
  name: options.name,
@@ -791,7 +792,7 @@ async function postTappCreate(options, fetchImpl = fetch) {
791
792
  }
792
793
  async function addTappService(args) {
793
794
  try {
794
- const options = parseTappServiceAddOptions(args);
795
+ const options = await withSdkAuth(parseTappServiceAddOptions(args));
795
796
  const operation = await withCliSpinner(`Adding Tapi service ${options.serviceCall} to ${options.tappId}`, () => postTappServiceAdd(options));
796
797
  console.log(JSON.stringify(operation, null, 2));
797
798
  return 0;
@@ -804,11 +805,7 @@ async function addTappService(args) {
804
805
  async function postTappServiceAdd(options) {
805
806
  const response = await fetch(`${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/tapps/${encodeURIComponent(options.tappId)}/services/${encodeURIComponent(options.serviceCall)}`, {
806
807
  method: "POST",
807
- headers: {
808
- "Content-Type": "application/json",
809
- "X-API-Key": options.apiKey,
810
- "X-Tapi-Project": options.tappId,
811
- },
808
+ headers: sdkJsonHeaders(options.authToken, options.tappId),
812
809
  body: JSON.stringify({
813
810
  serviceMapId: options.serviceMapId,
814
811
  entry: options.entry,
@@ -825,7 +822,6 @@ async function postTappServiceAdd(options) {
825
822
  function parseQueueCreateOptions(args) {
826
823
  const workspace = loadWorkspace();
827
824
  let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
828
- let apiKey = envString("TAPI_API_KEY") || "";
829
825
  let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
830
826
  let displayName = "";
831
827
  let maxSlots = 8;
@@ -845,14 +841,6 @@ function parseQueueCreateOptions(args) {
845
841
  apiBaseUrl = arg.slice("--server=".length);
846
842
  continue;
847
843
  }
848
- if (arg === "--api-key") {
849
- apiKey = requireOptionValue(args, ++index, "--api-key");
850
- continue;
851
- }
852
- if (arg.startsWith("--api-key=")) {
853
- apiKey = arg.slice("--api-key=".length);
854
- continue;
855
- }
856
844
  if (arg === "--project") {
857
845
  projectId = normalizeProjectValue(requireOptionValue(args, ++index, "--project"), "project");
858
846
  continue;
@@ -878,12 +866,8 @@ function parseQueueCreateOptions(args) {
878
866
  }
879
867
  throw new Error(`Unexpected queue create argument: ${arg}`);
880
868
  }
881
- if (!apiKey) {
882
- throw new Error("queue create requires --api-key or TAPI_API_KEY.");
883
- }
884
869
  return {
885
870
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
886
- apiKey,
887
871
  projectId,
888
872
  displayName,
889
873
  maxSlots,
@@ -892,7 +876,6 @@ function parseQueueCreateOptions(args) {
892
876
  function parseTappQueueAddOptions(args) {
893
877
  const workspace = loadWorkspace();
894
878
  let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
895
- let apiKey = envString("TAPI_API_KEY") || "";
896
879
  let tappId = "";
897
880
  let queueId = "";
898
881
  for (let index = 0; index < args.length; index += 1) {
@@ -911,14 +894,6 @@ function parseTappQueueAddOptions(args) {
911
894
  apiBaseUrl = arg.slice("--server=".length);
912
895
  continue;
913
896
  }
914
- if (arg === "--api-key") {
915
- apiKey = requireOptionValue(args, ++index, "--api-key");
916
- continue;
917
- }
918
- if (arg.startsWith("--api-key=")) {
919
- apiKey = arg.slice("--api-key=".length);
920
- continue;
921
- }
922
897
  if (arg.startsWith("--")) {
923
898
  throw new Error(`Unknown tapp queue add option: ${arg}`);
924
899
  }
@@ -932,9 +907,6 @@ function parseTappQueueAddOptions(args) {
932
907
  }
933
908
  throw new Error(`Unexpected tapp queue add argument: ${arg}`);
934
909
  }
935
- if (!apiKey) {
936
- throw new Error("tapp queue add requires --api-key or TAPI_API_KEY.");
937
- }
938
910
  if (!tappId) {
939
911
  throw new Error("tapp queue add requires a Tapp id.");
940
912
  }
@@ -943,7 +915,6 @@ function parseTappQueueAddOptions(args) {
943
915
  }
944
916
  return {
945
917
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
946
- apiKey,
947
918
  tappId,
948
919
  queueId,
949
920
  };
@@ -951,7 +922,6 @@ function parseTappQueueAddOptions(args) {
951
922
  function parseRunnerSlotSetOptions(args) {
952
923
  const workspace = loadWorkspace();
953
924
  let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
954
- let apiKey = envString("TAPI_API_KEY") || "";
955
925
  let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
956
926
  let runnerId = "";
957
927
  let slotIndex = 0;
@@ -972,14 +942,6 @@ function parseRunnerSlotSetOptions(args) {
972
942
  apiBaseUrl = arg.slice("--server=".length);
973
943
  continue;
974
944
  }
975
- if (arg === "--api-key") {
976
- apiKey = requireOptionValue(args, ++index, "--api-key");
977
- continue;
978
- }
979
- if (arg.startsWith("--api-key=")) {
980
- apiKey = arg.slice("--api-key=".length);
981
- continue;
982
- }
983
945
  if (arg === "--project") {
984
946
  projectId = normalizeProjectValue(requireOptionValue(args, ++index, "--project"), "project");
985
947
  continue;
@@ -1005,9 +967,6 @@ function parseRunnerSlotSetOptions(args) {
1005
967
  }
1006
968
  throw new Error(`Unexpected runner slot set argument: ${arg}`);
1007
969
  }
1008
- if (!apiKey) {
1009
- throw new Error("runner slot set requires --api-key or TAPI_API_KEY.");
1010
- }
1011
970
  if (!projectId) {
1012
971
  throw new Error("runner slot set requires --project or TAPI_PROJECT_ID.");
1013
972
  }
@@ -1022,7 +981,6 @@ function parseRunnerSlotSetOptions(args) {
1022
981
  }
1023
982
  return {
1024
983
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
1025
- apiKey,
1026
984
  projectId,
1027
985
  runnerId,
1028
986
  slotIndex,
@@ -1031,7 +989,7 @@ function parseRunnerSlotSetOptions(args) {
1031
989
  }
1032
990
  async function createQueue(args) {
1033
991
  try {
1034
- const options = parseQueueCreateOptions(args);
992
+ const options = await withSdkAuth(parseQueueCreateOptions(args));
1035
993
  const queue = await withCliSpinner(`Creating Tapi queue`, () => postQueueCreate(options));
1036
994
  console.log(JSON.stringify(queue, null, 2));
1037
995
  return 0;
@@ -1043,7 +1001,7 @@ async function createQueue(args) {
1043
1001
  }
1044
1002
  async function addTappQueue(args) {
1045
1003
  try {
1046
- const options = parseTappQueueAddOptions(args);
1004
+ const options = await withSdkAuth(parseTappQueueAddOptions(args));
1047
1005
  const result = await withCliSpinner(`Attaching Tapi queue ${options.queueId} to ${options.tappId}`, () => postTappQueueAdd(options));
1048
1006
  console.log(JSON.stringify(result, null, 2));
1049
1007
  return 0;
@@ -1055,7 +1013,7 @@ async function addTappQueue(args) {
1055
1013
  }
1056
1014
  async function setRunnerSlot(args) {
1057
1015
  try {
1058
- const options = parseRunnerSlotSetOptions(args);
1016
+ const options = await withSdkAuth(parseRunnerSlotSetOptions(args));
1059
1017
  const result = await withCliSpinner(`Assigning runner slot ${options.runnerId}/${options.slotIndex} to queue ${options.queueId}`, () => putRunnerSlot(options));
1060
1018
  console.log(JSON.stringify(result, null, 2));
1061
1019
  return 0;
@@ -1066,13 +1024,7 @@ async function setRunnerSlot(args) {
1066
1024
  }
1067
1025
  }
1068
1026
  async function postQueueCreate(options) {
1069
- const headers = {
1070
- "Content-Type": "application/json",
1071
- "X-API-Key": options.apiKey,
1072
- };
1073
- if (options.projectId) {
1074
- headers["X-Tapi-Project"] = options.projectId;
1075
- }
1027
+ const headers = sdkJsonHeaders(options.authToken, options.projectId);
1076
1028
  const response = await fetch(`${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/queues`, {
1077
1029
  method: "POST",
1078
1030
  headers,
@@ -1091,11 +1043,7 @@ async function postQueueCreate(options) {
1091
1043
  async function postTappQueueAdd(options) {
1092
1044
  const response = await fetch(`${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/tapps/${encodeURIComponent(options.tappId)}/queues`, {
1093
1045
  method: "POST",
1094
- headers: {
1095
- "Content-Type": "application/json",
1096
- "X-API-Key": options.apiKey,
1097
- "X-Tapi-Project": options.tappId,
1098
- },
1046
+ headers: sdkJsonHeaders(options.authToken, options.tappId),
1099
1047
  body: JSON.stringify({ queueId: options.queueId }),
1100
1048
  });
1101
1049
  const body = await readJsonBody(response);
@@ -1108,11 +1056,7 @@ async function postTappQueueAdd(options) {
1108
1056
  async function putRunnerSlot(options) {
1109
1057
  const response = await fetch(`${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/runners/${encodeURIComponent(options.runnerId)}/slots/${encodeURIComponent(String(options.slotIndex))}`, {
1110
1058
  method: "PUT",
1111
- headers: {
1112
- "Content-Type": "application/json",
1113
- "X-API-Key": options.apiKey,
1114
- "X-Tapi-Project": options.projectId,
1115
- },
1059
+ headers: sdkJsonHeaders(options.authToken, options.projectId),
1116
1060
  body: JSON.stringify({ queueId: options.queueId }),
1117
1061
  });
1118
1062
  const body = await readJsonBody(response);
@@ -1125,7 +1069,6 @@ async function putRunnerSlot(options) {
1125
1069
  export function parseRunnerSetupOptions(args) {
1126
1070
  const workspace = loadWorkspace();
1127
1071
  let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
1128
- let apiKey = envString("TAPI_API_KEY") || "";
1129
1072
  let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
1130
1073
  let runnerId = envString("TAPI_RUNNER_ID") || "";
1131
1074
  let port = Number(envString("TAPI_RUNNER_SETUP_PORT") || "17687");
@@ -1146,14 +1089,6 @@ export function parseRunnerSetupOptions(args) {
1146
1089
  apiBaseUrl = arg.slice("--server=".length);
1147
1090
  continue;
1148
1091
  }
1149
- if (arg === "--api-key") {
1150
- apiKey = requireOptionValue(args, ++index, "--api-key");
1151
- continue;
1152
- }
1153
- if (arg.startsWith("--api-key=")) {
1154
- apiKey = arg.slice("--api-key=".length);
1155
- continue;
1156
- }
1157
1092
  if (arg === "--project") {
1158
1093
  projectId = normalizeProjectValue(requireOptionValue(args, ++index, "--project"), "project");
1159
1094
  continue;
@@ -1191,9 +1126,6 @@ export function parseRunnerSetupOptions(args) {
1191
1126
  }
1192
1127
  throw new Error(`Unexpected runner setup argument: ${arg}`);
1193
1128
  }
1194
- if (!apiKey) {
1195
- throw new Error("runner setup requires --api-key or TAPI_API_KEY.");
1196
- }
1197
1129
  if (!projectId) {
1198
1130
  throw new Error("runner setup requires --project or TAPI_PROJECT_ID.");
1199
1131
  }
@@ -1205,7 +1137,6 @@ export function parseRunnerSetupOptions(args) {
1205
1137
  }
1206
1138
  return {
1207
1139
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
1208
- apiKey,
1209
1140
  projectId,
1210
1141
  runnerId,
1211
1142
  port,
@@ -1214,7 +1145,7 @@ export function parseRunnerSetupOptions(args) {
1214
1145
  }
1215
1146
  async function runRunnerSetup(args) {
1216
1147
  try {
1217
- const options = parseRunnerSetupOptions(args);
1148
+ const options = await withSdkAuth(parseRunnerSetupOptions(args));
1218
1149
  const handle = await startRunnerSetupServer(options);
1219
1150
  if (options.openBrowser) {
1220
1151
  openUrlInBrowser(handle.url);
@@ -1252,7 +1183,7 @@ export async function executeRunnerSetupAction(options, action, fetchImpl = fetc
1252
1183
  if (!queueId) {
1253
1184
  queue = await sdkJsonRequest(fetchImpl, `${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/queues`, {
1254
1185
  method: "POST",
1255
- headers: sdkJsonHeaders(options.apiKey, options.projectId),
1186
+ headers: sdkJsonHeaders(options.authToken, options.projectId),
1256
1187
  body: JSON.stringify({
1257
1188
  displayName: String(action.displayName || `${options.projectId} queue`).trim(),
1258
1189
  maxSlots,
@@ -1265,7 +1196,7 @@ export async function executeRunnerSetupAction(options, action, fetchImpl = fetc
1265
1196
  }
1266
1197
  const attachment = await sdkJsonRequest(fetchImpl, `${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/tapps/${encodeURIComponent(options.projectId)}/queues`, {
1267
1198
  method: "POST",
1268
- headers: sdkJsonHeaders(options.apiKey, options.projectId),
1199
+ headers: sdkJsonHeaders(options.authToken, options.projectId),
1269
1200
  body: JSON.stringify({ queueId }),
1270
1201
  });
1271
1202
  const slots = [];
@@ -1273,7 +1204,7 @@ export async function executeRunnerSetupAction(options, action, fetchImpl = fetc
1273
1204
  const slotIndex = slotStart + offset;
1274
1205
  slots.push(await sdkJsonRequest(fetchImpl, `${options.apiBaseUrl.replace(/\/+$/, "")}/api/sdk/v1/runners/${encodeURIComponent(options.runnerId)}/slots/${encodeURIComponent(String(slotIndex))}`, {
1275
1206
  method: "PUT",
1276
- headers: sdkJsonHeaders(options.apiKey, options.projectId),
1207
+ headers: sdkJsonHeaders(options.authToken, options.projectId),
1277
1208
  body: JSON.stringify({ queueId }),
1278
1209
  }));
1279
1210
  }
@@ -1416,12 +1347,22 @@ async function sdkJsonRequest(fetchImpl, url, init) {
1416
1347
  }
1417
1348
  return body ?? {};
1418
1349
  }
1419
- function sdkJsonHeaders(apiKey, projectId) {
1420
- return {
1350
+ function sdkJsonHeaders(authToken, projectId) {
1351
+ const headers = {
1421
1352
  "Content-Type": "application/json",
1422
- "X-API-Key": apiKey,
1423
- "X-Tapi-Project": projectId,
1353
+ Authorization: `Bearer ${requireSdkAuthToken(authToken)}`,
1424
1354
  };
1355
+ if (projectId) {
1356
+ headers["X-Tapi-Project"] = projectId;
1357
+ }
1358
+ return headers;
1359
+ }
1360
+ function requireSdkAuthToken(authToken) {
1361
+ const normalized = String(authToken || "").trim();
1362
+ if (!normalized) {
1363
+ throw new Error("Tapi sign-in is required. Run `tapi login`, then retry.");
1364
+ }
1365
+ return normalized;
1425
1366
  }
1426
1367
  function coercePositiveInteger(value, fallback, label) {
1427
1368
  const text = value === undefined || value === null || value === "" ? String(fallback) : String(value);
@@ -1444,7 +1385,6 @@ function parseNonNegativeInteger(value, label) {
1444
1385
  function parseTriggerSyncOptions(args) {
1445
1386
  const workspace = loadWorkspace();
1446
1387
  let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
1447
- let apiKey = envString("TAPI_API_KEY") || "";
1448
1388
  let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
1449
1389
  let configPath = "";
1450
1390
  const root = workspace?.root || process.cwd();
@@ -1464,14 +1404,6 @@ function parseTriggerSyncOptions(args) {
1464
1404
  apiBaseUrl = arg.slice("--server=".length);
1465
1405
  continue;
1466
1406
  }
1467
- if (arg === "--api-key") {
1468
- apiKey = requireOptionValue(args, ++index, "--api-key");
1469
- continue;
1470
- }
1471
- if (arg.startsWith("--api-key=")) {
1472
- apiKey = arg.slice("--api-key=".length);
1473
- continue;
1474
- }
1475
1407
  if (arg === "--project") {
1476
1408
  projectId = normalizeProjectValue(requireOptionValue(args, ++index, "--project"), "project");
1477
1409
  continue;
@@ -1490,29 +1422,25 @@ function parseTriggerSyncOptions(args) {
1490
1422
  }
1491
1423
  throw new Error(`Unknown triggers option: ${arg}`);
1492
1424
  }
1493
- if (!apiKey) {
1494
- throw new Error("triggers sync requires --api-key or TAPI_API_KEY.");
1495
- }
1496
1425
  if (!projectId) {
1497
1426
  throw new Error("triggers sync requires --project, TAPI_PROJECT_ID, or .tapi/project.json.");
1498
1427
  }
1499
1428
  return {
1500
1429
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
1501
- apiKey,
1502
1430
  projectId,
1503
1431
  configPath: configPath ? resolve(root, configPath) : defaultTriggerConfigPath(root),
1504
1432
  };
1505
1433
  }
1506
1434
  async function syncServiceTriggers(args) {
1507
1435
  try {
1508
- const options = parseTriggerSyncOptions(args);
1436
+ const options = await withSdkAuth(parseTriggerSyncOptions(args));
1509
1437
  const triggers = await readServiceTriggerConfig(options.configPath);
1510
1438
  if (triggers.length === 0) {
1511
1439
  throw new Error(`No Tapi service triggers found in ${options.configPath}.`);
1512
1440
  }
1513
1441
  const client = new TapiClient({
1514
1442
  baseUrl: options.apiBaseUrl,
1515
- apiKey: options.apiKey,
1443
+ authToken: options.authToken,
1516
1444
  tappId: options.projectId,
1517
1445
  projectId: options.projectId,
1518
1446
  });
@@ -1532,7 +1460,6 @@ async function syncServiceTriggers(args) {
1532
1460
  function parseSessionsOptions(args) {
1533
1461
  const workspace = loadWorkspace();
1534
1462
  let apiBaseUrl = envString("TAPI_BASE_URL") || workspace?.apiBaseUrl || DEFAULT_STUDIO_API_BASE_URL;
1535
- let apiKey = envString("TAPI_API_KEY") || "";
1536
1463
  let projectId = envString("TAPI_PROJECT_ID") || workspace?.projectId || "";
1537
1464
  let site = "";
1538
1465
  let sessionId = "";
@@ -1554,14 +1481,6 @@ function parseSessionsOptions(args) {
1554
1481
  apiBaseUrl = arg.slice("--server=".length);
1555
1482
  continue;
1556
1483
  }
1557
- if (arg === "--api-key") {
1558
- apiKey = requireOptionValue(args, ++index, "--api-key");
1559
- continue;
1560
- }
1561
- if (arg.startsWith("--api-key=")) {
1562
- apiKey = arg.slice("--api-key=".length);
1563
- continue;
1564
- }
1565
1484
  if (arg === "--project") {
1566
1485
  projectId = normalizeProjectValue(requireOptionValue(args, ++index, "--project"), "project");
1567
1486
  continue;
@@ -1596,9 +1515,6 @@ function parseSessionsOptions(args) {
1596
1515
  }
1597
1516
  throw new Error(`Unexpected sessions argument: ${arg}`);
1598
1517
  }
1599
- if (!apiKey) {
1600
- throw new Error("sessions command requires --api-key or TAPI_API_KEY.");
1601
- }
1602
1518
  if (!projectId) {
1603
1519
  throw new Error("sessions command requires --project, TAPI_PROJECT_ID, or .tapi/project.json.");
1604
1520
  }
@@ -1606,7 +1522,6 @@ function parseSessionsOptions(args) {
1606
1522
  sessionId,
1607
1523
  options: {
1608
1524
  apiBaseUrl: normalizeHttpUrl(apiBaseUrl, "Tapi API base URL"),
1609
- apiKey,
1610
1525
  projectId,
1611
1526
  site: site || undefined,
1612
1527
  json,
@@ -1617,18 +1532,19 @@ function parseSessionsOptions(args) {
1617
1532
  async function listDevSessions(args) {
1618
1533
  try {
1619
1534
  const { options } = parseSessionsOptions(args);
1620
- const sessions = await fetchDevSessions(options);
1621
- if (options.json) {
1535
+ const authenticated = await withSdkAuth(options);
1536
+ const sessions = await fetchDevSessions(authenticated);
1537
+ if (authenticated.json) {
1622
1538
  console.log(JSON.stringify(sessions, null, 2));
1623
1539
  return 0;
1624
1540
  }
1625
1541
  const rows = flattenDevSessions(sessions);
1626
1542
  if (rows.length === 0) {
1627
- console.log(`No Tapi dev sessions found for project ${sessions.projectId || options.projectId}.`);
1543
+ console.log(`No Tapi dev sessions found for project ${sessions.projectId || authenticated.projectId}.`);
1628
1544
  return 0;
1629
1545
  }
1630
- if (!options.noInteractive && process.stdin.isTTY && process.stdout.isTTY && rows.some((session) => session.openable)) {
1631
- return await runSessionsPicker(sessions, options);
1546
+ if (!authenticated.noInteractive && process.stdin.isTTY && process.stdout.isTTY && rows.some((session) => session.openable)) {
1547
+ return await runSessionsPicker(sessions, authenticated);
1632
1548
  }
1633
1549
  console.log(renderDevSessions(sessions));
1634
1550
  const openable = rows.find((session) => session.openable);
@@ -1645,15 +1561,16 @@ async function listDevSessions(args) {
1645
1561
  async function openDevSession(args) {
1646
1562
  try {
1647
1563
  const { sessionId, options } = parseSessionsOptions(args);
1564
+ const authenticated = await withSdkAuth(options);
1648
1565
  if (!sessionId) {
1649
1566
  throw new Error("sessions open requires a session id.");
1650
1567
  }
1651
- const sessions = await fetchDevSessions(options);
1568
+ const sessions = await fetchDevSessions(authenticated);
1652
1569
  const session = findDevSession(sessions, sessionId);
1653
1570
  if (!session) {
1654
1571
  throw new Error(`Tapi session not found: ${sessionId}`);
1655
1572
  }
1656
- return await openDevSessionInStudio(session, options);
1573
+ return await openDevSessionInStudio(session, authenticated);
1657
1574
  }
1658
1575
  catch (error) {
1659
1576
  console.error(formatError(error));
@@ -1663,7 +1580,7 @@ async function openDevSession(args) {
1663
1580
  async function fetchDevSessions(options) {
1664
1581
  const client = new TapiClient({
1665
1582
  baseUrl: options.apiBaseUrl,
1666
- apiKey: options.apiKey,
1583
+ authToken: options.authToken,
1667
1584
  tappId: options.projectId,
1668
1585
  projectId: options.projectId,
1669
1586
  });
@@ -2103,7 +2020,7 @@ function rejectNonPolicyTriggerRuntimeOwner(runtime, fieldName) {
2103
2020
  async function fetchApiCatalog(options) {
2104
2021
  const client = new TapiClient({
2105
2022
  baseUrl: options.apiBaseUrl,
2106
- apiKey: options.apiKey,
2023
+ authToken: options.authToken,
2107
2024
  tappId: options.projectId,
2108
2025
  projectId: options.projectId,
2109
2026
  });
@@ -4508,8 +4425,9 @@ function readSdkVersion() {
4508
4425
  function printHelp() {
4509
4426
  console.log(`Tapi CLI
4510
4427
 
4511
- Usage:
4512
- tapi init --project PROJECT
4428
+ Usage:
4429
+ tapi login
4430
+ tapi init --project PROJECT
4513
4431
  tapi link --project PROJECT
4514
4432
  tapi studio install [--channel pilot] [--api-base-url URL]
4515
4433
  tapi studio
@@ -4528,8 +4446,9 @@ Usage:
4528
4446
  tapi service status
4529
4447
  tapi doctor
4530
4448
 
4531
- Commands:
4532
- init Create .tapi/project.json for this repo
4449
+ Commands:
4450
+ login Sign in with Firebase credentials for SDK commands
4451
+ init Create .tapi/project.json for this repo
4533
4452
  link Rebind this repo to an existing Tapi project
4534
4453
  studio install Download, verify, and run the Tapi Studio installer
4535
4454
  studio Open Tapi Studio for this repo
@@ -4551,18 +4470,26 @@ Commands:
4551
4470
  doctor Alias for studio doctor
4552
4471
  `);
4553
4472
  }
4473
+ function printLoginHelp() {
4474
+ console.log(`Tapi login
4475
+
4476
+ Usage:
4477
+ tapi login
4478
+
4479
+ Signs in with the browser and caches Firebase credentials for SDK commands.
4480
+ `);
4481
+ }
4554
4482
  function printTappHelp() {
4555
4483
  console.log(`Tapi Tapp commands
4556
4484
 
4557
4485
  Usage:
4558
- tapi tapp create <tapp> [--name NAME] [--api-base-url URL] [--api-key KEY]
4559
- tapi tapp service add <tapp> <service.call> --service-map ID --entry ENTRY [--api-base-url URL] [--api-key KEY]
4560
- tapi tapp queue add <tapp> <queue-id> [--api-base-url URL] [--api-key KEY]
4486
+ tapi tapp create <tapp> [--name NAME] [--api-base-url URL]
4487
+ tapi tapp service add <tapp> <service.call> --service-map ID --entry ENTRY [--api-base-url URL]
4488
+ tapi tapp queue add <tapp> <queue-id> [--api-base-url URL]
4561
4489
 
4562
4490
  Options:
4563
4491
  --api-base-url <url> Tapi API base URL
4564
4492
  --server <url> Alias for --api-base-url
4565
- --api-key <key> Tapi API key; defaults to TAPI_API_KEY
4566
4493
  --tapp-id <id> Explicit Tapp id for tapp create
4567
4494
  --name <name> Display name for tapp create
4568
4495
  --service-map <id> Backing ServiceMap id
@@ -4574,12 +4501,11 @@ function printQueueHelp() {
4574
4501
  console.log(`Tapi queue commands
4575
4502
 
4576
4503
  Usage:
4577
- tapi queue create [display-name] [--max-slots N] [--project TAPP] [--api-base-url URL] [--api-key KEY]
4504
+ tapi queue create [display-name] [--max-slots N] [--project TAPP] [--api-base-url URL]
4578
4505
 
4579
4506
  Options:
4580
4507
  --api-base-url <url> Tapi API base URL
4581
4508
  --server <url> Alias for --api-base-url
4582
- --api-key <key> Tapi API key; defaults to TAPI_API_KEY
4583
4509
  --project <id> Optional project scope for the queue request
4584
4510
  --max-slots <n> Maximum runner slots this queue may use
4585
4511
  `);
@@ -4588,13 +4514,12 @@ function printRunnerHelp() {
4588
4514
  console.log(`Tapi runner commands
4589
4515
 
4590
4516
  Usage:
4591
- tapi runner setup --runner-id RUNNER --project TAPP [--port PORT] [--no-open] [--api-base-url URL] [--api-key KEY]
4592
- tapi runner slot set <runner-id> <slot-index> <queue-id> [--project TAPP] [--api-base-url URL] [--api-key KEY]
4517
+ tapi runner setup --runner-id RUNNER --project TAPP [--port PORT] [--no-open] [--api-base-url URL]
4518
+ tapi runner slot set <runner-id> <slot-index> <queue-id> [--project TAPP] [--api-base-url URL]
4593
4519
 
4594
4520
  Options:
4595
4521
  --api-base-url <url> Tapi API base URL
4596
4522
  --server <url> Alias for --api-base-url
4597
- --api-key <key> Tapi API key; defaults to TAPI_API_KEY
4598
4523
  --project <id> Tapi project id; defaults to TAPI_PROJECT_ID or .tapi/project.json
4599
4524
  --runner-id <id> Runner id for setup; defaults to TAPI_RUNNER_ID
4600
4525
  --port <n> Local setup UI port; defaults to 17687
@@ -4604,16 +4529,15 @@ Options:
4604
4529
  function printSessionsHelp() {
4605
4530
  console.log(`Tapi dev session commands
4606
4531
 
4607
- Usage:
4608
- tapi sessions [--api-base-url URL] [--api-key KEY] [--project PROJECT]
4609
- tapi sessions list [--json] [--site SITE]
4610
- tapi sessions open <session-id> [--api-base-url URL] [--api-key KEY] [--project PROJECT]
4611
-
4612
- Options:
4613
- --api-base-url <url> Tapi API base URL
4614
- --server <url> Alias for --api-base-url
4615
- --api-key <key> Tapi API key; defaults to TAPI_API_KEY
4616
- --project <id> Tapi project id; defaults to TAPI_PROJECT_ID or .tapi/project.json
4532
+ Usage:
4533
+ tapi sessions [--api-base-url URL] [--project PROJECT]
4534
+ tapi sessions list [--json] [--site SITE]
4535
+ tapi sessions open <session-id> [--api-base-url URL] [--project PROJECT]
4536
+
4537
+ Options:
4538
+ --api-base-url <url> Tapi API base URL
4539
+ --server <url> Alias for --api-base-url
4540
+ --project <id> Tapi project id; defaults to TAPI_PROJECT_ID or .tapi/project.json
4617
4541
  --site <name> Limit sessions to one sitemap/site
4618
4542
  --json Print raw grouped session JSON
4619
4543
  --no-interactive Print the grouped list without the arrow-key picker
@@ -4622,30 +4546,28 @@ Options:
4622
4546
  function printServicesHelp() {
4623
4547
  console.log(`Tapi ServiceMap service-run commands
4624
4548
 
4625
- Usage:
4626
- tapi services describe <servicemap.run> [--api-base-url URL] [--api-key KEY] [--project PROJECT]
4627
- tapi services sync [--api-base-url URL] [--api-key KEY] [--project PROJECT]
4628
-
4629
- Options:
4630
- --api-base-url <url> Tapi API base URL
4631
- --server <url> Alias for --api-base-url
4632
- --api-key <key> Tapi API key; defaults to TAPI_API_KEY
4633
- --project <id> Tapi project id; defaults to TAPI_PROJECT_ID or .tapi/project.json
4549
+ Usage:
4550
+ tapi services describe <servicemap.run> [--api-base-url URL] [--project PROJECT]
4551
+ tapi services sync [--api-base-url URL] [--project PROJECT]
4552
+
4553
+ Options:
4554
+ --api-base-url <url> Tapi API base URL
4555
+ --server <url> Alias for --api-base-url
4556
+ --project <id> Tapi project id; defaults to TAPI_PROJECT_ID or .tapi/project.json
4634
4557
  --catalog <path> Catalog JSON output path
4635
4558
  `);
4636
4559
  }
4637
4560
  function printTriggersHelp() {
4638
4561
  console.log(`Tapi service trigger commands
4639
4562
 
4640
- Usage:
4641
- tapi triggers sync [--config FILE] [--api-base-url URL] [--api-key KEY] [--project PROJECT]
4642
-
4643
- Options:
4644
- --config <path> Trigger config path; defaults to tapi.config.ts/js/json in the workspace root
4645
- --api-base-url <url> Tapi API base URL
4646
- --server <url> Alias for --api-base-url
4647
- --api-key <key> Tapi API key; defaults to TAPI_API_KEY
4648
- --project <id> Tapi project id; defaults to TAPI_PROJECT_ID or .tapi/project.json
4563
+ Usage:
4564
+ tapi triggers sync [--config FILE] [--api-base-url URL] [--project PROJECT]
4565
+
4566
+ Options:
4567
+ --config <path> Trigger config path; defaults to tapi.config.ts/js/json in the workspace root
4568
+ --api-base-url <url> Tapi API base URL
4569
+ --server <url> Alias for --api-base-url
4570
+ --project <id> Tapi project id; defaults to TAPI_PROJECT_ID or .tapi/project.json
4649
4571
 
4650
4572
  Config:
4651
4573
  export default {
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { TapiClientOptions } from "./types.js";
2
2
  export declare class HttpClient {
3
3
  private readonly baseUrl;
4
- private readonly apiKey;
4
+ private readonly authToken;
5
5
  private readonly tappId;
6
6
  private readonly projectId?;
7
7
  private readonly fetchImpl;
package/dist/client.js CHANGED
@@ -1,13 +1,20 @@
1
1
  import { TapiError } from "./errors.js";
2
2
  export class HttpClient {
3
3
  baseUrl;
4
- apiKey;
4
+ authToken;
5
5
  tappId;
6
6
  projectId;
7
7
  fetchImpl;
8
8
  constructor(options) {
9
9
  this.baseUrl = options.baseUrl.replace(/\/+$/, "");
10
- this.apiKey = options.apiKey;
10
+ this.authToken = options.authToken.trim();
11
+ if (!this.authToken) {
12
+ throw new TapiError("authToken is required", {
13
+ status: 0,
14
+ code: "invalid_client_config",
15
+ details: { field: "authToken" },
16
+ });
17
+ }
11
18
  this.tappId = options.tappId.trim();
12
19
  if (!this.tappId) {
13
20
  throw new TapiError("tappId is required", {
@@ -35,7 +42,7 @@ export class HttpClient {
35
42
  const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
36
43
  method,
37
44
  headers: {
38
- "X-API-Key": this.apiKey,
45
+ Authorization: `Bearer ${this.authToken}`,
39
46
  "Content-Type": "application/json",
40
47
  "X-Tapi-Tapp": this.tappId,
41
48
  ...(this.projectId ? { "X-Tapi-Project": this.projectId } : {}),
package/dist/types.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { LateInput } from "./late-input.js";
2
2
  export type RunStatus = "queued" | "running" | "waiting_for_input" | "completed" | "failed" | "cancelled" | "needs_developer_attention";
3
3
  export interface TapiClientOptions {
4
4
  baseUrl: string;
5
- apiKey: string;
5
+ authToken: string;
6
6
  tappId: string;
7
7
  projectId?: string;
8
8
  dev?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tapi-dev/sdk",
3
- "version": "0.1.36",
3
+ "version": "0.1.38",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",