deepspace 0.2.4 → 0.2.6
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 +720 -14
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +80 -1
- package/dist/index.js +69 -0
- package/dist/index.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 +222 -5
- package/dist/worker.js +481 -14
- package/dist/worker.js.map +1 -1
- package/package.json +18 -9
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/cli.ts
|
|
4
|
-
import { defineCommand as
|
|
5
|
-
import { execSync as
|
|
6
|
-
import { readFileSync as
|
|
7
|
-
import { dirname as dirname2, join 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
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
|
|
10
10
|
// src/cli/commands/login.ts
|
|
@@ -204,7 +204,7 @@ function openBrowser(url) {
|
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
206
|
function sleep(ms) {
|
|
207
|
-
return new Promise((
|
|
207
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
208
208
|
}
|
|
209
209
|
async function doEmailLogin(email, password) {
|
|
210
210
|
const res = await fetch(`${AUTH_URL}/api/auth/sign-in/email`, {
|
|
@@ -1118,13 +1118,715 @@ var add_default = defineCommand9({
|
|
|
1118
1118
|
}
|
|
1119
1119
|
});
|
|
1120
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
|
+
|
|
1121
1823
|
// src/cli/cli.ts
|
|
1122
1824
|
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
1123
|
-
var pkg = JSON.parse(
|
|
1124
|
-
if (process.platform === "linux" && !process.env.SSL_CERT_FILE &&
|
|
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")) {
|
|
1125
1827
|
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
1126
1828
|
}
|
|
1127
|
-
var create2 =
|
|
1829
|
+
var create2 = defineCommand13({
|
|
1128
1830
|
meta: {
|
|
1129
1831
|
name: "create",
|
|
1130
1832
|
description: "Create a new DeepSpace app"
|
|
@@ -1140,10 +1842,10 @@ var create2 = defineCommand10({
|
|
|
1140
1842
|
const name = args.name ? ` ${args.name}` : "";
|
|
1141
1843
|
console.log(`Running: npm create deepspace${name}
|
|
1142
1844
|
`);
|
|
1143
|
-
|
|
1845
|
+
execSync5(`npm create deepspace@latest${name}`, { stdio: "inherit" });
|
|
1144
1846
|
}
|
|
1145
1847
|
});
|
|
1146
|
-
var main =
|
|
1848
|
+
var main = defineCommand13({
|
|
1147
1849
|
meta: {
|
|
1148
1850
|
name: "deepspace",
|
|
1149
1851
|
version: pkg.version,
|
|
@@ -1158,12 +1860,16 @@ var main = defineCommand10({
|
|
|
1158
1860
|
test: test_default,
|
|
1159
1861
|
"test-accounts": test_accounts_default,
|
|
1160
1862
|
add: add_default,
|
|
1863
|
+
integrations: integrations_default,
|
|
1864
|
+
invoke: invoke_default,
|
|
1161
1865
|
deploy: deploy_default,
|
|
1162
|
-
undeploy: undeploy_default
|
|
1163
|
-
|
|
1164
|
-
run() {
|
|
1165
|
-
console.log("deepspace CLI \u2014 run `npx deepspace --help` for usage");
|
|
1866
|
+
undeploy: undeploy_default,
|
|
1867
|
+
domain: domain_default
|
|
1166
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.
|
|
1167
1873
|
});
|
|
1168
1874
|
runMain(main);
|
|
1169
1875
|
//# sourceMappingURL=cli.js.map
|