@tapi-dev/sdk 0.1.2 → 0.1.3
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 +189 -189
- package/dist/cli.js +24 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,189 +1,189 @@
|
|
|
1
|
-
# TAPI JavaScript SDK
|
|
2
|
-
|
|
3
|
-
Official JavaScript and TypeScript client for TAPI developer APIs.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @tapi-dev/sdk
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
This package is ESM-first and works in runtimes with `fetch`, including modern Node.js and browser-like server runtimes.
|
|
12
|
-
|
|
13
|
-
## Install Tapi Studio
|
|
14
|
-
|
|
15
|
-
Tapi Studio is the desktop app used to author and test local Tapi integrations. Install the SDK first, then use the bundled CLI:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm install @tapi-dev/sdk
|
|
19
|
-
npx tapi studio install --channel pilot
|
|
20
|
-
npx tapi studio open
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
You can also run the CLI directly from npm without adding the package first:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
npx @tapi-dev/sdk studio install --channel pilot
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
The CLI reads the release manifest from:
|
|
30
|
-
|
|
31
|
-
```text
|
|
32
|
-
https://
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
It downloads the Windows installer, verifies the manifest SHA256, caches the installer locally, and runs it. The installer cache defaults to:
|
|
36
|
-
|
|
37
|
-
```text
|
|
38
|
-
%LOCALAPPDATA%\Tapi\Studio\downloads
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Useful commands:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npx tapi studio install --channel pilot
|
|
45
|
-
npx tapi studio install --channel pilot --download-only
|
|
46
|
-
npx tapi studio install --manifest https://
|
|
47
|
-
npx tapi studio open
|
|
48
|
-
npx tapi studio doctor
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Environment overrides:
|
|
52
|
-
|
|
53
|
-
```env
|
|
54
|
-
TAPI_STUDIO_CHANNEL=pilot
|
|
55
|
-
TAPI_STUDIO_MANIFEST_URL=https://
|
|
56
|
-
TAPI_DOWNLOADS_BASE_URL=https://
|
|
57
|
-
TAPI_STUDIO_EXE=C:\Users\you\AppData\Local\Tapi Studio\Tapi Studio.exe
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Tapi Studio desktop releases are currently published for Windows x64.
|
|
61
|
-
|
|
62
|
-
## Quick Start
|
|
63
|
-
|
|
64
|
-
Create one TAPI client in your app's server-side code:
|
|
65
|
-
|
|
66
|
-
```ts
|
|
67
|
-
import { TapiClient } from "@tapi-dev/sdk";
|
|
68
|
-
|
|
69
|
-
export const tapi = new TapiClient({
|
|
70
|
-
baseUrl: process.env.TAPI_BASE_URL!,
|
|
71
|
-
apiKey: process.env.TAPI_API_KEY!,
|
|
72
|
-
appId: process.env.TAPI_APP_ID,
|
|
73
|
-
});
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Then call a TAPI website API:
|
|
77
|
-
|
|
78
|
-
```ts
|
|
79
|
-
const run = await tapi.websiteApis.run("brokerage.submitTrade", {
|
|
80
|
-
inputs: {
|
|
81
|
-
symbol: "AAPL",
|
|
82
|
-
quantity: 1,
|
|
83
|
-
side: "buy",
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const completedRun = await tapi.runs.wait(run.id);
|
|
88
|
-
console.log(completedRun.status, completedRun.result);
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Do not expose `TAPI_API_KEY` in public browser bundles. Put the SDK behind your own backend route, server action, or job worker when using secret API keys.
|
|
92
|
-
|
|
93
|
-
## Configuration
|
|
94
|
-
|
|
95
|
-
```env
|
|
96
|
-
TAPI_BASE_URL=https://your-tapi-api-host
|
|
97
|
-
TAPI_API_KEY=tapi_your_api_key
|
|
98
|
-
TAPI_APP_ID=your-app-id
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
`appId` is optional. If provided, the SDK sends it as the `X-Tapi-App` header.
|
|
102
|
-
|
|
103
|
-
## Common Project Setup
|
|
104
|
-
|
|
105
|
-
A typical application keeps the client in one small module:
|
|
106
|
-
|
|
107
|
-
```text
|
|
108
|
-
src/
|
|
109
|
-
lib/
|
|
110
|
-
tapi.ts
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
// src/lib/tapi.ts
|
|
115
|
-
import { TapiClient } from "@tapi-dev/sdk";
|
|
116
|
-
|
|
117
|
-
export const tapi = new TapiClient({
|
|
118
|
-
baseUrl: process.env.TAPI_BASE_URL!,
|
|
119
|
-
apiKey: process.env.TAPI_API_KEY!,
|
|
120
|
-
appId: process.env.TAPI_APP_ID,
|
|
121
|
-
});
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Application code should import this shared client instead of constructing a new client in every file.
|
|
125
|
-
|
|
126
|
-
## Available Resources
|
|
127
|
-
|
|
128
|
-
```ts
|
|
129
|
-
await tapi.catalog.get();
|
|
130
|
-
await tapi.runners.list();
|
|
131
|
-
await tapi.runtime.requirements();
|
|
132
|
-
|
|
133
|
-
const run = await tapi.websiteApis.run("apiName.requestKey", {
|
|
134
|
-
inputs: { example: true },
|
|
135
|
-
priority: 5,
|
|
136
|
-
runnerId: "runner-id",
|
|
137
|
-
idempotencyKey: "request-123",
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
await tapi.runs.get(run.id);
|
|
141
|
-
await tapi.runs.wait(run.id, { intervalMs: 1000, timeoutMs: 300000 });
|
|
142
|
-
await tapi.runs.cancel(run.id);
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Website API requests are addressed as `<apiName>.<requestKey>`.
|
|
146
|
-
|
|
147
|
-
## Errors
|
|
148
|
-
|
|
149
|
-
Failed HTTP responses throw `TapiError`:
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
import { TapiError } from "@tapi-dev/sdk";
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
await tapi.catalog.get();
|
|
156
|
-
} catch (error) {
|
|
157
|
-
if (error instanceof TapiError) {
|
|
158
|
-
console.error(error.status, error.code, error.details);
|
|
159
|
-
}
|
|
160
|
-
throw error;
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
## TypeScript
|
|
165
|
-
|
|
166
|
-
The package includes generated TypeScript declarations. Common exported types include:
|
|
167
|
-
|
|
168
|
-
```ts
|
|
169
|
-
import type {
|
|
170
|
-
RuntimeRequirements,
|
|
171
|
-
SdkCatalog,
|
|
172
|
-
TapiRun,
|
|
173
|
-
TapiRunner,
|
|
174
|
-
WebsiteApiRunRequest,
|
|
175
|
-
} from "@tapi-dev/sdk";
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
## Local Development
|
|
179
|
-
|
|
180
|
-
From this SDK directory:
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
npm ci
|
|
184
|
-
npm test
|
|
185
|
-
npm run build
|
|
186
|
-
npm pack --dry-run
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
`npm pack --dry-run` shows the exact files that will be published.
|
|
1
|
+
# TAPI JavaScript SDK
|
|
2
|
+
|
|
3
|
+
Official JavaScript and TypeScript client for TAPI developer APIs.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @tapi-dev/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This package is ESM-first and works in runtimes with `fetch`, including modern Node.js and browser-like server runtimes.
|
|
12
|
+
|
|
13
|
+
## Install Tapi Studio
|
|
14
|
+
|
|
15
|
+
Tapi Studio is the desktop app used to author and test local Tapi integrations. Install the SDK first, then use the bundled CLI:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @tapi-dev/sdk
|
|
19
|
+
npx tapi studio install --channel pilot
|
|
20
|
+
npx tapi studio open
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
You can also run the CLI directly from npm without adding the package first:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx @tapi-dev/sdk studio install --channel pilot
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The CLI reads the release manifest from:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
https://d4xaf52nfwiok.cloudfront.net/studio/channels/pilot/latest.json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
It downloads the Windows installer, verifies the manifest SHA256, caches the installer locally, and runs it. The installer cache defaults to:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
%LOCALAPPDATA%\Tapi\Studio\downloads
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Useful commands:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx tapi studio install --channel pilot
|
|
45
|
+
npx tapi studio install --channel pilot --download-only
|
|
46
|
+
npx tapi studio install --manifest https://d4xaf52nfwiok.cloudfront.net/studio/channels/pilot/latest.json
|
|
47
|
+
npx tapi studio open
|
|
48
|
+
npx tapi studio doctor
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Environment overrides:
|
|
52
|
+
|
|
53
|
+
```env
|
|
54
|
+
TAPI_STUDIO_CHANNEL=pilot
|
|
55
|
+
TAPI_STUDIO_MANIFEST_URL=https://d4xaf52nfwiok.cloudfront.net/studio/channels/pilot/latest.json
|
|
56
|
+
TAPI_DOWNLOADS_BASE_URL=https://d4xaf52nfwiok.cloudfront.net
|
|
57
|
+
TAPI_STUDIO_EXE=C:\Users\you\AppData\Local\Tapi Studio\Tapi Studio.exe
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Tapi Studio desktop releases are currently published for Windows x64.
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
Create one TAPI client in your app's server-side code:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { TapiClient } from "@tapi-dev/sdk";
|
|
68
|
+
|
|
69
|
+
export const tapi = new TapiClient({
|
|
70
|
+
baseUrl: process.env.TAPI_BASE_URL!,
|
|
71
|
+
apiKey: process.env.TAPI_API_KEY!,
|
|
72
|
+
appId: process.env.TAPI_APP_ID,
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Then call a TAPI website API:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const run = await tapi.websiteApis.run("brokerage.submitTrade", {
|
|
80
|
+
inputs: {
|
|
81
|
+
symbol: "AAPL",
|
|
82
|
+
quantity: 1,
|
|
83
|
+
side: "buy",
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const completedRun = await tapi.runs.wait(run.id);
|
|
88
|
+
console.log(completedRun.status, completedRun.result);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Do not expose `TAPI_API_KEY` in public browser bundles. Put the SDK behind your own backend route, server action, or job worker when using secret API keys.
|
|
92
|
+
|
|
93
|
+
## Configuration
|
|
94
|
+
|
|
95
|
+
```env
|
|
96
|
+
TAPI_BASE_URL=https://your-tapi-api-host
|
|
97
|
+
TAPI_API_KEY=tapi_your_api_key
|
|
98
|
+
TAPI_APP_ID=your-app-id
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`appId` is optional. If provided, the SDK sends it as the `X-Tapi-App` header.
|
|
102
|
+
|
|
103
|
+
## Common Project Setup
|
|
104
|
+
|
|
105
|
+
A typical application keeps the client in one small module:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
src/
|
|
109
|
+
lib/
|
|
110
|
+
tapi.ts
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
// src/lib/tapi.ts
|
|
115
|
+
import { TapiClient } from "@tapi-dev/sdk";
|
|
116
|
+
|
|
117
|
+
export const tapi = new TapiClient({
|
|
118
|
+
baseUrl: process.env.TAPI_BASE_URL!,
|
|
119
|
+
apiKey: process.env.TAPI_API_KEY!,
|
|
120
|
+
appId: process.env.TAPI_APP_ID,
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Application code should import this shared client instead of constructing a new client in every file.
|
|
125
|
+
|
|
126
|
+
## Available Resources
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
await tapi.catalog.get();
|
|
130
|
+
await tapi.runners.list();
|
|
131
|
+
await tapi.runtime.requirements();
|
|
132
|
+
|
|
133
|
+
const run = await tapi.websiteApis.run("apiName.requestKey", {
|
|
134
|
+
inputs: { example: true },
|
|
135
|
+
priority: 5,
|
|
136
|
+
runnerId: "runner-id",
|
|
137
|
+
idempotencyKey: "request-123",
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await tapi.runs.get(run.id);
|
|
141
|
+
await tapi.runs.wait(run.id, { intervalMs: 1000, timeoutMs: 300000 });
|
|
142
|
+
await tapi.runs.cancel(run.id);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Website API requests are addressed as `<apiName>.<requestKey>`.
|
|
146
|
+
|
|
147
|
+
## Errors
|
|
148
|
+
|
|
149
|
+
Failed HTTP responses throw `TapiError`:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { TapiError } from "@tapi-dev/sdk";
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
await tapi.catalog.get();
|
|
156
|
+
} catch (error) {
|
|
157
|
+
if (error instanceof TapiError) {
|
|
158
|
+
console.error(error.status, error.code, error.details);
|
|
159
|
+
}
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## TypeScript
|
|
165
|
+
|
|
166
|
+
The package includes generated TypeScript declarations. Common exported types include:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import type {
|
|
170
|
+
RuntimeRequirements,
|
|
171
|
+
SdkCatalog,
|
|
172
|
+
TapiRun,
|
|
173
|
+
TapiRunner,
|
|
174
|
+
WebsiteApiRunRequest,
|
|
175
|
+
} from "@tapi-dev/sdk";
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Local Development
|
|
179
|
+
|
|
180
|
+
From this SDK directory:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npm ci
|
|
184
|
+
npm test
|
|
185
|
+
npm run build
|
|
186
|
+
npm pack --dry-run
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`npm pack --dry-run` shows the exact files that will be published.
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import { basename, join, resolve } from "node:path";
|
|
|
8
8
|
import { Readable } from "node:stream";
|
|
9
9
|
import { pipeline } from "node:stream/promises";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
-
const DEFAULT_DOWNLOADS_BASE_URL = "https://
|
|
11
|
+
const DEFAULT_DOWNLOADS_BASE_URL = "https://d4xaf52nfwiok.cloudfront.net";
|
|
12
12
|
const DEFAULT_CHANNEL = "pilot";
|
|
13
13
|
const SUPPORTED_STUDIO_PLATFORM = "windows-x86_64";
|
|
14
14
|
const sdkVersion = readSdkVersion();
|
|
@@ -109,10 +109,11 @@ export function parseStudioOptions(args) {
|
|
|
109
109
|
throw new Error(`Unknown option: ${arg}`);
|
|
110
110
|
}
|
|
111
111
|
const channel = raw.channel ?? parseChannel(envString("TAPI_STUDIO_CHANNEL") ?? DEFAULT_CHANNEL);
|
|
112
|
-
const downloadsBaseUrl = envString("TAPI_DOWNLOADS_BASE_URL") ?? DEFAULT_DOWNLOADS_BASE_URL;
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
const downloadsBaseUrl = normalizeHttpUrl(envString("TAPI_DOWNLOADS_BASE_URL") ?? DEFAULT_DOWNLOADS_BASE_URL, "TAPI_DOWNLOADS_BASE_URL");
|
|
113
|
+
const explicitManifestUrl = raw.manifestUrl ?? envString("TAPI_STUDIO_MANIFEST_URL");
|
|
114
|
+
const manifestUrl = explicitManifestUrl
|
|
115
|
+
? normalizeHttpUrl(explicitManifestUrl, "Studio manifest URL")
|
|
116
|
+
: `${downloadsBaseUrl.replace(/\/+$/, "")}/studio/channels/${channel}/latest.json`;
|
|
116
117
|
return {
|
|
117
118
|
channel,
|
|
118
119
|
manifestUrl,
|
|
@@ -152,7 +153,7 @@ export function validateStudioManifest(input) {
|
|
|
152
153
|
channel: requiredString(record, "channel"),
|
|
153
154
|
platform: requiredString(record, "platform"),
|
|
154
155
|
artifactName: requiredString(record, "artifactName"),
|
|
155
|
-
url: requiredString(record, "url"),
|
|
156
|
+
url: normalizeHttpUrl(requiredString(record, "url"), "Studio manifest url"),
|
|
156
157
|
sha256: requiredString(record, "sha256").toLowerCase(),
|
|
157
158
|
sizeBytes: optionalNumber(record, "sizeBytes"),
|
|
158
159
|
minSdkVersion: optionalString(record, "minSdkVersion"),
|
|
@@ -162,9 +163,6 @@ export function validateStudioManifest(input) {
|
|
|
162
163
|
ref: optionalString(record, "ref"),
|
|
163
164
|
installerKind: optionalString(record, "installerKind"),
|
|
164
165
|
};
|
|
165
|
-
if (!/^https?:\/\//i.test(manifest.url)) {
|
|
166
|
-
throw new Error("Studio manifest url must be an absolute HTTP(S) URL.");
|
|
167
|
-
}
|
|
168
166
|
if (!/^[a-f0-9]{64}$/i.test(manifest.sha256)) {
|
|
169
167
|
throw new Error("Studio manifest sha256 must be a 64-character hex digest.");
|
|
170
168
|
}
|
|
@@ -363,6 +361,23 @@ function envString(name) {
|
|
|
363
361
|
const value = process.env[name];
|
|
364
362
|
return value && value.trim() ? value.trim() : undefined;
|
|
365
363
|
}
|
|
364
|
+
function normalizeHttpUrl(value, label) {
|
|
365
|
+
const trimmed = value.trim();
|
|
366
|
+
const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
367
|
+
try {
|
|
368
|
+
const url = new URL(withScheme);
|
|
369
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
370
|
+
throw new Error(`${label} must use HTTP or HTTPS.`);
|
|
371
|
+
}
|
|
372
|
+
return url.toString();
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
if (error instanceof Error && error.message.includes("must use HTTP or HTTPS")) {
|
|
376
|
+
throw error;
|
|
377
|
+
}
|
|
378
|
+
throw new Error(`${label} must be a valid HTTP(S) URL.`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
366
381
|
function hasHelpFlag(args) {
|
|
367
382
|
return args.some((arg) => arg === "--help" || arg === "-h");
|
|
368
383
|
}
|