deepspace 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +734 -17
- package/dist/cli.js.map +1 -1
- package/dist/testing.d.ts +115 -0
- package/dist/testing.js +175 -0
- package/dist/testing.js.map +1 -0
- package/dist/worker.d.ts +43 -4
- package/dist/worker.js +22 -2
- package/dist/worker.js.map +1 -1
- package/package.json +13 -4
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/cli.ts
|
|
4
|
-
import { defineCommand as
|
|
5
|
-
import { execSync as
|
|
4
|
+
import { defineCommand as defineCommand13, runMain } from "citty";
|
|
5
|
+
import { execSync as execSync5 } from "child_process";
|
|
6
|
+
import { readFileSync as readFileSync9, existsSync as existsSync11 } from "fs";
|
|
7
|
+
import { dirname as dirname2, join as join12 } from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
6
9
|
|
|
7
10
|
// src/cli/commands/login.ts
|
|
8
11
|
import { defineCommand } from "citty";
|
|
@@ -201,7 +204,7 @@ function openBrowser(url) {
|
|
|
201
204
|
});
|
|
202
205
|
}
|
|
203
206
|
function sleep(ms) {
|
|
204
|
-
return new Promise((
|
|
207
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
205
208
|
}
|
|
206
209
|
async function doEmailLogin(email, password) {
|
|
207
210
|
const res = await fetch(`${AUTH_URL}/api/auth/sign-in/email`, {
|
|
@@ -365,7 +368,7 @@ var dev_default = defineCommand3({
|
|
|
365
368
|
console.log("Starting dev server...\n");
|
|
366
369
|
const vite = spawn(
|
|
367
370
|
"npx",
|
|
368
|
-
["vite", "--port", String(port), "--strictPort"],
|
|
371
|
+
["vite", "--port", String(port), "--strictPort", "--host"],
|
|
369
372
|
{
|
|
370
373
|
cwd: appDir,
|
|
371
374
|
stdio: "inherit",
|
|
@@ -497,11 +500,14 @@ function ensurePlaywright(appDir) {
|
|
|
497
500
|
console.log("Installing Playwright...");
|
|
498
501
|
execSync("npm install -D @playwright/test", { cwd: appDir, stdio: "pipe" });
|
|
499
502
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
503
|
+
const isLinux = process.platform === "linux";
|
|
504
|
+
const isRoot = isLinux && typeof process.getuid === "function" && process.getuid() === 0;
|
|
505
|
+
const args = isRoot ? ["install", "--with-deps", "chromium"] : ["install", "chromium"];
|
|
506
|
+
const result = spawnSync("npx", ["playwright", ...args], { cwd: appDir, stdio: "inherit" });
|
|
507
|
+
if (result.status !== 0) {
|
|
508
|
+
console.error("\nPlaywright install failed. On Linux, system libs may be missing \u2014 try:");
|
|
509
|
+
console.error(" sudo npx playwright install --with-deps chromium");
|
|
510
|
+
process.exit(1);
|
|
505
511
|
}
|
|
506
512
|
}
|
|
507
513
|
function resolvePort2(arg) {
|
|
@@ -1112,8 +1118,715 @@ var add_default = defineCommand9({
|
|
|
1112
1118
|
}
|
|
1113
1119
|
});
|
|
1114
1120
|
|
|
1121
|
+
// src/cli/commands/domain.ts
|
|
1122
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
1123
|
+
import { execSync as execSync4 } from "child_process";
|
|
1124
|
+
|
|
1125
|
+
// src/cli/lib/app-context.ts
|
|
1126
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
|
|
1127
|
+
import { join as join11, resolve as resolve5 } from "path";
|
|
1128
|
+
import { parse as parseToml4 } from "smol-toml";
|
|
1129
|
+
function detectAppName(cwd = process.cwd()) {
|
|
1130
|
+
const wranglerPath = join11(resolve5(cwd), "wrangler.toml");
|
|
1131
|
+
if (!existsSync10(wranglerPath)) return null;
|
|
1132
|
+
try {
|
|
1133
|
+
const cfg = parseToml4(readFileSync7(wranglerPath, "utf-8"));
|
|
1134
|
+
return typeof cfg.name === "string" && cfg.name.length > 0 ? cfg.name : null;
|
|
1135
|
+
} catch {
|
|
1136
|
+
return null;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
function requireAppName(explicit, cwd) {
|
|
1140
|
+
if (explicit) return explicit;
|
|
1141
|
+
const detected = detectAppName(cwd);
|
|
1142
|
+
if (detected) return detected;
|
|
1143
|
+
throw new Error(
|
|
1144
|
+
"No app specified. Pass --app <name>, or run from an app directory with a wrangler.toml."
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// src/cli/commands/domain.ts
|
|
1149
|
+
var API_URL = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
1150
|
+
async function api(token, path, init) {
|
|
1151
|
+
const res = await fetch(`${API_URL}${path}`, {
|
|
1152
|
+
...init,
|
|
1153
|
+
headers: {
|
|
1154
|
+
Authorization: `Bearer ${token}`,
|
|
1155
|
+
"Content-Type": "application/json",
|
|
1156
|
+
...init?.headers ?? {}
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
const text = await res.text();
|
|
1160
|
+
if (!res.ok) {
|
|
1161
|
+
let msg = text;
|
|
1162
|
+
try {
|
|
1163
|
+
msg = JSON.parse(text).error ?? text;
|
|
1164
|
+
} catch {
|
|
1165
|
+
}
|
|
1166
|
+
throw new Error(`API ${path} (${res.status}): ${msg}`);
|
|
1167
|
+
}
|
|
1168
|
+
return JSON.parse(text);
|
|
1169
|
+
}
|
|
1170
|
+
function fmtCents(cents) {
|
|
1171
|
+
if (cents === null || cents === void 0) return "\u2014";
|
|
1172
|
+
return `$${(cents / 100).toFixed(2)}`;
|
|
1173
|
+
}
|
|
1174
|
+
function fmtDate(iso) {
|
|
1175
|
+
if (!iso) return "\u2014";
|
|
1176
|
+
return iso.slice(0, 10);
|
|
1177
|
+
}
|
|
1178
|
+
async function confirm2(message) {
|
|
1179
|
+
if (!process.stdin.isTTY) {
|
|
1180
|
+
throw new Error(`${message} \u2014 pass --yes to confirm non-interactively`);
|
|
1181
|
+
}
|
|
1182
|
+
process.stdout.write(`${message} [y/N] `);
|
|
1183
|
+
return new Promise((resolve6) => {
|
|
1184
|
+
process.stdin.setEncoding("utf-8");
|
|
1185
|
+
process.stdin.once("data", (data) => {
|
|
1186
|
+
const answer = data.toString().trim().toLowerCase();
|
|
1187
|
+
resolve6(answer === "y" || answer === "yes");
|
|
1188
|
+
});
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
var search = defineCommand10({
|
|
1192
|
+
meta: { name: "search", description: "Search for available domains" },
|
|
1193
|
+
args: {
|
|
1194
|
+
query: { type: "positional", description: "Domain or keyword", required: true },
|
|
1195
|
+
limit: { type: "string", description: "Max results (default 10)", default: "10" },
|
|
1196
|
+
json: { type: "boolean", description: "Emit JSON instead of a table" }
|
|
1197
|
+
},
|
|
1198
|
+
async run({ args }) {
|
|
1199
|
+
const token = await ensureToken();
|
|
1200
|
+
const limit = parseInt(args.limit ?? "10", 10) || 10;
|
|
1201
|
+
const result = await api(
|
|
1202
|
+
token,
|
|
1203
|
+
`/api/domains/search?q=${encodeURIComponent(args.query)}&limit=${limit}`
|
|
1204
|
+
);
|
|
1205
|
+
if (args.json) {
|
|
1206
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
if (result.domains.length === 0) {
|
|
1210
|
+
console.log("No matches.");
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
for (const d of result.domains) {
|
|
1214
|
+
const tag = d.registrable ? "\u2713" : "\u2717";
|
|
1215
|
+
const price = d.registrable ? `${fmtCents(d.chargedCents)}/yr` : d.reason ?? "\u2014";
|
|
1216
|
+
const slowFlag = d.registrar && d.registrar !== "cloudflare" ? " (15-60 min provisioning)" : "";
|
|
1217
|
+
console.log(`${tag} ${d.name.padEnd(40)} ${price}${slowFlag}`);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
});
|
|
1221
|
+
var buy = defineCommand10({
|
|
1222
|
+
meta: { name: "buy", description: "Buy a domain and attach it to an app" },
|
|
1223
|
+
args: {
|
|
1224
|
+
domain: { type: "positional", description: "Domain to buy", required: true },
|
|
1225
|
+
app: { type: "string", description: "App to attach (defaults to ./wrangler.toml)" },
|
|
1226
|
+
yes: { type: "boolean", description: "Skip the confirmation prompt", default: false },
|
|
1227
|
+
"no-open": { type: "boolean", description: "Do not open browser; print Stripe URL", default: false },
|
|
1228
|
+
"no-wait": { type: "boolean", description: "Exit after creating session; do not poll until active", default: false },
|
|
1229
|
+
json: { type: "boolean", description: "Emit JSON to stdout", default: false }
|
|
1230
|
+
},
|
|
1231
|
+
async run({ args }) {
|
|
1232
|
+
const token = await ensureToken();
|
|
1233
|
+
const appName = requireAppName(args.app);
|
|
1234
|
+
const priceCheck = await api(token, "/api/domains/check", {
|
|
1235
|
+
method: "POST",
|
|
1236
|
+
body: JSON.stringify({ domains: [args.domain] })
|
|
1237
|
+
});
|
|
1238
|
+
const result = priceCheck.domains[0];
|
|
1239
|
+
if (!result || !result.registrable) {
|
|
1240
|
+
console.error(`Not available: ${result?.reason ?? "unknown"}`);
|
|
1241
|
+
process.exit(1);
|
|
1242
|
+
}
|
|
1243
|
+
const price = fmtCents(result.chargedCents);
|
|
1244
|
+
const expectedTime = result.registrar === "cloudflare" ? "~60-90 seconds" : "15-60 minutes (registry NS propagation)";
|
|
1245
|
+
if (!args.yes) {
|
|
1246
|
+
const ok = await confirm2(
|
|
1247
|
+
`Buy ${args.domain} for ${price}/yr (auto-renews; non-refundable), attach to ${appName}? Provisioning takes ${expectedTime}.`
|
|
1248
|
+
);
|
|
1249
|
+
if (!ok) {
|
|
1250
|
+
console.log("Cancelled.");
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
const checkout = await api(
|
|
1255
|
+
token,
|
|
1256
|
+
"/api/domains/checkout",
|
|
1257
|
+
{
|
|
1258
|
+
method: "POST",
|
|
1259
|
+
body: JSON.stringify({ domain: args.domain, appName })
|
|
1260
|
+
}
|
|
1261
|
+
);
|
|
1262
|
+
if (args.json) {
|
|
1263
|
+
process.stdout.write(JSON.stringify({ ...checkout, expectedTime, registrar: result.registrar }, null, 2) + "\n");
|
|
1264
|
+
} else {
|
|
1265
|
+
console.log(`Stripe Checkout: ${checkout.url}`);
|
|
1266
|
+
}
|
|
1267
|
+
if (!args["no-open"] && process.stdin.isTTY) {
|
|
1268
|
+
openBrowser2(checkout.url);
|
|
1269
|
+
}
|
|
1270
|
+
if (args["no-wait"]) return;
|
|
1271
|
+
const timeoutMs = result.registrar === "cloudflare" ? 5 * 60 * 1e3 : 60 * 60 * 1e3;
|
|
1272
|
+
if (!args.json) {
|
|
1273
|
+
console.log(`Waiting for payment & provisioning (up to ${expectedTime}). Press Ctrl-C anytime \u2014`);
|
|
1274
|
+
console.log(`provisioning continues server-side; check later with \`deepspace domain status ${args.domain}\`.`);
|
|
1275
|
+
}
|
|
1276
|
+
const deadline = Date.now() + timeoutMs;
|
|
1277
|
+
let lastStatus = "";
|
|
1278
|
+
while (Date.now() < deadline) {
|
|
1279
|
+
await new Promise((r) => setTimeout(r, 5e3));
|
|
1280
|
+
try {
|
|
1281
|
+
const detail = await api(
|
|
1282
|
+
token,
|
|
1283
|
+
`/api/domains/${checkout.purchaseId}`
|
|
1284
|
+
);
|
|
1285
|
+
if (detail.domain.status !== lastStatus) {
|
|
1286
|
+
lastStatus = detail.domain.status;
|
|
1287
|
+
if (!args.json) console.log(` \u2192 ${lastStatus}`);
|
|
1288
|
+
}
|
|
1289
|
+
if (detail.domain.status === "active") {
|
|
1290
|
+
if (args.json) {
|
|
1291
|
+
process.stdout.write(JSON.stringify({ ...checkout, status: "active", domain: args.domain }, null, 2) + "\n");
|
|
1292
|
+
} else {
|
|
1293
|
+
console.log(`\u2713 ${args.domain} is live and pointing at ${appName}`);
|
|
1294
|
+
}
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
if (detail.domain.status.startsWith("failed:")) {
|
|
1298
|
+
console.error(`Provisioning failed: ${detail.domain.statusError ?? detail.domain.status}`);
|
|
1299
|
+
process.exit(1);
|
|
1300
|
+
}
|
|
1301
|
+
} catch (err) {
|
|
1302
|
+
if (!args.json) console.error(`(poll error: ${err instanceof Error ? err.message : String(err)})`);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
console.error(`Polling timed out. Provisioning may still be in progress; check with:`);
|
|
1306
|
+
console.error(` deepspace domain status ${args.domain}`);
|
|
1307
|
+
process.exit(1);
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1310
|
+
var list2 = defineCommand10({
|
|
1311
|
+
meta: { name: "list", description: "List your domains" },
|
|
1312
|
+
args: {
|
|
1313
|
+
json: { type: "boolean", description: "Emit JSON" }
|
|
1314
|
+
},
|
|
1315
|
+
async run({ args }) {
|
|
1316
|
+
const token = await ensureToken();
|
|
1317
|
+
const result = await api(token, "/api/domains");
|
|
1318
|
+
if (args.json) {
|
|
1319
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
if (result.domains.length === 0) {
|
|
1323
|
+
console.log("No domains. Try `deepspace domain search <name>`.");
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
console.log(`DOMAIN APP STATUS EXPIRES AUTO-RENEW`);
|
|
1327
|
+
for (const d of result.domains) {
|
|
1328
|
+
console.log(
|
|
1329
|
+
`${d.domain.padEnd(35)} ${d.appName.padEnd(20)} ${d.status.padEnd(20)} ${fmtDate(d.expiresAt).padEnd(12)} ${d.autoRenew ? "on" : "off"}`
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
});
|
|
1334
|
+
var status = defineCommand10({
|
|
1335
|
+
meta: { name: "status", description: "Show details for one domain" },
|
|
1336
|
+
args: {
|
|
1337
|
+
domain: { type: "positional", description: "Domain", required: true },
|
|
1338
|
+
json: { type: "boolean" }
|
|
1339
|
+
},
|
|
1340
|
+
async run({ args }) {
|
|
1341
|
+
const token = await ensureToken();
|
|
1342
|
+
const all = await api(token, "/api/domains");
|
|
1343
|
+
const found = all.domains.find((d) => d.domain === args.domain);
|
|
1344
|
+
if (!found) {
|
|
1345
|
+
console.error(`No domain ${args.domain} in your account`);
|
|
1346
|
+
process.exit(1);
|
|
1347
|
+
}
|
|
1348
|
+
if (args.json) {
|
|
1349
|
+
process.stdout.write(JSON.stringify(found, null, 2) + "\n");
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
console.log(`Domain: ${found.domain}`);
|
|
1353
|
+
console.log(`Status: ${found.status}${found.statusError ? ` (${found.statusError})` : ""}`);
|
|
1354
|
+
console.log(`Attached app: ${found.appName}`);
|
|
1355
|
+
console.log(`Registrar: ${found.registrar}`);
|
|
1356
|
+
console.log(`Registered: ${fmtDate(found.registeredAt)}`);
|
|
1357
|
+
console.log(`Expires: ${fmtDate(found.expiresAt)}`);
|
|
1358
|
+
console.log(`Auto-renew: ${found.autoRenew ? "on" : "off"}`);
|
|
1359
|
+
console.log(`Charged: ${fmtCents(found.chargedCents)}/yr`);
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
var attach = defineCommand10({
|
|
1363
|
+
meta: { name: "attach", description: "Re-point a domain at a different app" },
|
|
1364
|
+
args: {
|
|
1365
|
+
domain: { type: "positional", description: "Domain to re-point", required: true },
|
|
1366
|
+
app: { type: "string", description: "Target app (defaults to ./wrangler.toml)" },
|
|
1367
|
+
json: { type: "boolean" }
|
|
1368
|
+
},
|
|
1369
|
+
async run({ args }) {
|
|
1370
|
+
const token = await ensureToken();
|
|
1371
|
+
const targetApp = requireAppName(args.app);
|
|
1372
|
+
const all = await api(token, "/api/domains");
|
|
1373
|
+
const found = all.domains.find((d) => d.domain === args.domain);
|
|
1374
|
+
if (!found) {
|
|
1375
|
+
console.error(`No domain ${args.domain} in your account`);
|
|
1376
|
+
process.exit(1);
|
|
1377
|
+
}
|
|
1378
|
+
const result = await api(
|
|
1379
|
+
token,
|
|
1380
|
+
`/api/domains/${found.id}/reattach`,
|
|
1381
|
+
{
|
|
1382
|
+
method: "POST",
|
|
1383
|
+
body: JSON.stringify({ appName: targetApp })
|
|
1384
|
+
}
|
|
1385
|
+
);
|
|
1386
|
+
if (args.json) {
|
|
1387
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
console.log(`\u2713 ${args.domain} now points at ${targetApp}`);
|
|
1391
|
+
}
|
|
1392
|
+
});
|
|
1393
|
+
var detach = defineCommand10({
|
|
1394
|
+
meta: {
|
|
1395
|
+
name: "detach",
|
|
1396
|
+
description: "Stop routing the domain (keeps the registration; auto-renew unchanged)"
|
|
1397
|
+
},
|
|
1398
|
+
args: {
|
|
1399
|
+
domain: { type: "positional", description: "Domain to detach", required: true },
|
|
1400
|
+
yes: { type: "boolean", description: "Skip the confirmation prompt" }
|
|
1401
|
+
},
|
|
1402
|
+
async run({ args }) {
|
|
1403
|
+
const token = await ensureToken();
|
|
1404
|
+
const all = await api(token, "/api/domains");
|
|
1405
|
+
const found = all.domains.find((d) => d.domain === args.domain);
|
|
1406
|
+
if (!found) {
|
|
1407
|
+
console.error(`No domain ${args.domain} in your account`);
|
|
1408
|
+
process.exit(1);
|
|
1409
|
+
}
|
|
1410
|
+
if (!args.yes) {
|
|
1411
|
+
const ok = await confirm2(
|
|
1412
|
+
`Detach ${args.domain}? Routing will stop; the domain stays registered (auto-renew is unchanged).`
|
|
1413
|
+
);
|
|
1414
|
+
if (!ok) {
|
|
1415
|
+
console.log("Cancelled.");
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
await api(token, `/api/domains/${found.id}`, { method: "DELETE" });
|
|
1420
|
+
console.log(`\u2713 ${args.domain} detached. Use \`deepspace domain attach\` to re-route, or \`renew --auto off\` to stop auto-renewal.`);
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
var renew = defineCommand10({
|
|
1424
|
+
meta: { name: "renew", description: "Toggle auto-renewal at the registrar" },
|
|
1425
|
+
args: {
|
|
1426
|
+
domain: { type: "positional", description: "Domain", required: true },
|
|
1427
|
+
auto: { type: "string", description: '"on" or "off"', required: true }
|
|
1428
|
+
},
|
|
1429
|
+
async run({ args }) {
|
|
1430
|
+
if (args.auto !== "on" && args.auto !== "off") {
|
|
1431
|
+
console.error('--auto must be "on" or "off"');
|
|
1432
|
+
process.exit(1);
|
|
1433
|
+
}
|
|
1434
|
+
const token = await ensureToken();
|
|
1435
|
+
const all = await api(token, "/api/domains");
|
|
1436
|
+
const found = all.domains.find((d) => d.domain === args.domain);
|
|
1437
|
+
if (!found) {
|
|
1438
|
+
console.error(`No domain ${args.domain} in your account`);
|
|
1439
|
+
process.exit(1);
|
|
1440
|
+
}
|
|
1441
|
+
const enabled = args.auto === "on";
|
|
1442
|
+
await api(token, `/api/domains/${found.id}/auto-renew`, {
|
|
1443
|
+
method: "POST",
|
|
1444
|
+
body: JSON.stringify({ autoRenew: enabled })
|
|
1445
|
+
});
|
|
1446
|
+
console.log(`\u2713 ${args.domain} auto-renew ${enabled ? "enabled" : "disabled"}`);
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
function openBrowser2(url) {
|
|
1450
|
+
try {
|
|
1451
|
+
if (process.platform === "darwin") execSync4(`open ${JSON.stringify(url)}`);
|
|
1452
|
+
else if (process.platform === "win32") execSync4(`start "" ${JSON.stringify(url)}`);
|
|
1453
|
+
else execSync4(`xdg-open ${JSON.stringify(url)} 2>/dev/null || true`);
|
|
1454
|
+
} catch {
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
var domain_default = defineCommand10({
|
|
1458
|
+
meta: { name: "domain", description: "Buy and manage custom domains" },
|
|
1459
|
+
subCommands: { search, buy, list: list2, status, attach, detach, renew }
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1462
|
+
// src/cli/commands/integrations.ts
|
|
1463
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
1464
|
+
|
|
1465
|
+
// src/cli/commands/_invoke-impl.ts
|
|
1466
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
1467
|
+
var API_URL2 = process.env.DEEPSPACE_API_URL ?? ENVS.prod.api;
|
|
1468
|
+
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
1469
|
+
function parseTarget(target) {
|
|
1470
|
+
if (!target || typeof target !== "string") {
|
|
1471
|
+
throw new Error("Missing target. Expected '<integration>/<endpoint>' (e.g. 'openai/chat-completion').");
|
|
1472
|
+
}
|
|
1473
|
+
const parts = target.split("/");
|
|
1474
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
1475
|
+
throw new Error(`Bad target '${target}'. Expected '<integration>/<endpoint>' (e.g. 'openai/chat-completion').`);
|
|
1476
|
+
}
|
|
1477
|
+
return { integration: parts[0], endpoint: parts[1] };
|
|
1478
|
+
}
|
|
1479
|
+
function resolveBody(opts) {
|
|
1480
|
+
if (opts.body != null && opts.bodyFile != null) {
|
|
1481
|
+
throw new Error("Pass either --body or --body-file, not both.");
|
|
1482
|
+
}
|
|
1483
|
+
let raw;
|
|
1484
|
+
if (opts.body != null) {
|
|
1485
|
+
raw = opts.body;
|
|
1486
|
+
} else if (opts.bodyFile != null) {
|
|
1487
|
+
raw = opts.bodyFile === "-" ? readFileSync8(0, "utf-8") : readFileSync8(opts.bodyFile, "utf-8");
|
|
1488
|
+
} else {
|
|
1489
|
+
return "{}";
|
|
1490
|
+
}
|
|
1491
|
+
try {
|
|
1492
|
+
JSON.parse(raw);
|
|
1493
|
+
} catch (err) {
|
|
1494
|
+
const msg = err instanceof Error ? err.message : "invalid JSON";
|
|
1495
|
+
throw new Error(`Body is not valid JSON: ${msg}`);
|
|
1496
|
+
}
|
|
1497
|
+
return raw;
|
|
1498
|
+
}
|
|
1499
|
+
function flushAndExit(code) {
|
|
1500
|
+
return new Promise(() => {
|
|
1501
|
+
process.stdout.write("", () => process.exit(code));
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
async function fetchCatalog() {
|
|
1505
|
+
const res = await fetch(`${API_URL2}/api/integrations`);
|
|
1506
|
+
if (!res.ok) {
|
|
1507
|
+
throw new Error(`Failed to fetch integration catalog (${res.status})`);
|
|
1508
|
+
}
|
|
1509
|
+
return await res.json();
|
|
1510
|
+
}
|
|
1511
|
+
function findEndpoint(catalog, integration, endpoint) {
|
|
1512
|
+
const endpoints = catalog.integrations[integration];
|
|
1513
|
+
if (!endpoints) return null;
|
|
1514
|
+
return endpoints.find((e) => e.endpoint === endpoint) ?? null;
|
|
1515
|
+
}
|
|
1516
|
+
function formatCurrency(value, currency) {
|
|
1517
|
+
if (currency === "USD") return `$${value}`;
|
|
1518
|
+
return `${value} ${currency}`;
|
|
1519
|
+
}
|
|
1520
|
+
async function runList(args) {
|
|
1521
|
+
const catalog = await fetchCatalog();
|
|
1522
|
+
if (args.json) {
|
|
1523
|
+
process.stdout.write(JSON.stringify(catalog) + "\n");
|
|
1524
|
+
await flushAndExit(0);
|
|
1525
|
+
}
|
|
1526
|
+
const names = Object.keys(catalog.integrations).sort();
|
|
1527
|
+
if (names.length === 0) {
|
|
1528
|
+
console.log("No integrations available.");
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
for (const name of names) {
|
|
1532
|
+
console.log(name);
|
|
1533
|
+
const endpoints = [...catalog.integrations[name]].sort((a, b) => a.endpoint.localeCompare(b.endpoint));
|
|
1534
|
+
const widest = Math.max(...endpoints.map((e) => e.endpoint.length));
|
|
1535
|
+
for (const ep of endpoints) {
|
|
1536
|
+
const pad = ep.endpoint.padEnd(widest);
|
|
1537
|
+
const cost = formatCurrency(ep.billing.baseCost, ep.billing.currency);
|
|
1538
|
+
console.log(` ${pad} ${ep.billing.model.padEnd(12)} ${cost}`);
|
|
1539
|
+
}
|
|
1540
|
+
console.log();
|
|
1541
|
+
}
|
|
1542
|
+
console.log("Run 'deepspace integrations info <integration>/<endpoint>' for the request schema.");
|
|
1543
|
+
}
|
|
1544
|
+
async function runInfo(args) {
|
|
1545
|
+
const { integration, endpoint } = parseTarget(args.target);
|
|
1546
|
+
const catalog = await fetchCatalog();
|
|
1547
|
+
const info2 = findEndpoint(catalog, integration, endpoint);
|
|
1548
|
+
if (!info2) {
|
|
1549
|
+
const available = catalog.integrations[integration];
|
|
1550
|
+
if (!available) {
|
|
1551
|
+
const names = Object.keys(catalog.integrations).sort().join(", ");
|
|
1552
|
+
throw new Error(`Unknown integration '${integration}'. Available: ${names}`);
|
|
1553
|
+
}
|
|
1554
|
+
const endpoints = available.map((e) => e.endpoint).join(", ");
|
|
1555
|
+
throw new Error(`Unknown endpoint '${endpoint}' for '${integration}'. Available: ${endpoints}`);
|
|
1556
|
+
}
|
|
1557
|
+
if (args.json) {
|
|
1558
|
+
process.stdout.write(JSON.stringify(info2) + "\n");
|
|
1559
|
+
await flushAndExit(0);
|
|
1560
|
+
}
|
|
1561
|
+
console.log(`${integration}/${endpoint}`);
|
|
1562
|
+
console.log(` billing: ${info2.billing.model}, ${formatCurrency(info2.billing.baseCost, info2.billing.currency)} per call`);
|
|
1563
|
+
console.log();
|
|
1564
|
+
console.log("Input schema:");
|
|
1565
|
+
console.log(info2.inputSchema ? JSON.stringify(info2.inputSchema, null, 2) : " (no schema registered)");
|
|
1566
|
+
console.log();
|
|
1567
|
+
console.log("Example body:");
|
|
1568
|
+
console.log(info2.example ? JSON.stringify(info2.example, null, 2) : " (no example available)");
|
|
1569
|
+
}
|
|
1570
|
+
async function runInvoke(args) {
|
|
1571
|
+
const { integration, endpoint } = parseTarget(args.target);
|
|
1572
|
+
const body = resolveBody({ body: args.body, bodyFile: args.bodyFile });
|
|
1573
|
+
const timeoutMs = args.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
1574
|
+
let jwt;
|
|
1575
|
+
try {
|
|
1576
|
+
jwt = await ensureToken();
|
|
1577
|
+
} catch (err) {
|
|
1578
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1579
|
+
throw new Error(msg);
|
|
1580
|
+
}
|
|
1581
|
+
const controller = new AbortController();
|
|
1582
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1583
|
+
const startedAt = Date.now();
|
|
1584
|
+
let res;
|
|
1585
|
+
try {
|
|
1586
|
+
res = await fetch(`${API_URL2}/api/integrations/${integration}/${endpoint}`, {
|
|
1587
|
+
method: "POST",
|
|
1588
|
+
headers: {
|
|
1589
|
+
"Content-Type": "application/json",
|
|
1590
|
+
Authorization: `Bearer ${jwt}`
|
|
1591
|
+
},
|
|
1592
|
+
body,
|
|
1593
|
+
signal: controller.signal
|
|
1594
|
+
});
|
|
1595
|
+
} catch (err) {
|
|
1596
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
1597
|
+
throw new Error(`Request timed out after ${timeoutMs}ms`);
|
|
1598
|
+
}
|
|
1599
|
+
throw new Error(err instanceof Error ? err.message : "Request failed");
|
|
1600
|
+
} finally {
|
|
1601
|
+
clearTimeout(timer);
|
|
1602
|
+
}
|
|
1603
|
+
const elapsed = Date.now() - startedAt;
|
|
1604
|
+
let payload;
|
|
1605
|
+
try {
|
|
1606
|
+
payload = await res.json();
|
|
1607
|
+
} catch {
|
|
1608
|
+
throw new Error(`Request failed (${res.status}) with non-JSON response`);
|
|
1609
|
+
}
|
|
1610
|
+
if (args.json) {
|
|
1611
|
+
process.stdout.write(JSON.stringify(payload) + "\n");
|
|
1612
|
+
await flushAndExit(payload?.success === false || !res.ok ? 1 : 0);
|
|
1613
|
+
}
|
|
1614
|
+
const ok = payload?.success !== false && res.ok;
|
|
1615
|
+
if (ok) {
|
|
1616
|
+
const data = "data" in payload ? payload.data : payload;
|
|
1617
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1618
|
+
console.error(`
|
|
1619
|
+
\u2713 ${integration}/${endpoint} (${elapsed}ms)`);
|
|
1620
|
+
return;
|
|
1621
|
+
}
|
|
1622
|
+
const errMsg = payload.error ?? `Request failed (${res.status})`;
|
|
1623
|
+
console.error(`\u2717 ${integration}/${endpoint} (${res.status}, ${elapsed}ms): ${errMsg}`);
|
|
1624
|
+
if (Array.isArray(payload.issues)) {
|
|
1625
|
+
for (const issue of payload.issues) {
|
|
1626
|
+
const path = issue.path?.length ? issue.path.join(".") : "(root)";
|
|
1627
|
+
console.error(` - ${path}: ${issue.message}`);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
if (res.status === 401) {
|
|
1631
|
+
console.error("\nHint: run 'deepspace login' to authenticate.");
|
|
1632
|
+
}
|
|
1633
|
+
process.exit(1);
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
// src/cli/commands/integrations.ts
|
|
1637
|
+
var list3 = defineCommand11({
|
|
1638
|
+
meta: {
|
|
1639
|
+
name: "list",
|
|
1640
|
+
description: "List all available integrations"
|
|
1641
|
+
},
|
|
1642
|
+
args: {
|
|
1643
|
+
json: {
|
|
1644
|
+
type: "boolean",
|
|
1645
|
+
description: "Print raw JSON (machine-readable)",
|
|
1646
|
+
default: false
|
|
1647
|
+
}
|
|
1648
|
+
},
|
|
1649
|
+
async run({ args }) {
|
|
1650
|
+
try {
|
|
1651
|
+
await runList({ json: args.json });
|
|
1652
|
+
} catch (err) {
|
|
1653
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
1654
|
+
process.exit(1);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
var info = defineCommand11({
|
|
1659
|
+
meta: {
|
|
1660
|
+
name: "info",
|
|
1661
|
+
description: "Print schema + example body for an integration endpoint"
|
|
1662
|
+
},
|
|
1663
|
+
args: {
|
|
1664
|
+
target: {
|
|
1665
|
+
type: "positional",
|
|
1666
|
+
description: "<integration>/<endpoint> (e.g. 'openai/chat-completion')",
|
|
1667
|
+
required: true
|
|
1668
|
+
},
|
|
1669
|
+
json: {
|
|
1670
|
+
type: "boolean",
|
|
1671
|
+
description: "Print raw JSON (machine-readable)",
|
|
1672
|
+
default: false
|
|
1673
|
+
}
|
|
1674
|
+
},
|
|
1675
|
+
async run({ args }) {
|
|
1676
|
+
try {
|
|
1677
|
+
await runInfo({ target: args.target, json: args.json });
|
|
1678
|
+
} catch (err) {
|
|
1679
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
1680
|
+
process.exit(1);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
});
|
|
1684
|
+
var invoke = defineCommand11({
|
|
1685
|
+
meta: {
|
|
1686
|
+
name: "invoke",
|
|
1687
|
+
description: "Invoke a platform integration as the logged-in user"
|
|
1688
|
+
},
|
|
1689
|
+
args: {
|
|
1690
|
+
target: {
|
|
1691
|
+
type: "positional",
|
|
1692
|
+
description: "<integration>/<endpoint> (e.g. 'openai/chat-completion')",
|
|
1693
|
+
required: true
|
|
1694
|
+
},
|
|
1695
|
+
body: {
|
|
1696
|
+
type: "string",
|
|
1697
|
+
alias: "d",
|
|
1698
|
+
description: "Inline JSON body",
|
|
1699
|
+
required: false
|
|
1700
|
+
},
|
|
1701
|
+
"body-file": {
|
|
1702
|
+
type: "string",
|
|
1703
|
+
alias: "f",
|
|
1704
|
+
description: "Read JSON body from file (use - for stdin)",
|
|
1705
|
+
required: false
|
|
1706
|
+
},
|
|
1707
|
+
json: {
|
|
1708
|
+
type: "boolean",
|
|
1709
|
+
description: "Print only the response JSON (machine-readable)",
|
|
1710
|
+
default: false
|
|
1711
|
+
},
|
|
1712
|
+
timeout: {
|
|
1713
|
+
type: "string",
|
|
1714
|
+
description: "Request timeout in milliseconds (default: 120000)",
|
|
1715
|
+
required: false
|
|
1716
|
+
}
|
|
1717
|
+
},
|
|
1718
|
+
async run({ args }) {
|
|
1719
|
+
try {
|
|
1720
|
+
const timeout = args.timeout != null ? Number(args.timeout) : void 0;
|
|
1721
|
+
if (timeout != null && (!Number.isFinite(timeout) || timeout <= 0)) {
|
|
1722
|
+
throw new Error(`Invalid --timeout '${args.timeout}'. Must be a positive number of milliseconds.`);
|
|
1723
|
+
}
|
|
1724
|
+
await runInvoke({
|
|
1725
|
+
target: args.target,
|
|
1726
|
+
body: args.body,
|
|
1727
|
+
bodyFile: args["body-file"],
|
|
1728
|
+
json: args.json,
|
|
1729
|
+
timeout
|
|
1730
|
+
});
|
|
1731
|
+
} catch (err) {
|
|
1732
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
1733
|
+
process.exit(1);
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
});
|
|
1737
|
+
var integrations_default = defineCommand11({
|
|
1738
|
+
meta: {
|
|
1739
|
+
name: "integrations",
|
|
1740
|
+
description: "List, inspect, and invoke platform integrations"
|
|
1741
|
+
},
|
|
1742
|
+
subCommands: {
|
|
1743
|
+
list: list3,
|
|
1744
|
+
info,
|
|
1745
|
+
invoke
|
|
1746
|
+
}
|
|
1747
|
+
});
|
|
1748
|
+
|
|
1749
|
+
// src/cli/commands/invoke.ts
|
|
1750
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
1751
|
+
var invoke_default = defineCommand12({
|
|
1752
|
+
meta: {
|
|
1753
|
+
name: "invoke",
|
|
1754
|
+
description: "Invoke a platform integration as the logged-in user"
|
|
1755
|
+
},
|
|
1756
|
+
args: {
|
|
1757
|
+
target: {
|
|
1758
|
+
type: "positional",
|
|
1759
|
+
description: "<integration>/<endpoint> (e.g. 'openai/chat-completion')",
|
|
1760
|
+
required: false
|
|
1761
|
+
},
|
|
1762
|
+
body: {
|
|
1763
|
+
type: "string",
|
|
1764
|
+
alias: "d",
|
|
1765
|
+
description: "Inline JSON body",
|
|
1766
|
+
required: false
|
|
1767
|
+
},
|
|
1768
|
+
"body-file": {
|
|
1769
|
+
type: "string",
|
|
1770
|
+
alias: "f",
|
|
1771
|
+
description: "Read JSON body from file (use - for stdin)",
|
|
1772
|
+
required: false
|
|
1773
|
+
},
|
|
1774
|
+
json: {
|
|
1775
|
+
type: "boolean",
|
|
1776
|
+
description: "Print only the response JSON (machine-readable)",
|
|
1777
|
+
default: false
|
|
1778
|
+
},
|
|
1779
|
+
info: {
|
|
1780
|
+
type: "boolean",
|
|
1781
|
+
description: "Print schema + example body for the endpoint, then exit",
|
|
1782
|
+
default: false
|
|
1783
|
+
},
|
|
1784
|
+
list: {
|
|
1785
|
+
type: "boolean",
|
|
1786
|
+
description: "Print all available integrations, then exit",
|
|
1787
|
+
default: false
|
|
1788
|
+
},
|
|
1789
|
+
timeout: {
|
|
1790
|
+
type: "string",
|
|
1791
|
+
description: "Request timeout in milliseconds (default: 120000)",
|
|
1792
|
+
required: false
|
|
1793
|
+
}
|
|
1794
|
+
},
|
|
1795
|
+
async run({ args }) {
|
|
1796
|
+
try {
|
|
1797
|
+
if (args.list) {
|
|
1798
|
+
await runList({ json: args.json });
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
if (args.info) {
|
|
1802
|
+
await runInfo({ target: args.target, json: args.json });
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1805
|
+
const timeout = args.timeout != null ? Number(args.timeout) : void 0;
|
|
1806
|
+
if (timeout != null && (!Number.isFinite(timeout) || timeout <= 0)) {
|
|
1807
|
+
throw new Error(`Invalid --timeout '${args.timeout}'. Must be a positive number of milliseconds.`);
|
|
1808
|
+
}
|
|
1809
|
+
await runInvoke({
|
|
1810
|
+
target: args.target,
|
|
1811
|
+
body: args.body,
|
|
1812
|
+
bodyFile: args["body-file"],
|
|
1813
|
+
json: args.json,
|
|
1814
|
+
timeout
|
|
1815
|
+
});
|
|
1816
|
+
} catch (err) {
|
|
1817
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
1818
|
+
process.exit(1);
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
});
|
|
1822
|
+
|
|
1115
1823
|
// src/cli/cli.ts
|
|
1116
|
-
var
|
|
1824
|
+
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
1825
|
+
var pkg = JSON.parse(readFileSync9(join12(__dirname, "..", "package.json"), "utf-8"));
|
|
1826
|
+
if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync11("/etc/ssl/certs/ca-certificates.crt")) {
|
|
1827
|
+
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
1828
|
+
}
|
|
1829
|
+
var create2 = defineCommand13({
|
|
1117
1830
|
meta: {
|
|
1118
1831
|
name: "create",
|
|
1119
1832
|
description: "Create a new DeepSpace app"
|
|
@@ -1129,13 +1842,13 @@ var create2 = defineCommand10({
|
|
|
1129
1842
|
const name = args.name ? ` ${args.name}` : "";
|
|
1130
1843
|
console.log(`Running: npm create deepspace${name}
|
|
1131
1844
|
`);
|
|
1132
|
-
|
|
1845
|
+
execSync5(`npm create deepspace@latest${name}`, { stdio: "inherit" });
|
|
1133
1846
|
}
|
|
1134
1847
|
});
|
|
1135
|
-
var main =
|
|
1848
|
+
var main = defineCommand13({
|
|
1136
1849
|
meta: {
|
|
1137
1850
|
name: "deepspace",
|
|
1138
|
-
version:
|
|
1851
|
+
version: pkg.version,
|
|
1139
1852
|
description: "DeepSpace SDK CLI"
|
|
1140
1853
|
},
|
|
1141
1854
|
subCommands: {
|
|
@@ -1147,12 +1860,16 @@ var main = defineCommand10({
|
|
|
1147
1860
|
test: test_default,
|
|
1148
1861
|
"test-accounts": test_accounts_default,
|
|
1149
1862
|
add: add_default,
|
|
1863
|
+
integrations: integrations_default,
|
|
1864
|
+
invoke: invoke_default,
|
|
1150
1865
|
deploy: deploy_default,
|
|
1151
|
-
undeploy: undeploy_default
|
|
1152
|
-
|
|
1153
|
-
run() {
|
|
1154
|
-
console.log("deepspace CLI \u2014 run `npx deepspace --help` for usage");
|
|
1866
|
+
undeploy: undeploy_default,
|
|
1867
|
+
domain: domain_default
|
|
1155
1868
|
}
|
|
1869
|
+
// No `run()` here — citty cascades parent run() AFTER subcommand finishes,
|
|
1870
|
+
// which corrupts agent-friendly output (`--json`) and prints noise on every
|
|
1871
|
+
// command. Citty's default behavior when no subcommand matches is to print
|
|
1872
|
+
// help, which is what we want for `deepspace` with no args.
|
|
1156
1873
|
});
|
|
1157
1874
|
runMain(main);
|
|
1158
1875
|
//# sourceMappingURL=cli.js.map
|