@tapi-dev/sdk 0.1.21 → 0.1.24
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 +12 -13
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +201 -170
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/services.d.ts +17 -0
- package/dist/{website-apis.js → services.js} +18 -18
- package/dist/triggers.js +6 -6
- package/dist/types.d.ts +13 -2
- package/package.json +1 -1
- package/dist/website-apis.d.ts +0 -17
package/README.md
CHANGED
|
@@ -63,19 +63,18 @@ tapi sessions
|
|
|
63
63
|
tapi publish
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
Local
|
|
67
|
-
|
|
68
|
-
```text
|
|
69
|
-
.tapi/project.json
|
|
70
|
-
.tapi/
|
|
71
|
-
.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
SDK calls read the active release catalog, not mutable Studio drafts.
|
|
66
|
+
Local project identity lives in the app repo:
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
.tapi/project.json
|
|
70
|
+
.tapi/generated/catalog.json
|
|
71
|
+
src/tapi.generated.ts
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Sitemaps and API contracts are saved in the bound Tapi server project. Run
|
|
75
|
+
`tapi publish` to mark ready server-side requests as published and activate one
|
|
76
|
+
immutable server release. Runtime SDK calls read the active release catalog, not
|
|
77
|
+
mutable Studio drafts.
|
|
79
78
|
|
|
80
79
|
```bash
|
|
81
80
|
TAPI_API_KEY=tapi_project_key tapi publish
|
package/dist/cli.d.ts
CHANGED
|
@@ -75,6 +75,8 @@ export interface ServiceStatus {
|
|
|
75
75
|
installedVersion?: string;
|
|
76
76
|
}
|
|
77
77
|
export type FetchLike = typeof fetch;
|
|
78
|
+
type StudioPortAvailable = (port: number) => Promise<boolean>;
|
|
79
|
+
type StudioPortReaper = (preferred: number, count: number) => Promise<number>;
|
|
78
80
|
export declare function runCli(argv?: string[]): Promise<number>;
|
|
79
81
|
export declare function parseStudioOptions(args: string[]): StudioCliOptions;
|
|
80
82
|
export declare function trySelectDevSessionInOpenStudio(session: TapiDevSession, options: StudioCliOptions, fetchImpl?: FetchLike): Promise<{
|
|
@@ -86,6 +88,7 @@ export declare function installedServiceVersionFromPathName(pathName: string): s
|
|
|
86
88
|
export declare function serviceNeedsInstall(status: ServiceStatus, manifest: ServiceReleaseManifest): boolean;
|
|
87
89
|
export declare function getDefaultStudioCacheDir(): string;
|
|
88
90
|
export declare function getDefaultServiceCacheDir(): string;
|
|
91
|
+
export declare function getDefaultMachineServiceReleasesDir(): string | undefined;
|
|
89
92
|
interface ServiceInstallLogPaths {
|
|
90
93
|
logPath: string;
|
|
91
94
|
resultPath: string;
|
|
@@ -113,11 +116,19 @@ export declare function compareVersions(left: string, right: string): number;
|
|
|
113
116
|
export declare function memoizeStudioInstallToken(options: StudioCliOptions, installToken?: string): string;
|
|
114
117
|
export declare function findPortablePlaywrightBrowsersDir(exePath: string): string | undefined;
|
|
115
118
|
export declare function waitForHttpOk(url: string, timeoutMs?: number, intervalMs?: number, fetchImpl?: FetchLike): Promise<void>;
|
|
119
|
+
export declare function chooseStudioPort(preferred?: number, options?: {
|
|
120
|
+
count?: number;
|
|
121
|
+
portAvailable?: StudioPortAvailable;
|
|
122
|
+
reapBlockedPorts?: StudioPortReaper;
|
|
123
|
+
}): Promise<number>;
|
|
116
124
|
export declare function studioInstanceRecordPathForOptions(options: StudioCliOptions): string;
|
|
117
125
|
export declare function findPortableStudioServerExe(releaseDir: string, manifest: StudioReleaseManifest): string | undefined;
|
|
126
|
+
export declare function estimateRequiredExtractionBytes(zipBytes: number): number;
|
|
127
|
+
export declare function formatInsufficientExtractionSpace(zipPath: string, destination: string, availableBytes: number, requiredBytes: number): string;
|
|
118
128
|
export declare function buildExpandArchiveCommand(zipPath: string, destination: string): string;
|
|
119
129
|
export declare function formatZipExtractionFailure(zipPath: string, destination: string, error: unknown): string;
|
|
120
130
|
export declare function pruneManagedFiles(directory: string, keepPaths: string[], isManagedFileName: (name: string) => boolean, maxNoncurrentEntries?: number, label?: string): Promise<number>;
|
|
131
|
+
export declare function pruneTapiReleaseDirs(directory: string, keepPaths: string[], isManagedDirName: (name: string) => boolean, maxNoncurrentEntries?: number, label?: string): Promise<number>;
|
|
121
132
|
export declare function isTapiStudioDownloadArtifact(fileName: string): boolean;
|
|
122
133
|
export declare function isTapiServiceDownloadArtifact(fileName: string): boolean;
|
|
123
134
|
export {};
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createServer } from "node:http";
|
|
|
4
4
|
import { createServer as createNetServer } from "node:net";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
6
6
|
import { createReadStream, createWriteStream, existsSync, readFileSync, readdirSync } from "node:fs";
|
|
7
|
-
import { mkdir, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
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";
|
|
10
10
|
import { performance } from "node:perf_hooks";
|
|
@@ -27,6 +27,8 @@ const MAX_WIDE_EVENT_PROCESS_ROOTS = 5;
|
|
|
27
27
|
const MAX_NONCURRENT_MANAGED_DOWNLOADS = 0;
|
|
28
28
|
const MAX_NONCURRENT_MANAGED_RELEASES = 0;
|
|
29
29
|
const MANAGED_RELEASE_MARKER = ".tapi-managed-release";
|
|
30
|
+
const MIN_EXTRACTION_REQUIRED_BYTES = 1024 * 1024 * 1024;
|
|
31
|
+
const EXTRACTION_REQUIRED_MULTIPLIER = 3;
|
|
30
32
|
class StudioInstallApprovalError extends Error {
|
|
31
33
|
code;
|
|
32
34
|
status;
|
|
@@ -138,13 +140,6 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
138
140
|
}
|
|
139
141
|
return runServiceCommand(subcommand, rest);
|
|
140
142
|
}
|
|
141
|
-
if (command === "publish") {
|
|
142
|
-
if (hasHelpFlag([subcommand, ...rest])) {
|
|
143
|
-
printPublishHelp();
|
|
144
|
-
return 0;
|
|
145
|
-
}
|
|
146
|
-
return publishLocalApis([subcommand, ...rest].filter(Boolean));
|
|
147
|
-
}
|
|
148
143
|
if (command === "apis") {
|
|
149
144
|
if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h" || hasHelpFlag(rest)) {
|
|
150
145
|
printApisHelp();
|
|
@@ -438,7 +433,7 @@ async function describeApiOperation(args) {
|
|
|
438
433
|
apiKey: options.apiKey,
|
|
439
434
|
projectId: options.projectId,
|
|
440
435
|
});
|
|
441
|
-
const description = await withCliSpinner(`Fetching Tapi API description for ${operation}`, () => client.
|
|
436
|
+
const description = await withCliSpinner(`Fetching Tapi API description for ${operation}`, () => client.services.describe(operation));
|
|
442
437
|
console.log(JSON.stringify(description, null, 2));
|
|
443
438
|
return 0;
|
|
444
439
|
}
|
|
@@ -1215,7 +1210,7 @@ function renderGeneratedApiClient(catalog) {
|
|
|
1215
1210
|
}
|
|
1216
1211
|
}
|
|
1217
1212
|
const namespaceBlocks = [...namespaces.entries()].map(([namespace, operations]) => {
|
|
1218
|
-
const lines = operations.map(({ key, operationName }) => ` ${operationName}: (inputs: Record<string, unknown> = {}, options: GeneratedRunOptions = {}) => client.
|
|
1213
|
+
const lines = operations.map(({ key, operationName }) => ` ${operationName}: (inputs: Record<string, unknown> = {}, options: GeneratedRunOptions = {}) => client.services.run(${JSON.stringify(key)}, { ...options, inputs }),`);
|
|
1219
1214
|
return ` ${namespace}: {\n${lines.join("\n")}\n },`;
|
|
1220
1215
|
});
|
|
1221
1216
|
return `/* Generated by Tapi. Do not edit by hand. */
|
|
@@ -1521,12 +1516,26 @@ async function installService(options) {
|
|
|
1521
1516
|
manifest: serviceManifestEventSummary(manifest),
|
|
1522
1517
|
});
|
|
1523
1518
|
ensureCompatibleServiceManifest(manifest);
|
|
1519
|
+
const serviceStatus = await getServiceStatus().catch(() => ({
|
|
1520
|
+
installed: false,
|
|
1521
|
+
name: "tapi-service",
|
|
1522
|
+
status: "unknown",
|
|
1523
|
+
}));
|
|
1524
1524
|
const serviceCacheDir = getDefaultServiceCacheDir();
|
|
1525
1525
|
const downloadsDir = join(serviceCacheDir, "downloads");
|
|
1526
1526
|
const releasesDir = join(serviceCacheDir, "releases");
|
|
1527
1527
|
await mkdir(downloadsDir, { recursive: true });
|
|
1528
1528
|
await mkdir(releasesDir, { recursive: true });
|
|
1529
1529
|
const zipPath = join(downloadsDir, cachedServiceArtifactName(manifest));
|
|
1530
|
+
const releaseDir = join(releasesDir, safePathSegment(manifest.version));
|
|
1531
|
+
await pruneServiceInstallStorageBeforeDownload({
|
|
1532
|
+
downloadsDir,
|
|
1533
|
+
zipPath,
|
|
1534
|
+
releasesDir,
|
|
1535
|
+
releaseDir,
|
|
1536
|
+
manifestVersion: manifest.version,
|
|
1537
|
+
installedVersion: serviceStatus.installedVersion,
|
|
1538
|
+
});
|
|
1530
1539
|
const verified = await hasVerifiedCachedInstaller(zipPath, manifest.sha256);
|
|
1531
1540
|
if (verified) {
|
|
1532
1541
|
console.log(`Using cached Tapi Service artifact: ${zipPath}`);
|
|
@@ -1552,7 +1561,8 @@ async function installService(options) {
|
|
|
1552
1561
|
});
|
|
1553
1562
|
return 0;
|
|
1554
1563
|
}
|
|
1555
|
-
|
|
1564
|
+
await pruneManagedFiles(downloadsDir, [zipPath], isTapiServiceDownloadArtifact, MAX_NONCURRENT_MANAGED_DOWNLOADS, "old Tapi Service download");
|
|
1565
|
+
await pruneTapiReleaseDirs(releasesDir, [releaseDir], isSafeManagedReleaseDirName, MAX_NONCURRENT_MANAGED_RELEASES, "old Tapi Service release");
|
|
1556
1566
|
await withCliSpinner(`Extracting Tapi Service ${manifest.version}`, () => extractZip(zipPath, releaseDir));
|
|
1557
1567
|
await markManagedReleaseDir(releaseDir, manifest.version);
|
|
1558
1568
|
const installer = join(releaseDir, manifest.installer || "install_tapi_service.ps1");
|
|
@@ -1597,7 +1607,7 @@ async function installService(options) {
|
|
|
1597
1607
|
version: manifest.version,
|
|
1598
1608
|
});
|
|
1599
1609
|
await pruneManagedFiles(downloadsDir, [zipPath], isTapiServiceDownloadArtifact, MAX_NONCURRENT_MANAGED_DOWNLOADS, "old Tapi Service download");
|
|
1600
|
-
await
|
|
1610
|
+
await pruneTapiReleaseDirs(releasesDir, [releaseDir], isSafeManagedReleaseDirName, MAX_NONCURRENT_MANAGED_RELEASES, "old Tapi Service release");
|
|
1601
1611
|
console.log("Tapi Service is installed and running.");
|
|
1602
1612
|
return 0;
|
|
1603
1613
|
}
|
|
@@ -1608,147 +1618,6 @@ async function installService(options) {
|
|
|
1608
1618
|
throw error;
|
|
1609
1619
|
}
|
|
1610
1620
|
}
|
|
1611
|
-
async function publishLocalApis(args) {
|
|
1612
|
-
try {
|
|
1613
|
-
const options = parseApiWorkspaceOptions(args);
|
|
1614
|
-
const workspace = loadWorkspace();
|
|
1615
|
-
const root = workspace?.root || process.cwd();
|
|
1616
|
-
const sitemaps = await readLocalSitemaps(join(root, ".tapi", "sitemaps"));
|
|
1617
|
-
const apis = await readLocalGeneratedApis(join(root, ".tapi", "apis"));
|
|
1618
|
-
if (apis.length === 0) {
|
|
1619
|
-
throw new Error("No local generated APIs found under .tapi/apis.");
|
|
1620
|
-
}
|
|
1621
|
-
const { sitemapCount, contracts, requests, releaseVersion } = await withCliSpinner("Publishing local Tapi APIs", async () => {
|
|
1622
|
-
let sitemapCount = 0;
|
|
1623
|
-
for (const sitemap of sitemaps) {
|
|
1624
|
-
const site = String(sitemap.site || "").trim();
|
|
1625
|
-
if (!site || !isRecord(sitemap.siteMap)) {
|
|
1626
|
-
continue;
|
|
1627
|
-
}
|
|
1628
|
-
await putJson(`${options.apiBaseUrl.replace(/\/+$/, "")}/api/v1/sitemaps/${encodeURIComponent(site)}?project=${encodeURIComponent(options.projectId)}`, { site_map: sitemap.siteMap }, options.apiKey, options.projectId);
|
|
1629
|
-
sitemapCount += 1;
|
|
1630
|
-
}
|
|
1631
|
-
let contracts = 0;
|
|
1632
|
-
let requests = 0;
|
|
1633
|
-
for (const api of apis) {
|
|
1634
|
-
const apiId = String(api.id || api.name || "").trim();
|
|
1635
|
-
if (!apiId) {
|
|
1636
|
-
continue;
|
|
1637
|
-
}
|
|
1638
|
-
const site = String(api.site || "").trim();
|
|
1639
|
-
await putJson(`${options.apiBaseUrl.replace(/\/+$/, "")}/api/v1/generated-apis/contracts/${encodeURIComponent(apiId)}?project=${encodeURIComponent(options.projectId)}`, { generated_api: api }, options.apiKey, options.projectId);
|
|
1640
|
-
contracts += 1;
|
|
1641
|
-
const apiRequests = api.requests && typeof api.requests === "object" ? Object.values(api.requests) : [];
|
|
1642
|
-
for (const request of apiRequests) {
|
|
1643
|
-
if (!isRecord(request)) {
|
|
1644
|
-
continue;
|
|
1645
|
-
}
|
|
1646
|
-
const requestId = String(request.id || request.key || "").trim();
|
|
1647
|
-
const status = String(request.status || "").trim();
|
|
1648
|
-
if (!requestId || !["ready", "published"].includes(status)) {
|
|
1649
|
-
continue;
|
|
1650
|
-
}
|
|
1651
|
-
await postJson(`${options.apiBaseUrl.replace(/\/+$/, "")}/api/v1/generated-apis/contracts/${encodeURIComponent(apiId)}/requests/${encodeURIComponent(requestId)}/publish?project=${encodeURIComponent(options.projectId)}`, { site }, options.apiKey, options.projectId);
|
|
1652
|
-
requests += 1;
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
const releaseResponse = await postJson(`${options.apiBaseUrl.replace(/\/+$/, "")}/api/v1/generated-apis/releases?project=${encodeURIComponent(options.projectId)}`, { environment: "production" }, options.apiKey, options.projectId);
|
|
1656
|
-
const release = isRecord(releaseResponse) && isRecord(releaseResponse.release) ? releaseResponse.release : {};
|
|
1657
|
-
const releaseVersion = typeof release.version === "string" ? release.version : "unknown";
|
|
1658
|
-
return { sitemapCount, contracts, requests, releaseVersion };
|
|
1659
|
-
});
|
|
1660
|
-
console.log(`Published ${requests} API request(s) from ${contracts} contract(s), synced ${sitemapCount} sitemap(s), activated release ${releaseVersion}.`);
|
|
1661
|
-
return 0;
|
|
1662
|
-
}
|
|
1663
|
-
catch (error) {
|
|
1664
|
-
console.error(formatError(error));
|
|
1665
|
-
return 1;
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
async function readLocalSitemaps(root) {
|
|
1669
|
-
const sitemaps = [];
|
|
1670
|
-
if (!existsSync(root)) {
|
|
1671
|
-
return sitemaps;
|
|
1672
|
-
}
|
|
1673
|
-
const entries = await readdir(root, { withFileTypes: true });
|
|
1674
|
-
for (const entry of entries) {
|
|
1675
|
-
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
1676
|
-
continue;
|
|
1677
|
-
}
|
|
1678
|
-
const path = join(root, entry.name);
|
|
1679
|
-
try {
|
|
1680
|
-
const payload = JSON.parse(readFileSync(path, "utf8"));
|
|
1681
|
-
if (!isRecord(payload) || !isRecord(payload.siteMap)) {
|
|
1682
|
-
continue;
|
|
1683
|
-
}
|
|
1684
|
-
const site = String(payload.site || payload.siteMap.site || "").trim();
|
|
1685
|
-
if (site) {
|
|
1686
|
-
sitemaps.push({ site, siteMap: payload.siteMap });
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
catch {
|
|
1690
|
-
continue;
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
return sitemaps;
|
|
1694
|
-
}
|
|
1695
|
-
async function readLocalGeneratedApis(root) {
|
|
1696
|
-
const apis = [];
|
|
1697
|
-
if (!existsSync(root)) {
|
|
1698
|
-
return apis;
|
|
1699
|
-
}
|
|
1700
|
-
const entries = await readdir(root, { withFileTypes: true });
|
|
1701
|
-
for (const entry of entries) {
|
|
1702
|
-
const path = join(root, entry.name);
|
|
1703
|
-
if (entry.isDirectory()) {
|
|
1704
|
-
apis.push(...await readLocalGeneratedApis(path));
|
|
1705
|
-
continue;
|
|
1706
|
-
}
|
|
1707
|
-
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
1708
|
-
continue;
|
|
1709
|
-
}
|
|
1710
|
-
try {
|
|
1711
|
-
const payload = JSON.parse(readFileSync(path, "utf8"));
|
|
1712
|
-
if (isRecord(payload) && isRecord(payload.api)) {
|
|
1713
|
-
apis.push(payload.api);
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
catch {
|
|
1717
|
-
continue;
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
return apis;
|
|
1721
|
-
}
|
|
1722
|
-
async function putJson(url, body, apiKey, projectId) {
|
|
1723
|
-
return requestJson("PUT", url, body, apiKey, projectId);
|
|
1724
|
-
}
|
|
1725
|
-
async function postJson(url, body, apiKey, projectId) {
|
|
1726
|
-
return requestJson("POST", url, body, apiKey, projectId);
|
|
1727
|
-
}
|
|
1728
|
-
async function requestJson(method, url, body, apiKey, projectId) {
|
|
1729
|
-
const response = await fetch(url, {
|
|
1730
|
-
method,
|
|
1731
|
-
headers: {
|
|
1732
|
-
Authorization: `Bearer ${apiKey}`,
|
|
1733
|
-
"Content-Type": "application/json",
|
|
1734
|
-
"X-Tapi-Project": projectId,
|
|
1735
|
-
},
|
|
1736
|
-
body: JSON.stringify(body),
|
|
1737
|
-
});
|
|
1738
|
-
const text = await response.text();
|
|
1739
|
-
if (!response.ok) {
|
|
1740
|
-
throw new Error(`Tapi publish request failed: HTTP ${response.status} ${text}`);
|
|
1741
|
-
}
|
|
1742
|
-
if (!text.trim()) {
|
|
1743
|
-
return {};
|
|
1744
|
-
}
|
|
1745
|
-
try {
|
|
1746
|
-
return JSON.parse(text);
|
|
1747
|
-
}
|
|
1748
|
-
catch {
|
|
1749
|
-
return {};
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
1621
|
export function getDefaultStudioCacheDir() {
|
|
1753
1622
|
const homeOverride = envString("TAPI_STUDIO_HOME");
|
|
1754
1623
|
if (homeOverride) {
|
|
@@ -1769,6 +1638,27 @@ export function getDefaultServiceCacheDir() {
|
|
|
1769
1638
|
}
|
|
1770
1639
|
return join(process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache"), "tapi", "service");
|
|
1771
1640
|
}
|
|
1641
|
+
export function getDefaultMachineServiceReleasesDir() {
|
|
1642
|
+
if (process.platform !== "win32") {
|
|
1643
|
+
return undefined;
|
|
1644
|
+
}
|
|
1645
|
+
const programData = process.env.PROGRAMDATA?.trim() || "C:\\ProgramData";
|
|
1646
|
+
return join(programData, "Tapi", "Service", "releases");
|
|
1647
|
+
}
|
|
1648
|
+
async function pruneServiceInstallStorageBeforeDownload(options) {
|
|
1649
|
+
await pruneManagedFiles(options.downloadsDir, [options.zipPath], isTapiServiceDownloadArtifact, MAX_NONCURRENT_MANAGED_DOWNLOADS, "old Tapi Service download");
|
|
1650
|
+
await rm(options.releaseDir, { recursive: true, force: true }).catch(() => undefined);
|
|
1651
|
+
await pruneTapiReleaseDirs(options.releasesDir, [options.releaseDir], isSafeManagedReleaseDirName, MAX_NONCURRENT_MANAGED_RELEASES, "old Tapi Service release");
|
|
1652
|
+
const machineReleasesDir = getDefaultMachineServiceReleasesDir();
|
|
1653
|
+
if (!machineReleasesDir) {
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
const keepPaths = [join(machineReleasesDir, safePathSegment(options.manifestVersion))];
|
|
1657
|
+
if (options.installedVersion) {
|
|
1658
|
+
keepPaths.push(join(machineReleasesDir, safePathSegment(options.installedVersion)));
|
|
1659
|
+
}
|
|
1660
|
+
await pruneTapiReleaseDirs(machineReleasesDir, keepPaths, isSafeManagedReleaseDirName, MAX_NONCURRENT_MANAGED_RELEASES, "old machine-scope Tapi Service release");
|
|
1661
|
+
}
|
|
1772
1662
|
export function getDefaultServiceInstallLogDir() {
|
|
1773
1663
|
return join(getDefaultServiceCacheDir(), "logs");
|
|
1774
1664
|
}
|
|
@@ -2340,9 +2230,11 @@ async function installPortableStudioServer(options, manifest, event) {
|
|
|
2340
2230
|
return artifactPath;
|
|
2341
2231
|
}
|
|
2342
2232
|
const releaseDir = portableStudioServerReleaseDir(manifest, options.installDir);
|
|
2233
|
+
await pruneManagedFiles(options.cacheDir, [artifactPath], isTapiStudioDownloadArtifact, MAX_NONCURRENT_MANAGED_DOWNLOADS, "old Tapi Studio download");
|
|
2234
|
+
await pruneTapiReleaseDirs(dirname(releaseDir), [releaseDir], isSafeManagedReleaseDirName, MAX_NONCURRENT_MANAGED_RELEASES, "old Tapi Studio server release");
|
|
2343
2235
|
const exePath = await withCliSpinner(`Extracting Tapi Studio server ${manifest.version}`, () => extractPortableStudioServerZip(artifactPath, releaseDir, manifest));
|
|
2344
2236
|
await pruneManagedFiles(options.cacheDir, [artifactPath], isTapiStudioDownloadArtifact, MAX_NONCURRENT_MANAGED_DOWNLOADS, "old Tapi Studio download");
|
|
2345
|
-
await
|
|
2237
|
+
await pruneTapiReleaseDirs(dirname(releaseDir), [releaseDir], isSafeManagedReleaseDirName, MAX_NONCURRENT_MANAGED_RELEASES, "old Tapi Studio server release");
|
|
2346
2238
|
console.log(`Installed Tapi Studio server ${manifest.version}: ${releaseDir}`);
|
|
2347
2239
|
return exePath;
|
|
2348
2240
|
}
|
|
@@ -2354,6 +2246,7 @@ async function launchPortableStudioServer(exePath, options, manifest) {
|
|
|
2354
2246
|
env.TAPI_RUNTIME_NAMESPACE = "installed";
|
|
2355
2247
|
env.TAPI_RUNTIME_CONTROL_PORT = env.TAPI_RUNTIME_CONTROL_PORT || "8765";
|
|
2356
2248
|
env.TAPI_PROCESS_ROLE = "studio_server";
|
|
2249
|
+
env.TAPI_STUDIO_EXIT_ON_IDLE = "1";
|
|
2357
2250
|
env.TAPI_STUDIO_OPEN_BROWSER = "0";
|
|
2358
2251
|
env.TAPI_STUDIO_HOST = host;
|
|
2359
2252
|
env.TAPI_STUDIO_PORT = String(port);
|
|
@@ -2458,12 +2351,28 @@ function delay(ms) {
|
|
|
2458
2351
|
setTimeout(resolvePromise, Math.max(0, ms));
|
|
2459
2352
|
});
|
|
2460
2353
|
}
|
|
2461
|
-
async function chooseStudioPort(preferred = 18766) {
|
|
2462
|
-
|
|
2463
|
-
|
|
2354
|
+
export async function chooseStudioPort(preferred = 18766, options = {}) {
|
|
2355
|
+
const count = options.count ?? 25;
|
|
2356
|
+
const portAvailable = options.portAvailable ?? isPortAvailable;
|
|
2357
|
+
const reapBlockedPorts = options.reapBlockedPorts ?? reapPortableStudioServersBlockingPorts;
|
|
2358
|
+
const initiallyReaped = await reapBlockedPorts(preferred, count);
|
|
2359
|
+
if (initiallyReaped > 0) {
|
|
2360
|
+
await delay(250);
|
|
2361
|
+
}
|
|
2362
|
+
for (let port = preferred; port < preferred + count; port += 1) {
|
|
2363
|
+
if (await portAvailable(port)) {
|
|
2464
2364
|
return port;
|
|
2465
2365
|
}
|
|
2466
2366
|
}
|
|
2367
|
+
const reaped = await reapBlockedPorts(preferred, count);
|
|
2368
|
+
if (reaped > 0) {
|
|
2369
|
+
await delay(250);
|
|
2370
|
+
for (let port = preferred; port < preferred + count; port += 1) {
|
|
2371
|
+
if (await portAvailable(port)) {
|
|
2372
|
+
return port;
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2467
2376
|
throw new Error(`No available local Studio port found starting at ${preferred}.`);
|
|
2468
2377
|
}
|
|
2469
2378
|
async function isPortAvailable(port) {
|
|
@@ -2475,6 +2384,56 @@ async function isPortAvailable(port) {
|
|
|
2475
2384
|
});
|
|
2476
2385
|
});
|
|
2477
2386
|
}
|
|
2387
|
+
async function reapPortableStudioServersBlockingPorts(preferred, count) {
|
|
2388
|
+
if (process.platform !== "win32") {
|
|
2389
|
+
return 0;
|
|
2390
|
+
}
|
|
2391
|
+
const start = Math.max(1, Math.min(65535, Math.trunc(preferred)));
|
|
2392
|
+
const end = Math.max(start, Math.min(65535, start + Math.max(1, Math.trunc(count)) - 1));
|
|
2393
|
+
const script = `
|
|
2394
|
+
$ErrorActionPreference = 'SilentlyContinue'
|
|
2395
|
+
$ports = ${start}..${end}
|
|
2396
|
+
$connections = @(Get-NetTCPConnection -State Listen | Where-Object { $ports -contains $_.LocalPort })
|
|
2397
|
+
$owners = @{}
|
|
2398
|
+
foreach ($conn in $connections) {
|
|
2399
|
+
$pidValue = [int]$conn.OwningProcess
|
|
2400
|
+
if ($pidValue -le 0 -or $owners.ContainsKey($pidValue)) { continue }
|
|
2401
|
+
$proc = Get-CimInstance Win32_Process -Filter "ProcessId=$pidValue"
|
|
2402
|
+
if ($null -eq $proc) { continue }
|
|
2403
|
+
$path = [string]$proc.ExecutablePath
|
|
2404
|
+
$cmd = [string]$proc.CommandLine
|
|
2405
|
+
$isPortableStudio = (
|
|
2406
|
+
$path -match '\\\\Tapi\\\\Studio\\\\server\\\\releases\\\\' -or
|
|
2407
|
+
$cmd -match 'tapi-studio-server'
|
|
2408
|
+
)
|
|
2409
|
+
if ($isPortableStudio) {
|
|
2410
|
+
$owners[$pidValue] = $true
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
$stopped = 0
|
|
2414
|
+
foreach ($pidValue in $owners.Keys) {
|
|
2415
|
+
try {
|
|
2416
|
+
Stop-Process -Id $pidValue -Force -ErrorAction Stop
|
|
2417
|
+
$stopped += 1
|
|
2418
|
+
} catch {}
|
|
2419
|
+
}
|
|
2420
|
+
[pscustomobject]@{ stopped = $stopped } | ConvertTo-Json -Compress
|
|
2421
|
+
`;
|
|
2422
|
+
try {
|
|
2423
|
+
const output = await runProcessCapture("powershell.exe", [
|
|
2424
|
+
"-NoProfile",
|
|
2425
|
+
"-ExecutionPolicy",
|
|
2426
|
+
"Bypass",
|
|
2427
|
+
"-Command",
|
|
2428
|
+
script,
|
|
2429
|
+
]);
|
|
2430
|
+
const payload = JSON.parse(output.trim() || "{}");
|
|
2431
|
+
return typeof payload.stopped === "number" ? payload.stopped : 0;
|
|
2432
|
+
}
|
|
2433
|
+
catch {
|
|
2434
|
+
return 0;
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2478
2437
|
async function runDoctor(options) {
|
|
2479
2438
|
console.log(`Tapi SDK: ${sdkVersion}`);
|
|
2480
2439
|
console.log(`Node: ${process.version}`);
|
|
@@ -3026,6 +2985,7 @@ function cachedServiceArtifactName(manifest) {
|
|
|
3026
2985
|
async function extractZip(zipPath, destination) {
|
|
3027
2986
|
await rm(destination, { recursive: true, force: true });
|
|
3028
2987
|
await mkdir(destination, { recursive: true });
|
|
2988
|
+
await ensureEnoughDiskSpaceForExtraction(zipPath, destination);
|
|
3029
2989
|
try {
|
|
3030
2990
|
await runProcessCapture("powershell.exe", [
|
|
3031
2991
|
"-NoProfile",
|
|
@@ -3039,6 +2999,60 @@ async function extractZip(zipPath, destination) {
|
|
|
3039
2999
|
throw new Error(formatZipExtractionFailure(zipPath, destination, error), { cause: error });
|
|
3040
3000
|
}
|
|
3041
3001
|
}
|
|
3002
|
+
async function ensureEnoughDiskSpaceForExtraction(zipPath, destination) {
|
|
3003
|
+
const zipInfo = await stat(zipPath).catch(() => undefined);
|
|
3004
|
+
if (!zipInfo?.isFile()) {
|
|
3005
|
+
return;
|
|
3006
|
+
}
|
|
3007
|
+
const requiredBytes = estimateRequiredExtractionBytes(zipInfo.size);
|
|
3008
|
+
const availableBytes = await availableBytesForPath(destination);
|
|
3009
|
+
if (availableBytes === undefined || availableBytes >= requiredBytes) {
|
|
3010
|
+
return;
|
|
3011
|
+
}
|
|
3012
|
+
throw new Error(formatInsufficientExtractionSpace(zipPath, destination, availableBytes, requiredBytes));
|
|
3013
|
+
}
|
|
3014
|
+
export function estimateRequiredExtractionBytes(zipBytes) {
|
|
3015
|
+
const scaled = Math.ceil(Math.max(0, zipBytes) * EXTRACTION_REQUIRED_MULTIPLIER);
|
|
3016
|
+
return Math.max(MIN_EXTRACTION_REQUIRED_BYTES, scaled);
|
|
3017
|
+
}
|
|
3018
|
+
async function availableBytesForPath(targetPath) {
|
|
3019
|
+
let probe = resolve(targetPath);
|
|
3020
|
+
for (;;) {
|
|
3021
|
+
if (existsSync(probe)) {
|
|
3022
|
+
try {
|
|
3023
|
+
const info = await statfs(probe);
|
|
3024
|
+
return Number(info.bavail) * Number(info.bsize);
|
|
3025
|
+
}
|
|
3026
|
+
catch {
|
|
3027
|
+
return undefined;
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
const parent = dirname(probe);
|
|
3031
|
+
if (!parent || parent === probe) {
|
|
3032
|
+
return undefined;
|
|
3033
|
+
}
|
|
3034
|
+
probe = parent;
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
export function formatInsufficientExtractionSpace(zipPath, destination, availableBytes, requiredBytes) {
|
|
3038
|
+
return [
|
|
3039
|
+
`Not enough free disk space to extract ${zipPath}.`,
|
|
3040
|
+
`Destination: ${destination}.`,
|
|
3041
|
+
`Available: ${formatBytes(availableBytes)}; estimated required before extraction: ${formatBytes(requiredBytes)}.`,
|
|
3042
|
+
"Tapi prunes old managed downloads and releases before extraction, but this drive is still too full.",
|
|
3043
|
+
"Free space under %LOCALAPPDATA%\\Tapi or move Studio downloads/releases with TAPI_STUDIO_HOME / --cache-dir / --install-dir.",
|
|
3044
|
+
].join(" ");
|
|
3045
|
+
}
|
|
3046
|
+
function formatBytes(bytes) {
|
|
3047
|
+
if (!Number.isFinite(bytes) || bytes < 0) {
|
|
3048
|
+
return "unknown";
|
|
3049
|
+
}
|
|
3050
|
+
const gib = bytes / (1024 * 1024 * 1024);
|
|
3051
|
+
if (gib >= 1) {
|
|
3052
|
+
return `${gib.toFixed(2)} GB`;
|
|
3053
|
+
}
|
|
3054
|
+
return `${(bytes / (1024 * 1024)).toFixed(0)} MB`;
|
|
3055
|
+
}
|
|
3042
3056
|
export function buildExpandArchiveCommand(zipPath, destination) {
|
|
3043
3057
|
return `$ErrorActionPreference = 'Stop'; try { Expand-Archive -LiteralPath ${powerShellSingleQuoted(zipPath)} -DestinationPath ${powerShellSingleQuoted(destination)} -Force -ErrorAction Stop } catch { Write-Error $_; exit 1 }`;
|
|
3044
3058
|
}
|
|
@@ -3090,7 +3104,7 @@ export async function pruneManagedFiles(directory, keepPaths, isManagedFileName,
|
|
|
3090
3104
|
}
|
|
3091
3105
|
return removed;
|
|
3092
3106
|
}
|
|
3093
|
-
async function
|
|
3107
|
+
export async function pruneTapiReleaseDirs(directory, keepPaths, isManagedDirName, maxNoncurrentEntries = 0, label = "old Tapi release") {
|
|
3094
3108
|
const keep = normalizedKeepPathSet(keepPaths);
|
|
3095
3109
|
let entries;
|
|
3096
3110
|
try {
|
|
@@ -3108,7 +3122,7 @@ async function pruneManagedDirs(directory, keepPaths, isManagedDirName, maxNoncu
|
|
|
3108
3122
|
if (keep.has(normalizeFilesystemPath(candidate))) {
|
|
3109
3123
|
continue;
|
|
3110
3124
|
}
|
|
3111
|
-
if (!
|
|
3125
|
+
if (!(await isTapiManagedReleaseDir(candidate))) {
|
|
3112
3126
|
continue;
|
|
3113
3127
|
}
|
|
3114
3128
|
const info = await stat(candidate).catch(() => undefined);
|
|
@@ -3134,6 +3148,34 @@ async function pruneManagedDirs(directory, keepPaths, isManagedDirName, maxNoncu
|
|
|
3134
3148
|
}
|
|
3135
3149
|
return removed;
|
|
3136
3150
|
}
|
|
3151
|
+
async function isTapiManagedReleaseDir(directory) {
|
|
3152
|
+
if (existsSync(join(directory, MANAGED_RELEASE_MARKER))) {
|
|
3153
|
+
return true;
|
|
3154
|
+
}
|
|
3155
|
+
if (looksLikeTapiServiceReleaseDir(directory) || looksLikeTapiStudioReleaseDir(directory)) {
|
|
3156
|
+
return true;
|
|
3157
|
+
}
|
|
3158
|
+
const entries = await readdir(directory).catch(() => undefined);
|
|
3159
|
+
return Array.isArray(entries) && entries.length === 0;
|
|
3160
|
+
}
|
|
3161
|
+
function looksLikeTapiServiceReleaseDir(directory) {
|
|
3162
|
+
return (existsSync(join(directory, "tapi-service-host.exe"))
|
|
3163
|
+
&& existsSync(join(directory, "install_tapi_service.ps1"))
|
|
3164
|
+
&& (existsSync(join(directory, "tapi-service-worker.exe"))
|
|
3165
|
+
|| existsSync(join(directory, "tapi-service-worker", "tapi-service-worker.exe"))));
|
|
3166
|
+
}
|
|
3167
|
+
function looksLikeTapiStudioReleaseDir(directory) {
|
|
3168
|
+
return Boolean(findPortableStudioServerExe(directory, {
|
|
3169
|
+
version: "",
|
|
3170
|
+
channel: "",
|
|
3171
|
+
platform: SUPPORTED_STUDIO_PLATFORM,
|
|
3172
|
+
artifactName: "tapi-studio-server.zip",
|
|
3173
|
+
url: "",
|
|
3174
|
+
sha256: "",
|
|
3175
|
+
installerKind: "portable-server",
|
|
3176
|
+
serverExecutable: "tapi-studio-server.exe",
|
|
3177
|
+
}));
|
|
3178
|
+
}
|
|
3137
3179
|
export function isTapiStudioDownloadArtifact(fileName) {
|
|
3138
3180
|
const normalized = fileName.toLowerCase();
|
|
3139
3181
|
return (normalized.endsWith(".zip")
|
|
@@ -3310,7 +3352,6 @@ Usage:
|
|
|
3310
3352
|
tapi apis generate
|
|
3311
3353
|
tapi triggers sync
|
|
3312
3354
|
tapi sessions
|
|
3313
|
-
tapi publish
|
|
3314
3355
|
tapi service status
|
|
3315
3356
|
tapi doctor
|
|
3316
3357
|
|
|
@@ -3326,7 +3367,6 @@ Commands:
|
|
|
3326
3367
|
apis generate Generate a TypeScript runtime wrapper from the catalog
|
|
3327
3368
|
triggers sync Upsert API-call triggers from tapi.config
|
|
3328
3369
|
sessions List dev-mode API sessions and open takeover sessions
|
|
3329
|
-
publish Upload local .tapi API drafts and publish ready requests
|
|
3330
3370
|
service Inspect or control the local Tapi Windows service
|
|
3331
3371
|
doctor Alias for studio doctor
|
|
3332
3372
|
`);
|
|
@@ -3451,15 +3491,6 @@ Commands:
|
|
|
3451
3491
|
repair Restart tapi-service using the current installed service
|
|
3452
3492
|
`);
|
|
3453
3493
|
}
|
|
3454
|
-
function printPublishHelp() {
|
|
3455
|
-
console.log(`Tapi publish
|
|
3456
|
-
|
|
3457
|
-
Usage:
|
|
3458
|
-
tapi publish [--api-base-url URL] [--api-key KEY] [--project PROJECT]
|
|
3459
|
-
|
|
3460
|
-
Publishes ready generated API requests from local .tapi/apis files.
|
|
3461
|
-
`);
|
|
3462
|
-
}
|
|
3463
3494
|
function formatError(error) {
|
|
3464
3495
|
return error instanceof Error ? error.message : String(error);
|
|
3465
3496
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,9 @@ import { RunnersResource } from "./runners.js";
|
|
|
4
4
|
import { RunsResource } from "./runs.js";
|
|
5
5
|
import { RuntimeResource } from "./runtime.js";
|
|
6
6
|
import { SessionsResource } from "./sessions.js";
|
|
7
|
+
import { ServicesResource } from "./services.js";
|
|
7
8
|
import { TriggersResource } from "./triggers.js";
|
|
8
9
|
import type { TapiClientOptions } from "./types.js";
|
|
9
|
-
import { WebsiteApisResource } from "./website-apis.js";
|
|
10
10
|
export declare class TapiClient {
|
|
11
11
|
readonly catalog: CatalogResource;
|
|
12
12
|
readonly cloudRuns: CloudRunsResource;
|
|
@@ -14,8 +14,8 @@ export declare class TapiClient {
|
|
|
14
14
|
readonly runs: RunsResource;
|
|
15
15
|
readonly runtime: RuntimeResource;
|
|
16
16
|
readonly sessions: SessionsResource;
|
|
17
|
+
readonly services: ServicesResource;
|
|
17
18
|
readonly triggers: TriggersResource;
|
|
18
|
-
readonly websiteApis: WebsiteApisResource;
|
|
19
19
|
constructor(options: TapiClientOptions);
|
|
20
20
|
}
|
|
21
21
|
export * from "./errors.js";
|
package/dist/index.js
CHANGED
|
@@ -5,8 +5,8 @@ import { RunnersResource } from "./runners.js";
|
|
|
5
5
|
import { RunsResource } from "./runs.js";
|
|
6
6
|
import { RuntimeResource } from "./runtime.js";
|
|
7
7
|
import { SessionsResource } from "./sessions.js";
|
|
8
|
+
import { ServicesResource } from "./services.js";
|
|
8
9
|
import { TriggersResource } from "./triggers.js";
|
|
9
|
-
import { WebsiteApisResource } from "./website-apis.js";
|
|
10
10
|
export class TapiClient {
|
|
11
11
|
catalog;
|
|
12
12
|
cloudRuns;
|
|
@@ -14,8 +14,8 @@ export class TapiClient {
|
|
|
14
14
|
runs;
|
|
15
15
|
runtime;
|
|
16
16
|
sessions;
|
|
17
|
+
services;
|
|
17
18
|
triggers;
|
|
18
|
-
websiteApis;
|
|
19
19
|
constructor(options) {
|
|
20
20
|
const http = new HttpClient(options);
|
|
21
21
|
this.catalog = new CatalogResource(http);
|
|
@@ -24,8 +24,8 @@ export class TapiClient {
|
|
|
24
24
|
this.runs = new RunsResource(http);
|
|
25
25
|
this.runtime = new RuntimeResource(http, options);
|
|
26
26
|
this.sessions = new SessionsResource(http);
|
|
27
|
+
this.services = new ServicesResource(http, options);
|
|
27
28
|
this.triggers = new TriggersResource(http);
|
|
28
|
-
this.websiteApis = new WebsiteApisResource(http, options);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
export * from "./errors.js";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HttpClient } from "./client.js";
|
|
2
|
+
import type { CloudBatchRun, CloudRunQuote, ServiceCloudBatchRequest, ServiceCloudQuoteRequest, ServiceOperation, ServiceRunRequest, TapiRun } from "./types.js";
|
|
3
|
+
import type { TapiClientOptions } from "./types.js";
|
|
4
|
+
export declare class ServicesResource {
|
|
5
|
+
private readonly http;
|
|
6
|
+
private readonly dev;
|
|
7
|
+
constructor(http: HttpClient, options: TapiClientOptions);
|
|
8
|
+
run(serviceRequest: string, request?: ServiceRunRequest): Promise<TapiRun>;
|
|
9
|
+
quoteCloud(serviceRequest: string, request?: ServiceCloudQuoteRequest): Promise<CloudRunQuote>;
|
|
10
|
+
runCloud(serviceRequest: string, request?: ServiceRunRequest & {
|
|
11
|
+
cloud?: ServiceCloudBatchRequest["cloud"];
|
|
12
|
+
user?: ServiceCloudBatchRequest["user"];
|
|
13
|
+
payment?: ServiceCloudBatchRequest["payment"];
|
|
14
|
+
}): Promise<CloudBatchRun>;
|
|
15
|
+
runCloudBatch(serviceRequest: string, request: ServiceCloudBatchRequest): Promise<CloudBatchRun>;
|
|
16
|
+
describe(serviceRequest: string): Promise<ServiceOperation>;
|
|
17
|
+
}
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
import { TapiError } from "./errors.js";
|
|
2
2
|
import { attachLateInputs, serializeInputs } from "./late-input.js";
|
|
3
|
-
export class
|
|
3
|
+
export class ServicesResource {
|
|
4
4
|
http;
|
|
5
5
|
dev;
|
|
6
6
|
constructor(http, options) {
|
|
7
7
|
this.http = http;
|
|
8
8
|
this.dev = options.dev === true;
|
|
9
9
|
}
|
|
10
|
-
async run(
|
|
11
|
-
const {
|
|
10
|
+
async run(serviceRequest, request = {}) {
|
|
11
|
+
const { serviceName, requestKey } = splitServiceRequest(serviceRequest);
|
|
12
12
|
const { bodyInputs, lateInputs } = serializeInputs(request.inputs);
|
|
13
13
|
const body = {
|
|
14
14
|
...request,
|
|
15
15
|
...(bodyInputs === undefined ? {} : { inputs: bodyInputs }),
|
|
16
16
|
...(this.dev ? { dev: true } : {}),
|
|
17
17
|
};
|
|
18
|
-
const run = await this.http.post(`/api/sdk/v1/
|
|
18
|
+
const run = await this.http.post(`/api/sdk/v1/services/${encodeURIComponent(serviceName)}/requests/${encodeURIComponent(requestKey)}/runs`, body);
|
|
19
19
|
if (run.id && lateInputs.length > 0) {
|
|
20
20
|
attachLateInputs(lateInputs, run.id, (runId, inputKey, value) => this.http.post(`/api/sdk/v1/runs/${encodeURIComponent(runId)}/inputs/${encodeURIComponent(inputKey)}`, { value }));
|
|
21
21
|
}
|
|
22
22
|
return run;
|
|
23
23
|
}
|
|
24
|
-
quoteCloud(
|
|
25
|
-
const {
|
|
26
|
-
return this.http.post(`/api/sdk/v1/
|
|
24
|
+
quoteCloud(serviceRequest, request = {}) {
|
|
25
|
+
const { serviceName, requestKey } = splitServiceRequest(serviceRequest);
|
|
26
|
+
return this.http.post(`/api/sdk/v1/services/${encodeURIComponent(serviceName)}/requests/${encodeURIComponent(requestKey)}/cloud/quote`, request);
|
|
27
27
|
}
|
|
28
|
-
runCloud(
|
|
29
|
-
return this.runCloudBatch(
|
|
28
|
+
runCloud(serviceRequest, request = {}) {
|
|
29
|
+
return this.runCloudBatch(serviceRequest, {
|
|
30
30
|
inputs: [request.inputs ?? {}],
|
|
31
31
|
cloud: request.cloud,
|
|
32
32
|
user: request.user,
|
|
@@ -35,10 +35,10 @@ export class WebsiteApisResource {
|
|
|
35
35
|
idempotencyKey: request.idempotencyKey,
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
-
async runCloudBatch(
|
|
39
|
-
const {
|
|
38
|
+
async runCloudBatch(serviceRequest, request) {
|
|
39
|
+
const { serviceName, requestKey } = splitServiceRequest(serviceRequest);
|
|
40
40
|
try {
|
|
41
|
-
return await this.http.post(`/api/sdk/v1/
|
|
41
|
+
return await this.http.post(`/api/sdk/v1/services/${encodeURIComponent(serviceName)}/requests/${encodeURIComponent(requestKey)}/cloud/batch`, request);
|
|
42
42
|
}
|
|
43
43
|
catch (error) {
|
|
44
44
|
if (error instanceof TapiError && error.status === 402 && isRecord(error.details)) {
|
|
@@ -47,19 +47,19 @@ export class WebsiteApisResource {
|
|
|
47
47
|
throw error;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
-
describe(
|
|
51
|
-
const {
|
|
52
|
-
return this.http.get(`/api/sdk/v1/
|
|
50
|
+
describe(serviceRequest) {
|
|
51
|
+
const { serviceName, requestKey } = splitServiceRequest(serviceRequest);
|
|
52
|
+
return this.http.get(`/api/sdk/v1/services/${encodeURIComponent(serviceName)}/requests/${encodeURIComponent(requestKey)}`);
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
function
|
|
55
|
+
function splitServiceRequest(value) {
|
|
56
56
|
const text = value.trim();
|
|
57
57
|
const index = text.lastIndexOf(".");
|
|
58
58
|
if (index <= 0 || index === text.length - 1) {
|
|
59
|
-
throw new Error("
|
|
59
|
+
throw new Error("service request must be formatted as '<serviceName>.<requestKey>'");
|
|
60
60
|
}
|
|
61
61
|
return {
|
|
62
|
-
|
|
62
|
+
serviceName: text.slice(0, index),
|
|
63
63
|
requestKey: text.slice(index + 1),
|
|
64
64
|
};
|
|
65
65
|
}
|
package/dist/triggers.js
CHANGED
|
@@ -4,16 +4,16 @@ export class TriggersResource {
|
|
|
4
4
|
this.http = http;
|
|
5
5
|
}
|
|
6
6
|
list() {
|
|
7
|
-
return this.http.get("/api/sdk/v1/
|
|
7
|
+
return this.http.get("/api/sdk/v1/service-triggers");
|
|
8
8
|
}
|
|
9
9
|
create(request) {
|
|
10
|
-
return this.http.post("/api/sdk/v1/
|
|
10
|
+
return this.http.post("/api/sdk/v1/service-triggers", request);
|
|
11
11
|
}
|
|
12
12
|
get(triggerId) {
|
|
13
|
-
return this.http.get(`/api/sdk/v1/
|
|
13
|
+
return this.http.get(`/api/sdk/v1/service-triggers/${encodeURIComponent(triggerId)}`);
|
|
14
14
|
}
|
|
15
15
|
update(triggerId, request) {
|
|
16
|
-
return this.http.patch(`/api/sdk/v1/
|
|
16
|
+
return this.http.patch(`/api/sdk/v1/service-triggers/${encodeURIComponent(triggerId)}`, request);
|
|
17
17
|
}
|
|
18
18
|
enable(triggerId) {
|
|
19
19
|
return this.update(triggerId, { enabled: true });
|
|
@@ -22,9 +22,9 @@ export class TriggersResource {
|
|
|
22
22
|
return this.update(triggerId, { enabled: false });
|
|
23
23
|
}
|
|
24
24
|
delete(triggerId) {
|
|
25
|
-
return this.http.delete(`/api/sdk/v1/
|
|
25
|
+
return this.http.delete(`/api/sdk/v1/service-triggers/${encodeURIComponent(triggerId)}`);
|
|
26
26
|
}
|
|
27
27
|
fire(triggerId) {
|
|
28
|
-
return this.http.post(`/api/sdk/v1/
|
|
28
|
+
return this.http.post(`/api/sdk/v1/service-triggers/${encodeURIComponent(triggerId)}/run`);
|
|
29
29
|
}
|
|
30
30
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface WebsiteApiRunRequest {
|
|
|
16
16
|
runnerId?: string;
|
|
17
17
|
idempotencyKey?: string;
|
|
18
18
|
site?: string;
|
|
19
|
+
sitemapId?: string;
|
|
19
20
|
}
|
|
20
21
|
export interface CloudRunOptions {
|
|
21
22
|
windows?: boolean;
|
|
@@ -46,6 +47,9 @@ export interface WebsiteApiCloudBatchRequest {
|
|
|
46
47
|
site?: string;
|
|
47
48
|
idempotencyKey?: string;
|
|
48
49
|
}
|
|
50
|
+
export type ServiceRunRequest = WebsiteApiRunRequest;
|
|
51
|
+
export type ServiceCloudQuoteRequest = WebsiteApiCloudQuoteRequest;
|
|
52
|
+
export type ServiceCloudBatchRequest = WebsiteApiCloudBatchRequest;
|
|
49
53
|
export interface CloudRunQuote {
|
|
50
54
|
inputCount: number;
|
|
51
55
|
windows: boolean;
|
|
@@ -155,10 +159,11 @@ export interface WebsiteApiOperation {
|
|
|
155
159
|
namespace: string;
|
|
156
160
|
operation: string;
|
|
157
161
|
site?: string;
|
|
162
|
+
sitemapId?: string;
|
|
158
163
|
version?: string;
|
|
159
164
|
status?: string;
|
|
160
165
|
publishedAt?: string | null;
|
|
161
|
-
|
|
166
|
+
deviceMode?: "desktop" | "mobile" | string;
|
|
162
167
|
runtime?: RuntimeRunOptions | Record<string, unknown>;
|
|
163
168
|
inputs: WebsiteApiInputContract[];
|
|
164
169
|
outputs?: WebsiteApiOutputContract[];
|
|
@@ -181,6 +186,7 @@ export interface WebsiteApiTriggerCreateRequest {
|
|
|
181
186
|
runnerId?: string;
|
|
182
187
|
priority?: number;
|
|
183
188
|
site?: string;
|
|
189
|
+
sitemapId?: string;
|
|
184
190
|
}
|
|
185
191
|
export interface WebsiteApiTriggerUpdateRequest {
|
|
186
192
|
enabled: boolean;
|
|
@@ -228,6 +234,9 @@ export interface TapiRun {
|
|
|
228
234
|
updatedAt?: string;
|
|
229
235
|
[key: string]: unknown;
|
|
230
236
|
}
|
|
237
|
+
export type ServiceInputContract = WebsiteApiInputContract;
|
|
238
|
+
export type ServiceOutputContract = WebsiteApiOutputContract;
|
|
239
|
+
export type ServiceOperation = WebsiteApiOperation;
|
|
231
240
|
export type TapiDevSessionStatus = "queued" | "running" | "waiting_for_input" | "awaiting_takeover" | "needs_developer_attention" | "completed" | "failed" | "cancelled" | string;
|
|
232
241
|
export interface TapiDevSession {
|
|
233
242
|
id: string;
|
|
@@ -367,7 +376,8 @@ export interface SdkCatalogRequest {
|
|
|
367
376
|
status: string;
|
|
368
377
|
operation?: string;
|
|
369
378
|
sdkName?: string;
|
|
370
|
-
|
|
379
|
+
sitemapId?: string;
|
|
380
|
+
deviceMode?: "desktop" | "mobile" | string;
|
|
371
381
|
runtime?: RuntimeRunOptions | Record<string, unknown>;
|
|
372
382
|
inputs?: WebsiteApiInputContract[];
|
|
373
383
|
inputSchema?: Record<string, unknown>;
|
|
@@ -379,6 +389,7 @@ export interface SdkCatalogApi {
|
|
|
379
389
|
name: string;
|
|
380
390
|
version: string;
|
|
381
391
|
site?: string;
|
|
392
|
+
sitemapId?: string;
|
|
382
393
|
requests: SdkCatalogRequest[];
|
|
383
394
|
[key: string]: unknown;
|
|
384
395
|
}
|
package/package.json
CHANGED
package/dist/website-apis.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { HttpClient } from "./client.js";
|
|
2
|
-
import type { CloudBatchRun, CloudRunQuote, TapiRun, WebsiteApiCloudBatchRequest, WebsiteApiCloudQuoteRequest, WebsiteApiOperation, WebsiteApiRunRequest } from "./types.js";
|
|
3
|
-
import type { TapiClientOptions } from "./types.js";
|
|
4
|
-
export declare class WebsiteApisResource {
|
|
5
|
-
private readonly http;
|
|
6
|
-
private readonly dev;
|
|
7
|
-
constructor(http: HttpClient, options: TapiClientOptions);
|
|
8
|
-
run(apiRequest: string, request?: WebsiteApiRunRequest): Promise<TapiRun>;
|
|
9
|
-
quoteCloud(apiRequest: string, request?: WebsiteApiCloudQuoteRequest): Promise<CloudRunQuote>;
|
|
10
|
-
runCloud(apiRequest: string, request?: WebsiteApiRunRequest & {
|
|
11
|
-
cloud?: WebsiteApiCloudBatchRequest["cloud"];
|
|
12
|
-
user?: WebsiteApiCloudBatchRequest["user"];
|
|
13
|
-
payment?: WebsiteApiCloudBatchRequest["payment"];
|
|
14
|
-
}): Promise<CloudBatchRun>;
|
|
15
|
-
runCloudBatch(apiRequest: string, request: WebsiteApiCloudBatchRequest): Promise<CloudBatchRun>;
|
|
16
|
-
describe(apiRequest: string): Promise<WebsiteApiOperation>;
|
|
17
|
-
}
|