deepspace 0.19.5 → 0.21.0
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/CHANGELOG.md +24 -0
- package/dist/{chunk-A7UFIOWO.js → chunk-KDCDRHBA.js} +7 -2
- package/dist/chunk-KDCDRHBA.js.map +1 -0
- package/dist/cli.js +219 -94
- package/dist/cli.js.map +1 -1
- package/dist/documentation-client-core.d.ts +3 -2
- package/dist/documentation-client-core.js +1 -1
- package/dist/documentation-default-renderer.cjs +48 -48
- package/dist/documentation-react.d.ts +2 -1
- package/dist/documentation-react.js +1 -1
- package/dist/documentation-runtime.js +7 -7
- package/dist/documentation-server-core.d.ts +3 -2
- package/dist/documentation-server-core.js +1 -1
- package/dist/documentation.d.ts +40 -12
- package/dist/documentation.js +40 -7
- package/dist/documentation.js.map +1 -1
- package/dist/index.d.ts +225 -21
- package/dist/index.js +262 -112
- package/dist/index.js.map +1 -1
- package/dist/{public-CFAj1hL2.d.ts → public-DenQ1TsZ.d.ts} +36 -11
- package/dist/server.d.ts +46 -29
- package/dist/server.js +1041 -254
- package/dist/server.js.map +1 -1
- package/dist/testing-mcp.d.ts +69 -0
- package/dist/testing-mcp.js +99 -0
- package/dist/testing-mcp.js.map +1 -0
- package/dist/testing.d.ts +17 -20
- package/dist/testing.js +47 -25
- package/dist/testing.js.map +1 -1
- package/dist/worker.d.ts +73 -32
- package/dist/worker.js +1094 -244
- package/dist/worker.js.map +1 -1
- package/features/ai-chat/feature.json +2 -2
- package/features/ai-chat/src/AiChatPage.tsx +56 -21
- package/features/ai-chat/src/ChatPanel.tsx +33 -10
- package/features/ai-chat/src/__tests__/ChatPanel.lifecycle.test.tsx +156 -0
- package/features/cron/src/CronLogPage.tsx +107 -69
- package/features/file-attachments/feature.json +2 -1
- package/features/file-manager/feature.json +2 -1
- package/features/file-manager/src/FileManagerPage.tsx +227 -194
- package/features/integration-test/src/IntegrationTestPage.tsx +54 -34
- package/package.json +5 -1
- package/dist/chunk-A7UFIOWO.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -530,7 +530,7 @@ import { readFileSync as readFileSync2, existsSync, mkdirSync } from "fs";
|
|
|
530
530
|
import { join } from "path";
|
|
531
531
|
import { homedir } from "os";
|
|
532
532
|
|
|
533
|
-
// src/
|
|
533
|
+
// src/shared/jwt.ts
|
|
534
534
|
function decodeJwtPayload(token) {
|
|
535
535
|
const payloadSegment = token.split(".")[1];
|
|
536
536
|
if (!payloadSegment) {
|
|
@@ -2677,11 +2677,15 @@ Free it with \`${killCmd}\`, or start on another port: \`deepspace dev start --p
|
|
|
2677
2677
|
}
|
|
2678
2678
|
const url = `http://${host === DEFAULT_DEV_HOST ? "localhost" : host}:${port}`;
|
|
2679
2679
|
say("Starting dev server...\n");
|
|
2680
|
-
const vite = spawn2(
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2680
|
+
const vite = spawn2(
|
|
2681
|
+
"npx",
|
|
2682
|
+
["vite", "--port", String(port), "--strictPort", "--host", host, "--clearScreen", "false"],
|
|
2683
|
+
{
|
|
2684
|
+
cwd: appDir,
|
|
2685
|
+
stdio: "inherit",
|
|
2686
|
+
env: wranglerViteEnv(process.env, wranglerConfig, { DEEPSPACE_PORT: String(port) })
|
|
2687
|
+
}
|
|
2688
|
+
);
|
|
2685
2689
|
const readiness = new AbortController();
|
|
2686
2690
|
if (args.json) {
|
|
2687
2691
|
void waitForPortListening(port, host, READINESS_TIMEOUT_MS, readiness.signal).then((ready) => {
|
|
@@ -3195,7 +3199,7 @@ async function syncTestAccountStore() {
|
|
|
3195
3199
|
// src/cli/commands/test.ts
|
|
3196
3200
|
var DEPLOY_URL2 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
3197
3201
|
var PLAYWRIGHT_OUTPUT_DIR = ".deepspace/test-results";
|
|
3198
|
-
function playwrightTestArgs(testFiles) {
|
|
3202
|
+
function playwrightTestArgs(testFiles, flags = {}) {
|
|
3199
3203
|
return [
|
|
3200
3204
|
"playwright",
|
|
3201
3205
|
"test",
|
|
@@ -3203,6 +3207,9 @@ function playwrightTestArgs(testFiles) {
|
|
|
3203
3207
|
"tests/playwright.config.ts",
|
|
3204
3208
|
"--output",
|
|
3205
3209
|
PLAYWRIGHT_OUTPUT_DIR,
|
|
3210
|
+
...flags.grep ? ["--grep", flags.grep] : [],
|
|
3211
|
+
...flags.project ? ["--project", flags.project] : [],
|
|
3212
|
+
...flags.headed ? ["--headed"] : [],
|
|
3206
3213
|
...testFiles
|
|
3207
3214
|
];
|
|
3208
3215
|
}
|
|
@@ -3223,6 +3230,21 @@ var test_default = defineDeepspaceCommand({
|
|
|
3223
3230
|
description: `Port for vite/webServer (default ${DEFAULT_PORT}, or $DEEPSPACE_PORT)`,
|
|
3224
3231
|
required: false
|
|
3225
3232
|
},
|
|
3233
|
+
grep: {
|
|
3234
|
+
type: "string",
|
|
3235
|
+
description: "Only run Playwright tests matching this pattern (forwarded to --grep)",
|
|
3236
|
+
required: false
|
|
3237
|
+
},
|
|
3238
|
+
project: {
|
|
3239
|
+
type: "string",
|
|
3240
|
+
description: "Playwright project to run (forwarded to --project)",
|
|
3241
|
+
required: false
|
|
3242
|
+
},
|
|
3243
|
+
headed: {
|
|
3244
|
+
type: "boolean",
|
|
3245
|
+
description: "Run Playwright browsers headed (forwarded to --headed)",
|
|
3246
|
+
default: false
|
|
3247
|
+
},
|
|
3226
3248
|
env: {
|
|
3227
3249
|
type: "string",
|
|
3228
3250
|
alias: "e",
|
|
@@ -3237,6 +3259,11 @@ var test_default = defineDeepspaceCommand({
|
|
|
3237
3259
|
preflightNodeVersion("test run");
|
|
3238
3260
|
const suite = args.suite ?? "default";
|
|
3239
3261
|
const wranglerEnv = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
3262
|
+
const forwarded = {
|
|
3263
|
+
grep: typeof args.grep === "string" && args.grep ? args.grep : void 0,
|
|
3264
|
+
project: typeof args.project === "string" && args.project ? args.project : void 0,
|
|
3265
|
+
headed: Boolean(args.headed)
|
|
3266
|
+
};
|
|
3240
3267
|
const start = resolve9(".");
|
|
3241
3268
|
const appDir = findAppDir(start);
|
|
3242
3269
|
if (!appDir) {
|
|
@@ -3293,32 +3320,36 @@ var test_default = defineDeepspaceCommand({
|
|
|
3293
3320
|
let exitCode = 0;
|
|
3294
3321
|
switch (suite) {
|
|
3295
3322
|
case "smoke":
|
|
3296
|
-
exitCode = runPlaywright(appDir, ["tests/smoke.spec.ts"], port, wranglerEnv);
|
|
3323
|
+
exitCode = runPlaywright(appDir, ["tests/smoke.spec.ts"], port, wranglerEnv, forwarded);
|
|
3297
3324
|
break;
|
|
3298
3325
|
case "api":
|
|
3299
|
-
exitCode = runPlaywright(appDir, ["tests/api.spec.ts"], port, wranglerEnv);
|
|
3326
|
+
exitCode = runPlaywright(appDir, ["tests/api.spec.ts"], port, wranglerEnv, forwarded);
|
|
3300
3327
|
break;
|
|
3301
3328
|
case "e2e":
|
|
3302
|
-
exitCode = runPlaywright(appDir, [], port, wranglerEnv);
|
|
3329
|
+
exitCode = runPlaywright(appDir, [], port, wranglerEnv, forwarded);
|
|
3303
3330
|
break;
|
|
3304
3331
|
case "unit":
|
|
3332
|
+
if (forwarded.grep || forwarded.project || forwarded.headed) {
|
|
3333
|
+
console.error("note: --grep/--project/--headed apply to Playwright suites and are ignored by `unit`");
|
|
3334
|
+
}
|
|
3305
3335
|
exitCode = runVitest(appDir);
|
|
3306
3336
|
break;
|
|
3307
3337
|
case "all":
|
|
3308
3338
|
exitCode = runVitest(appDir);
|
|
3309
|
-
if (exitCode === 0) exitCode = runPlaywright(appDir, [], port, wranglerEnv);
|
|
3339
|
+
if (exitCode === 0) exitCode = runPlaywright(appDir, [], port, wranglerEnv, forwarded);
|
|
3310
3340
|
break;
|
|
3311
3341
|
case "default":
|
|
3312
3342
|
exitCode = runPlaywright(
|
|
3313
3343
|
appDir,
|
|
3314
3344
|
["tests/smoke.spec.ts", "tests/api.spec.ts"],
|
|
3315
3345
|
port,
|
|
3316
|
-
wranglerEnv
|
|
3346
|
+
wranglerEnv,
|
|
3347
|
+
forwarded
|
|
3317
3348
|
);
|
|
3318
3349
|
break;
|
|
3319
3350
|
default:
|
|
3320
3351
|
if (suite.endsWith(".spec.ts")) {
|
|
3321
|
-
exitCode = runPlaywright(appDir, [suite], port, wranglerEnv);
|
|
3352
|
+
exitCode = runPlaywright(appDir, [suite], port, wranglerEnv, forwarded);
|
|
3322
3353
|
} else {
|
|
3323
3354
|
throw new Refusal(
|
|
3324
3355
|
`Unknown test suite: ${suite}
|
|
@@ -3335,7 +3366,7 @@ Available: smoke, api, e2e, unit, all`,
|
|
|
3335
3366
|
return { data: { suite, port, appDir, wranglerEnv: wranglerEnv ?? null } };
|
|
3336
3367
|
}
|
|
3337
3368
|
});
|
|
3338
|
-
function runPlaywright(appDir, testFiles, port, wranglerEnv) {
|
|
3369
|
+
function runPlaywright(appDir, testFiles, port, wranglerEnv, flags) {
|
|
3339
3370
|
let wranglerConfig;
|
|
3340
3371
|
try {
|
|
3341
3372
|
wranglerConfig = prepareWranglerEnvConfig(appDir, wranglerEnv);
|
|
@@ -3344,7 +3375,7 @@ function runPlaywright(appDir, testFiles, port, wranglerEnv) {
|
|
|
3344
3375
|
return 1;
|
|
3345
3376
|
}
|
|
3346
3377
|
try {
|
|
3347
|
-
const result = spawnSync4("npx", playwrightTestArgs(testFiles), {
|
|
3378
|
+
const result = spawnSync4("npx", playwrightTestArgs(testFiles, flags), {
|
|
3348
3379
|
cwd: appDir,
|
|
3349
3380
|
stdio: "inherit",
|
|
3350
3381
|
env: wranglerViteEnv(process.env, wranglerConfig, {
|
|
@@ -3453,7 +3484,7 @@ var create = defineDeepspaceCommand({
|
|
|
3453
3484
|
await ensureToken();
|
|
3454
3485
|
const email = args.email;
|
|
3455
3486
|
const password = args.password;
|
|
3456
|
-
const name = args.name;
|
|
3487
|
+
const name = args.name ?? email.split("@")[0];
|
|
3457
3488
|
const label = args.label;
|
|
3458
3489
|
let account;
|
|
3459
3490
|
try {
|
|
@@ -3477,6 +3508,7 @@ var create = defineDeepspaceCommand({
|
|
|
3477
3508
|
console.log(`Created test account:`);
|
|
3478
3509
|
console.log(` ID: ${account.id}`);
|
|
3479
3510
|
console.log(` Email: ${account.email}`);
|
|
3511
|
+
console.log(` Selector: ${name}`);
|
|
3480
3512
|
console.log(` Password: ${password}`);
|
|
3481
3513
|
console.log(` UserID: ${account.userId}`);
|
|
3482
3514
|
if (account.label) console.log(` Label: ${account.label}`);
|
|
@@ -3487,6 +3519,7 @@ Saved to ${TEST_ACCOUNTS_PATH}`);
|
|
|
3487
3519
|
data: {
|
|
3488
3520
|
id: account.id,
|
|
3489
3521
|
email: account.email,
|
|
3522
|
+
name,
|
|
3490
3523
|
// The password is already echoed on the human path and stored 0600 in
|
|
3491
3524
|
// ACCOUNTS_PATH — withholding it from --json would only force scripts
|
|
3492
3525
|
// to parse the file themselves.
|
|
@@ -3504,6 +3537,18 @@ var list = defineDeepspaceCommand({
|
|
|
3504
3537
|
name: "list",
|
|
3505
3538
|
description: "List your test accounts"
|
|
3506
3539
|
},
|
|
3540
|
+
args: {
|
|
3541
|
+
usable: {
|
|
3542
|
+
type: "boolean",
|
|
3543
|
+
description: "Only accounts with locally saved credentials (usable by the users() fixture)",
|
|
3544
|
+
default: false
|
|
3545
|
+
},
|
|
3546
|
+
reveal: {
|
|
3547
|
+
type: "boolean",
|
|
3548
|
+
description: "Print locally saved passwords instead of masking them",
|
|
3549
|
+
default: false
|
|
3550
|
+
}
|
|
3551
|
+
},
|
|
3507
3552
|
async run({ args }) {
|
|
3508
3553
|
await ensureToken();
|
|
3509
3554
|
let remote;
|
|
@@ -3512,42 +3557,47 @@ var list = defineDeepspaceCommand({
|
|
|
3512
3557
|
} catch (err) {
|
|
3513
3558
|
throw new Refusal(`Failed: ${err.message}`, "test_accounts_list_failed");
|
|
3514
3559
|
}
|
|
3515
|
-
const
|
|
3516
|
-
const
|
|
3517
|
-
|
|
3560
|
+
const localByEmail = new Map(loadAllTestAccounts().map((a) => [a.email, a]));
|
|
3561
|
+
const accounts = remote.map((a) => {
|
|
3562
|
+
const stored = localByEmail.get(a.email);
|
|
3563
|
+
return {
|
|
3564
|
+
id: a.id,
|
|
3565
|
+
email: a.email,
|
|
3566
|
+
userId: a.userId,
|
|
3567
|
+
label: a.label,
|
|
3568
|
+
createdAt: a.createdAt,
|
|
3569
|
+
name: stored?.name ?? null,
|
|
3570
|
+
password: stored?.password ?? null,
|
|
3571
|
+
usableByFixture: Boolean(stored?.password)
|
|
3572
|
+
};
|
|
3573
|
+
}).filter((a) => !args.usable || a.usableByFixture);
|
|
3574
|
+
if (accounts.length === 0) {
|
|
3518
3575
|
if (!args.json) {
|
|
3519
3576
|
console.log(
|
|
3520
|
-
"No test accounts. Create one with: deepspace test accounts create --email <email> --password <password>"
|
|
3577
|
+
remote.length > 0 ? `No test accounts with locally saved credentials (${remote.length} remote-only). Recreate them with: deepspace test accounts create --email <email> --password <password>` : "No test accounts. Create one with: deepspace test accounts create --email <email> --password <password>"
|
|
3521
3578
|
);
|
|
3522
3579
|
}
|
|
3523
3580
|
return { data: { accounts: [], count: 0, limit: 10 } };
|
|
3524
3581
|
}
|
|
3525
3582
|
if (!args.json) {
|
|
3526
|
-
console.log(`Test accounts (${remote.length}/10):
|
|
3583
|
+
console.log(`Test accounts (${remote.length}/10${args.usable ? ", usable only" : ""}):
|
|
3527
3584
|
`);
|
|
3528
|
-
for (const a of
|
|
3529
|
-
const stored = localByEmail.get(a.email);
|
|
3585
|
+
for (const a of accounts) {
|
|
3530
3586
|
const date = new Date(a.createdAt).toLocaleDateString();
|
|
3531
3587
|
console.log(` ${a.email}${a.label ? ` (${a.label})` : ""}`);
|
|
3532
3588
|
console.log(` ID: ${a.id} UserID: ${a.userId} Created: ${date}`);
|
|
3533
|
-
|
|
3534
|
-
|
|
3589
|
+
console.log(` Selector: ${a.name ?? "(none)"}`);
|
|
3590
|
+
if (a.usableByFixture) {
|
|
3591
|
+
console.log(` Password: ${args.reveal ? a.password : "(saved locally)"}`);
|
|
3535
3592
|
} else {
|
|
3536
|
-
console.log(` Password: (not saved locally)`);
|
|
3593
|
+
console.log(` Password: (not saved locally \u2014 not usable by the users() fixture)`);
|
|
3537
3594
|
}
|
|
3538
3595
|
}
|
|
3539
3596
|
}
|
|
3540
3597
|
return {
|
|
3541
3598
|
data: {
|
|
3542
|
-
accounts
|
|
3543
|
-
|
|
3544
|
-
email: a.email,
|
|
3545
|
-
userId: a.userId,
|
|
3546
|
-
label: a.label,
|
|
3547
|
-
createdAt: a.createdAt,
|
|
3548
|
-
password: localByEmail.get(a.email)?.password ?? null
|
|
3549
|
-
})),
|
|
3550
|
-
count: remote.length,
|
|
3599
|
+
accounts,
|
|
3600
|
+
count: accounts.length,
|
|
3551
3601
|
limit: 10
|
|
3552
3602
|
}
|
|
3553
3603
|
};
|
|
@@ -3799,13 +3849,13 @@ var MAX_UPLOAD_REQUEST_BYTES = 25 * 1024 * 1024;
|
|
|
3799
3849
|
var UPLOAD_PART_BYTES = 20 * 1024 * 1024;
|
|
3800
3850
|
var MAX_UPLOAD_PARTS = Math.ceil(MAX_APP_FILE_BYTES / UPLOAD_PART_BYTES);
|
|
3801
3851
|
var MAX_BASE64_UPLOAD_BYTES = Math.floor(MAX_UPLOAD_REQUEST_BYTES * 3 / 4);
|
|
3802
|
-
function planUploadParts(totalBytes
|
|
3852
|
+
function planUploadParts(totalBytes) {
|
|
3803
3853
|
const parts = [];
|
|
3804
|
-
for (let start = 0; start < totalBytes; start +=
|
|
3854
|
+
for (let start = 0; start < totalBytes; start += UPLOAD_PART_BYTES) {
|
|
3805
3855
|
parts.push({
|
|
3806
3856
|
partNumber: parts.length + 1,
|
|
3807
3857
|
start,
|
|
3808
|
-
end: Math.min(start +
|
|
3858
|
+
end: Math.min(start + UPLOAD_PART_BYTES, totalBytes)
|
|
3809
3859
|
});
|
|
3810
3860
|
}
|
|
3811
3861
|
return parts;
|
|
@@ -3820,20 +3870,6 @@ var ACCOUNT_STORAGE_LIMIT_BYTES = {
|
|
|
3820
3870
|
};
|
|
3821
3871
|
var REPO_STORE_LIMIT_BYTES = 256 * 1024 * 1024;
|
|
3822
3872
|
var MAX_DEPLOY_ASSET_FILE_BYTES = 25 * 1024 * 1024;
|
|
3823
|
-
var DANGEROUS_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
3824
|
-
"text/html",
|
|
3825
|
-
"application/xhtml+xml",
|
|
3826
|
-
"image/svg+xml",
|
|
3827
|
-
"application/xml",
|
|
3828
|
-
"text/xml",
|
|
3829
|
-
"application/javascript",
|
|
3830
|
-
"text/javascript",
|
|
3831
|
-
"application/ecmascript",
|
|
3832
|
-
"text/ecmascript"
|
|
3833
|
-
]);
|
|
3834
|
-
function activeContentMessage(mimeType) {
|
|
3835
|
-
return `${mimeType} is not accepted: the files API serves your app's own origin, so stored HTML/SVG/JavaScript would execute there with your users' sessions. Ship it as a build asset under \`public/\` instead, where it is served as part of your app rather than as user-uploaded content.`;
|
|
3836
|
-
}
|
|
3837
3873
|
function formatBytes(bytes) {
|
|
3838
3874
|
if (bytes >= 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GiB`;
|
|
3839
3875
|
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
|
|
@@ -4431,6 +4467,7 @@ async function buildDeployBundle(options) {
|
|
|
4431
4467
|
if (lintFindings && lintFindings.length > 0) {
|
|
4432
4468
|
p6.log.warn(formatSchemaLintFindings(lintFindings).join("\n"));
|
|
4433
4469
|
}
|
|
4470
|
+
const buildStarted = Date.now();
|
|
4434
4471
|
spinner2.start("Building...");
|
|
4435
4472
|
let preparedWranglerConfig;
|
|
4436
4473
|
try {
|
|
@@ -4453,7 +4490,7 @@ async function buildDeployBundle(options) {
|
|
|
4453
4490
|
} finally {
|
|
4454
4491
|
preparedWranglerConfig?.cleanup();
|
|
4455
4492
|
}
|
|
4456
|
-
spinner2.stop(
|
|
4493
|
+
spinner2.stop(`Built (${Math.round((Date.now() - buildStarted) / 1e3)}s)`);
|
|
4457
4494
|
const deployConfigPath = join15(appDir, ".wrangler", "deploy", "config.json");
|
|
4458
4495
|
if (!existsSync14(deployConfigPath)) {
|
|
4459
4496
|
output.die(
|
|
@@ -4615,6 +4652,51 @@ import * as p7 from "@clack/prompts";
|
|
|
4615
4652
|
import { existsSync as existsSync15 } from "fs";
|
|
4616
4653
|
import { join as join16 } from "path";
|
|
4617
4654
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
4655
|
+
|
|
4656
|
+
// src/shared/api-error.ts
|
|
4657
|
+
var SLUG_RE = /^[a-z0-9]+(?:[_.-][a-z0-9]+)*$/;
|
|
4658
|
+
var HUMAN_MESSAGES = {
|
|
4659
|
+
owner_connect_not_ready: "This app isn't ready to accept payments yet \u2014 the app owner needs to finish payment setup.",
|
|
4660
|
+
invalid_json: "The request body was not valid JSON.",
|
|
4661
|
+
invalid_body: "The request body is invalid.",
|
|
4662
|
+
invalid_product_id: "Invalid product id.",
|
|
4663
|
+
duplicate_product_id: "Duplicate product id.",
|
|
4664
|
+
unknown_product: "Unknown product.",
|
|
4665
|
+
too_many_products: "Too many products declared.",
|
|
4666
|
+
invalid_amount: "Invalid amount.",
|
|
4667
|
+
invalid_name: "Invalid product name.",
|
|
4668
|
+
invalid_description: "Invalid product description.",
|
|
4669
|
+
unsupported_currency: "Unsupported currency.",
|
|
4670
|
+
unknown_app: "Unknown app.",
|
|
4671
|
+
not_app_owner: "Only the app owner (or a collaborator) can do this.",
|
|
4672
|
+
invalid_return_url: "Invalid return URL.",
|
|
4673
|
+
invalid_cancel_url: "Invalid cancel URL.",
|
|
4674
|
+
unknown_plan: "Unknown subscription plan.",
|
|
4675
|
+
unknown_price: "No price is configured for that billing interval.",
|
|
4676
|
+
no_subscription: "No subscription found for this account."
|
|
4677
|
+
};
|
|
4678
|
+
function normalizeApiError(status3, body) {
|
|
4679
|
+
const payload = body !== null && typeof body === "object" ? body : {};
|
|
4680
|
+
const rawError = typeof payload.error === "string" ? payload.error : void 0;
|
|
4681
|
+
const message = typeof payload.message === "string" ? payload.message : void 0;
|
|
4682
|
+
const humanError = rawError !== void 0 && !SLUG_RE.test(rawError) ? rawError : void 0;
|
|
4683
|
+
const code = rawError !== void 0 && SLUG_RE.test(rawError) ? rawError : typeof payload.code === "string" && SLUG_RE.test(payload.code) ? payload.code : void 0;
|
|
4684
|
+
const details = {};
|
|
4685
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
4686
|
+
if (key === "success" || key === "error" || key === "message" || key === "issues" || key === "code")
|
|
4687
|
+
continue;
|
|
4688
|
+
details[key] = value;
|
|
4689
|
+
}
|
|
4690
|
+
return {
|
|
4691
|
+
error: message ?? humanError ?? (code !== void 0 ? HUMAN_MESSAGES[code] : void 0) ?? rawError ?? `Request failed (${status3})`,
|
|
4692
|
+
...code !== void 0 ? { code } : {},
|
|
4693
|
+
status: status3,
|
|
4694
|
+
...Object.keys(details).length > 0 ? { details } : {},
|
|
4695
|
+
...Array.isArray(payload.issues) ? { issues: payload.issues } : {}
|
|
4696
|
+
};
|
|
4697
|
+
}
|
|
4698
|
+
|
|
4699
|
+
// src/cli/commands/deploy/commerce.ts
|
|
4618
4700
|
var API_URL2 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
4619
4701
|
function errorMessage2(error) {
|
|
4620
4702
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -4688,7 +4770,7 @@ Stripe will keep charging them until you cancel. Cancel at the end of their curr
|
|
|
4688
4770
|
});
|
|
4689
4771
|
const body = await response.json().catch(() => ({}));
|
|
4690
4772
|
if (!response.ok || !body.success) {
|
|
4691
|
-
lastError = body.error
|
|
4773
|
+
lastError = normalizeApiError(response.status, body).error;
|
|
4692
4774
|
spinner2.stop(`Cancel failed for ${change.slug}: ${lastError}`);
|
|
4693
4775
|
stopped = true;
|
|
4694
4776
|
break;
|
|
@@ -4746,7 +4828,7 @@ async function syncSubscriptionPlans(appDir, appName, token, nonInteractive) {
|
|
|
4746
4828
|
if (!response.ok || !body.ok) {
|
|
4747
4829
|
spinner2.stop("Plan sync skipped");
|
|
4748
4830
|
p7.log.warn(
|
|
4749
|
-
`Plan sync failed: ${body.error
|
|
4831
|
+
`Plan sync failed: ${normalizeApiError(response.status, body).error}${body.hint ? ` \u2014 ${body.hint}` : ""}`
|
|
4750
4832
|
);
|
|
4751
4833
|
return;
|
|
4752
4834
|
}
|
|
@@ -4804,7 +4886,7 @@ async function syncOneTimeProducts(appDir, appName, token) {
|
|
|
4804
4886
|
}
|
|
4805
4887
|
spinner2?.stop("Product sync skipped");
|
|
4806
4888
|
p7.log.warn(
|
|
4807
|
-
`Product sync failed: ${body.error
|
|
4889
|
+
`Product sync failed: ${normalizeApiError(response.status, body).error}${body.details ? ` \u2014 ${JSON.stringify(body.details)}` : ""}`
|
|
4808
4890
|
);
|
|
4809
4891
|
} catch (error) {
|
|
4810
4892
|
spinner2?.stop("Product sync skipped");
|
|
@@ -5672,7 +5754,7 @@ function detachedHeadRefusal() {
|
|
|
5672
5754
|
var CLOUDFLARE_DEPLOY_ERROR_HINT = "Deployment reached the DeepSpace deploy worker, but Cloudflare's deployment control-plane API failed. This is often caused by a Cloudflare Dashboard/API incident or Workers for Platforms entitlement outage. Already deployed apps should keep serving; wait for Cloudflare to recover and retry.";
|
|
5673
5755
|
var DEPLOY_SERVICE_LIMIT_HINT = "The DeepSpace deploy service hit a resource limit processing this deploy (the deploy worker was terminated before it could answer). This is a DeepSpace-side failure, not a Cloudflare incident \u2014 your app was not changed. Retry; if it keeps happening, report the app id and asset count.";
|
|
5674
5756
|
var REQUIRED_ASSET_TRANSPORT = "content-addressed-v1";
|
|
5675
|
-
var UPLOAD_CONCURRENCY =
|
|
5757
|
+
var UPLOAD_CONCURRENCY = 16;
|
|
5676
5758
|
async function deployBuiltBundle(options) {
|
|
5677
5759
|
const {
|
|
5678
5760
|
deployUrl,
|
|
@@ -5761,15 +5843,19 @@ async function deployBuiltBundle(options) {
|
|
|
5761
5843
|
form.append("deployKey", repository.deployKey);
|
|
5762
5844
|
return form;
|
|
5763
5845
|
};
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5846
|
+
let commitStarted = Date.now();
|
|
5847
|
+
const postCommit = async () => {
|
|
5848
|
+
commitStarted = Date.now();
|
|
5849
|
+
return postWithRetry(
|
|
5850
|
+
`${deployUrl}/api/deploy/${appId}`,
|
|
5851
|
+
() => ({
|
|
5852
|
+
method: "POST",
|
|
5853
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
5854
|
+
body: makeForm()
|
|
5855
|
+
}),
|
|
5856
|
+
{ retryServerErrors: false, attempts: 2, timeoutMs: 24e4 }
|
|
5857
|
+
);
|
|
5858
|
+
};
|
|
5773
5859
|
let response;
|
|
5774
5860
|
try {
|
|
5775
5861
|
response = await postCommit();
|
|
@@ -5816,6 +5902,7 @@ async function deployBuiltBundle(options) {
|
|
|
5816
5902
|
body.code
|
|
5817
5903
|
);
|
|
5818
5904
|
}
|
|
5905
|
+
p10.log.info(`Platform commit: ${Math.round((Date.now() - commitStarted) / 1e3)}s`);
|
|
5819
5906
|
if (body.onBehalfOfOwner) {
|
|
5820
5907
|
p10.log.warn("This release is attributed to the app's owner, not to your account.");
|
|
5821
5908
|
}
|
|
@@ -5934,8 +6021,12 @@ async function uploadDeployAssets(options) {
|
|
|
5934
6021
|
spinner2.message(`Uploading assets \u2014 ${uploaded}/${total}...`);
|
|
5935
6022
|
}
|
|
5936
6023
|
};
|
|
6024
|
+
const uploadStarted = Date.now();
|
|
5937
6025
|
try {
|
|
5938
6026
|
await Promise.all(Array.from({ length: Math.min(UPLOAD_CONCURRENCY, missing.length) }, worker));
|
|
6027
|
+
p10.log.info(
|
|
6028
|
+
`Uploaded ${total} file(s) in ${Math.round((Date.now() - uploadStarted) / 1e3)}s`
|
|
6029
|
+
);
|
|
5939
6030
|
} catch (error) {
|
|
5940
6031
|
spinner2.stop("Deploy failed");
|
|
5941
6032
|
if (error instanceof AssetUploadError) {
|
|
@@ -5981,13 +6072,14 @@ function errorMessage4(error) {
|
|
|
5981
6072
|
}
|
|
5982
6073
|
async function postWithRetry(url, makeInit, {
|
|
5983
6074
|
attempts = 4,
|
|
5984
|
-
retryServerErrors = true
|
|
6075
|
+
retryServerErrors = true,
|
|
6076
|
+
timeoutMs = 6e4
|
|
5985
6077
|
} = {}) {
|
|
5986
6078
|
let lastResponse;
|
|
5987
6079
|
let lastError;
|
|
5988
6080
|
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
5989
6081
|
try {
|
|
5990
|
-
const response = await fetch(url, makeInit());
|
|
6082
|
+
const response = await fetch(url, { ...makeInit(), signal: AbortSignal.timeout(timeoutMs) });
|
|
5991
6083
|
const transient = retryServerErrors && (response.status >= 500 || response.status === 408 || response.status === 429);
|
|
5992
6084
|
if (!transient) return response;
|
|
5993
6085
|
lastResponse = response;
|
|
@@ -6232,6 +6324,7 @@ var deploy_default = defineCommand3({
|
|
|
6232
6324
|
spinner: spinner2
|
|
6233
6325
|
});
|
|
6234
6326
|
let serving = "unverifiable";
|
|
6327
|
+
const edgeWaitStarted = Date.now();
|
|
6235
6328
|
if (body.url) {
|
|
6236
6329
|
spinner2.message("Waiting for the edge to serve this release...");
|
|
6237
6330
|
serving = await waitForLiveRelease(body.url, body.releaseStamp, 9e4);
|
|
@@ -6261,7 +6354,9 @@ var deploy_default = defineCommand3({
|
|
|
6261
6354
|
return;
|
|
6262
6355
|
}
|
|
6263
6356
|
}
|
|
6264
|
-
spinner2.stop(
|
|
6357
|
+
spinner2.stop(
|
|
6358
|
+
`Deployed! (edge confirmed in ${Math.round((Date.now() - edgeWaitStarted) / 1e3)}s)`
|
|
6359
|
+
);
|
|
6265
6360
|
p12.log.success(`Live at: ${body.url}`);
|
|
6266
6361
|
await syncCommerce(appDir, appId, token, output.nonInteractive);
|
|
6267
6362
|
if (output.json) {
|
|
@@ -6522,7 +6617,10 @@ var undeploy_default = defineDeepspaceCommand({
|
|
|
6522
6617
|
}
|
|
6523
6618
|
);
|
|
6524
6619
|
}
|
|
6525
|
-
throw new Refusal(
|
|
6620
|
+
throw new Refusal(
|
|
6621
|
+
body.error ?? `Undeploy error (${res.status})`,
|
|
6622
|
+
body.code ?? "undeploy_failed"
|
|
6623
|
+
);
|
|
6526
6624
|
}
|
|
6527
6625
|
s?.stop("Removed");
|
|
6528
6626
|
const hosts = body.releasedHosts ?? [];
|
|
@@ -8491,9 +8589,6 @@ async function uploadAppFile(baseUrl, token, appId, localPath, key, onProgress)
|
|
|
8491
8589
|
throw new ApiError(oversizeMessage(size), 0, "too_large", path);
|
|
8492
8590
|
}
|
|
8493
8591
|
const declaredType = contentTypeFor(localPath);
|
|
8494
|
-
if (DANGEROUS_MIME_TYPES.has(declaredType)) {
|
|
8495
|
-
throw new ApiError(activeContentMessage(declaredType), 0, "active_content", path);
|
|
8496
|
-
}
|
|
8497
8592
|
if (size > UPLOAD_PART_BYTES) {
|
|
8498
8593
|
return uploadInParts(baseUrl, token, appId, localPath, key, size, onProgress);
|
|
8499
8594
|
}
|
|
@@ -8514,24 +8609,35 @@ function isRetryable(err) {
|
|
|
8514
8609
|
async function uploadInParts(baseUrl, token, appId, localPath, key, size, onProgress) {
|
|
8515
8610
|
const contentType = contentTypeFor(localPath);
|
|
8516
8611
|
const initPath = filesPath(appId, `/multipart?key=${encodeKeyPath(key)}`);
|
|
8517
|
-
const
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
)
|
|
8525
|
-
|
|
8526
|
-
|
|
8612
|
+
const requestId = randomUUID2();
|
|
8613
|
+
const initBody = JSON.stringify({
|
|
8614
|
+
requestId,
|
|
8615
|
+
name: basename3(localPath),
|
|
8616
|
+
mimeType: contentType,
|
|
8617
|
+
size
|
|
8618
|
+
});
|
|
8619
|
+
const sendInit = () => request(baseUrl, token, initPath, {
|
|
8620
|
+
method: "POST",
|
|
8621
|
+
headers: { "Content-Type": "application/json" },
|
|
8622
|
+
body: initBody
|
|
8623
|
+
}).then((response) => readJson(response, initPath));
|
|
8624
|
+
let session;
|
|
8625
|
+
try {
|
|
8626
|
+
session = await sendInit();
|
|
8627
|
+
} catch (error) {
|
|
8628
|
+
if (!isRetryable(error)) throw error;
|
|
8629
|
+
session = await sendInit();
|
|
8630
|
+
}
|
|
8631
|
+
const query = `uploadKey=${encodeKeyPath(session.uploadKey)}&uploadId=${encodeURIComponent(session.uploadId)}&reservationId=${encodeURIComponent(session.reservationId)}`;
|
|
8632
|
+
if (!session.uploadId || !session.uploadKey || !session.reservationId || session.partSize !== UPLOAD_PART_BYTES) {
|
|
8527
8633
|
throw new ApiError(
|
|
8528
|
-
`The platform
|
|
8634
|
+
`The platform returned an unusable part size or multipart session (${session.partSize}).`,
|
|
8529
8635
|
0,
|
|
8530
8636
|
"invalid_response",
|
|
8531
8637
|
initPath
|
|
8532
8638
|
);
|
|
8533
8639
|
}
|
|
8534
|
-
const plan = planUploadParts(size
|
|
8640
|
+
const plan = planUploadParts(size);
|
|
8535
8641
|
const file = await openAsBlob(localPath, { type: contentType });
|
|
8536
8642
|
const uploaded = [];
|
|
8537
8643
|
try {
|
|
@@ -9211,7 +9317,9 @@ async function runList(args) {
|
|
|
9211
9317
|
for (const ep of endpoints) {
|
|
9212
9318
|
const pad = ep.endpoint.padEnd(widest);
|
|
9213
9319
|
const cost = formatCurrency(ep.billing.baseCost, ep.billing.currency);
|
|
9214
|
-
|
|
9320
|
+
const oauth = ep.requiresOAuth ? " [oauth]" : "";
|
|
9321
|
+
console.log(` ${pad} ${ep.billing.model.padEnd(12)} ${cost}${oauth}`);
|
|
9322
|
+
if (ep.description) console.log(` ${ep.description}`);
|
|
9215
9323
|
}
|
|
9216
9324
|
console.log();
|
|
9217
9325
|
}
|
|
@@ -9240,9 +9348,15 @@ async function runInfo(args) {
|
|
|
9240
9348
|
}
|
|
9241
9349
|
if (!args.json) {
|
|
9242
9350
|
console.log(`${integration}/${endpoint}`);
|
|
9351
|
+
if (info2.description) console.log(` ${info2.description}`);
|
|
9243
9352
|
console.log(
|
|
9244
9353
|
` billing: ${formatCurrency(info2.billing.baseCost, info2.billing.currency)} ${billingUnit(info2.billing.model)}`
|
|
9245
9354
|
);
|
|
9355
|
+
if (info2.requiresOAuth) {
|
|
9356
|
+
console.log(
|
|
9357
|
+
" Requires OAuth: without a connected account, a call succeeds with data { requiresOAuth: true, authUrl } \u2014 open the authUrl, then retry."
|
|
9358
|
+
);
|
|
9359
|
+
}
|
|
9246
9360
|
console.log();
|
|
9247
9361
|
console.log("Input schema:");
|
|
9248
9362
|
console.log(
|
|
@@ -9251,6 +9365,11 @@ async function runInfo(args) {
|
|
|
9251
9365
|
console.log();
|
|
9252
9366
|
console.log("Example body:");
|
|
9253
9367
|
console.log(info2.example ? JSON.stringify(info2.example, null, 2) : " (no example available)");
|
|
9368
|
+
if (info2.outputSchema) {
|
|
9369
|
+
console.log();
|
|
9370
|
+
console.log("Output schema:");
|
|
9371
|
+
console.log(JSON.stringify(info2.outputSchema, null, 2));
|
|
9372
|
+
}
|
|
9254
9373
|
}
|
|
9255
9374
|
return { data: info2 };
|
|
9256
9375
|
}
|
|
@@ -9331,17 +9450,19 @@ async function runInvoke(args) {
|
|
|
9331
9450
|
}
|
|
9332
9451
|
return { data: payload };
|
|
9333
9452
|
}
|
|
9334
|
-
const
|
|
9335
|
-
const lines = [`${integration}/${endpoint} (${res.status}, ${elapsed}ms): ${
|
|
9336
|
-
if (
|
|
9337
|
-
for (const issue of
|
|
9453
|
+
const norm = normalizeApiError(res.status, payload);
|
|
9454
|
+
const lines = [`${integration}/${endpoint} (${res.status}, ${elapsed}ms): ${norm.error}`];
|
|
9455
|
+
if (norm.issues) {
|
|
9456
|
+
for (const issue of norm.issues) {
|
|
9338
9457
|
const path = issue.path?.length ? issue.path.join(".") : "(root)";
|
|
9339
9458
|
lines.push(` - ${path}: ${issue.message}`);
|
|
9340
9459
|
}
|
|
9341
9460
|
}
|
|
9342
|
-
const code =
|
|
9461
|
+
const code = norm.code ?? "integration_call_failed";
|
|
9343
9462
|
const rest = { ...payload };
|
|
9344
9463
|
delete rest.error;
|
|
9464
|
+
delete rest.message;
|
|
9465
|
+
delete rest.code;
|
|
9345
9466
|
throw new Refusal(lines.join("\n"), code, {
|
|
9346
9467
|
...res.status === 401 ? { action: cliAction("deepspace", "auth", "login") } : {},
|
|
9347
9468
|
extra: rest
|
|
@@ -12587,7 +12708,7 @@ function conflictMarkerFiles(diffCheckOutput) {
|
|
|
12587
12708
|
function pullAfterLandAction(primaryDir, intoBranch, landedOid) {
|
|
12588
12709
|
if (currentBranch(primaryDir) !== intoBranch) return null;
|
|
12589
12710
|
if (resolveCommit(primaryDir, "HEAD") === landedOid) return null;
|
|
12590
|
-
return { cwd: primaryDir, argv: ["deepspace", "pull"] };
|
|
12711
|
+
return executableAction({ cwd: primaryDir, argv: ["deepspace", "pull"] });
|
|
12591
12712
|
}
|
|
12592
12713
|
function landResumeArgv(args) {
|
|
12593
12714
|
const argv = ["deepspace", "workspace", "land"];
|
|
@@ -12868,6 +12989,7 @@ var landWorkspaceCommand = defineDeepspaceCommand({
|
|
|
12868
12989
|
}
|
|
12869
12990
|
spinner2?.stop(`Landed ${id} into ${intoBranch} at ${landedOid.slice(0, 10)}.`);
|
|
12870
12991
|
const primaryDir = primaryAppDir(appDir);
|
|
12992
|
+
const action = pullAfterLandAction(primaryDir, intoBranch, landedOid);
|
|
12871
12993
|
const cleanup = keepWorktree ? null : cleanupWorkspaceLocal(appDir, id, intoBranch, { expectedBranchOid: landedOid });
|
|
12872
12994
|
const inOwnWorktree = !cleanup && inOwnLinkedWorktree(appDir, id);
|
|
12873
12995
|
const retainedWorktree = cleanup?.worktreeRetained ?? (keepWorktree || inOwnWorktree ? appDir : null);
|
|
@@ -12901,7 +13023,6 @@ var landWorkspaceCommand = defineDeepspaceCommand({
|
|
|
12901
13023
|
{ actionRequired: true, action: action2, extra: data }
|
|
12902
13024
|
);
|
|
12903
13025
|
}
|
|
12904
|
-
const action = pullAfterLandAction(primaryDir, intoBranch, landedOid);
|
|
12905
13026
|
return { data, ...action ? { action } : {} };
|
|
12906
13027
|
}
|
|
12907
13028
|
});
|
|
@@ -13628,7 +13749,7 @@ import { join as join23, resolve as resolve20 } from "path";
|
|
|
13628
13749
|
// package.json
|
|
13629
13750
|
var package_default = {
|
|
13630
13751
|
name: "deepspace",
|
|
13631
|
-
version: "0.
|
|
13752
|
+
version: "0.21.0",
|
|
13632
13753
|
engines: {
|
|
13633
13754
|
node: ">=22.15.0 <23 || >=24 <25 || >=26 <27"
|
|
13634
13755
|
},
|
|
@@ -13661,6 +13782,10 @@ var package_default = {
|
|
|
13661
13782
|
types: "./dist/testing.d.ts",
|
|
13662
13783
|
import: "./dist/testing.js"
|
|
13663
13784
|
},
|
|
13785
|
+
"./testing/mcp": {
|
|
13786
|
+
types: "./dist/testing-mcp.d.ts",
|
|
13787
|
+
import: "./dist/testing-mcp.js"
|
|
13788
|
+
},
|
|
13664
13789
|
"./documentation": {
|
|
13665
13790
|
types: "./dist/documentation.d.ts",
|
|
13666
13791
|
import: "./dist/documentation.js"
|