deepspace 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1306 -2004
- package/dist/cli.js.map +1 -1
- package/dist/index.js +446 -539
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +17 -8
- package/dist/server.js +16 -3
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +13 -6
- package/dist/worker.js +15 -2
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
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
|
|
4
|
+
import { defineCommand as defineCommand22, runMain } from "citty";
|
|
5
5
|
import { execSync as execSync4 } from "child_process";
|
|
6
|
-
import { readFileSync as readFileSync14, existsSync as
|
|
7
|
-
import { dirname as dirname4, join as
|
|
6
|
+
import { readFileSync as readFileSync14, existsSync as existsSync14 } from "fs";
|
|
7
|
+
import { dirname as dirname4, join as join16 } from "path";
|
|
8
8
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9
9
|
|
|
10
10
|
// src/cli/lib/cli-errors.ts
|
|
@@ -13,13 +13,14 @@ var API_ERROR_HINTS = {
|
|
|
13
13
|
app_not_found: "App not found. Check the app name (wrangler.toml `name`) and that it has been deployed.",
|
|
14
14
|
not_app_owner_or_collaborator: "You must be the app owner or a collaborator to do this.",
|
|
15
15
|
test_account_cannot_be_collaborator: "Test accounts cannot be added as collaborators. Use a real DeepSpace account.",
|
|
16
|
-
user_not_found: "No DeepSpace user with that email.
|
|
16
|
+
user_not_found: "No DeepSpace user with that email. They need to log in to DeepSpace at least once (`deepspace login`, or sign in to any app) before they can be referenced by email."
|
|
17
17
|
};
|
|
18
18
|
function formatCliError(err) {
|
|
19
19
|
const message = err instanceof Error ? err.message : String(err);
|
|
20
|
-
const
|
|
20
|
+
const code = err?.code;
|
|
21
|
+
const slug = typeof code === "string" ? code : /^API \S+ \(\d+\): ([a-z0-9_]+)$/.exec(message)?.[1];
|
|
21
22
|
const hint = slug ? API_ERROR_HINTS[slug] : void 0;
|
|
22
|
-
return hint ? `${message}
|
|
23
|
+
return hint && !message.includes(hint) ? `${message}
|
|
23
24
|
${hint}` : message;
|
|
24
25
|
}
|
|
25
26
|
function renderCliError(err) {
|
|
@@ -50,8 +51,8 @@ function wrapCommandErrors(cmd) {
|
|
|
50
51
|
|
|
51
52
|
// src/cli/commands/login.ts
|
|
52
53
|
import { defineCommand } from "citty";
|
|
53
|
-
import { writeFileSync as
|
|
54
|
-
import { join as
|
|
54
|
+
import { writeFileSync as writeFileSync5, mkdirSync } from "fs";
|
|
55
|
+
import { join as join3 } from "path";
|
|
55
56
|
import { homedir } from "os";
|
|
56
57
|
import { randomBytes, createHash } from "crypto";
|
|
57
58
|
import * as p2 from "@clack/prompts";
|
|
@@ -74,12 +75,301 @@ function createSpinner() {
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
// src/cli/env.ts
|
|
77
|
-
import { existsSync as
|
|
78
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
79
|
+
|
|
80
|
+
// src/cli/lib/secrets.ts
|
|
81
|
+
var GENERATED_SECRETS_DIVIDER = "# --- DeepSpace secrets cache: generated by `npx deepspace secrets`; do not edit manually ---";
|
|
82
|
+
var SECRET_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
83
|
+
var CONFIG_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
84
|
+
var MAX_SECRET_VALUE_BYTES = 32 * 1024;
|
|
85
|
+
function validateConfigName(configName) {
|
|
86
|
+
if (!CONFIG_NAME_RE.test(configName)) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Invalid config "${configName}". Use letters, numbers, underscores, or hyphens; start with a letter or number.`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return configName;
|
|
92
|
+
}
|
|
93
|
+
function validateSecretName(name) {
|
|
94
|
+
if (!SECRET_NAME_RE.test(name)) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Invalid secret name "${name}". Use letters, numbers, and underscores; start with a letter or underscore.`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return name;
|
|
100
|
+
}
|
|
101
|
+
function validateSecretValue(key, value) {
|
|
102
|
+
if (new TextEncoder().encode(value).byteLength > MAX_SECRET_VALUE_BYTES) {
|
|
103
|
+
throw new Error(`Secret "${key}" exceeds ${MAX_SECRET_VALUE_BYTES} bytes`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function defaultConfigNameForEnv(wranglerEnv) {
|
|
107
|
+
return wranglerEnv ?? "prd";
|
|
108
|
+
}
|
|
109
|
+
async function secretsApi(deployUrl, token, path, init = {}) {
|
|
110
|
+
const res = await fetch(`${deployUrl}/api/secrets${path}`, {
|
|
111
|
+
...init,
|
|
112
|
+
headers: {
|
|
113
|
+
Authorization: `Bearer ${token}`,
|
|
114
|
+
"Content-Type": "application/json",
|
|
115
|
+
...init.headers ?? {}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
const body = await res.json().catch(() => ({}));
|
|
119
|
+
if (!res.ok) {
|
|
120
|
+
throw new Error(`Secrets API ${path} (${res.status}): ${body.error ?? "request failed"}`);
|
|
121
|
+
}
|
|
122
|
+
return body;
|
|
123
|
+
}
|
|
124
|
+
function listSecrets(deployUrl, token, appId, configName) {
|
|
125
|
+
return secretsApi(deployUrl, token, `/${appId}/configs/${encodeURIComponent(configName)}`);
|
|
126
|
+
}
|
|
127
|
+
function fetchSecretsValues(deployUrl, token, appId, configName) {
|
|
128
|
+
return secretsApi(deployUrl, token, `/${appId}/configs/${encodeURIComponent(configName)}/values`);
|
|
129
|
+
}
|
|
130
|
+
function getSecretPlain(deployUrl, token, appId, configName, key) {
|
|
131
|
+
return secretsApi(
|
|
132
|
+
deployUrl,
|
|
133
|
+
token,
|
|
134
|
+
`/${appId}/configs/${encodeURIComponent(configName)}/secrets/${encodeURIComponent(key)}?plain=true`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
function setSecret(deployUrl, token, appId, configName, key, value) {
|
|
138
|
+
return secretsApi(
|
|
139
|
+
deployUrl,
|
|
140
|
+
token,
|
|
141
|
+
`/${appId}/configs/${encodeURIComponent(configName)}/secrets/${encodeURIComponent(key)}`,
|
|
142
|
+
{ method: "PUT", body: JSON.stringify({ value }) }
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
function deleteSecret(deployUrl, token, appId, configName, key) {
|
|
146
|
+
return secretsApi(
|
|
147
|
+
deployUrl,
|
|
148
|
+
token,
|
|
149
|
+
`/${appId}/configs/${encodeURIComponent(configName)}/secrets/${encodeURIComponent(key)}`,
|
|
150
|
+
{ method: "DELETE" }
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
function uploadSecrets(deployUrl, token, appId, configName, secrets, replace) {
|
|
154
|
+
return secretsApi(
|
|
155
|
+
deployUrl,
|
|
156
|
+
token,
|
|
157
|
+
`/${appId}/configs/${encodeURIComponent(configName)}/secrets`,
|
|
158
|
+
{ method: "PUT", body: JSON.stringify({ secrets, replace }) }
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
function listConfigs(deployUrl, token, appId) {
|
|
162
|
+
return secretsApi(deployUrl, token, `/${appId}/configs`);
|
|
163
|
+
}
|
|
164
|
+
function createConfig(deployUrl, token, appId, configName, copyFrom) {
|
|
165
|
+
return secretsApi(deployUrl, token, `/${appId}/configs`, {
|
|
166
|
+
method: "POST",
|
|
167
|
+
body: JSON.stringify({ name: configName, ...copyFrom ? { copyFrom } : {} })
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function deleteConfig(deployUrl, token, appId, configName) {
|
|
171
|
+
return secretsApi(deployUrl, token, `/${appId}/configs/${encodeURIComponent(configName)}`, {
|
|
172
|
+
method: "DELETE"
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
async function pullAppSecretsCache(deployUrl, token, appId, configName) {
|
|
176
|
+
try {
|
|
177
|
+
const { secrets } = await fetchSecretsValues(deployUrl, token, appId, configName);
|
|
178
|
+
return { appId, configName, values: secrets };
|
|
179
|
+
} catch (err) {
|
|
180
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
181
|
+
if (msg.includes("(404)")) return null;
|
|
182
|
+
throw err;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function quoteDotenvValue(value) {
|
|
186
|
+
if (/^[A-Za-z0-9_./:@+-]*$/.test(value) && value !== "") return value;
|
|
187
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
188
|
+
return `"${escaped}"`;
|
|
189
|
+
}
|
|
190
|
+
function renderSecretsCache(secrets, source) {
|
|
191
|
+
const keys = Object.keys(secrets).sort();
|
|
192
|
+
const lines = [
|
|
193
|
+
GENERATED_SECRETS_DIVIDER,
|
|
194
|
+
`# app ${source.appId} \xB7 config ${source.configName}`,
|
|
195
|
+
"# Use `npx deepspace secrets` to change these values."
|
|
196
|
+
];
|
|
197
|
+
for (const key of keys) {
|
|
198
|
+
validateSecretValue(key, secrets[key]);
|
|
199
|
+
lines.push(`${key}=${quoteDotenvValue(secrets[key])}`);
|
|
200
|
+
}
|
|
201
|
+
return lines.join("\n");
|
|
202
|
+
}
|
|
203
|
+
function stripGeneratedSecretsCache(content) {
|
|
204
|
+
const idx = content.indexOf(GENERATED_SECRETS_DIVIDER);
|
|
205
|
+
if (idx === -1) return content;
|
|
206
|
+
return content.slice(0, idx).trimEnd() + "\n";
|
|
207
|
+
}
|
|
208
|
+
function parseSecretsUpload(content) {
|
|
209
|
+
const trimmed = content.trim();
|
|
210
|
+
if (!trimmed) return {};
|
|
211
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
212
|
+
const parsed = JSON.parse(trimmed);
|
|
213
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
214
|
+
throw new Error("JSON upload must be an object of KEY=value strings.");
|
|
215
|
+
}
|
|
216
|
+
const out2 = {};
|
|
217
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
218
|
+
validateSecretName(key);
|
|
219
|
+
if (typeof value !== "string") throw new Error(`Secret "${key}" must be a string`);
|
|
220
|
+
validateSecretValue(key, value);
|
|
221
|
+
out2[key] = value;
|
|
222
|
+
}
|
|
223
|
+
return out2;
|
|
224
|
+
}
|
|
225
|
+
const out = {};
|
|
226
|
+
for (const rawLine of trimmed.split("\n")) {
|
|
227
|
+
const line = rawLine.trim();
|
|
228
|
+
if (!line || line.startsWith("#")) continue;
|
|
229
|
+
const eq = line.indexOf("=");
|
|
230
|
+
if (eq <= 0) continue;
|
|
231
|
+
const key = line.slice(0, eq).trim();
|
|
232
|
+
if (!SECRET_NAME_RE.test(key)) continue;
|
|
233
|
+
let value = line.slice(eq + 1).trim();
|
|
234
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
235
|
+
value = unescapeDotenv(value.slice(1, -1));
|
|
236
|
+
}
|
|
237
|
+
validateSecretValue(key, value);
|
|
238
|
+
out[key] = value;
|
|
239
|
+
}
|
|
240
|
+
return out;
|
|
241
|
+
}
|
|
242
|
+
function unescapeDotenv(v) {
|
|
243
|
+
let out = "";
|
|
244
|
+
for (let i = 0; i < v.length; i++) {
|
|
245
|
+
if (v[i] === "\\" && i + 1 < v.length) {
|
|
246
|
+
const c = v[++i];
|
|
247
|
+
out += c === "n" ? "\n" : c === "r" ? "\r" : c;
|
|
248
|
+
} else {
|
|
249
|
+
out += v[i];
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return out;
|
|
253
|
+
}
|
|
254
|
+
function formatSecretsDownload(secrets, format) {
|
|
255
|
+
const entries = Object.entries(secrets).sort(([a], [b]) => a.localeCompare(b));
|
|
256
|
+
if (format === "json") {
|
|
257
|
+
return JSON.stringify(Object.fromEntries(entries), null, 2) + "\n";
|
|
258
|
+
}
|
|
259
|
+
if (format === "shell") {
|
|
260
|
+
return entries.map(([k, v]) => `export ${k}=${quoteDotenvValue(v)}`).join("\n") + "\n";
|
|
261
|
+
}
|
|
262
|
+
return entries.map(([k, v]) => `${k}=${quoteDotenvValue(v)}`).join("\n") + "\n";
|
|
263
|
+
}
|
|
264
|
+
async function refreshSecretsCache(deployUrl, token, appId, wranglerEnv) {
|
|
265
|
+
const configName = defaultConfigNameForEnv(wranglerEnv);
|
|
266
|
+
const pulled = await pullAppSecretsCache(deployUrl, token, appId, configName);
|
|
267
|
+
if (!pulled) return null;
|
|
268
|
+
const count = Object.keys(pulled.values).length;
|
|
269
|
+
return {
|
|
270
|
+
pulled,
|
|
271
|
+
rendered: renderSecretsCache(pulled.values, pulled),
|
|
272
|
+
summary: `Secrets: ${pulled.configName} (${count} secret${count === 1 ? "" : "s"})`
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// src/cli/lib/app-identity.ts
|
|
277
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
278
|
+
import { join, resolve } from "path";
|
|
279
|
+
import { parse as parseToml } from "smol-toml";
|
|
280
|
+
var APP_ID_RE = /^app_[0-9A-HJKMNP-TV-Z]{26}$/;
|
|
281
|
+
var CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
282
|
+
function mintAppId(now = Date.now()) {
|
|
283
|
+
let ts = "";
|
|
284
|
+
let t = now;
|
|
285
|
+
for (let i = 0; i < 10; i++) {
|
|
286
|
+
ts = CROCKFORD[t % 32] + ts;
|
|
287
|
+
t = Math.floor(t / 32);
|
|
288
|
+
}
|
|
289
|
+
const rand = new Uint8Array(10);
|
|
290
|
+
crypto.getRandomValues(rand);
|
|
291
|
+
let rs = "";
|
|
292
|
+
let acc = 0;
|
|
293
|
+
let bits = 0;
|
|
294
|
+
for (const byte of rand) {
|
|
295
|
+
acc = acc << 8 | byte;
|
|
296
|
+
bits += 8;
|
|
297
|
+
while (bits >= 5) {
|
|
298
|
+
bits -= 5;
|
|
299
|
+
rs += CROCKFORD[acc >> bits & 31];
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return `app_${ts}${rs}`.slice(0, 30);
|
|
303
|
+
}
|
|
304
|
+
async function resolveOwnedAppId(deployUrl, token, appName) {
|
|
305
|
+
try {
|
|
306
|
+
const res = await fetch(`${deployUrl}/api/apps`, {
|
|
307
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
308
|
+
});
|
|
309
|
+
if (!res.ok) return null;
|
|
310
|
+
const body = await res.json();
|
|
311
|
+
const match = body.apps?.find((a) => a.name === appName);
|
|
312
|
+
return match?.appId ?? null;
|
|
313
|
+
} catch {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function readAppId(cwd = process.cwd(), wranglerEnv) {
|
|
318
|
+
const wranglerPath = join(resolve(cwd), "wrangler.toml");
|
|
319
|
+
if (!existsSync(wranglerPath)) return null;
|
|
320
|
+
let cfg;
|
|
321
|
+
try {
|
|
322
|
+
cfg = parseToml(readFileSync(wranglerPath, "utf-8"));
|
|
323
|
+
} catch (err) {
|
|
324
|
+
throw new Error(
|
|
325
|
+
`Could not parse ${wranglerPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
const vars = wranglerEnv ? cfg.env?.[wranglerEnv]?.vars : cfg.vars;
|
|
329
|
+
const id = vars?.DEEPSPACE_APP_ID;
|
|
330
|
+
return typeof id === "string" && APP_ID_RE.test(id) ? id : null;
|
|
331
|
+
}
|
|
332
|
+
function writeAppId(cwd, appId, opts = {}) {
|
|
333
|
+
const wranglerPath = join(resolve(cwd), "wrangler.toml");
|
|
334
|
+
if (!existsSync(wranglerPath)) {
|
|
335
|
+
throw new Error(`No wrangler.toml in ${resolve(cwd)}`);
|
|
336
|
+
}
|
|
337
|
+
const existing = readAppId(cwd, opts.wranglerEnv);
|
|
338
|
+
if (existing && !opts.force) {
|
|
339
|
+
if (existing === appId) return;
|
|
340
|
+
throw new Error(
|
|
341
|
+
`wrangler.toml already carries ${existing}. The app id is immutable \u2014 use --new-id only to fork this repo as a separate app.`
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
let src = readFileSync(wranglerPath, "utf-8");
|
|
345
|
+
const header = opts.wranglerEnv ? `[env.${opts.wranglerEnv}.vars]` : "[vars]";
|
|
346
|
+
const line = `DEEPSPACE_APP_ID = "${appId}"`;
|
|
347
|
+
const headerRe = new RegExp(`^\\${header.replace(/\./g, "\\.")}[ \\t]*$`, "m");
|
|
348
|
+
const idLineRe = /^DEEPSPACE_APP_ID\s*=.*$/m;
|
|
349
|
+
const match = headerRe.exec(src);
|
|
350
|
+
if (match) {
|
|
351
|
+
const blockStart = match.index + match[0].length;
|
|
352
|
+
const rest = src.slice(blockStart);
|
|
353
|
+
const nextSection = rest.search(/^\s*\[/m);
|
|
354
|
+
const blockEnd = nextSection === -1 ? src.length : blockStart + nextSection;
|
|
355
|
+
const block = src.slice(blockStart, blockEnd);
|
|
356
|
+
const newBlock = idLineRe.test(block) ? block.replace(idLineRe, line) : `
|
|
357
|
+
${line}${block}`;
|
|
358
|
+
src = src.slice(0, blockStart) + newBlock + src.slice(blockEnd);
|
|
359
|
+
} else {
|
|
360
|
+
src = src.trimEnd() + `
|
|
361
|
+
|
|
362
|
+
${header}
|
|
363
|
+
${line}
|
|
364
|
+
`;
|
|
365
|
+
}
|
|
366
|
+
writeFileSync(wranglerPath, src);
|
|
367
|
+
}
|
|
78
368
|
|
|
79
369
|
// src/cli/lib/secure-file.ts
|
|
80
|
-
import { chmodSync, writeFileSync } from "fs";
|
|
370
|
+
import { chmodSync, writeFileSync as writeFileSync2 } from "fs";
|
|
81
371
|
function writeSecretFileSync(path, content) {
|
|
82
|
-
|
|
372
|
+
writeFileSync2(path, content, { mode: 384 });
|
|
83
373
|
try {
|
|
84
374
|
chmodSync(path, 384);
|
|
85
375
|
} catch {
|
|
@@ -87,9 +377,9 @@ function writeSecretFileSync(path, content) {
|
|
|
87
377
|
}
|
|
88
378
|
|
|
89
379
|
// src/cli/lib/wrangler-env.ts
|
|
90
|
-
import { existsSync, readFileSync, rmSync, writeFileSync as
|
|
91
|
-
import { join } from "path";
|
|
92
|
-
import { parse as
|
|
380
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
381
|
+
import { join as join2 } from "path";
|
|
382
|
+
import { parse as parseToml2, stringify as stringifyToml } from "smol-toml";
|
|
93
383
|
|
|
94
384
|
// src/server/rooms/app-name.ts
|
|
95
385
|
var APP_NAME_RULES = {
|
|
@@ -187,35 +477,35 @@ function resolveAppNameForEnv(config, envName) {
|
|
|
187
477
|
}
|
|
188
478
|
return resolveAppName(envBlock.name);
|
|
189
479
|
}
|
|
190
|
-
function devVarsPathFor(
|
|
191
|
-
if (opts.sharedDevVarsCache || !envName) return
|
|
192
|
-
return
|
|
480
|
+
function devVarsPathFor(appDir, envName, opts = {}) {
|
|
481
|
+
if (opts.sharedDevVarsCache || !envName) return join2(appDir, ".dev.vars");
|
|
482
|
+
return join2(appDir, `.dev.vars.${envName}`);
|
|
193
483
|
}
|
|
194
|
-
function legacyEnvDevVarsPathFor(
|
|
195
|
-
return envName ?
|
|
484
|
+
function legacyEnvDevVarsPathFor(appDir, envName) {
|
|
485
|
+
return envName ? join2(appDir, `.dev.vars.${envName}`) : void 0;
|
|
196
486
|
}
|
|
197
|
-
function warnIfLegacyEnvDevVarsExists(
|
|
198
|
-
const legacyPath = legacyEnvDevVarsPathFor(
|
|
199
|
-
if (!legacyPath || !
|
|
487
|
+
function warnIfLegacyEnvDevVarsExists(appDir, envName, warn = console.warn) {
|
|
488
|
+
const legacyPath = legacyEnvDevVarsPathFor(appDir, envName);
|
|
489
|
+
if (!legacyPath || !existsSync2(legacyPath)) return;
|
|
200
490
|
warn(
|
|
201
|
-
`Warning: ${legacyPath} exists, but DeepSpace now uses ${devVarsPathFor(
|
|
491
|
+
`Warning: ${legacyPath} exists, but DeepSpace now uses ${devVarsPathFor(appDir, envName, { sharedDevVarsCache: true })} for every environment. This run will ignore ${legacyPath}; move any secrets you still need into .dev.vars, then remove the env-specific file when you're ready.`
|
|
202
492
|
);
|
|
203
493
|
}
|
|
204
|
-
function prepareWranglerEnvConfig(
|
|
494
|
+
function prepareWranglerEnvConfig(appDir, envName, opts = {}) {
|
|
205
495
|
if (!envName) return { cleanup() {
|
|
206
496
|
} };
|
|
207
497
|
if (!opts.sharedDevVarsCache) return { wranglerEnv: envName, cleanup() {
|
|
208
498
|
} };
|
|
209
|
-
warnIfLegacyEnvDevVarsExists(
|
|
210
|
-
const config = readWranglerConfig(
|
|
499
|
+
warnIfLegacyEnvDevVarsExists(appDir, envName, opts.warn);
|
|
500
|
+
const config = readWranglerConfig(appDir);
|
|
211
501
|
const resolved = resolveAppNameForEnv(config, envName);
|
|
212
502
|
if (!resolved.ok) throw new Error(resolved.reason);
|
|
213
503
|
const flattened = flattenWranglerEnvConfig(config, envName);
|
|
214
|
-
const configPath =
|
|
215
|
-
|
|
504
|
+
const configPath = join2(
|
|
505
|
+
appDir,
|
|
216
506
|
`.wrangler.deepspace.${safeGeneratedConfigSegment(envName)}.${process.pid}.${++generatedConfigSeq}.toml`
|
|
217
507
|
);
|
|
218
|
-
|
|
508
|
+
writeFileSync3(
|
|
219
509
|
configPath,
|
|
220
510
|
[
|
|
221
511
|
"# Generated by DeepSpace for this CLI run.",
|
|
@@ -271,17 +561,17 @@ var WranglerConfigError = class extends Error {
|
|
|
271
561
|
this.name = "WranglerConfigError";
|
|
272
562
|
}
|
|
273
563
|
};
|
|
274
|
-
function readWranglerConfig(
|
|
275
|
-
const path =
|
|
276
|
-
const raw =
|
|
564
|
+
function readWranglerConfig(appDir) {
|
|
565
|
+
const path = join2(appDir, "wrangler.toml");
|
|
566
|
+
const raw = readFileSync2(path, "utf-8");
|
|
277
567
|
try {
|
|
278
|
-
return
|
|
568
|
+
return parseToml2(raw);
|
|
279
569
|
} catch (err) {
|
|
280
570
|
throw new WranglerConfigError(path, err);
|
|
281
571
|
}
|
|
282
572
|
}
|
|
283
|
-
function hasWranglerConfig(
|
|
284
|
-
return
|
|
573
|
+
function hasWranglerConfig(appDir) {
|
|
574
|
+
return existsSync2(join2(appDir, "wrangler.toml"));
|
|
285
575
|
}
|
|
286
576
|
|
|
287
577
|
// src/cli/env.ts
|
|
@@ -303,21 +593,12 @@ async function fetchPublicKey(authUrl) {
|
|
|
303
593
|
}
|
|
304
594
|
return data.publicKey;
|
|
305
595
|
}
|
|
306
|
-
function
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
313
|
-
const rawName = wranglerEnv2 ? config.env?.[wranglerEnv2]?.name : config.name;
|
|
314
|
-
if (rawName !== resolved.name) {
|
|
315
|
-
const where = wranglerEnv2 ? `[env.${wranglerEnv2}].name` : "`name`";
|
|
316
|
-
throw new Error(
|
|
317
|
-
`wrangler.toml: ${where} "${rawName}" is not in canonical form. Update it to "${resolved.name}" and re-run.`
|
|
318
|
-
);
|
|
319
|
-
}
|
|
320
|
-
return resolved.name;
|
|
596
|
+
function requireAppIdFor(appDir, wranglerEnv) {
|
|
597
|
+
const id = readAppId(appDir, wranglerEnv);
|
|
598
|
+
if (id) return id;
|
|
599
|
+
throw new Error(
|
|
600
|
+
"wrangler.toml has no DEEPSPACE_APP_ID" + (wranglerEnv ? ` for [env.${wranglerEnv}]` : "") + ". Run `deepspace init` (or `deepspace init --env <name>`) and retry."
|
|
601
|
+
);
|
|
321
602
|
}
|
|
322
603
|
async function mintAppOwnerJwt(authUrl, callerJwt, appName) {
|
|
323
604
|
const res = await fetch(`${authUrl}/api/auth/mint-app-token`, {
|
|
@@ -326,7 +607,7 @@ async function mintAppOwnerJwt(authUrl, callerJwt, appName) {
|
|
|
326
607
|
"Content-Type": "application/json",
|
|
327
608
|
Authorization: `Bearer ${callerJwt}`
|
|
328
609
|
},
|
|
329
|
-
body: JSON.stringify({ appName })
|
|
610
|
+
body: JSON.stringify({ appId: appName })
|
|
330
611
|
});
|
|
331
612
|
if (!res.ok) {
|
|
332
613
|
const err = await res.text().catch(() => "");
|
|
@@ -340,6 +621,7 @@ async function mintAppOwnerJwt(authUrl, callerJwt, appName) {
|
|
|
340
621
|
}
|
|
341
622
|
async function fetchAppIdentityToken(deployUrl, callerJwt, appName) {
|
|
342
623
|
const res = await fetch(`${deployUrl}/api/apps/${encodeURIComponent(appName)}/identity-token`, {
|
|
624
|
+
// appName carries the appId
|
|
343
625
|
method: "POST",
|
|
344
626
|
headers: { Authorization: `Bearer ${callerJwt}` }
|
|
345
627
|
});
|
|
@@ -368,7 +650,6 @@ var SDK_MANAGED_KEYS = /* @__PURE__ */ new Set([
|
|
|
368
650
|
"ALLOW_DEBUG_ROUTES"
|
|
369
651
|
]);
|
|
370
652
|
var DEV_VARS_DIVIDER = "# --- not managed by the SDK; preserved across dev/test runs ---";
|
|
371
|
-
var GENERATED_SECRETS_DIVIDER = "# --- DeepSpace secrets cache: generated by `npx deepspace secrets`; do not edit manually ---";
|
|
372
653
|
function extractCustomDevVars(content) {
|
|
373
654
|
const lines = content.split("\n");
|
|
374
655
|
const out = [];
|
|
@@ -409,11 +690,6 @@ function extractCustomDevVars(content) {
|
|
|
409
690
|
while (out.length && out[out.length - 1].trim() === "") out.pop();
|
|
410
691
|
return out.join("\n");
|
|
411
692
|
}
|
|
412
|
-
function stripGeneratedSecretsCache(content) {
|
|
413
|
-
const idx = content.indexOf(GENERATED_SECRETS_DIVIDER);
|
|
414
|
-
if (idx < 0) return content;
|
|
415
|
-
return content.slice(0, idx).replace(/\s+$/u, "");
|
|
416
|
-
}
|
|
417
693
|
function removeDevVarsKeys(content, keys) {
|
|
418
694
|
const lines = content.split("\n");
|
|
419
695
|
const out = [];
|
|
@@ -529,12 +805,12 @@ function unescapeQuotedDevVar(value) {
|
|
|
529
805
|
}
|
|
530
806
|
return out;
|
|
531
807
|
}
|
|
532
|
-
async function writeDevVars(
|
|
808
|
+
async function writeDevVars(appDir, ownerId, callerJwt, wranglerEnv, opts = {}) {
|
|
533
809
|
const urls = PLATFORM_URLS;
|
|
534
810
|
const publicKey = await fetchPublicKey(urls.auth);
|
|
535
|
-
const
|
|
536
|
-
const appOwnerJwt = await mintAppOwnerJwt(urls.auth, callerJwt,
|
|
537
|
-
const appIdentityToken = await fetchAppIdentityToken(urls.deploy, callerJwt,
|
|
811
|
+
const appId = opts.appId ?? requireAppIdFor(appDir, wranglerEnv);
|
|
812
|
+
const appOwnerJwt = await mintAppOwnerJwt(urls.auth, callerJwt, appId);
|
|
813
|
+
const appIdentityToken = await fetchAppIdentityToken(urls.deploy, callerJwt, appId);
|
|
538
814
|
const sdkVars = [
|
|
539
815
|
`AUTH_JWT_PUBLIC_KEY="${publicKey}"`,
|
|
540
816
|
`AUTH_JWT_ISSUER=${urls.auth}/api/auth`,
|
|
@@ -549,20 +825,27 @@ async function writeDevVars(appDir2, ownerId, callerJwt, wranglerEnv2, opts = {}
|
|
|
549
825
|
`ALLOW_DEBUG_ROUTES=true`
|
|
550
826
|
].join("\n");
|
|
551
827
|
const useSharedDevVarsCache = opts.sharedDevVarsCache ?? opts.generatedSecretsCache !== void 0;
|
|
552
|
-
const devVarsPath = devVarsPathFor(
|
|
828
|
+
const devVarsPath = devVarsPathFor(appDir, wranglerEnv, {
|
|
553
829
|
sharedDevVarsCache: useSharedDevVarsCache
|
|
554
830
|
});
|
|
555
|
-
const existing =
|
|
556
|
-
const custom = opts.generatedSecretsCache ?? extractCustomDevVars(stripGeneratedSecretsCache(existing));
|
|
557
|
-
|
|
831
|
+
const existing = existsSync3(devVarsPath) ? readFileSync3(devVarsPath, "utf-8") : "";
|
|
832
|
+
const custom = opts.generatedSecretsCache ?? extractCustomDevVars(stripGeneratedSecretsCache(existing).trimEnd());
|
|
833
|
+
let body;
|
|
834
|
+
if (!custom) {
|
|
835
|
+
body = `${sdkVars}
|
|
836
|
+
`;
|
|
837
|
+
} else if (opts.generatedSecretsCache) {
|
|
838
|
+
body = `${sdkVars}
|
|
558
839
|
|
|
559
840
|
${custom}
|
|
560
|
-
|
|
841
|
+
`;
|
|
842
|
+
} else {
|
|
843
|
+
body = `${sdkVars}
|
|
561
844
|
|
|
562
845
|
${DEV_VARS_DIVIDER}
|
|
563
846
|
${custom}
|
|
564
|
-
` : `${sdkVars}
|
|
565
847
|
`;
|
|
848
|
+
}
|
|
566
849
|
writeSecretFileSync(devVarsPath, body);
|
|
567
850
|
}
|
|
568
851
|
|
|
@@ -592,6 +875,7 @@ function openBrowser(url) {
|
|
|
592
875
|
|
|
593
876
|
// src/cli/commands/login.ts
|
|
594
877
|
var AUTH_URL = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
|
|
878
|
+
var API_URL = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
595
879
|
var login_default = defineCommand({
|
|
596
880
|
meta: {
|
|
597
881
|
name: "login",
|
|
@@ -650,6 +934,7 @@ var login_default = defineCommand({
|
|
|
650
934
|
process.exit(1);
|
|
651
935
|
}
|
|
652
936
|
storeCredentials(result.sessionToken, result.jwt);
|
|
937
|
+
await provisionProfile(result.jwt);
|
|
653
938
|
s.stop("Authenticated");
|
|
654
939
|
p2.log.success(`Logged in as ${result.name ?? result.email}`);
|
|
655
940
|
p2.note(
|
|
@@ -688,13 +973,19 @@ async function pollForCompletion(sessionId, codeVerifier) {
|
|
|
688
973
|
return null;
|
|
689
974
|
}
|
|
690
975
|
function storeCredentials(sessionToken, jwt) {
|
|
691
|
-
const dir =
|
|
976
|
+
const dir = join3(homedir(), ".deepspace");
|
|
692
977
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
693
|
-
|
|
694
|
-
|
|
978
|
+
writeFileSync5(join3(dir, "session"), sessionToken, { mode: 384 });
|
|
979
|
+
writeFileSync5(join3(dir, "token"), jwt, { mode: 384 });
|
|
980
|
+
}
|
|
981
|
+
async function provisionProfile(jwt) {
|
|
982
|
+
try {
|
|
983
|
+
await fetch(`${API_URL}/api/users/me`, { headers: { Authorization: `Bearer ${jwt}` } });
|
|
984
|
+
} catch {
|
|
985
|
+
}
|
|
695
986
|
}
|
|
696
987
|
function sleep(ms) {
|
|
697
|
-
return new Promise((
|
|
988
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
698
989
|
}
|
|
699
990
|
function base64url(bytes) {
|
|
700
991
|
return bytes.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
@@ -721,24 +1012,25 @@ async function doEmailLogin(email, password) {
|
|
|
721
1012
|
throw new Error("JWT issuance failed");
|
|
722
1013
|
}
|
|
723
1014
|
storeCredentials(sessionToken, jwt);
|
|
1015
|
+
await provisionProfile(jwt);
|
|
724
1016
|
}
|
|
725
1017
|
|
|
726
1018
|
// src/cli/commands/logout.ts
|
|
727
1019
|
import { defineCommand as defineCommand2 } from "citty";
|
|
728
|
-
import { existsSync as
|
|
729
|
-
import { join as
|
|
1020
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, rmSync as rmSync2 } from "fs";
|
|
1021
|
+
import { join as join4 } from "path";
|
|
730
1022
|
import { homedir as homedir2 } from "os";
|
|
731
1023
|
var AUTH_URL2 = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
|
|
732
|
-
var DIR =
|
|
733
|
-
var SESSION_PATH =
|
|
734
|
-
var TOKEN_PATH =
|
|
1024
|
+
var DIR = join4(homedir2(), ".deepspace");
|
|
1025
|
+
var SESSION_PATH = join4(DIR, "session");
|
|
1026
|
+
var TOKEN_PATH = join4(DIR, "token");
|
|
735
1027
|
var logout_default = defineCommand2({
|
|
736
1028
|
meta: {
|
|
737
1029
|
name: "logout",
|
|
738
1030
|
description: "Sign out and remove cached credentials"
|
|
739
1031
|
},
|
|
740
1032
|
async run() {
|
|
741
|
-
const sessionToken =
|
|
1033
|
+
const sessionToken = existsSync4(SESSION_PATH) ? readFileSync4(SESSION_PATH, "utf-8").trim() : null;
|
|
742
1034
|
if (sessionToken) {
|
|
743
1035
|
try {
|
|
744
1036
|
await fetch(`${AUTH_URL2}/api/auth/sign-out`, {
|
|
@@ -753,7 +1045,7 @@ var logout_default = defineCommand2({
|
|
|
753
1045
|
}
|
|
754
1046
|
let removed = 0;
|
|
755
1047
|
for (const path of [SESSION_PATH, TOKEN_PATH]) {
|
|
756
|
-
if (
|
|
1048
|
+
if (existsSync4(path)) {
|
|
757
1049
|
rmSync2(path);
|
|
758
1050
|
removed++;
|
|
759
1051
|
}
|
|
@@ -772,8 +1064,8 @@ import { resolve as resolve4, basename, join as join9 } from "path";
|
|
|
772
1064
|
import spawn from "cross-spawn";
|
|
773
1065
|
|
|
774
1066
|
// src/cli/auth.ts
|
|
775
|
-
import { readFileSync as
|
|
776
|
-
import { join as
|
|
1067
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync6, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
1068
|
+
import { join as join5 } from "path";
|
|
777
1069
|
import { homedir as homedir3 } from "os";
|
|
778
1070
|
|
|
779
1071
|
// src/cli/jwt.ts
|
|
@@ -787,26 +1079,26 @@ function decodeJwtPayload(token) {
|
|
|
787
1079
|
|
|
788
1080
|
// src/cli/auth.ts
|
|
789
1081
|
var AUTH_URL3 = process.env.DEEPSPACE_AUTH_URL ?? PLATFORM_URLS.auth;
|
|
790
|
-
var DIR2 =
|
|
791
|
-
var SESSION_PATH2 =
|
|
792
|
-
var TOKEN_PATH2 =
|
|
1082
|
+
var DIR2 = join5(homedir3(), ".deepspace");
|
|
1083
|
+
var SESSION_PATH2 = join5(DIR2, "session");
|
|
1084
|
+
var TOKEN_PATH2 = join5(DIR2, "token");
|
|
793
1085
|
async function ensureToken() {
|
|
794
|
-
if (
|
|
795
|
-
const existing =
|
|
1086
|
+
if (existsSync5(TOKEN_PATH2)) {
|
|
1087
|
+
const existing = readFileSync5(TOKEN_PATH2, "utf-8").trim();
|
|
796
1088
|
if (isTokenValid(existing)) {
|
|
797
1089
|
return existing;
|
|
798
1090
|
}
|
|
799
1091
|
}
|
|
800
|
-
if (!
|
|
1092
|
+
if (!existsSync5(SESSION_PATH2)) {
|
|
801
1093
|
throw new Error("Not logged in. Run `deepspace login` first.");
|
|
802
1094
|
}
|
|
803
|
-
const sessionToken =
|
|
1095
|
+
const sessionToken = readFileSync5(SESSION_PATH2, "utf-8").trim();
|
|
804
1096
|
const token = await exchangeSession(AUTH_URL3, sessionToken);
|
|
805
1097
|
if (!token) {
|
|
806
1098
|
throw new Error("Session expired. Run `deepspace login` to re-authenticate.");
|
|
807
1099
|
}
|
|
808
1100
|
mkdirSync2(DIR2, { recursive: true, mode: 448 });
|
|
809
|
-
|
|
1101
|
+
writeFileSync6(TOKEN_PATH2, token, { mode: 384 });
|
|
810
1102
|
return token;
|
|
811
1103
|
}
|
|
812
1104
|
function isTokenValid(jwt) {
|
|
@@ -820,24 +1112,26 @@ function isTokenValid(jwt) {
|
|
|
820
1112
|
|
|
821
1113
|
// src/cli/lib/app-context.ts
|
|
822
1114
|
import {
|
|
823
|
-
existsSync as
|
|
824
|
-
readFileSync as
|
|
1115
|
+
existsSync as existsSync6,
|
|
1116
|
+
readFileSync as readFileSync6,
|
|
825
1117
|
readdirSync,
|
|
826
|
-
writeFileSync as
|
|
1118
|
+
writeFileSync as writeFileSync7,
|
|
827
1119
|
mkdirSync as mkdirSync3,
|
|
828
1120
|
renameSync,
|
|
829
1121
|
unlinkSync
|
|
830
1122
|
} from "fs";
|
|
831
|
-
import { join as
|
|
832
|
-
import { parse as
|
|
1123
|
+
import { join as join6, resolve as resolve2, dirname, isAbsolute, sep } from "path";
|
|
1124
|
+
import { parse as parseToml3 } from "smol-toml";
|
|
833
1125
|
function detectAppName(cwd = process.cwd()) {
|
|
834
|
-
const wranglerPath =
|
|
835
|
-
if (!
|
|
1126
|
+
const wranglerPath = join6(resolve2(cwd), "wrangler.toml");
|
|
1127
|
+
if (!existsSync6(wranglerPath)) return null;
|
|
836
1128
|
try {
|
|
837
|
-
const cfg =
|
|
1129
|
+
const cfg = parseToml3(readFileSync6(wranglerPath, "utf-8"));
|
|
838
1130
|
return typeof cfg.name === "string" && cfg.name.length > 0 ? cfg.name : null;
|
|
839
|
-
} catch {
|
|
840
|
-
|
|
1131
|
+
} catch (err) {
|
|
1132
|
+
throw new Error(
|
|
1133
|
+
`Could not parse ${wranglerPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
1134
|
+
);
|
|
841
1135
|
}
|
|
842
1136
|
}
|
|
843
1137
|
function requireAppName(explicit, cwd) {
|
|
@@ -849,9 +1143,9 @@ function requireAppName(explicit, cwd) {
|
|
|
849
1143
|
);
|
|
850
1144
|
}
|
|
851
1145
|
function findAppDir(start = process.cwd()) {
|
|
852
|
-
let dir =
|
|
1146
|
+
let dir = resolve2(start);
|
|
853
1147
|
for (; ; ) {
|
|
854
|
-
if (
|
|
1148
|
+
if (existsSync6(join6(dir, "wrangler.toml"))) return dir;
|
|
855
1149
|
const parent = dirname(dir);
|
|
856
1150
|
if (parent === dir) return null;
|
|
857
1151
|
dir = parent;
|
|
@@ -859,7 +1153,7 @@ function findAppDir(start = process.cwd()) {
|
|
|
859
1153
|
}
|
|
860
1154
|
function findChildApps(dir) {
|
|
861
1155
|
try {
|
|
862
|
-
return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory() &&
|
|
1156
|
+
return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync6(join6(dir, e.name, "wrangler.toml"))).map((e) => e.name).sort();
|
|
863
1157
|
} catch {
|
|
864
1158
|
return [];
|
|
865
1159
|
}
|
|
@@ -867,7 +1161,7 @@ function findChildApps(dir) {
|
|
|
867
1161
|
function writeLaunchFile(launchPath, config) {
|
|
868
1162
|
mkdirSync3(dirname(launchPath), { recursive: true });
|
|
869
1163
|
const tmpPath = `${launchPath}.${process.pid}.tmp`;
|
|
870
|
-
|
|
1164
|
+
writeFileSync7(tmpPath, JSON.stringify(config, null, 2) + "\n");
|
|
871
1165
|
try {
|
|
872
1166
|
renameSync(tmpPath, launchPath);
|
|
873
1167
|
} catch (err) {
|
|
@@ -880,15 +1174,15 @@ function writeLaunchFile(launchPath, config) {
|
|
|
880
1174
|
}
|
|
881
1175
|
function readLaunchFile(launchPath) {
|
|
882
1176
|
const empty = { version: "0.0.1", configurations: [] };
|
|
883
|
-
if (!
|
|
884
|
-
const raw =
|
|
1177
|
+
if (!existsSync6(launchPath)) return empty;
|
|
1178
|
+
const raw = readFileSync6(launchPath, "utf-8").trim();
|
|
885
1179
|
if (!raw) return empty;
|
|
886
1180
|
const parsed = JSON.parse(raw);
|
|
887
1181
|
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.configurations)) return null;
|
|
888
1182
|
return parsed;
|
|
889
1183
|
}
|
|
890
|
-
function writeLaunchConfigIfMissing(
|
|
891
|
-
const launchPath =
|
|
1184
|
+
function writeLaunchConfigIfMissing(appDir, appName, port) {
|
|
1185
|
+
const launchPath = join6(appDir, ".claude", "launch.json");
|
|
892
1186
|
try {
|
|
893
1187
|
const config = readLaunchFile(launchPath);
|
|
894
1188
|
if (!config) return;
|
|
@@ -903,8 +1197,8 @@ function writeLaunchConfigIfMissing(appDir2, appName, port) {
|
|
|
903
1197
|
} catch {
|
|
904
1198
|
}
|
|
905
1199
|
}
|
|
906
|
-
function detectClaudeWorktree(
|
|
907
|
-
const segments =
|
|
1200
|
+
function detectClaudeWorktree(appDir) {
|
|
1201
|
+
const segments = resolve2(appDir).split(sep);
|
|
908
1202
|
for (let i = segments.length - 3; i >= 1; i--) {
|
|
909
1203
|
if (segments[i] === ".claude" && segments[i + 1] === "worktrees" && segments[i + 2]) {
|
|
910
1204
|
return {
|
|
@@ -923,16 +1217,16 @@ function deriveWorktreePort(worktreeName) {
|
|
|
923
1217
|
for (const ch of worktreeName) hash = hash * 31 + ch.charCodeAt(0) >>> 0;
|
|
924
1218
|
return WORKTREE_PORT_MIN + hash % WORKTREE_PORT_RANGE;
|
|
925
1219
|
}
|
|
926
|
-
function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName,
|
|
927
|
-
const launchPath =
|
|
1220
|
+
function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir, opts) {
|
|
1221
|
+
const launchPath = join6(mainRepoRoot, ".claude", "launch.json");
|
|
928
1222
|
const entryName = WORKTREE_ENTRY_PREFIX + worktreeName;
|
|
929
1223
|
try {
|
|
930
1224
|
const config = readLaunchFile(launchPath);
|
|
931
1225
|
if (!config) return null;
|
|
932
|
-
const worktreesRoot =
|
|
1226
|
+
const worktreesRoot = join6(mainRepoRoot, ".claude", "worktrees") + sep;
|
|
933
1227
|
config.configurations = config.configurations.filter((c) => {
|
|
934
1228
|
if (c?.name === entryName) return false;
|
|
935
|
-
const isStaleWorktreeEntry = typeof c?.name === "string" && c.name.startsWith(WORKTREE_ENTRY_PREFIX) && typeof c.cwd === "string" && isAbsolute(c.cwd) && c.cwd.startsWith(worktreesRoot) && !
|
|
1229
|
+
const isStaleWorktreeEntry = typeof c?.name === "string" && c.name.startsWith(WORKTREE_ENTRY_PREFIX) && typeof c.cwd === "string" && isAbsolute(c.cwd) && c.cwd.startsWith(worktreesRoot) && !existsSync6(c.cwd);
|
|
936
1230
|
return !isStaleWorktreeEntry;
|
|
937
1231
|
});
|
|
938
1232
|
let port = opts.port;
|
|
@@ -953,7 +1247,7 @@ function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir2, opts) {
|
|
|
953
1247
|
// the Claude Code desktop launch.json reference alongside `port`) and
|
|
954
1248
|
// verified end-to-end: with it set, the preview server builds from the
|
|
955
1249
|
// worktree instead of the session's main repo.
|
|
956
|
-
cwd:
|
|
1250
|
+
cwd: resolve2(appDir)
|
|
957
1251
|
});
|
|
958
1252
|
writeLaunchFile(launchPath, config);
|
|
959
1253
|
return { entryName, port };
|
|
@@ -961,11 +1255,11 @@ function upsertWorktreeLaunchConfig(mainRepoRoot, worktreeName, appDir2, opts) {
|
|
|
961
1255
|
return null;
|
|
962
1256
|
}
|
|
963
1257
|
}
|
|
964
|
-
function resolveWorktreePort(
|
|
965
|
-
const worktree = detectClaudeWorktree(
|
|
1258
|
+
function resolveWorktreePort(appDir) {
|
|
1259
|
+
const worktree = detectClaudeWorktree(appDir);
|
|
966
1260
|
if (!worktree) return null;
|
|
967
1261
|
try {
|
|
968
|
-
const config = readLaunchFile(
|
|
1262
|
+
const config = readLaunchFile(join6(worktree.mainRepoRoot, ".claude", "launch.json"));
|
|
969
1263
|
const entry = config?.configurations.find(
|
|
970
1264
|
(c) => c?.name === WORKTREE_ENTRY_PREFIX + worktree.worktreeName
|
|
971
1265
|
);
|
|
@@ -975,28 +1269,44 @@ function resolveWorktreePort(appDir2) {
|
|
|
975
1269
|
}
|
|
976
1270
|
return deriveWorktreePort(worktree.worktreeName);
|
|
977
1271
|
}
|
|
1272
|
+
function requireAppIdArg(explicit) {
|
|
1273
|
+
if (explicit) {
|
|
1274
|
+
if (!APP_ID_RE.test(explicit)) {
|
|
1275
|
+
throw new Error(`"${explicit}" is not an app id (app_\u2026). Pass --app <appId>.`);
|
|
1276
|
+
}
|
|
1277
|
+
return explicit;
|
|
1278
|
+
}
|
|
1279
|
+
const appDir = findAppDir();
|
|
1280
|
+
const id = appDir ? readAppId(appDir) : null;
|
|
1281
|
+
if (!id) {
|
|
1282
|
+
throw new Error(
|
|
1283
|
+
"No app id. Run from an app directory whose wrangler.toml carries DEEPSPACE_APP_ID, or pass --app <appId>."
|
|
1284
|
+
);
|
|
1285
|
+
}
|
|
1286
|
+
return id;
|
|
1287
|
+
}
|
|
978
1288
|
|
|
979
1289
|
// src/cli/lib/install-status.ts
|
|
980
|
-
import { existsSync as
|
|
981
|
-
import { join as
|
|
982
|
-
function ensureInstallReady(
|
|
983
|
-
if (
|
|
984
|
-
const sentinelDir =
|
|
985
|
-
const errPath =
|
|
986
|
-
const startedPath =
|
|
987
|
-
const donePath =
|
|
988
|
-
const logPath =
|
|
989
|
-
if (
|
|
1290
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
1291
|
+
import { join as join7 } from "path";
|
|
1292
|
+
function ensureInstallReady(appDir) {
|
|
1293
|
+
if (existsSync7(join7(appDir, "node_modules", "deepspace", "package.json"))) return;
|
|
1294
|
+
const sentinelDir = join7(appDir, ".deepspace");
|
|
1295
|
+
const errPath = join7(sentinelDir, "install.err");
|
|
1296
|
+
const startedPath = join7(sentinelDir, "install.started");
|
|
1297
|
+
const donePath = join7(sentinelDir, "install.done");
|
|
1298
|
+
const logPath = join7(sentinelDir, "install.log");
|
|
1299
|
+
if (existsSync7(errPath)) {
|
|
990
1300
|
console.error("Background install failed:");
|
|
991
|
-
console.error(
|
|
992
|
-
if (
|
|
1301
|
+
console.error(readFileSync7(errPath, "utf-8").trimEnd());
|
|
1302
|
+
if (existsSync7(logPath)) console.error(`
|
|
993
1303
|
Full log: ${logPath}`);
|
|
994
1304
|
console.error("\nRun `npm install` (or `bun install`) manually, then retry.");
|
|
995
1305
|
process.exit(1);
|
|
996
1306
|
}
|
|
997
|
-
if (
|
|
1307
|
+
if (existsSync7(startedPath) && !existsSync7(donePath)) {
|
|
998
1308
|
console.error("Dependencies are still installing in the background.");
|
|
999
|
-
if (
|
|
1309
|
+
if (existsSync7(logPath)) console.error(`Tail progress: tail -f ${logPath}`);
|
|
1000
1310
|
console.error("Retry once it finishes.");
|
|
1001
1311
|
process.exit(1);
|
|
1002
1312
|
}
|
|
@@ -1006,9 +1316,9 @@ Full log: ${logPath}`);
|
|
|
1006
1316
|
|
|
1007
1317
|
// src/cli/lib/preflight.ts
|
|
1008
1318
|
import { execFileSync } from "child_process";
|
|
1009
|
-
import { existsSync as
|
|
1319
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1010
1320
|
import * as nodeModule from "module";
|
|
1011
|
-
import { resolve as
|
|
1321
|
+
import { resolve as resolve3 } from "path";
|
|
1012
1322
|
function preflightNodeVersion(command, hasRegisterHooks = typeof nodeModule.registerHooks === "function") {
|
|
1013
1323
|
if (hasRegisterHooks) return;
|
|
1014
1324
|
console.error(
|
|
@@ -1018,23 +1328,23 @@ function preflightNodeVersion(command, hasRegisterHooks = typeof nodeModule.regi
|
|
|
1018
1328
|
process.exit(1);
|
|
1019
1329
|
}
|
|
1020
1330
|
var DLL_NOT_FOUND = -1073741515;
|
|
1021
|
-
function preflightWindowsWorkerd(
|
|
1331
|
+
function preflightWindowsWorkerd(appDir) {
|
|
1022
1332
|
if (process.platform !== "win32") return;
|
|
1023
|
-
const workerdExe =
|
|
1024
|
-
|
|
1333
|
+
const workerdExe = resolve3(
|
|
1334
|
+
appDir,
|
|
1025
1335
|
"node_modules",
|
|
1026
1336
|
"@cloudflare",
|
|
1027
1337
|
"workerd-windows-64",
|
|
1028
1338
|
"bin",
|
|
1029
1339
|
"workerd.exe"
|
|
1030
1340
|
);
|
|
1031
|
-
if (!
|
|
1341
|
+
if (!existsSync8(workerdExe)) return;
|
|
1032
1342
|
try {
|
|
1033
1343
|
execFileSync(workerdExe, ["--help"], { stdio: "pipe", timeout: 1e4 });
|
|
1034
1344
|
return;
|
|
1035
1345
|
} catch (err) {
|
|
1036
|
-
const
|
|
1037
|
-
if (
|
|
1346
|
+
const status3 = err.status;
|
|
1347
|
+
if (status3 !== DLL_NOT_FOUND) return;
|
|
1038
1348
|
console.error("");
|
|
1039
1349
|
console.error(" workerd (Cloudflare Workers runtime) cannot start on this system.");
|
|
1040
1350
|
console.error(" It needs the Microsoft Visual C++ Redistributable, which is not installed.");
|
|
@@ -1052,7 +1362,7 @@ function preflightWindowsWorkerd(appDir2) {
|
|
|
1052
1362
|
|
|
1053
1363
|
// src/cli/lib/macos-junk.ts
|
|
1054
1364
|
import { readdirSync as readdirSync2, rmSync as rmSync3 } from "fs";
|
|
1055
|
-
import { join as
|
|
1365
|
+
import { join as join8 } from "path";
|
|
1056
1366
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".wrangler", ".vite"]);
|
|
1057
1367
|
function removeMacosJunk(dir) {
|
|
1058
1368
|
let removed = 0;
|
|
@@ -1063,7 +1373,7 @@ function removeMacosJunk(dir) {
|
|
|
1063
1373
|
return removed;
|
|
1064
1374
|
}
|
|
1065
1375
|
for (const entry of entries) {
|
|
1066
|
-
const path =
|
|
1376
|
+
const path = join8(dir, entry.name);
|
|
1067
1377
|
if (entry.isDirectory()) {
|
|
1068
1378
|
if (!SKIP_DIRS.has(entry.name)) removed += removeMacosJunk(path);
|
|
1069
1379
|
continue;
|
|
@@ -1079,741 +1389,17 @@ function removeMacosJunk(dir) {
|
|
|
1079
1389
|
return removed;
|
|
1080
1390
|
}
|
|
1081
1391
|
|
|
1082
|
-
// src/cli/lib/
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
"ai",
|
|
1092
|
-
"r2_bucket",
|
|
1093
|
-
"kv_namespace",
|
|
1094
|
-
"d1",
|
|
1095
|
-
"queue",
|
|
1096
|
-
"browser_rendering",
|
|
1097
|
-
"analytics_engine",
|
|
1098
|
-
"hyperdrive"
|
|
1099
|
-
]);
|
|
1100
|
-
var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
|
|
1101
|
-
"ASSETS",
|
|
1102
|
-
"PLATFORM_WORKER",
|
|
1103
|
-
"API_WORKER",
|
|
1104
|
-
"APP_NAME",
|
|
1105
|
-
"OWNER_USER_ID",
|
|
1106
|
-
"AUTH_JWT_PUBLIC_KEY",
|
|
1107
|
-
"AUTH_JWT_ISSUER",
|
|
1108
|
-
"AUTH_WORKER_URL",
|
|
1109
|
-
"APP_IDENTITY_TOKEN",
|
|
1110
|
-
"APP_OWNER_JWT",
|
|
1111
|
-
"USAGE_EVENTS"
|
|
1112
|
-
]);
|
|
1113
|
-
function validateBindingManifest(manifest) {
|
|
1114
|
-
if (!Array.isArray(manifest)) {
|
|
1115
|
-
return { valid: false, errors: [{ reason: "Manifest must be an array" }] };
|
|
1116
|
-
}
|
|
1117
|
-
const errors = [];
|
|
1118
|
-
const seenNames = /* @__PURE__ */ new Set();
|
|
1119
|
-
for (const entry of manifest) {
|
|
1120
|
-
if (!entry || typeof entry !== "object") {
|
|
1121
|
-
errors.push({ reason: `Entry must be an object (got ${entry === null ? "null" : typeof entry})` });
|
|
1122
|
-
continue;
|
|
1123
|
-
}
|
|
1124
|
-
const e = entry;
|
|
1125
|
-
const type = e.type;
|
|
1126
|
-
const name = e.name;
|
|
1127
|
-
if (typeof type !== "string" || !ALLOWED_BINDING_TYPES.has(type)) {
|
|
1128
|
-
errors.push({
|
|
1129
|
-
reason: `Disallowed binding type: ${typeof type === "string" ? type : `(${typeof type})`}. Allowed: ${[...ALLOWED_BINDING_TYPES].sort().join(", ")}`
|
|
1130
|
-
});
|
|
1131
|
-
continue;
|
|
1132
|
-
}
|
|
1133
|
-
if (typeof name !== "string" || !name) {
|
|
1134
|
-
errors.push({ reason: `Binding of type "${type}" missing 'name' (string)` });
|
|
1135
|
-
continue;
|
|
1136
|
-
}
|
|
1137
|
-
if (RESERVED_BINDING_NAMES.has(name)) {
|
|
1138
|
-
errors.push({ reason: `Binding name "${name}" is reserved by the SDK` });
|
|
1139
|
-
continue;
|
|
1140
|
-
}
|
|
1141
|
-
if (seenNames.has(name)) {
|
|
1142
|
-
errors.push({ reason: `Duplicate binding name "${name}"` });
|
|
1143
|
-
continue;
|
|
1144
|
-
}
|
|
1145
|
-
seenNames.add(name);
|
|
1146
|
-
const binding = entry;
|
|
1147
|
-
const fieldErr = requiredFieldError(binding);
|
|
1148
|
-
if (fieldErr) errors.push({ binding, reason: fieldErr });
|
|
1392
|
+
// src/cli/lib/port.ts
|
|
1393
|
+
var DEFAULT_PORT = 5173;
|
|
1394
|
+
function resolvePort(arg) {
|
|
1395
|
+
const raw = arg ?? process.env.DEEPSPACE_PORT;
|
|
1396
|
+
if (!raw) return DEFAULT_PORT;
|
|
1397
|
+
const n = Number(raw);
|
|
1398
|
+
if (!Number.isInteger(n) || n < 1 || n > 65535) {
|
|
1399
|
+
console.error(`Invalid port: ${raw}. Must be an integer between 1 and 65535.`);
|
|
1400
|
+
process.exit(1);
|
|
1149
1401
|
}
|
|
1150
|
-
|
|
1151
|
-
return { valid: true, bindings: manifest };
|
|
1152
|
-
}
|
|
1153
|
-
function requiredFieldError(b) {
|
|
1154
|
-
switch (b.type) {
|
|
1155
|
-
case "vectorize": {
|
|
1156
|
-
if (!b.index_name) return `vectorize binding "${b.name}" missing index_name (or "auto")`;
|
|
1157
|
-
if (b.index_name === AUTO_PROVISION_SENTINEL) {
|
|
1158
|
-
if (typeof b.dimensions !== "number" || b.dimensions <= 0) {
|
|
1159
|
-
return `vectorize binding "${b.name}" with index_name="auto" requires "dimensions" (positive number)`;
|
|
1160
|
-
}
|
|
1161
|
-
if (!b.metric || !VECTORIZE_METRICS.has(b.metric)) {
|
|
1162
|
-
return `vectorize binding "${b.name}" with index_name="auto" requires "metric" (one of: ${[...VECTORIZE_METRICS].join(", ")})`;
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
return null;
|
|
1166
|
-
}
|
|
1167
|
-
case "r2_bucket":
|
|
1168
|
-
return b.bucket_name ? null : `r2_bucket binding "${b.name}" missing bucket_name (or "auto")`;
|
|
1169
|
-
case "kv_namespace": {
|
|
1170
|
-
if (!b.namespace_id) return `kv_namespace binding "${b.name}" missing namespace_id (or "auto")`;
|
|
1171
|
-
if (b.namespace_id === AUTO_PROVISION_SENTINEL && !b.title) {
|
|
1172
|
-
return `kv_namespace binding "${b.name}" with namespace_id="auto" requires "title"`;
|
|
1173
|
-
}
|
|
1174
|
-
return null;
|
|
1175
|
-
}
|
|
1176
|
-
case "d1": {
|
|
1177
|
-
if (!b.id) return `d1 binding "${b.name}" missing id (or "auto")`;
|
|
1178
|
-
if (b.id === AUTO_PROVISION_SENTINEL && !b.database_name) {
|
|
1179
|
-
return `d1 binding "${b.name}" with id="auto" requires "database_name"`;
|
|
1180
|
-
}
|
|
1181
|
-
return null;
|
|
1182
|
-
}
|
|
1183
|
-
case "queue":
|
|
1184
|
-
return b.queue_name ? null : `queue binding "${b.name}" missing queue_name (or "auto")`;
|
|
1185
|
-
case "hyperdrive":
|
|
1186
|
-
if (!b.id) return `hyperdrive binding "${b.name}" missing id`;
|
|
1187
|
-
if (b.id === AUTO_PROVISION_SENTINEL) {
|
|
1188
|
-
return `hyperdrive binding "${b.name}": auto-provisioning not yet supported (provide a real id)`;
|
|
1189
|
-
}
|
|
1190
|
-
return null;
|
|
1191
|
-
case "ai":
|
|
1192
|
-
case "browser_rendering":
|
|
1193
|
-
case "analytics_engine":
|
|
1194
|
-
return null;
|
|
1195
|
-
// no required fields beyond `name`
|
|
1196
|
-
default: {
|
|
1197
|
-
const _exhaustive = b;
|
|
1198
|
-
void _exhaustive;
|
|
1199
|
-
return null;
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
function bindingManifestFromOutputConfig(outputConfig) {
|
|
1204
|
-
const out = [];
|
|
1205
|
-
const aiBinding = pluckString(outputConfig.ai, "binding");
|
|
1206
|
-
if (aiBinding) out.push({ type: "ai", name: aiBinding });
|
|
1207
|
-
for (const v of asObjectArray(outputConfig.vectorize)) {
|
|
1208
|
-
const name = pluckString(v, "binding");
|
|
1209
|
-
const index_name = pluckString(v, "index_name");
|
|
1210
|
-
if (!name || !index_name) continue;
|
|
1211
|
-
const dimensions = pluckNumber(v, "dimensions");
|
|
1212
|
-
const metric = pluckString(v, "metric");
|
|
1213
|
-
out.push({
|
|
1214
|
-
type: "vectorize",
|
|
1215
|
-
name,
|
|
1216
|
-
index_name,
|
|
1217
|
-
...dimensions != null && { dimensions },
|
|
1218
|
-
...metric && (metric === "cosine" || metric === "euclidean" || metric === "dot-product") && { metric }
|
|
1219
|
-
});
|
|
1220
|
-
}
|
|
1221
|
-
for (const r of asObjectArray(outputConfig.r2_buckets)) {
|
|
1222
|
-
const name = pluckString(r, "binding");
|
|
1223
|
-
const bucket_name = pluckString(r, "bucket_name");
|
|
1224
|
-
if (name && bucket_name) out.push({ type: "r2_bucket", name, bucket_name });
|
|
1225
|
-
}
|
|
1226
|
-
for (const k of asObjectArray(outputConfig.kv_namespaces)) {
|
|
1227
|
-
const name = pluckString(k, "binding");
|
|
1228
|
-
const namespace_id = pluckString(k, "id");
|
|
1229
|
-
if (!name || !namespace_id) continue;
|
|
1230
|
-
const title = pluckString(k, "title");
|
|
1231
|
-
out.push({
|
|
1232
|
-
type: "kv_namespace",
|
|
1233
|
-
name,
|
|
1234
|
-
namespace_id,
|
|
1235
|
-
...title && { title }
|
|
1236
|
-
});
|
|
1237
|
-
}
|
|
1238
|
-
for (const d of asObjectArray(outputConfig.d1_databases)) {
|
|
1239
|
-
const name = pluckString(d, "binding");
|
|
1240
|
-
const id = pluckString(d, "database_id");
|
|
1241
|
-
if (!name || !id) continue;
|
|
1242
|
-
const database_name = pluckString(d, "database_name");
|
|
1243
|
-
out.push({
|
|
1244
|
-
type: "d1",
|
|
1245
|
-
name,
|
|
1246
|
-
id,
|
|
1247
|
-
...database_name && { database_name }
|
|
1248
|
-
});
|
|
1249
|
-
}
|
|
1250
|
-
const queues = isObject(outputConfig.queues) ? outputConfig.queues : null;
|
|
1251
|
-
for (const q of asObjectArray(queues?.producers)) {
|
|
1252
|
-
const name = pluckString(q, "binding");
|
|
1253
|
-
const queue_name = pluckString(q, "queue");
|
|
1254
|
-
if (name && queue_name) out.push({ type: "queue", name, queue_name });
|
|
1255
|
-
}
|
|
1256
|
-
const browserBinding = pluckString(outputConfig.browser, "binding");
|
|
1257
|
-
if (browserBinding) out.push({ type: "browser_rendering", name: browserBinding });
|
|
1258
|
-
for (const a of asObjectArray(outputConfig.analytics_engine_datasets)) {
|
|
1259
|
-
const name = pluckString(a, "binding");
|
|
1260
|
-
const dataset = pluckString(a, "dataset");
|
|
1261
|
-
if (name) out.push({ type: "analytics_engine", name, dataset: dataset ?? void 0 });
|
|
1262
|
-
}
|
|
1263
|
-
for (const h of asObjectArray(outputConfig.hyperdrive)) {
|
|
1264
|
-
const name = pluckString(h, "binding");
|
|
1265
|
-
const id = pluckString(h, "id");
|
|
1266
|
-
if (name && id) out.push({ type: "hyperdrive", name, id });
|
|
1267
|
-
}
|
|
1268
|
-
return out;
|
|
1269
|
-
}
|
|
1270
|
-
function isObject(v) {
|
|
1271
|
-
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
1272
|
-
}
|
|
1273
|
-
function pluckString(v, key) {
|
|
1274
|
-
if (!isObject(v)) return null;
|
|
1275
|
-
const val = v[key];
|
|
1276
|
-
return typeof val === "string" && val.length > 0 ? val : null;
|
|
1277
|
-
}
|
|
1278
|
-
function pluckNumber(v, key) {
|
|
1279
|
-
if (!isObject(v)) return null;
|
|
1280
|
-
const val = v[key];
|
|
1281
|
-
return typeof val === "number" && Number.isFinite(val) ? val : null;
|
|
1282
|
-
}
|
|
1283
|
-
function asObjectArray(v) {
|
|
1284
|
-
if (!Array.isArray(v)) return [];
|
|
1285
|
-
return v.filter(isObject);
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
// src/cli/lib/secrets.ts
|
|
1289
|
-
var DEEPSPACE_SECRETS_PROJECT_VAR = "DEEPSPACE_SECRETS_PROJECT";
|
|
1290
|
-
var DEEPSPACE_SECRETS_CONFIG_VAR = "DEEPSPACE_SECRETS_CONFIG";
|
|
1291
|
-
var GENERATED_SECRETS_DIVIDER2 = "# --- DeepSpace secrets cache: generated by `npx deepspace secrets`; do not edit manually ---";
|
|
1292
|
-
var REQUIRED_SECRETS_BEGIN = "# --- DeepSpace detected secrets (names only; values live in `npx deepspace secrets`) ---";
|
|
1293
|
-
var REQUIRED_SECRETS_END = "# --- end DeepSpace detected secrets ---";
|
|
1294
|
-
var SECRET_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1295
|
-
var PROJECT_NAME_RE = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
1296
|
-
var CONFIG_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
1297
|
-
var MAX_SECRET_VALUE_BYTES = 32 * 1024;
|
|
1298
|
-
function validateProjectName(projectName) {
|
|
1299
|
-
const trimmed = projectName.trim();
|
|
1300
|
-
if (!PROJECT_NAME_RE.test(trimmed)) {
|
|
1301
|
-
throw new Error(
|
|
1302
|
-
`Invalid secrets project "${projectName}". Use lowercase letters, numbers, and hyphens; start with a letter or number.`
|
|
1303
|
-
);
|
|
1304
|
-
}
|
|
1305
|
-
return trimmed;
|
|
1306
|
-
}
|
|
1307
|
-
function validateConfigName(configName) {
|
|
1308
|
-
const trimmed = configName.trim();
|
|
1309
|
-
if (!CONFIG_NAME_RE.test(trimmed)) {
|
|
1310
|
-
throw new Error(
|
|
1311
|
-
`Invalid secrets config "${configName}". Use letters, numbers, underscores, or hyphens; start with a letter or number.`
|
|
1312
|
-
);
|
|
1313
|
-
}
|
|
1314
|
-
return trimmed;
|
|
1315
|
-
}
|
|
1316
|
-
function validateSecretName(name) {
|
|
1317
|
-
const trimmed = name.trim();
|
|
1318
|
-
if (!SECRET_NAME_RE.test(trimmed)) {
|
|
1319
|
-
throw new Error(
|
|
1320
|
-
`Invalid secret name "${name}". Secret names must use letters, numbers, and underscores, and may not start with a number.`
|
|
1321
|
-
);
|
|
1322
|
-
}
|
|
1323
|
-
if (RESERVED_BINDING_NAMES.has(trimmed)) {
|
|
1324
|
-
throw new Error(`"${trimmed}" is reserved by DeepSpace and cannot be used as an app secret.`);
|
|
1325
|
-
}
|
|
1326
|
-
return trimmed;
|
|
1327
|
-
}
|
|
1328
|
-
function validateSecretValue(key, value) {
|
|
1329
|
-
const bytes = new TextEncoder().encode(value).byteLength;
|
|
1330
|
-
if (bytes > MAX_SECRET_VALUE_BYTES) {
|
|
1331
|
-
throw new Error(`Secret "${key}" exceeds ${MAX_SECRET_VALUE_BYTES} bytes.`);
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
function defaultConfigNameForEnv(wranglerEnv2) {
|
|
1335
|
-
return wranglerEnv2 ? validateConfigName(wranglerEnv2) : "prd";
|
|
1336
|
-
}
|
|
1337
|
-
function readTopLevelAppName(appDir2) {
|
|
1338
|
-
const cfg = readWranglerConfig(appDir2);
|
|
1339
|
-
const resolved = resolveAppNameForEnv(cfg, void 0);
|
|
1340
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
1341
|
-
if (cfg.name !== resolved.name) {
|
|
1342
|
-
throw new Error(
|
|
1343
|
-
`wrangler.toml: \`name\` "${cfg.name}" is not in canonical form. Update it to "${resolved.name}" and re-run.`
|
|
1344
|
-
);
|
|
1345
|
-
}
|
|
1346
|
-
return resolved.name;
|
|
1347
|
-
}
|
|
1348
|
-
function resolveSecretsAppName(appDir2, wranglerEnv2) {
|
|
1349
|
-
const cfg = readWranglerConfig(appDir2);
|
|
1350
|
-
const resolved = resolveAppNameForEnv(cfg, wranglerEnv2);
|
|
1351
|
-
if (!resolved.ok) throw new Error(resolved.reason);
|
|
1352
|
-
return resolved.name;
|
|
1353
|
-
}
|
|
1354
|
-
function stringVar(value) {
|
|
1355
|
-
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
1356
|
-
}
|
|
1357
|
-
function resolveSecretsLink(appDir2, wranglerEnv2) {
|
|
1358
|
-
const cfg = readWranglerConfig(appDir2);
|
|
1359
|
-
const defaultProjectName = readTopLevelAppName(appDir2);
|
|
1360
|
-
const defaultConfigName = defaultConfigNameForEnv(wranglerEnv2);
|
|
1361
|
-
const topVars = cfg.vars ?? {};
|
|
1362
|
-
const envVars = wranglerEnv2 ? cfg.env?.[wranglerEnv2]?.vars ?? {} : {};
|
|
1363
|
-
const projectName = stringVar(envVars[DEEPSPACE_SECRETS_PROJECT_VAR]) ?? stringVar(topVars[DEEPSPACE_SECRETS_PROJECT_VAR]);
|
|
1364
|
-
if (!projectName) {
|
|
1365
|
-
return { linked: false, defaultProjectName, defaultConfigName, wranglerEnv: wranglerEnv2 };
|
|
1366
|
-
}
|
|
1367
|
-
const configName = stringVar(envVars[DEEPSPACE_SECRETS_CONFIG_VAR]) ?? (wranglerEnv2 ? defaultConfigName : stringVar(topVars[DEEPSPACE_SECRETS_CONFIG_VAR]) ?? "prd");
|
|
1368
|
-
return {
|
|
1369
|
-
linked: true,
|
|
1370
|
-
projectName: validateProjectName(projectName),
|
|
1371
|
-
configName: validateConfigName(configName),
|
|
1372
|
-
wranglerEnv: wranglerEnv2
|
|
1373
|
-
};
|
|
1374
|
-
}
|
|
1375
|
-
function resolveSecretsTarget(appDir2, opts) {
|
|
1376
|
-
const link = resolveSecretsLink(appDir2, opts.wranglerEnv);
|
|
1377
|
-
const projectName = validateProjectName(
|
|
1378
|
-
opts.project ?? (link.linked ? link.projectName : link.defaultProjectName)
|
|
1379
|
-
);
|
|
1380
|
-
const configName = validateConfigName(
|
|
1381
|
-
opts.config ?? (link.linked ? link.configName : link.defaultConfigName)
|
|
1382
|
-
);
|
|
1383
|
-
return { projectName, configName, wranglerEnv: opts.wranglerEnv };
|
|
1384
|
-
}
|
|
1385
|
-
function renderSecretsCache(secrets, source) {
|
|
1386
|
-
const keys = Object.keys(secrets).sort();
|
|
1387
|
-
const lines = [
|
|
1388
|
-
GENERATED_SECRETS_DIVIDER2,
|
|
1389
|
-
`# Source: project=${source.projectName} config=${source.configName}`,
|
|
1390
|
-
`# Pulled: ${source.pulledAt ?? (/* @__PURE__ */ new Date()).toISOString()}`,
|
|
1391
|
-
"# Use `npx deepspace secrets` to change these values."
|
|
1392
|
-
];
|
|
1393
|
-
for (const key of keys) {
|
|
1394
|
-
validateSecretName(key);
|
|
1395
|
-
validateSecretValue(key, secrets[key]);
|
|
1396
|
-
lines.push(`${key}=${quoteDotenvValue(secrets[key])}`);
|
|
1397
|
-
}
|
|
1398
|
-
return lines.join("\n");
|
|
1399
|
-
}
|
|
1400
|
-
function stripGeneratedSecretsCache2(content) {
|
|
1401
|
-
const idx = content.indexOf(GENERATED_SECRETS_DIVIDER2);
|
|
1402
|
-
if (idx < 0) return content.replace(/\s+$/u, "");
|
|
1403
|
-
return content.slice(0, idx).replace(/\s+$/u, "");
|
|
1404
|
-
}
|
|
1405
|
-
function formatSecretsDownload(secrets, format) {
|
|
1406
|
-
const entries = Object.entries(secrets).sort(([a], [b]) => a.localeCompare(b));
|
|
1407
|
-
switch (format) {
|
|
1408
|
-
case "json":
|
|
1409
|
-
case "dotnet-json":
|
|
1410
|
-
return JSON.stringify(Object.fromEntries(entries), null, 2) + "\n";
|
|
1411
|
-
case "env":
|
|
1412
|
-
return entries.map(([key, value]) => `${key}=${quoteDotenvValue(value)}`).join("\n") + "\n";
|
|
1413
|
-
case "env-no-quotes":
|
|
1414
|
-
case "docker":
|
|
1415
|
-
return entries.map(([key, value]) => `${key}=${value}`).join("\n") + "\n";
|
|
1416
|
-
case "yaml":
|
|
1417
|
-
return entries.map(([key, value]) => `${key}: ${JSON.stringify(value)}`).join("\n") + "\n";
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
function writeSecretsCacheFile(appDir2, secrets, source) {
|
|
1421
|
-
const devVarsPath = secretsCacheFilePath(appDir2);
|
|
1422
|
-
const existing = existsSync8(devVarsPath) ? readFileSync7(devVarsPath, "utf-8") : "";
|
|
1423
|
-
const head = stripGeneratedSecretsCache2(existing);
|
|
1424
|
-
const rendered = renderSecretsCache(secrets, source);
|
|
1425
|
-
writeSecretFileSync(devVarsPath, head ? `${head}
|
|
1426
|
-
|
|
1427
|
-
${rendered}
|
|
1428
|
-
` : `${rendered}
|
|
1429
|
-
`);
|
|
1430
|
-
}
|
|
1431
|
-
function clearSecretsCacheFile(appDir2) {
|
|
1432
|
-
const devVarsPath = secretsCacheFilePath(appDir2);
|
|
1433
|
-
if (!existsSync8(devVarsPath)) return;
|
|
1434
|
-
const existing = readFileSync7(devVarsPath, "utf-8");
|
|
1435
|
-
const next = stripGeneratedSecretsCache2(existing);
|
|
1436
|
-
writeSecretFileSync(devVarsPath, next ? `${next}
|
|
1437
|
-
` : "");
|
|
1438
|
-
}
|
|
1439
|
-
function secretsTargetMatchesLink(appDir2, target) {
|
|
1440
|
-
const link = resolveSecretsLink(appDir2, target.wranglerEnv);
|
|
1441
|
-
return link.linked && link.projectName === target.projectName && link.configName === target.configName;
|
|
1442
|
-
}
|
|
1443
|
-
function appScopedSecretsApiOptions(appDir2, target) {
|
|
1444
|
-
const link = resolveSecretsLink(appDir2, target.wranglerEnv);
|
|
1445
|
-
if (!link.linked || link.projectName !== target.projectName) return {};
|
|
1446
|
-
return { appName: resolveSecretsAppName(appDir2, target.wranglerEnv) };
|
|
1447
|
-
}
|
|
1448
|
-
function secretsCacheFilePath(appDir2) {
|
|
1449
|
-
return join8(appDir2, ".dev.vars");
|
|
1450
|
-
}
|
|
1451
|
-
async function pullLinkedSecretsCache(deployUrl, token, appDir2, wranglerEnv2) {
|
|
1452
|
-
const link = resolveSecretsLink(appDir2, wranglerEnv2);
|
|
1453
|
-
if (!link.linked) return null;
|
|
1454
|
-
const values = await fetchSecretsValues(deployUrl, token, link.projectName, link.configName, {
|
|
1455
|
-
appName: resolveSecretsAppName(appDir2, wranglerEnv2)
|
|
1456
|
-
});
|
|
1457
|
-
return {
|
|
1458
|
-
linked: true,
|
|
1459
|
-
projectName: link.projectName,
|
|
1460
|
-
configName: link.configName,
|
|
1461
|
-
wranglerEnv: wranglerEnv2,
|
|
1462
|
-
values
|
|
1463
|
-
};
|
|
1464
|
-
}
|
|
1465
|
-
async function fetchSecretsValues(deployUrl, token, projectName, configName, options = {}) {
|
|
1466
|
-
const body = await secretsApi(
|
|
1467
|
-
deployUrl,
|
|
1468
|
-
token,
|
|
1469
|
-
`/api/secrets/${encodeURIComponent(projectName)}/configs/${encodeURIComponent(configName)}/values`,
|
|
1470
|
-
{},
|
|
1471
|
-
options
|
|
1472
|
-
);
|
|
1473
|
-
return body.secrets;
|
|
1474
|
-
}
|
|
1475
|
-
async function listSecrets(deployUrl, token, projectName, configName, options = {}) {
|
|
1476
|
-
return secretsApi(
|
|
1477
|
-
deployUrl,
|
|
1478
|
-
token,
|
|
1479
|
-
`/api/secrets/${encodeURIComponent(projectName)}/configs/${encodeURIComponent(configName)}`,
|
|
1480
|
-
{},
|
|
1481
|
-
options
|
|
1482
|
-
);
|
|
1483
|
-
}
|
|
1484
|
-
async function getSecretPlain(deployUrl, token, projectName, configName, key, options = {}) {
|
|
1485
|
-
const body = await secretsApi(
|
|
1486
|
-
deployUrl,
|
|
1487
|
-
token,
|
|
1488
|
-
`/api/secrets/${encodeURIComponent(projectName)}/configs/${encodeURIComponent(configName)}/secrets/${encodeURIComponent(key)}?plain=true`,
|
|
1489
|
-
{},
|
|
1490
|
-
options
|
|
1491
|
-
);
|
|
1492
|
-
return body.value;
|
|
1493
|
-
}
|
|
1494
|
-
async function deleteSecret(deployUrl, token, projectName, configName, key, options = {}) {
|
|
1495
|
-
await secretsApi(
|
|
1496
|
-
deployUrl,
|
|
1497
|
-
token,
|
|
1498
|
-
`/api/secrets/${encodeURIComponent(projectName)}/configs/${encodeURIComponent(configName)}/secrets/${encodeURIComponent(key)}`,
|
|
1499
|
-
{ method: "DELETE" },
|
|
1500
|
-
options
|
|
1501
|
-
);
|
|
1502
|
-
}
|
|
1503
|
-
async function uploadSecrets(deployUrl, token, projectName, configName, secrets, replace, options = {}) {
|
|
1504
|
-
await secretsApi(
|
|
1505
|
-
deployUrl,
|
|
1506
|
-
token,
|
|
1507
|
-
`/api/secrets/${encodeURIComponent(projectName)}/configs/${encodeURIComponent(configName)}/secrets`,
|
|
1508
|
-
{
|
|
1509
|
-
method: "PUT",
|
|
1510
|
-
body: JSON.stringify({ secrets, replace })
|
|
1511
|
-
},
|
|
1512
|
-
options
|
|
1513
|
-
);
|
|
1514
|
-
}
|
|
1515
|
-
async function listConfigs(deployUrl, token, projectName, options = {}) {
|
|
1516
|
-
const body = await secretsApi(
|
|
1517
|
-
deployUrl,
|
|
1518
|
-
token,
|
|
1519
|
-
`/api/secrets/${encodeURIComponent(projectName)}/configs`,
|
|
1520
|
-
{},
|
|
1521
|
-
options
|
|
1522
|
-
);
|
|
1523
|
-
return body.configs;
|
|
1524
|
-
}
|
|
1525
|
-
async function createConfig(deployUrl, token, projectName, configName, copyFromConfigName, options = {}) {
|
|
1526
|
-
return secretsApi(
|
|
1527
|
-
deployUrl,
|
|
1528
|
-
token,
|
|
1529
|
-
`/api/secrets/${encodeURIComponent(projectName)}/configs`,
|
|
1530
|
-
{
|
|
1531
|
-
method: "POST",
|
|
1532
|
-
body: JSON.stringify({
|
|
1533
|
-
name: configName,
|
|
1534
|
-
...copyFromConfigName ? { copyFrom: copyFromConfigName } : {}
|
|
1535
|
-
})
|
|
1536
|
-
},
|
|
1537
|
-
options
|
|
1538
|
-
);
|
|
1539
|
-
}
|
|
1540
|
-
async function deleteConfig(deployUrl, token, projectName, configName, options = {}) {
|
|
1541
|
-
await secretsApi(
|
|
1542
|
-
deployUrl,
|
|
1543
|
-
token,
|
|
1544
|
-
`/api/secrets/${encodeURIComponent(projectName)}/configs/${encodeURIComponent(configName)}`,
|
|
1545
|
-
{ method: "DELETE" },
|
|
1546
|
-
options
|
|
1547
|
-
);
|
|
1548
|
-
}
|
|
1549
|
-
async function setServerSecretsLink(deployUrl, token, appName, projectName) {
|
|
1550
|
-
await secretsApi(deployUrl, token, `/api/secrets-links/${encodeURIComponent(appName)}`, {
|
|
1551
|
-
method: "PUT",
|
|
1552
|
-
body: JSON.stringify({ projectName })
|
|
1553
|
-
});
|
|
1554
|
-
}
|
|
1555
|
-
async function clearServerSecretsLink(deployUrl, token, appName) {
|
|
1556
|
-
await secretsApi(deployUrl, token, `/api/secrets-links/${encodeURIComponent(appName)}`, {
|
|
1557
|
-
method: "DELETE"
|
|
1558
|
-
});
|
|
1559
|
-
}
|
|
1560
|
-
function parseSecretsUpload(content) {
|
|
1561
|
-
const trimmed = content.trim();
|
|
1562
|
-
if (!trimmed) return {};
|
|
1563
|
-
if (trimmed.startsWith("{")) {
|
|
1564
|
-
const parsed2 = JSON.parse(trimmed);
|
|
1565
|
-
if (!parsed2 || typeof parsed2 !== "object" || Array.isArray(parsed2)) {
|
|
1566
|
-
throw new Error("JSON upload must be an object of KEY=value strings.");
|
|
1567
|
-
}
|
|
1568
|
-
const out2 = {};
|
|
1569
|
-
for (const [key, value] of Object.entries(parsed2)) {
|
|
1570
|
-
const name = validateSecretName(key);
|
|
1571
|
-
if (typeof value !== "string") {
|
|
1572
|
-
throw new Error(`Secret "${name}" must be a string.`);
|
|
1573
|
-
}
|
|
1574
|
-
validateSecretValue(name, value);
|
|
1575
|
-
out2[name] = value;
|
|
1576
|
-
}
|
|
1577
|
-
return out2;
|
|
1578
|
-
}
|
|
1579
|
-
const parsed = parseDevVars(content);
|
|
1580
|
-
const out = {};
|
|
1581
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
1582
|
-
const name = validateSecretName(key);
|
|
1583
|
-
validateSecretValue(name, value);
|
|
1584
|
-
out[name] = value;
|
|
1585
|
-
}
|
|
1586
|
-
return out;
|
|
1587
|
-
}
|
|
1588
|
-
function writeWranglerSecretsLink(appDir2, target, opts = {}) {
|
|
1589
|
-
const path = join8(resolve3(appDir2), "wrangler.toml");
|
|
1590
|
-
const raw = readFileSync7(path, "utf-8");
|
|
1591
|
-
const table = target.wranglerEnv ? `env.${target.wranglerEnv}.vars` : "vars";
|
|
1592
|
-
const vars = {
|
|
1593
|
-
[DEEPSPACE_SECRETS_CONFIG_VAR]: target.configName
|
|
1594
|
-
};
|
|
1595
|
-
if (!target.wranglerEnv || opts.projectOverride) {
|
|
1596
|
-
vars[DEEPSPACE_SECRETS_PROJECT_VAR] = target.projectName;
|
|
1597
|
-
} else {
|
|
1598
|
-
const link = resolveSecretsLink(appDir2, void 0);
|
|
1599
|
-
if (!link.linked) {
|
|
1600
|
-
vars[DEEPSPACE_SECRETS_PROJECT_VAR] = target.projectName;
|
|
1601
|
-
}
|
|
1602
|
-
}
|
|
1603
|
-
writeFileSync7(path, setTomlVars(raw, table, vars));
|
|
1604
|
-
}
|
|
1605
|
-
function removeWranglerSecretsLink(appDir2, wranglerEnv2) {
|
|
1606
|
-
removeWranglerSecretsLinkKeys(appDir2, wranglerEnv2, ["project", "config"]);
|
|
1607
|
-
}
|
|
1608
|
-
function removeWranglerSecretsLinkKeys(appDir2, wranglerEnv2, keys) {
|
|
1609
|
-
const path = join8(resolve3(appDir2), "wrangler.toml");
|
|
1610
|
-
const raw = readFileSync7(path, "utf-8");
|
|
1611
|
-
const table = wranglerEnv2 ? `env.${wranglerEnv2}.vars` : "vars";
|
|
1612
|
-
const vars = {};
|
|
1613
|
-
if (keys.includes("project")) vars[DEEPSPACE_SECRETS_PROJECT_VAR] = void 0;
|
|
1614
|
-
if (keys.includes("config")) vars[DEEPSPACE_SECRETS_CONFIG_VAR] = void 0;
|
|
1615
|
-
if (Object.keys(vars).length === 0) return;
|
|
1616
|
-
writeFileSync7(path, setTomlVars(raw, table, vars));
|
|
1617
|
-
}
|
|
1618
|
-
function updateWranglerRequiredSecrets(appDir2, secretNames, opts = {}) {
|
|
1619
|
-
const path = join8(resolve3(appDir2), "wrangler.toml");
|
|
1620
|
-
const raw = readFileSync7(path, "utf-8");
|
|
1621
|
-
const existing = readRequiredSecretNamesFromToml(raw);
|
|
1622
|
-
const incoming = normalizeRequiredSecretNames(secretNames);
|
|
1623
|
-
const next = opts.replace ? incoming : normalizeRequiredSecretNames([...existing, ...incoming]);
|
|
1624
|
-
writeFileSync7(
|
|
1625
|
-
path,
|
|
1626
|
-
setRequiredSecretNamesInToml(raw, next, existing.length > 0 || hasRequiredSecretsBlock(raw))
|
|
1627
|
-
);
|
|
1628
|
-
return next;
|
|
1629
|
-
}
|
|
1630
|
-
function removeWranglerRequiredSecrets(appDir2, secretNames) {
|
|
1631
|
-
const path = join8(resolve3(appDir2), "wrangler.toml");
|
|
1632
|
-
const raw = readFileSync7(path, "utf-8");
|
|
1633
|
-
const remove2 = new Set(normalizeRequiredSecretNames(secretNames));
|
|
1634
|
-
const next = readRequiredSecretNamesFromToml(raw).filter((key) => !remove2.has(key));
|
|
1635
|
-
writeFileSync7(path, setRequiredSecretNamesInToml(raw, next, hasRequiredSecretsBlock(raw)));
|
|
1636
|
-
return next;
|
|
1637
|
-
}
|
|
1638
|
-
function quoteDotenvValue(value) {
|
|
1639
|
-
if (value === "") return '""';
|
|
1640
|
-
if (/^[^\s"'#=]+$/u.test(value)) return value;
|
|
1641
|
-
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
1642
|
-
}
|
|
1643
|
-
var SecretsApiError = class extends Error {
|
|
1644
|
-
status;
|
|
1645
|
-
detail;
|
|
1646
|
-
constructor(path, status2, detail) {
|
|
1647
|
-
super(`Secrets API ${path} (${status2}): ${detail}`);
|
|
1648
|
-
this.name = "SecretsApiError";
|
|
1649
|
-
this.status = status2;
|
|
1650
|
-
this.detail = detail;
|
|
1651
|
-
}
|
|
1652
|
-
};
|
|
1653
|
-
async function secretsApi(deployUrl, token, path, init = {}, options = {}) {
|
|
1654
|
-
const scopedPath = withSecretsApiOptions(path, options);
|
|
1655
|
-
let res = await fetch(`${deployUrl}${scopedPath}`, {
|
|
1656
|
-
...init,
|
|
1657
|
-
headers: {
|
|
1658
|
-
Authorization: `Bearer ${token}`,
|
|
1659
|
-
"Content-Type": "application/json",
|
|
1660
|
-
...init.headers ?? {}
|
|
1661
|
-
}
|
|
1662
|
-
});
|
|
1663
|
-
let text2 = await res.text();
|
|
1664
|
-
const parsedError = parseSecretsApiError(text2);
|
|
1665
|
-
if (!res.ok && res.status === 404 && parsedError === "app_not_found" && options.appName) {
|
|
1666
|
-
res = await fetch(`${deployUrl}${path}`, {
|
|
1667
|
-
...init,
|
|
1668
|
-
headers: {
|
|
1669
|
-
Authorization: `Bearer ${token}`,
|
|
1670
|
-
"Content-Type": "application/json",
|
|
1671
|
-
...init.headers ?? {}
|
|
1672
|
-
}
|
|
1673
|
-
});
|
|
1674
|
-
text2 = await res.text();
|
|
1675
|
-
}
|
|
1676
|
-
if (!res.ok) {
|
|
1677
|
-
let detail = text2;
|
|
1678
|
-
try {
|
|
1679
|
-
detail = JSON.parse(text2).error ?? text2;
|
|
1680
|
-
} catch {
|
|
1681
|
-
}
|
|
1682
|
-
throw new SecretsApiError(path, res.status, detail);
|
|
1683
|
-
}
|
|
1684
|
-
if (!text2) return void 0;
|
|
1685
|
-
return JSON.parse(text2);
|
|
1686
|
-
}
|
|
1687
|
-
function withSecretsApiOptions(path, options) {
|
|
1688
|
-
if (!options.appName) return path;
|
|
1689
|
-
const sep2 = path.includes("?") ? "&" : "?";
|
|
1690
|
-
return `${path}${sep2}appName=${encodeURIComponent(options.appName)}`;
|
|
1691
|
-
}
|
|
1692
|
-
function parseSecretsApiError(text2) {
|
|
1693
|
-
try {
|
|
1694
|
-
const parsed = JSON.parse(text2);
|
|
1695
|
-
return typeof parsed.error === "string" ? parsed.error : null;
|
|
1696
|
-
} catch {
|
|
1697
|
-
return null;
|
|
1698
|
-
}
|
|
1699
|
-
}
|
|
1700
|
-
function setTomlVars(raw, table, vars) {
|
|
1701
|
-
const lines = raw.replace(/\r\n/g, "\n").split("\n");
|
|
1702
|
-
const header = `[${table}]`;
|
|
1703
|
-
const headerRe = new RegExp(`^\\s*\\[${escapeRegExp(table)}\\]\\s*(?:#.*)?$`);
|
|
1704
|
-
let start = lines.findIndex((line) => headerRe.test(line));
|
|
1705
|
-
if (start < 0) {
|
|
1706
|
-
const additions = Object.entries(vars).filter(([, value]) => value !== void 0).map(([key, value]) => `${key} = ${JSON.stringify(value)}`);
|
|
1707
|
-
const prefix = raw.trimEnd();
|
|
1708
|
-
return `${prefix}${prefix ? "\n\n" : ""}${header}
|
|
1709
|
-
${additions.join("\n")}
|
|
1710
|
-
`;
|
|
1711
|
-
}
|
|
1712
|
-
let end = start + 1;
|
|
1713
|
-
while (end < lines.length && !/^\s*\[\[?[^\]]+\]\]?\s*(?:#.*)?$/u.test(lines[end])) end++;
|
|
1714
|
-
const tableLines = lines.slice(start + 1, end);
|
|
1715
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1716
|
-
const nextTableLines = [];
|
|
1717
|
-
for (const line of tableLines) {
|
|
1718
|
-
const key = Object.keys(vars).find(
|
|
1719
|
-
(name) => new RegExp(`^\\s*${escapeRegExp(name)}\\s*=`).test(line)
|
|
1720
|
-
);
|
|
1721
|
-
if (!key) {
|
|
1722
|
-
nextTableLines.push(line);
|
|
1723
|
-
continue;
|
|
1724
|
-
}
|
|
1725
|
-
seen.add(key);
|
|
1726
|
-
const value = vars[key];
|
|
1727
|
-
if (value !== void 0) nextTableLines.push(`${key} = ${JSON.stringify(value)}`);
|
|
1728
|
-
}
|
|
1729
|
-
for (const [key, value] of Object.entries(vars)) {
|
|
1730
|
-
if (!seen.has(key) && value !== void 0) {
|
|
1731
|
-
nextTableLines.push(`${key} = ${JSON.stringify(value)}`);
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
1734
|
-
lines.splice(start + 1, end - start - 1, ...nextTableLines);
|
|
1735
|
-
return lines.join("\n").replace(/\s*$/u, "\n");
|
|
1736
|
-
}
|
|
1737
|
-
function readRequiredSecretNamesFromToml(raw) {
|
|
1738
|
-
const block = findRequiredSecretsBlock(raw);
|
|
1739
|
-
if (!block) return [];
|
|
1740
|
-
const names = [];
|
|
1741
|
-
for (const line of block.lines.slice(block.start + 1, block.end)) {
|
|
1742
|
-
const match = /^\s*#\s+([A-Za-z_][A-Za-z0-9_]*)\s*$/u.exec(line);
|
|
1743
|
-
if (match) names.push(validateSecretName(match[1]));
|
|
1744
|
-
}
|
|
1745
|
-
return normalizeRequiredSecretNames(names);
|
|
1746
|
-
}
|
|
1747
|
-
function setRequiredSecretNamesInToml(raw, names, keepEmptyBlock) {
|
|
1748
|
-
const normalized = raw.replace(/\r\n/g, "\n").replace(/\s*$/u, "\n");
|
|
1749
|
-
const block = findRequiredSecretsBlock(normalized);
|
|
1750
|
-
if (names.length === 0 && !block && !keepEmptyBlock) return normalized;
|
|
1751
|
-
const rendered = renderRequiredSecretsBlock(names);
|
|
1752
|
-
if (block) {
|
|
1753
|
-
const lines2 = [...block.lines];
|
|
1754
|
-
lines2.splice(block.start, block.end - block.start + 1, ...rendered);
|
|
1755
|
-
return lines2.join("\n").replace(/\s*$/u, "\n");
|
|
1756
|
-
}
|
|
1757
|
-
const lines = normalized.split("\n");
|
|
1758
|
-
if (lines[lines.length - 1] === "") lines.pop();
|
|
1759
|
-
const insertAt = requiredSecretsInsertIndex(lines);
|
|
1760
|
-
const insertion = insertAt < lines.length ? [...rendered, ""] : lines.length > 0 ? ["", ...rendered] : rendered;
|
|
1761
|
-
lines.splice(insertAt, 0, ...insertion);
|
|
1762
|
-
return lines.join("\n").replace(/\s*$/u, "\n");
|
|
1763
|
-
}
|
|
1764
|
-
function renderRequiredSecretsBlock(names) {
|
|
1765
|
-
return [
|
|
1766
|
-
REQUIRED_SECRETS_BEGIN,
|
|
1767
|
-
...names.length > 0 ? names.map((name) => `# ${name}`) : ["# (none declared)"],
|
|
1768
|
-
REQUIRED_SECRETS_END
|
|
1769
|
-
];
|
|
1770
|
-
}
|
|
1771
|
-
function normalizeRequiredSecretNames(secretNames) {
|
|
1772
|
-
return Array.from(new Set(Array.from(secretNames, (name) => validateSecretName(name)))).sort(
|
|
1773
|
-
(a, b) => a.localeCompare(b)
|
|
1774
|
-
);
|
|
1775
|
-
}
|
|
1776
|
-
function hasRequiredSecretsBlock(raw) {
|
|
1777
|
-
return findRequiredSecretsBlock(raw) !== null;
|
|
1778
|
-
}
|
|
1779
|
-
function findRequiredSecretsBlock(raw) {
|
|
1780
|
-
const lines = raw.replace(/\r\n/g, "\n").split("\n");
|
|
1781
|
-
const start = lines.findIndex((line) => requiredSecretsBeginRe().test(line));
|
|
1782
|
-
if (start < 0) return null;
|
|
1783
|
-
const end = lines.findIndex((line, index) => index > start && requiredSecretsEndRe().test(line));
|
|
1784
|
-
return { lines, start, end: end < 0 ? start : end };
|
|
1785
|
-
}
|
|
1786
|
-
function requiredSecretsInsertIndex(lines) {
|
|
1787
|
-
const varsHeader = /^\s*\[vars\]\s*(?:#.*)?$/u;
|
|
1788
|
-
const tableHeader = /^\s*\[\[?[^\]]+\]\]?\s*(?:#.*)?$/u;
|
|
1789
|
-
const varsStart = lines.findIndex((line) => varsHeader.test(line));
|
|
1790
|
-
if (varsStart < 0) return lines.length;
|
|
1791
|
-
let end = varsStart + 1;
|
|
1792
|
-
while (end < lines.length && !tableHeader.test(lines[end])) end++;
|
|
1793
|
-
while (end > varsStart + 1 && lines[end - 1].trim() === "") end--;
|
|
1794
|
-
return end;
|
|
1795
|
-
}
|
|
1796
|
-
function requiredSecretsBeginRe() {
|
|
1797
|
-
return /^# --- DeepSpace (?:detected|required) secrets\b.*---$/u;
|
|
1798
|
-
}
|
|
1799
|
-
function requiredSecretsEndRe() {
|
|
1800
|
-
return /^# --- end DeepSpace (?:detected|required) secrets ---$/u;
|
|
1801
|
-
}
|
|
1802
|
-
function escapeRegExp(value) {
|
|
1803
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
// src/cli/lib/port.ts
|
|
1807
|
-
var DEFAULT_PORT = 5173;
|
|
1808
|
-
function resolvePort(arg) {
|
|
1809
|
-
const raw = arg ?? process.env.DEEPSPACE_PORT;
|
|
1810
|
-
if (!raw) return DEFAULT_PORT;
|
|
1811
|
-
const n = Number(raw);
|
|
1812
|
-
if (!Number.isInteger(n) || n < 1 || n > 65535) {
|
|
1813
|
-
console.error(`Invalid port: ${raw}. Must be an integer between 1 and 65535.`);
|
|
1814
|
-
process.exit(1);
|
|
1815
|
-
}
|
|
1816
|
-
return n;
|
|
1402
|
+
return n;
|
|
1817
1403
|
}
|
|
1818
1404
|
|
|
1819
1405
|
// src/cli/commands/dev.ts
|
|
@@ -1842,10 +1428,10 @@ var dev_default = defineCommand3({
|
|
|
1842
1428
|
},
|
|
1843
1429
|
async run({ args }) {
|
|
1844
1430
|
preflightNodeVersion("dev");
|
|
1845
|
-
const
|
|
1431
|
+
const wranglerEnv = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
1846
1432
|
const start = resolve4(args.dir ?? ".");
|
|
1847
|
-
const
|
|
1848
|
-
if (!
|
|
1433
|
+
const appDir = findAppDir(start);
|
|
1434
|
+
if (!appDir) {
|
|
1849
1435
|
console.error(`No wrangler.toml found at or above ${start}.`);
|
|
1850
1436
|
const children = findChildApps(start);
|
|
1851
1437
|
if (children.length > 0) {
|
|
@@ -1856,20 +1442,20 @@ var dev_default = defineCommand3({
|
|
|
1856
1442
|
}
|
|
1857
1443
|
process.exit(1);
|
|
1858
1444
|
}
|
|
1859
|
-
const junk = removeMacosJunk(
|
|
1445
|
+
const junk = removeMacosJunk(appDir);
|
|
1860
1446
|
if (junk > 0) console.log(`Removed ${junk} macOS metadata file(s) (._*, .DS_Store)`);
|
|
1861
|
-
const worktree = detectClaudeWorktree(
|
|
1447
|
+
const worktree = detectClaudeWorktree(appDir);
|
|
1862
1448
|
const explicitPort = Boolean(args.port);
|
|
1863
1449
|
let port = worktree && !explicitPort ? deriveWorktreePort(worktree.worktreeName) : resolvePort(args.port);
|
|
1864
1450
|
if (worktree) {
|
|
1865
1451
|
const upserted = upsertWorktreeLaunchConfig(
|
|
1866
1452
|
worktree.mainRepoRoot,
|
|
1867
1453
|
worktree.worktreeName,
|
|
1868
|
-
|
|
1454
|
+
appDir,
|
|
1869
1455
|
{
|
|
1870
1456
|
port,
|
|
1871
1457
|
probePort: !explicitPort,
|
|
1872
|
-
extraArgs: [...
|
|
1458
|
+
extraArgs: [...wranglerEnv ? ["--env", wranglerEnv] : []]
|
|
1873
1459
|
}
|
|
1874
1460
|
);
|
|
1875
1461
|
if (upserted) {
|
|
@@ -1888,8 +1474,8 @@ var dev_default = defineCommand3({
|
|
|
1888
1474
|
);
|
|
1889
1475
|
}
|
|
1890
1476
|
}
|
|
1891
|
-
writeLaunchConfigIfMissing(
|
|
1892
|
-
ensureInstallReady(
|
|
1477
|
+
writeLaunchConfigIfMissing(appDir, detectAppName(appDir) ?? basename(appDir), port);
|
|
1478
|
+
ensureInstallReady(appDir);
|
|
1893
1479
|
let token;
|
|
1894
1480
|
try {
|
|
1895
1481
|
token = await ensureToken();
|
|
@@ -1905,39 +1491,40 @@ var dev_default = defineCommand3({
|
|
|
1905
1491
|
process.exit(1);
|
|
1906
1492
|
}
|
|
1907
1493
|
console.log(`Logged in as ${payload.name ?? payload.email}`);
|
|
1908
|
-
if (
|
|
1494
|
+
if (wranglerEnv) console.log(`Wrangler env: ${wranglerEnv}`);
|
|
1909
1495
|
console.log(`Port: ${port}`);
|
|
1910
1496
|
let generatedSecretsCache;
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1497
|
+
const appIdForSecrets = readAppId(appDir, wranglerEnv);
|
|
1498
|
+
if (appIdForSecrets) {
|
|
1499
|
+
try {
|
|
1500
|
+
const refreshed = await refreshSecretsCache(DEPLOY_URL, token, appIdForSecrets, wranglerEnv);
|
|
1501
|
+
if (refreshed) {
|
|
1502
|
+
generatedSecretsCache = refreshed.rendered;
|
|
1503
|
+
console.log(refreshed.summary);
|
|
1504
|
+
}
|
|
1505
|
+
} catch (err) {
|
|
1506
|
+
console.error(
|
|
1507
|
+
`Failed to refresh app secrets: ${err instanceof Error ? err.message : String(err)}`
|
|
1917
1508
|
);
|
|
1509
|
+
process.exit(1);
|
|
1918
1510
|
}
|
|
1919
|
-
} catch (err) {
|
|
1920
|
-
console.error(
|
|
1921
|
-
`Failed to refresh linked secrets: ${err instanceof Error ? err.message : String(err)}`
|
|
1922
|
-
);
|
|
1923
|
-
process.exit(1);
|
|
1924
1511
|
}
|
|
1925
1512
|
const sharedDevVarsCache = generatedSecretsCache !== void 0;
|
|
1926
|
-
await writeDevVars(
|
|
1513
|
+
await writeDevVars(appDir, payload.sub, token, wranglerEnv, {
|
|
1927
1514
|
generatedSecretsCache,
|
|
1928
1515
|
sharedDevVarsCache
|
|
1929
1516
|
});
|
|
1930
1517
|
let wranglerConfig;
|
|
1931
1518
|
try {
|
|
1932
|
-
wranglerConfig = prepareWranglerEnvConfig(
|
|
1519
|
+
wranglerConfig = prepareWranglerEnvConfig(appDir, wranglerEnv, { sharedDevVarsCache });
|
|
1933
1520
|
} catch (err) {
|
|
1934
1521
|
console.error(err instanceof Error ? err.message : String(err));
|
|
1935
1522
|
process.exit(1);
|
|
1936
1523
|
}
|
|
1937
|
-
preflightWindowsWorkerd(
|
|
1524
|
+
preflightWindowsWorkerd(appDir);
|
|
1938
1525
|
console.log("Starting dev server...\n");
|
|
1939
1526
|
const vite = spawn("npx", ["vite", "--port", String(port), "--strictPort", "--host"], {
|
|
1940
|
-
cwd:
|
|
1527
|
+
cwd: appDir,
|
|
1941
1528
|
stdio: "inherit",
|
|
1942
1529
|
env: wranglerViteEnv(process.env, wranglerConfig, { DEEPSPACE_PORT: String(port) })
|
|
1943
1530
|
});
|
|
@@ -2110,18 +1697,18 @@ import { sync as spawnSync3 } from "cross-spawn";
|
|
|
2110
1697
|
// src/cli/lib/playwright.ts
|
|
2111
1698
|
import { execSync } from "child_process";
|
|
2112
1699
|
import { sync as spawnSync2 } from "cross-spawn";
|
|
2113
|
-
function ensurePlaywright(
|
|
1700
|
+
function ensurePlaywright(appDir) {
|
|
2114
1701
|
try {
|
|
2115
|
-
execSync("npx playwright --version", { cwd:
|
|
1702
|
+
execSync("npx playwright --version", { cwd: appDir, stdio: "pipe" });
|
|
2116
1703
|
} catch {
|
|
2117
1704
|
console.log("Installing Playwright...");
|
|
2118
|
-
execSync("npm install -D @playwright/test", { cwd:
|
|
1705
|
+
execSync("npm install -D @playwright/test", { cwd: appDir, stdio: "pipe" });
|
|
2119
1706
|
}
|
|
2120
1707
|
const isLinux = process.platform === "linux";
|
|
2121
1708
|
const isRoot = isLinux && typeof process.getuid === "function" && process.getuid() === 0;
|
|
2122
1709
|
const args = isRoot ? ["install", "--with-deps", "chromium"] : ["install", "chromium"];
|
|
2123
1710
|
const result = spawnSync2("npx", ["playwright", ...args], {
|
|
2124
|
-
cwd:
|
|
1711
|
+
cwd: appDir,
|
|
2125
1712
|
stdio: "inherit"
|
|
2126
1713
|
});
|
|
2127
1714
|
if (result.status !== 0) {
|
|
@@ -2158,10 +1745,10 @@ var test_default = defineCommand5({
|
|
|
2158
1745
|
async run({ args }) {
|
|
2159
1746
|
preflightNodeVersion("test");
|
|
2160
1747
|
const suite = args.suite ?? "default";
|
|
2161
|
-
const
|
|
1748
|
+
const wranglerEnv = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
2162
1749
|
const start = resolve5(".");
|
|
2163
|
-
const
|
|
2164
|
-
if (!
|
|
1750
|
+
const appDir = findAppDir(start);
|
|
1751
|
+
if (!appDir) {
|
|
2165
1752
|
console.error(`No wrangler.toml found at or above ${start}.`);
|
|
2166
1753
|
const children = findChildApps(start);
|
|
2167
1754
|
if (children.length > 0) {
|
|
@@ -2172,14 +1759,14 @@ var test_default = defineCommand5({
|
|
|
2172
1759
|
}
|
|
2173
1760
|
process.exit(1);
|
|
2174
1761
|
}
|
|
2175
|
-
const worktreePort = args.port ? null : resolveWorktreePort(
|
|
1762
|
+
const worktreePort = args.port ? null : resolveWorktreePort(appDir);
|
|
2176
1763
|
const port = worktreePort ?? resolvePort(args.port);
|
|
2177
1764
|
if (worktreePort && process.env.DEEPSPACE_PORT) {
|
|
2178
1765
|
console.log(
|
|
2179
1766
|
`Ignoring DEEPSPACE_PORT=${process.env.DEEPSPACE_PORT} inside a worktree \u2014 targeting per-worktree port ${port}. Pass --port to override.`
|
|
2180
1767
|
);
|
|
2181
1768
|
}
|
|
2182
|
-
ensureInstallReady(
|
|
1769
|
+
ensureInstallReady(appDir);
|
|
2183
1770
|
let token;
|
|
2184
1771
|
let ownerId;
|
|
2185
1772
|
try {
|
|
@@ -2191,72 +1778,73 @@ var test_default = defineCommand5({
|
|
|
2191
1778
|
process.exit(1);
|
|
2192
1779
|
}
|
|
2193
1780
|
let generatedSecretsCache;
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
1781
|
+
const appIdForSecrets = readAppId(appDir, wranglerEnv);
|
|
1782
|
+
if (appIdForSecrets) {
|
|
1783
|
+
try {
|
|
1784
|
+
const refreshed = await refreshSecretsCache(DEPLOY_URL2, token, appIdForSecrets, wranglerEnv);
|
|
1785
|
+
if (refreshed) {
|
|
1786
|
+
generatedSecretsCache = refreshed.rendered;
|
|
1787
|
+
console.log(refreshed.summary);
|
|
1788
|
+
}
|
|
1789
|
+
} catch (err) {
|
|
1790
|
+
console.error(
|
|
1791
|
+
`Failed to refresh app secrets: ${err instanceof Error ? err.message : String(err)}`
|
|
2200
1792
|
);
|
|
1793
|
+
process.exit(1);
|
|
2201
1794
|
}
|
|
2202
|
-
} catch (err) {
|
|
2203
|
-
console.error(
|
|
2204
|
-
`Failed to refresh linked secrets: ${err instanceof Error ? err.message : String(err)}`
|
|
2205
|
-
);
|
|
2206
|
-
process.exit(1);
|
|
2207
1795
|
}
|
|
2208
1796
|
const sharedDevVarsCache = generatedSecretsCache !== void 0;
|
|
2209
|
-
await writeDevVars(
|
|
1797
|
+
await writeDevVars(appDir, ownerId, token, wranglerEnv, {
|
|
2210
1798
|
generatedSecretsCache,
|
|
2211
1799
|
sharedDevVarsCache
|
|
2212
1800
|
});
|
|
2213
1801
|
if (suite !== "unit") {
|
|
2214
|
-
preflightWindowsWorkerd(
|
|
2215
|
-
ensurePlaywright(
|
|
1802
|
+
preflightWindowsWorkerd(appDir);
|
|
1803
|
+
ensurePlaywright(appDir);
|
|
2216
1804
|
}
|
|
2217
1805
|
let exitCode = 0;
|
|
2218
1806
|
switch (suite) {
|
|
2219
1807
|
case "smoke":
|
|
2220
1808
|
exitCode = runPlaywright(
|
|
2221
|
-
|
|
1809
|
+
appDir,
|
|
2222
1810
|
["tests/smoke.spec.ts"],
|
|
2223
1811
|
port,
|
|
2224
|
-
|
|
1812
|
+
wranglerEnv,
|
|
2225
1813
|
sharedDevVarsCache
|
|
2226
1814
|
);
|
|
2227
1815
|
break;
|
|
2228
1816
|
case "api":
|
|
2229
1817
|
exitCode = runPlaywright(
|
|
2230
|
-
|
|
1818
|
+
appDir,
|
|
2231
1819
|
["tests/api.spec.ts"],
|
|
2232
1820
|
port,
|
|
2233
|
-
|
|
1821
|
+
wranglerEnv,
|
|
2234
1822
|
sharedDevVarsCache
|
|
2235
1823
|
);
|
|
2236
1824
|
break;
|
|
2237
1825
|
case "e2e":
|
|
2238
|
-
exitCode = runPlaywright(
|
|
1826
|
+
exitCode = runPlaywright(appDir, [], port, wranglerEnv, sharedDevVarsCache);
|
|
2239
1827
|
break;
|
|
2240
1828
|
case "unit":
|
|
2241
|
-
exitCode = runVitest(
|
|
1829
|
+
exitCode = runVitest(appDir);
|
|
2242
1830
|
break;
|
|
2243
1831
|
case "all":
|
|
2244
|
-
exitCode = runVitest(
|
|
1832
|
+
exitCode = runVitest(appDir);
|
|
2245
1833
|
if (exitCode === 0)
|
|
2246
|
-
exitCode = runPlaywright(
|
|
1834
|
+
exitCode = runPlaywright(appDir, [], port, wranglerEnv, sharedDevVarsCache);
|
|
2247
1835
|
break;
|
|
2248
1836
|
case "default":
|
|
2249
1837
|
exitCode = runPlaywright(
|
|
2250
|
-
|
|
1838
|
+
appDir,
|
|
2251
1839
|
["tests/smoke.spec.ts", "tests/api.spec.ts"],
|
|
2252
1840
|
port,
|
|
2253
|
-
|
|
1841
|
+
wranglerEnv,
|
|
2254
1842
|
sharedDevVarsCache
|
|
2255
1843
|
);
|
|
2256
1844
|
break;
|
|
2257
1845
|
default:
|
|
2258
1846
|
if (suite.endsWith(".spec.ts")) {
|
|
2259
|
-
exitCode = runPlaywright(
|
|
1847
|
+
exitCode = runPlaywright(appDir, [suite], port, wranglerEnv, sharedDevVarsCache);
|
|
2260
1848
|
} else {
|
|
2261
1849
|
console.error(`Unknown test suite: ${suite}`);
|
|
2262
1850
|
console.error("Available: smoke, api, e2e, unit, all");
|
|
@@ -2266,10 +1854,10 @@ var test_default = defineCommand5({
|
|
|
2266
1854
|
process.exit(exitCode);
|
|
2267
1855
|
}
|
|
2268
1856
|
});
|
|
2269
|
-
function runPlaywright(
|
|
1857
|
+
function runPlaywright(appDir, testFiles, port, wranglerEnv, sharedDevVarsCache = false) {
|
|
2270
1858
|
let wranglerConfig;
|
|
2271
1859
|
try {
|
|
2272
|
-
wranglerConfig = prepareWranglerEnvConfig(
|
|
1860
|
+
wranglerConfig = prepareWranglerEnvConfig(appDir, wranglerEnv, { sharedDevVarsCache });
|
|
2273
1861
|
} catch (err) {
|
|
2274
1862
|
console.error(err instanceof Error ? err.message : String(err));
|
|
2275
1863
|
return 1;
|
|
@@ -2279,7 +1867,7 @@ function runPlaywright(appDir2, testFiles, port, wranglerEnv2, sharedDevVarsCach
|
|
|
2279
1867
|
"npx",
|
|
2280
1868
|
["playwright", "test", "--config", "tests/playwright.config.ts", ...testFiles],
|
|
2281
1869
|
{
|
|
2282
|
-
cwd:
|
|
1870
|
+
cwd: appDir,
|
|
2283
1871
|
stdio: "inherit",
|
|
2284
1872
|
env: wranglerViteEnv(process.env, wranglerConfig, { DEEPSPACE_PORT: String(port) })
|
|
2285
1873
|
}
|
|
@@ -2289,9 +1877,9 @@ function runPlaywright(appDir2, testFiles, port, wranglerEnv2, sharedDevVarsCach
|
|
|
2289
1877
|
wranglerConfig.cleanup();
|
|
2290
1878
|
}
|
|
2291
1879
|
}
|
|
2292
|
-
function runVitest(
|
|
1880
|
+
function runVitest(appDir) {
|
|
2293
1881
|
const result = spawnSync3("npx", ["vitest", "run", "--passWithNoTests"], {
|
|
2294
|
-
cwd:
|
|
1882
|
+
cwd: appDir,
|
|
2295
1883
|
stdio: "inherit"
|
|
2296
1884
|
});
|
|
2297
1885
|
return result.status ?? 1;
|
|
@@ -2329,15 +1917,15 @@ var screenshot_default = defineCommand6({
|
|
|
2329
1917
|
}
|
|
2330
1918
|
},
|
|
2331
1919
|
run({ args }) {
|
|
2332
|
-
const
|
|
2333
|
-
ensurePlaywright(
|
|
1920
|
+
const appDir = resolve6(".");
|
|
1921
|
+
ensurePlaywright(appDir);
|
|
2334
1922
|
const playwrightArgs = ["playwright", "screenshot", args.url, args.output];
|
|
2335
1923
|
if (args["full-page"]) playwrightArgs.push("--full-page");
|
|
2336
1924
|
if (args["wait-for-timeout"]) {
|
|
2337
1925
|
playwrightArgs.push("--wait-for-timeout", String(args["wait-for-timeout"]));
|
|
2338
1926
|
}
|
|
2339
1927
|
const result = spawnSync4("npx", playwrightArgs, {
|
|
2340
|
-
cwd:
|
|
1928
|
+
cwd: appDir,
|
|
2341
1929
|
stdio: "inherit"
|
|
2342
1930
|
});
|
|
2343
1931
|
process.exit(result.status ?? 1);
|
|
@@ -2631,18 +2219,222 @@ var test_accounts_default = defineCommand7({
|
|
|
2631
2219
|
delete: del,
|
|
2632
2220
|
clear
|
|
2633
2221
|
}
|
|
2634
|
-
});
|
|
2222
|
+
});
|
|
2223
|
+
|
|
2224
|
+
// src/cli/commands/deploy.ts
|
|
2225
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
2226
|
+
import { execSync as execSync2 } from "child_process";
|
|
2227
|
+
import { randomUUID } from "crypto";
|
|
2228
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync3, existsSync as existsSync10, writeFileSync as writeFileSync9 } from "fs";
|
|
2229
|
+
import { join as join11, resolve as resolve7, dirname as dirname2 } from "path";
|
|
2230
|
+
import { pathToFileURL } from "url";
|
|
2231
|
+
import * as p4 from "@clack/prompts";
|
|
2232
|
+
|
|
2233
|
+
// src/server/rooms/binding-manifest.ts
|
|
2234
|
+
var AUTO_PROVISION_SENTINEL = "auto";
|
|
2235
|
+
var VECTORIZE_METRICS = /* @__PURE__ */ new Set(["cosine", "euclidean", "dot-product"]);
|
|
2236
|
+
var ALLOWED_BINDING_TYPES = /* @__PURE__ */ new Set([
|
|
2237
|
+
"vectorize",
|
|
2238
|
+
"ai",
|
|
2239
|
+
"r2_bucket",
|
|
2240
|
+
"kv_namespace",
|
|
2241
|
+
"d1",
|
|
2242
|
+
"queue",
|
|
2243
|
+
"browser_rendering",
|
|
2244
|
+
"analytics_engine",
|
|
2245
|
+
"hyperdrive"
|
|
2246
|
+
]);
|
|
2247
|
+
var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
|
|
2248
|
+
"ASSETS",
|
|
2249
|
+
"PLATFORM_WORKER",
|
|
2250
|
+
"API_WORKER",
|
|
2251
|
+
"APP_NAME",
|
|
2252
|
+
"OWNER_USER_ID",
|
|
2253
|
+
"AUTH_JWT_PUBLIC_KEY",
|
|
2254
|
+
"AUTH_JWT_ISSUER",
|
|
2255
|
+
"AUTH_WORKER_URL",
|
|
2256
|
+
"APP_IDENTITY_TOKEN",
|
|
2257
|
+
"APP_OWNER_JWT",
|
|
2258
|
+
"USAGE_EVENTS"
|
|
2259
|
+
]);
|
|
2260
|
+
function validateBindingManifest(manifest) {
|
|
2261
|
+
if (!Array.isArray(manifest)) {
|
|
2262
|
+
return { valid: false, errors: [{ reason: "Manifest must be an array" }] };
|
|
2263
|
+
}
|
|
2264
|
+
const errors = [];
|
|
2265
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
2266
|
+
for (const entry of manifest) {
|
|
2267
|
+
if (!entry || typeof entry !== "object") {
|
|
2268
|
+
errors.push({ reason: `Entry must be an object (got ${entry === null ? "null" : typeof entry})` });
|
|
2269
|
+
continue;
|
|
2270
|
+
}
|
|
2271
|
+
const e = entry;
|
|
2272
|
+
const type = e.type;
|
|
2273
|
+
const name = e.name;
|
|
2274
|
+
if (typeof type !== "string" || !ALLOWED_BINDING_TYPES.has(type)) {
|
|
2275
|
+
errors.push({
|
|
2276
|
+
reason: `Disallowed binding type: ${typeof type === "string" ? type : `(${typeof type})`}. Allowed: ${[...ALLOWED_BINDING_TYPES].sort().join(", ")}`
|
|
2277
|
+
});
|
|
2278
|
+
continue;
|
|
2279
|
+
}
|
|
2280
|
+
if (typeof name !== "string" || !name) {
|
|
2281
|
+
errors.push({ reason: `Binding of type "${type}" missing 'name' (string)` });
|
|
2282
|
+
continue;
|
|
2283
|
+
}
|
|
2284
|
+
if (RESERVED_BINDING_NAMES.has(name)) {
|
|
2285
|
+
errors.push({ reason: `Binding name "${name}" is reserved by the SDK` });
|
|
2286
|
+
continue;
|
|
2287
|
+
}
|
|
2288
|
+
if (seenNames.has(name)) {
|
|
2289
|
+
errors.push({ reason: `Duplicate binding name "${name}"` });
|
|
2290
|
+
continue;
|
|
2291
|
+
}
|
|
2292
|
+
seenNames.add(name);
|
|
2293
|
+
const binding = entry;
|
|
2294
|
+
const fieldErr = requiredFieldError(binding);
|
|
2295
|
+
if (fieldErr) errors.push({ binding, reason: fieldErr });
|
|
2296
|
+
}
|
|
2297
|
+
if (errors.length) return { valid: false, errors };
|
|
2298
|
+
return { valid: true, bindings: manifest };
|
|
2299
|
+
}
|
|
2300
|
+
function requiredFieldError(b) {
|
|
2301
|
+
switch (b.type) {
|
|
2302
|
+
case "vectorize": {
|
|
2303
|
+
if (!b.index_name) return `vectorize binding "${b.name}" missing index_name (or "auto")`;
|
|
2304
|
+
if (b.index_name === AUTO_PROVISION_SENTINEL) {
|
|
2305
|
+
if (typeof b.dimensions !== "number" || b.dimensions <= 0) {
|
|
2306
|
+
return `vectorize binding "${b.name}" with index_name="auto" requires "dimensions" (positive number)`;
|
|
2307
|
+
}
|
|
2308
|
+
if (!b.metric || !VECTORIZE_METRICS.has(b.metric)) {
|
|
2309
|
+
return `vectorize binding "${b.name}" with index_name="auto" requires "metric" (one of: ${[...VECTORIZE_METRICS].join(", ")})`;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
return null;
|
|
2313
|
+
}
|
|
2314
|
+
case "r2_bucket":
|
|
2315
|
+
return b.bucket_name ? null : `r2_bucket binding "${b.name}" missing bucket_name (or "auto")`;
|
|
2316
|
+
case "kv_namespace": {
|
|
2317
|
+
if (!b.namespace_id) return `kv_namespace binding "${b.name}" missing namespace_id (or "auto")`;
|
|
2318
|
+
if (b.namespace_id === AUTO_PROVISION_SENTINEL && !b.title) {
|
|
2319
|
+
return `kv_namespace binding "${b.name}" with namespace_id="auto" requires "title"`;
|
|
2320
|
+
}
|
|
2321
|
+
return null;
|
|
2322
|
+
}
|
|
2323
|
+
case "d1": {
|
|
2324
|
+
if (!b.id) return `d1 binding "${b.name}" missing id (or "auto")`;
|
|
2325
|
+
if (b.id === AUTO_PROVISION_SENTINEL && !b.database_name) {
|
|
2326
|
+
return `d1 binding "${b.name}" with id="auto" requires "database_name"`;
|
|
2327
|
+
}
|
|
2328
|
+
return null;
|
|
2329
|
+
}
|
|
2330
|
+
case "queue":
|
|
2331
|
+
return b.queue_name ? null : `queue binding "${b.name}" missing queue_name (or "auto")`;
|
|
2332
|
+
case "hyperdrive":
|
|
2333
|
+
if (!b.id) return `hyperdrive binding "${b.name}" missing id`;
|
|
2334
|
+
if (b.id === AUTO_PROVISION_SENTINEL) {
|
|
2335
|
+
return `hyperdrive binding "${b.name}": auto-provisioning not yet supported (provide a real id)`;
|
|
2336
|
+
}
|
|
2337
|
+
return null;
|
|
2338
|
+
case "ai":
|
|
2339
|
+
case "browser_rendering":
|
|
2340
|
+
case "analytics_engine":
|
|
2341
|
+
return null;
|
|
2342
|
+
// no required fields beyond `name`
|
|
2343
|
+
default: {
|
|
2344
|
+
const _exhaustive = b;
|
|
2345
|
+
void _exhaustive;
|
|
2346
|
+
return null;
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
function bindingManifestFromOutputConfig(outputConfig) {
|
|
2351
|
+
const out = [];
|
|
2352
|
+
const aiBinding = pluckString(outputConfig.ai, "binding");
|
|
2353
|
+
if (aiBinding) out.push({ type: "ai", name: aiBinding });
|
|
2354
|
+
for (const v of asObjectArray(outputConfig.vectorize)) {
|
|
2355
|
+
const name = pluckString(v, "binding");
|
|
2356
|
+
const index_name = pluckString(v, "index_name");
|
|
2357
|
+
if (!name || !index_name) continue;
|
|
2358
|
+
const dimensions = pluckNumber(v, "dimensions");
|
|
2359
|
+
const metric = pluckString(v, "metric");
|
|
2360
|
+
out.push({
|
|
2361
|
+
type: "vectorize",
|
|
2362
|
+
name,
|
|
2363
|
+
index_name,
|
|
2364
|
+
...dimensions != null && { dimensions },
|
|
2365
|
+
...metric && (metric === "cosine" || metric === "euclidean" || metric === "dot-product") && { metric }
|
|
2366
|
+
});
|
|
2367
|
+
}
|
|
2368
|
+
for (const r of asObjectArray(outputConfig.r2_buckets)) {
|
|
2369
|
+
const name = pluckString(r, "binding");
|
|
2370
|
+
const bucket_name = pluckString(r, "bucket_name");
|
|
2371
|
+
if (name && bucket_name) out.push({ type: "r2_bucket", name, bucket_name });
|
|
2372
|
+
}
|
|
2373
|
+
for (const k of asObjectArray(outputConfig.kv_namespaces)) {
|
|
2374
|
+
const name = pluckString(k, "binding");
|
|
2375
|
+
const namespace_id = pluckString(k, "id");
|
|
2376
|
+
if (!name || !namespace_id) continue;
|
|
2377
|
+
const title = pluckString(k, "title");
|
|
2378
|
+
out.push({
|
|
2379
|
+
type: "kv_namespace",
|
|
2380
|
+
name,
|
|
2381
|
+
namespace_id,
|
|
2382
|
+
...title && { title }
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
for (const d of asObjectArray(outputConfig.d1_databases)) {
|
|
2386
|
+
const name = pluckString(d, "binding");
|
|
2387
|
+
const id = pluckString(d, "database_id");
|
|
2388
|
+
if (!name || !id) continue;
|
|
2389
|
+
const database_name = pluckString(d, "database_name");
|
|
2390
|
+
out.push({
|
|
2391
|
+
type: "d1",
|
|
2392
|
+
name,
|
|
2393
|
+
id,
|
|
2394
|
+
...database_name && { database_name }
|
|
2395
|
+
});
|
|
2396
|
+
}
|
|
2397
|
+
const queues = isObject(outputConfig.queues) ? outputConfig.queues : null;
|
|
2398
|
+
for (const q of asObjectArray(queues?.producers)) {
|
|
2399
|
+
const name = pluckString(q, "binding");
|
|
2400
|
+
const queue_name = pluckString(q, "queue");
|
|
2401
|
+
if (name && queue_name) out.push({ type: "queue", name, queue_name });
|
|
2402
|
+
}
|
|
2403
|
+
const browserBinding = pluckString(outputConfig.browser, "binding");
|
|
2404
|
+
if (browserBinding) out.push({ type: "browser_rendering", name: browserBinding });
|
|
2405
|
+
for (const a of asObjectArray(outputConfig.analytics_engine_datasets)) {
|
|
2406
|
+
const name = pluckString(a, "binding");
|
|
2407
|
+
const dataset = pluckString(a, "dataset");
|
|
2408
|
+
if (name) out.push({ type: "analytics_engine", name, dataset: dataset ?? void 0 });
|
|
2409
|
+
}
|
|
2410
|
+
for (const h of asObjectArray(outputConfig.hyperdrive)) {
|
|
2411
|
+
const name = pluckString(h, "binding");
|
|
2412
|
+
const id = pluckString(h, "id");
|
|
2413
|
+
if (name && id) out.push({ type: "hyperdrive", name, id });
|
|
2414
|
+
}
|
|
2415
|
+
return out;
|
|
2416
|
+
}
|
|
2417
|
+
function isObject(v) {
|
|
2418
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
2419
|
+
}
|
|
2420
|
+
function pluckString(v, key) {
|
|
2421
|
+
if (!isObject(v)) return null;
|
|
2422
|
+
const val = v[key];
|
|
2423
|
+
return typeof val === "string" && val.length > 0 ? val : null;
|
|
2424
|
+
}
|
|
2425
|
+
function pluckNumber(v, key) {
|
|
2426
|
+
if (!isObject(v)) return null;
|
|
2427
|
+
const val = v[key];
|
|
2428
|
+
return typeof val === "number" && Number.isFinite(val) ? val : null;
|
|
2429
|
+
}
|
|
2430
|
+
function asObjectArray(v) {
|
|
2431
|
+
if (!Array.isArray(v)) return [];
|
|
2432
|
+
return v.filter(isObject);
|
|
2433
|
+
}
|
|
2635
2434
|
|
|
2636
2435
|
// src/cli/commands/deploy.ts
|
|
2637
|
-
import { defineCommand as defineCommand8 } from "citty";
|
|
2638
|
-
import { execSync as execSync2 } from "child_process";
|
|
2639
|
-
import { randomUUID } from "crypto";
|
|
2640
|
-
import { readFileSync as readFileSync9, readdirSync as readdirSync3, existsSync as existsSync10 } from "fs";
|
|
2641
|
-
import { join as join11, resolve as resolve7, dirname as dirname2 } from "path";
|
|
2642
|
-
import { pathToFileURL } from "url";
|
|
2643
|
-
import * as p4 from "@clack/prompts";
|
|
2644
2436
|
var DEPLOY_URL3 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
2645
|
-
var
|
|
2437
|
+
var API_URL2 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
2646
2438
|
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.";
|
|
2647
2439
|
var RESERVED_RUN_WORKER_FIRST = /* @__PURE__ */ new Set([
|
|
2648
2440
|
"/api/*",
|
|
@@ -2670,8 +2462,8 @@ function extractRunWorkerFirst(cfg) {
|
|
|
2670
2462
|
function errMessage(err) {
|
|
2671
2463
|
return err instanceof Error ? err.message : String(err);
|
|
2672
2464
|
}
|
|
2673
|
-
function isLikelyCloudflareDeployFailure(
|
|
2674
|
-
if (
|
|
2465
|
+
function isLikelyCloudflareDeployFailure(status3, error) {
|
|
2466
|
+
if (status3 >= 500) return true;
|
|
2675
2467
|
if (!error) return false;
|
|
2676
2468
|
return [
|
|
2677
2469
|
"Upload session failed",
|
|
@@ -2681,9 +2473,9 @@ function isLikelyCloudflareDeployFailure(status2, error) {
|
|
|
2681
2473
|
"Internal Server Error"
|
|
2682
2474
|
].some((needle) => error.includes(needle));
|
|
2683
2475
|
}
|
|
2684
|
-
function formatDeployWorkerError(
|
|
2685
|
-
const detail = error ?? `Deployment error (${
|
|
2686
|
-
if (!isLikelyCloudflareDeployFailure(
|
|
2476
|
+
function formatDeployWorkerError(status3, error) {
|
|
2477
|
+
const detail = error ?? `Deployment error (${status3})`;
|
|
2478
|
+
if (!isLikelyCloudflareDeployFailure(status3, error)) return detail;
|
|
2687
2479
|
return `${CLOUDFLARE_DEPLOY_ERROR_HINT}
|
|
2688
2480
|
|
|
2689
2481
|
Underlying error: ${detail}`;
|
|
@@ -2735,10 +2527,10 @@ async function postWithRetry(url, makeInit, {
|
|
|
2735
2527
|
if (lastRes) return lastRes;
|
|
2736
2528
|
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
2737
2529
|
}
|
|
2738
|
-
async function abortStagedUpload(
|
|
2530
|
+
async function abortStagedUpload(appId, uploadId, totalGroups, token) {
|
|
2739
2531
|
try {
|
|
2740
2532
|
await fetch(
|
|
2741
|
-
`${DEPLOY_URL3}/api/deploy/${
|
|
2533
|
+
`${DEPLOY_URL3}/api/deploy/${appId}/assets?uploadId=${uploadId}&totalGroups=${totalGroups}`,
|
|
2742
2534
|
{ method: "DELETE", headers: { Authorization: `Bearer ${token}` } }
|
|
2743
2535
|
);
|
|
2744
2536
|
} catch {
|
|
@@ -2759,19 +2551,24 @@ var deploy_default = defineCommand8({
|
|
|
2759
2551
|
type: "string",
|
|
2760
2552
|
description: "wrangler.toml [env.<name>] block to deploy (e.g. --env staging). Omit to deploy the top-level config.",
|
|
2761
2553
|
required: false
|
|
2554
|
+
},
|
|
2555
|
+
rename: {
|
|
2556
|
+
type: "boolean",
|
|
2557
|
+
description: "Confirm that a changed wrangler `name` renames this app (its URL moves; data, secrets, and collaborators travel). Without this flag an interactive prompt asks.",
|
|
2558
|
+
default: false
|
|
2762
2559
|
}
|
|
2763
2560
|
},
|
|
2764
2561
|
async run({ args }) {
|
|
2765
2562
|
preflightNodeVersion("deploy");
|
|
2766
|
-
const
|
|
2563
|
+
const appDir = resolve7(args.dir ?? ".");
|
|
2767
2564
|
const envName = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
2768
|
-
if (!hasWranglerConfig(
|
|
2565
|
+
if (!hasWranglerConfig(appDir)) {
|
|
2769
2566
|
console.error("No wrangler.toml found. Are you in a DeepSpace app directory?");
|
|
2770
2567
|
process.exit(1);
|
|
2771
2568
|
}
|
|
2772
|
-
ensureInstallReady(
|
|
2569
|
+
ensureInstallReady(appDir);
|
|
2773
2570
|
p4.intro("Deploying DeepSpace app");
|
|
2774
|
-
const wranglerConfig = readWranglerConfig(
|
|
2571
|
+
const wranglerConfig = readWranglerConfig(appDir);
|
|
2775
2572
|
const nameRes = resolveAppNameForEnv(wranglerConfig, envName);
|
|
2776
2573
|
if (!nameRes.ok) {
|
|
2777
2574
|
p4.cancel(nameRes.reason);
|
|
@@ -2786,6 +2583,7 @@ var deploy_default = defineCommand8({
|
|
|
2786
2583
|
process.exit(1);
|
|
2787
2584
|
}
|
|
2788
2585
|
const appName = nameRes.name;
|
|
2586
|
+
let appId = readAppId(appDir, envName);
|
|
2789
2587
|
p4.log.info(envName ? `App: ${appName} (env: ${envName})` : `App: ${appName}`);
|
|
2790
2588
|
let token;
|
|
2791
2589
|
let ownerId;
|
|
@@ -2796,41 +2594,42 @@ var deploy_default = defineCommand8({
|
|
|
2796
2594
|
p4.cancel(errMessage(err));
|
|
2797
2595
|
process.exit(1);
|
|
2798
2596
|
}
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2597
|
+
if (!appId) {
|
|
2598
|
+
const adopted = await resolveOwnedAppId(DEPLOY_URL3, token, appName);
|
|
2599
|
+
appId = adopted ?? mintAppId();
|
|
2600
|
+
writeAppId(appDir, appId, { wranglerEnv: envName });
|
|
2601
|
+
p4.log.info(
|
|
2602
|
+
adopted ? `Adopted existing app id ${appId} for ${appName} \u2014 commit wrangler.toml.` : `Minted app id ${appId} \u2014 commit wrangler.toml.`
|
|
2603
|
+
);
|
|
2805
2604
|
}
|
|
2605
|
+
p4.log.info(`Id: ${appId}`);
|
|
2806
2606
|
let linkedSecrets = null;
|
|
2807
2607
|
try {
|
|
2808
|
-
|
|
2809
|
-
if (
|
|
2810
|
-
|
|
2811
|
-
await writeDevVars(
|
|
2812
|
-
|
|
2608
|
+
const refreshed = await refreshSecretsCache(DEPLOY_URL3, token, appId, envName);
|
|
2609
|
+
if (refreshed) {
|
|
2610
|
+
linkedSecrets = refreshed.pulled;
|
|
2611
|
+
await writeDevVars(appDir, ownerId, token, envName, {
|
|
2612
|
+
appId,
|
|
2613
|
+
generatedSecretsCache: refreshed.rendered,
|
|
2813
2614
|
sharedDevVarsCache: true
|
|
2814
2615
|
});
|
|
2815
|
-
p4.log.info(
|
|
2816
|
-
`Secrets cache: ${linkedSecrets.projectName}/${linkedSecrets.configName} (${Object.keys(linkedSecrets.values).length} secret${Object.keys(linkedSecrets.values).length === 1 ? "" : "s"})`
|
|
2817
|
-
);
|
|
2616
|
+
p4.log.info(refreshed.summary);
|
|
2818
2617
|
}
|
|
2819
2618
|
} catch (err) {
|
|
2820
|
-
p4.cancel(`Failed to refresh
|
|
2619
|
+
p4.cancel(`Failed to refresh app secrets before deploy: ${errMessage(err)}`);
|
|
2821
2620
|
process.exit(1);
|
|
2822
2621
|
}
|
|
2823
|
-
const junk = removeMacosJunk(
|
|
2622
|
+
const junk = removeMacosJunk(appDir);
|
|
2824
2623
|
if (junk > 0) p4.log.info(`Removed ${junk} macOS metadata file(s) (._*, .DS_Store)`);
|
|
2825
2624
|
const s = createSpinner();
|
|
2826
2625
|
s.start("Building...");
|
|
2827
2626
|
let preparedWranglerConfig;
|
|
2828
2627
|
try {
|
|
2829
|
-
preparedWranglerConfig = prepareWranglerEnvConfig(
|
|
2830
|
-
sharedDevVarsCache:
|
|
2628
|
+
preparedWranglerConfig = prepareWranglerEnvConfig(appDir, envName, {
|
|
2629
|
+
sharedDevVarsCache: linkedSecrets !== null
|
|
2831
2630
|
});
|
|
2832
2631
|
execSync2("npx vite build", {
|
|
2833
|
-
cwd:
|
|
2632
|
+
cwd: appDir,
|
|
2834
2633
|
stdio: "pipe",
|
|
2835
2634
|
env: wranglerViteEnv(process.env, preparedWranglerConfig)
|
|
2836
2635
|
});
|
|
@@ -2844,7 +2643,7 @@ var deploy_default = defineCommand8({
|
|
|
2844
2643
|
preparedWranglerConfig?.cleanup();
|
|
2845
2644
|
}
|
|
2846
2645
|
s.stop("Built");
|
|
2847
|
-
const deployConfigPath = join11(
|
|
2646
|
+
const deployConfigPath = join11(appDir, ".wrangler", "deploy", "config.json");
|
|
2848
2647
|
if (!existsSync10(deployConfigPath)) {
|
|
2849
2648
|
p4.cancel("Build output config not found at .wrangler/deploy/config.json");
|
|
2850
2649
|
process.exit(1);
|
|
@@ -2906,15 +2705,13 @@ var deploy_default = defineCommand8({
|
|
|
2906
2705
|
`Custom bindings: ${customBindings.map((b) => `${b.name} (${b.type})`).join(", ")}`
|
|
2907
2706
|
);
|
|
2908
2707
|
}
|
|
2909
|
-
const devVarsPath = devVarsPathFor(
|
|
2910
|
-
sharedDevVarsCache:
|
|
2708
|
+
const devVarsPath = devVarsPathFor(appDir, envName, {
|
|
2709
|
+
sharedDevVarsCache: linkedSecrets !== null
|
|
2911
2710
|
});
|
|
2912
2711
|
let userSecrets = {};
|
|
2913
2712
|
if (existsSync10(devVarsPath)) {
|
|
2914
2713
|
const rawDevVars = readFileSync9(devVarsPath, "utf-8");
|
|
2915
|
-
const customSection = extractCustomDevVars(
|
|
2916
|
-
linkedSecrets ? rawDevVars : stripGeneratedSecretsCache2(rawDevVars)
|
|
2917
|
-
);
|
|
2714
|
+
const customSection = extractCustomDevVars(rawDevVars);
|
|
2918
2715
|
if (customSection) {
|
|
2919
2716
|
try {
|
|
2920
2717
|
userSecrets = parseDevVars(customSection);
|
|
@@ -2945,10 +2742,8 @@ var deploy_default = defineCommand8({
|
|
|
2945
2742
|
}
|
|
2946
2743
|
if (userSecretNames.length) {
|
|
2947
2744
|
p4.log.info(`App secrets (from .dev.vars): ${userSecretNames.join(", ")}`);
|
|
2948
|
-
if (!
|
|
2949
|
-
p4.log.info(
|
|
2950
|
-
"Legacy pass-through; move these to managed secrets with `npx deepspace secrets setup`."
|
|
2951
|
-
);
|
|
2745
|
+
if (!linkedSecrets) {
|
|
2746
|
+
p4.log.info("Not in the app store yet \u2014 migrating after this deploy.");
|
|
2952
2747
|
}
|
|
2953
2748
|
}
|
|
2954
2749
|
s.start(`Deploying to ${appName}.app.space...`);
|
|
@@ -2970,7 +2765,7 @@ var deploy_default = defineCommand8({
|
|
|
2970
2765
|
const groupJson = JSON.stringify(assetGroups[i]);
|
|
2971
2766
|
s.message(`Uploading assets \u2014 group ${i + 1}/${totalGroups}...`);
|
|
2972
2767
|
const groupRes = await postWithRetry(
|
|
2973
|
-
`${DEPLOY_URL3}/api/deploy/${
|
|
2768
|
+
`${DEPLOY_URL3}/api/deploy/${appId}/assets?uploadId=${uploadId}&groupIndex=${i}&totalGroups=${totalGroups}`,
|
|
2974
2769
|
() => ({
|
|
2975
2770
|
method: "POST",
|
|
2976
2771
|
headers: {
|
|
@@ -2982,7 +2777,7 @@ var deploy_default = defineCommand8({
|
|
|
2982
2777
|
);
|
|
2983
2778
|
if (!groupRes.ok) {
|
|
2984
2779
|
const errBody = await groupRes.json().catch(() => ({}));
|
|
2985
|
-
await abortStagedUpload(
|
|
2780
|
+
await abortStagedUpload(appId, uploadId, totalGroups, token);
|
|
2986
2781
|
s.stop("Deploy failed");
|
|
2987
2782
|
p4.cancel(
|
|
2988
2783
|
`Asset upload failed on group ${i + 1}/${totalGroups}: ${errBody.error ?? `HTTP ${groupRes.status}`}`
|
|
@@ -2991,13 +2786,21 @@ var deploy_default = defineCommand8({
|
|
|
2991
2786
|
}
|
|
2992
2787
|
}
|
|
2993
2788
|
} catch (err) {
|
|
2994
|
-
await abortStagedUpload(
|
|
2789
|
+
await abortStagedUpload(appId, uploadId, totalGroups, token);
|
|
2995
2790
|
s.stop("Deploy failed");
|
|
2996
2791
|
p4.cancel(`Asset upload failed (network): ${errMessage(err)}`);
|
|
2997
2792
|
process.exit(1);
|
|
2998
2793
|
}
|
|
2999
2794
|
s.message(`Deploying to ${appName}.app.space...`);
|
|
3000
2795
|
}
|
|
2796
|
+
const commitUrl = `${DEPLOY_URL3}/api/deploy/${appId}`;
|
|
2797
|
+
let confirmRename = args.rename === true;
|
|
2798
|
+
const bailDeploy = async (message, stopLabel = "Deploy failed") => {
|
|
2799
|
+
if (uploadId) await abortStagedUpload(appId, uploadId, totalGroups, token);
|
|
2800
|
+
if (stopLabel !== null) s.stop(stopLabel);
|
|
2801
|
+
p4.cancel(message);
|
|
2802
|
+
process.exit(1);
|
|
2803
|
+
};
|
|
3001
2804
|
const makeForm = () => {
|
|
3002
2805
|
const form = new FormData();
|
|
3003
2806
|
form.append("worker", new Blob([workerJs], { type: "application/javascript" }), "worker.js");
|
|
@@ -3019,38 +2822,45 @@ var deploy_default = defineCommand8({
|
|
|
3019
2822
|
if (extraRoutes.length) {
|
|
3020
2823
|
form.append("extraRunWorkerFirst", JSON.stringify(extraRoutes));
|
|
3021
2824
|
}
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
}
|
|
2825
|
+
form.append("name", appName);
|
|
2826
|
+
if (confirmRename) form.append("confirmRename", "true");
|
|
3025
2827
|
return form;
|
|
3026
2828
|
};
|
|
3027
|
-
const
|
|
2829
|
+
const postCommit = async () => postWithRetry(
|
|
2830
|
+
commitUrl,
|
|
2831
|
+
() => ({
|
|
2832
|
+
method: "POST",
|
|
2833
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
2834
|
+
body: makeForm()
|
|
2835
|
+
}),
|
|
2836
|
+
// Commit runs a real deploy — don't retry its 5xx (could double-deploy);
|
|
2837
|
+
// only a thrown fetch (dropped connection) is retried, and the staged
|
|
2838
|
+
// groups survive on the worker until this request succeeds.
|
|
2839
|
+
{ retryServerErrors: false }
|
|
2840
|
+
);
|
|
3028
2841
|
let res;
|
|
3029
2842
|
try {
|
|
3030
|
-
res = await
|
|
3031
|
-
commitUrl,
|
|
3032
|
-
() => ({
|
|
3033
|
-
method: "POST",
|
|
3034
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
3035
|
-
body: makeForm()
|
|
3036
|
-
}),
|
|
3037
|
-
// Commit runs a real deploy — don't retry its 5xx (could double-deploy);
|
|
3038
|
-
// only a thrown fetch (dropped connection) is retried, and the staged
|
|
3039
|
-
// groups survive on the worker until this request succeeds.
|
|
3040
|
-
{ retryServerErrors: false }
|
|
3041
|
-
);
|
|
2843
|
+
res = await postCommit();
|
|
3042
2844
|
} catch (err) {
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
2845
|
+
await bailDeploy(`Deploy request failed: ${errMessage(err)}`);
|
|
2846
|
+
throw err;
|
|
2847
|
+
}
|
|
2848
|
+
let body = await res.json().catch(() => ({}));
|
|
2849
|
+
if (res.status === 409 && body.code === "rename_required" && !confirmRename) {
|
|
2850
|
+
s.stop("Rename confirmation needed");
|
|
2851
|
+
const yes = await p4.confirm({
|
|
2852
|
+
message: `This deploy renames the app: ${body.fromHost} \u2192 ${body.toHost}. The URL changes and the old one stops serving right away; data, secrets, and collaborators travel with it. (Meant a separate app? Run \`deepspace init --new-id\` instead.) Rename?`
|
|
2853
|
+
});
|
|
2854
|
+
if (p4.isCancel(yes) || !yes) {
|
|
2855
|
+
await bailDeploy("Deploy cancelled.", null);
|
|
2856
|
+
}
|
|
2857
|
+
confirmRename = true;
|
|
2858
|
+
s.start(`Deploying to ${appName}.app.space...`);
|
|
2859
|
+
res = await postCommit();
|
|
2860
|
+
body = await res.json().catch(() => ({}));
|
|
3047
2861
|
}
|
|
3048
|
-
const body = await res.json().catch(() => ({}));
|
|
3049
2862
|
if (!res.ok || !body.success) {
|
|
3050
|
-
|
|
3051
|
-
s.stop("Deploy failed");
|
|
3052
|
-
p4.cancel(formatDeployWorkerError(res.status, body.error));
|
|
3053
|
-
process.exit(1);
|
|
2863
|
+
await bailDeploy(formatDeployWorkerError(res.status, body.error));
|
|
3054
2864
|
}
|
|
3055
2865
|
if (body.onBehalfOfOwner) {
|
|
3056
2866
|
p4.log.warn(`Deployed on behalf of owner ${body.onBehalfOfOwner}`);
|
|
@@ -3064,26 +2874,66 @@ var deploy_default = defineCommand8({
|
|
|
3064
2874
|
"First requests may briefly hit the assets transitional page; retry in a minute."
|
|
3065
2875
|
);
|
|
3066
2876
|
p4.log.success(`Live at: ${body.url}`);
|
|
3067
|
-
await
|
|
3068
|
-
|
|
2877
|
+
await migrateLegacyDevVarsSecrets({
|
|
2878
|
+
deployUrl: DEPLOY_URL3,
|
|
2879
|
+
token,
|
|
2880
|
+
appId,
|
|
2881
|
+
envName,
|
|
2882
|
+
devVarsPath,
|
|
2883
|
+
userSecrets,
|
|
2884
|
+
userSecretNames,
|
|
2885
|
+
hadStore: linkedSecrets !== null
|
|
2886
|
+
});
|
|
2887
|
+
await syncSubscriptionPlans(appDir, appId, token);
|
|
2888
|
+
await syncOneTimeProducts(appDir, appId, token);
|
|
3069
2889
|
p4.outro("Done");
|
|
3070
2890
|
return;
|
|
3071
2891
|
}
|
|
3072
2892
|
}
|
|
3073
2893
|
s.stop("Deployed!");
|
|
3074
2894
|
p4.log.success(`Live at: ${body.url}`);
|
|
3075
|
-
await
|
|
3076
|
-
|
|
2895
|
+
await migrateLegacyDevVarsSecrets({
|
|
2896
|
+
deployUrl: DEPLOY_URL3,
|
|
2897
|
+
token,
|
|
2898
|
+
appId,
|
|
2899
|
+
envName,
|
|
2900
|
+
devVarsPath,
|
|
2901
|
+
userSecrets,
|
|
2902
|
+
userSecretNames,
|
|
2903
|
+
hadStore: linkedSecrets !== null
|
|
2904
|
+
});
|
|
2905
|
+
await syncSubscriptionPlans(appDir, appId, token);
|
|
2906
|
+
await syncOneTimeProducts(appDir, appId, token);
|
|
3077
2907
|
p4.outro("Done");
|
|
3078
2908
|
}
|
|
3079
2909
|
});
|
|
3080
|
-
async function
|
|
3081
|
-
const
|
|
2910
|
+
async function migrateLegacyDevVarsSecrets(opts) {
|
|
2911
|
+
const { deployUrl, token, appId, envName, devVarsPath, userSecrets, userSecretNames, hadStore } = opts;
|
|
2912
|
+
if (hadStore || userSecretNames.length === 0) return;
|
|
2913
|
+
const configName = defaultConfigNameForEnv(envName);
|
|
2914
|
+
try {
|
|
2915
|
+
await uploadSecrets(deployUrl, token, appId, configName, userSecrets, false);
|
|
2916
|
+
if (existsSync10(devVarsPath)) {
|
|
2917
|
+
const cleaned = removeDevVarsKeys(readFileSync9(devVarsPath, "utf-8"), new Set(userSecretNames));
|
|
2918
|
+
writeFileSync9(devVarsPath, cleaned.endsWith("\n") ? cleaned : `${cleaned}
|
|
2919
|
+
`);
|
|
2920
|
+
}
|
|
2921
|
+
p4.log.success(
|
|
2922
|
+
`Migrated ${userSecretNames.length} secret${userSecretNames.length === 1 ? "" : "s"} from .dev.vars into the app store (config ${configName}) \u2014 now managed by \`deepspace secrets\`.`
|
|
2923
|
+
);
|
|
2924
|
+
} catch (err) {
|
|
2925
|
+
p4.log.warn(
|
|
2926
|
+
`Couldn't migrate .dev.vars secrets into the app store (${errMessage(err)}); they deployed as bindings for now and the next deploy will retry.`
|
|
2927
|
+
);
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
async function syncSubscriptionPlans(appDir, appName, token) {
|
|
2931
|
+
const subsPath = join11(appDir, "src", "subscriptions.ts");
|
|
3082
2932
|
if (!existsSync10(subsPath)) return;
|
|
3083
2933
|
let plans;
|
|
3084
2934
|
try {
|
|
3085
2935
|
const esbuild = await import("esbuild");
|
|
3086
|
-
const outDir = join11(
|
|
2936
|
+
const outDir = join11(appDir, ".wrangler", "deploy");
|
|
3087
2937
|
const outFile = join11(outDir, "subscriptions.bundle.mjs");
|
|
3088
2938
|
await esbuild.build({
|
|
3089
2939
|
entryPoints: [subsPath],
|
|
@@ -3110,13 +2960,13 @@ async function syncSubscriptionPlans(appDir2, appName, token) {
|
|
|
3110
2960
|
s.start("Syncing subscription plans...");
|
|
3111
2961
|
let res;
|
|
3112
2962
|
try {
|
|
3113
|
-
res = await fetch(`${
|
|
2963
|
+
res = await fetch(`${API_URL2}/api/subscriptions/sync`, {
|
|
3114
2964
|
method: "POST",
|
|
3115
2965
|
headers: {
|
|
3116
2966
|
Authorization: `Bearer ${token}`,
|
|
3117
2967
|
"Content-Type": "application/json"
|
|
3118
2968
|
},
|
|
3119
|
-
body: JSON.stringify({ appName, plans })
|
|
2969
|
+
body: JSON.stringify({ appId: appName, plans })
|
|
3120
2970
|
});
|
|
3121
2971
|
} catch (err) {
|
|
3122
2972
|
s.stop("Plan sync skipped");
|
|
@@ -3184,7 +3034,7 @@ current billing period? (Skip to leave them grandfathered.)`
|
|
|
3184
3034
|
let stopped = false;
|
|
3185
3035
|
try {
|
|
3186
3036
|
while (iteration < MAX_ITERATIONS) {
|
|
3187
|
-
const res = await fetch(`${
|
|
3037
|
+
const res = await fetch(`${API_URL2}/api/subscriptions/admin-cancel`, {
|
|
3188
3038
|
method: "POST",
|
|
3189
3039
|
headers: {
|
|
3190
3040
|
Authorization: `Bearer ${token}`,
|
|
@@ -3225,13 +3075,13 @@ current billing period? (Skip to leave them grandfathered.)`
|
|
|
3225
3075
|
}
|
|
3226
3076
|
}
|
|
3227
3077
|
}
|
|
3228
|
-
async function syncOneTimeProducts(
|
|
3229
|
-
const productsPath = join11(
|
|
3078
|
+
async function syncOneTimeProducts(appDir, appName, token) {
|
|
3079
|
+
const productsPath = join11(appDir, "src", "products.ts");
|
|
3230
3080
|
if (!existsSync10(productsPath)) return;
|
|
3231
3081
|
let products;
|
|
3232
3082
|
try {
|
|
3233
3083
|
const esbuild = await import("esbuild");
|
|
3234
|
-
const outDir = join11(
|
|
3084
|
+
const outDir = join11(appDir, ".wrangler", "deploy");
|
|
3235
3085
|
const outFile = join11(outDir, "products.bundle.mjs");
|
|
3236
3086
|
await esbuild.build({
|
|
3237
3087
|
entryPoints: [productsPath],
|
|
@@ -3258,13 +3108,13 @@ async function syncOneTimeProducts(appDir2, appName, token) {
|
|
|
3258
3108
|
s?.start("Syncing one-time products...");
|
|
3259
3109
|
let res;
|
|
3260
3110
|
try {
|
|
3261
|
-
res = await fetch(`${
|
|
3111
|
+
res = await fetch(`${API_URL2}/api/charges/products/sync`, {
|
|
3262
3112
|
method: "POST",
|
|
3263
3113
|
headers: {
|
|
3264
3114
|
Authorization: `Bearer ${token}`,
|
|
3265
3115
|
"Content-Type": "application/json"
|
|
3266
3116
|
},
|
|
3267
|
-
body: JSON.stringify({ appName, products })
|
|
3117
|
+
body: JSON.stringify({ appId: appName, products })
|
|
3268
3118
|
});
|
|
3269
3119
|
} catch (err) {
|
|
3270
3120
|
s?.stop("Product sync skipped");
|
|
@@ -3335,7 +3185,7 @@ var undeploy_default = defineCommand9({
|
|
|
3335
3185
|
args: {
|
|
3336
3186
|
name: {
|
|
3337
3187
|
type: "positional",
|
|
3338
|
-
description: "App
|
|
3188
|
+
description: "App id to undeploy (reads DEEPSPACE_APP_ID from wrangler.toml if omitted)",
|
|
3339
3189
|
required: false
|
|
3340
3190
|
},
|
|
3341
3191
|
env: {
|
|
@@ -3345,21 +3195,17 @@ var undeploy_default = defineCommand9({
|
|
|
3345
3195
|
}
|
|
3346
3196
|
},
|
|
3347
3197
|
async run({ args }) {
|
|
3348
|
-
let appName = args.name;
|
|
3349
3198
|
const envName = typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
if (!nameRes.ok) {
|
|
3355
|
-
p5.cancel(nameRes.reason);
|
|
3356
|
-
process.exit(1);
|
|
3357
|
-
}
|
|
3358
|
-
appName = nameRes.name;
|
|
3359
|
-
}
|
|
3199
|
+
let appId = args.name;
|
|
3200
|
+
if (appId && !APP_ID_RE.test(appId)) {
|
|
3201
|
+
p5.cancel(`"${appId}" is not an app id (app_\u2026). Undeploy targets the id \u2014 run from the app directory, or find it in wrangler.toml.`);
|
|
3202
|
+
process.exit(1);
|
|
3360
3203
|
}
|
|
3361
|
-
if (!
|
|
3362
|
-
|
|
3204
|
+
if (!appId && hasWranglerConfig(process.cwd())) {
|
|
3205
|
+
appId = readAppId(process.cwd(), envName) ?? void 0;
|
|
3206
|
+
}
|
|
3207
|
+
if (!appId) {
|
|
3208
|
+
p5.cancel("Provide an app id or run from a DeepSpace app directory with a DEEPSPACE_APP_ID.");
|
|
3363
3209
|
process.exit(1);
|
|
3364
3210
|
}
|
|
3365
3211
|
let token;
|
|
@@ -3369,10 +3215,10 @@ var undeploy_default = defineCommand9({
|
|
|
3369
3215
|
p5.cancel(err instanceof Error ? err.message : String(err));
|
|
3370
3216
|
process.exit(1);
|
|
3371
3217
|
}
|
|
3372
|
-
p5.intro(`Undeploying ${
|
|
3218
|
+
p5.intro(`Undeploying ${appId}`);
|
|
3373
3219
|
const s = createSpinner();
|
|
3374
3220
|
s.start("Removing...");
|
|
3375
|
-
const res = await fetch(`${DEPLOY_URL4}/api/deploy/${
|
|
3221
|
+
const res = await fetch(`${DEPLOY_URL4}/api/deploy/${appId}`, {
|
|
3376
3222
|
method: "DELETE",
|
|
3377
3223
|
headers: { Authorization: `Bearer ${token}` }
|
|
3378
3224
|
});
|
|
@@ -3383,7 +3229,10 @@ var undeploy_default = defineCommand9({
|
|
|
3383
3229
|
process.exit(1);
|
|
3384
3230
|
}
|
|
3385
3231
|
s.stop("Removed");
|
|
3386
|
-
|
|
3232
|
+
const hosts = body.releasedHosts ?? [];
|
|
3233
|
+
p5.outro(
|
|
3234
|
+
hosts.length ? `${hosts.join(", ")} taken down. The app keeps its id \u2014 redeploy to bring it back.` : `App ${appId} taken down.`
|
|
3235
|
+
);
|
|
3387
3236
|
}
|
|
3388
3237
|
});
|
|
3389
3238
|
|
|
@@ -3484,14 +3333,14 @@ var add_default = defineCommand11({
|
|
|
3484
3333
|
}
|
|
3485
3334
|
},
|
|
3486
3335
|
run({ args }) {
|
|
3487
|
-
const
|
|
3488
|
-
ensureInstallReady(
|
|
3489
|
-
const script = findScript(
|
|
3336
|
+
const appDir = resolve8(args.dir ?? ".");
|
|
3337
|
+
ensureInstallReady(appDir);
|
|
3338
|
+
const script = findScript(appDir);
|
|
3490
3339
|
if (!script) {
|
|
3491
3340
|
console.error("Could not find the deepspace feature installer.");
|
|
3492
3341
|
console.error("Looked in:");
|
|
3493
|
-
console.error(` ${join12(
|
|
3494
|
-
console.error(` ${join12(
|
|
3342
|
+
console.error(` ${join12(appDir, "node_modules", "deepspace", "scripts", "add-feature.cjs")}`);
|
|
3343
|
+
console.error(` ${join12(appDir, ".deepspace", "scripts", "add-feature.cjs")}`);
|
|
3495
3344
|
console.error("Are you in a DeepSpace app directory? Try `npm install` first.");
|
|
3496
3345
|
process.exit(1);
|
|
3497
3346
|
}
|
|
@@ -3501,13 +3350,13 @@ var add_default = defineCommand11({
|
|
|
3501
3350
|
} else if (args.info) {
|
|
3502
3351
|
passArgs.push("--info", args.info);
|
|
3503
3352
|
} else if (args.feature) {
|
|
3504
|
-
passArgs.push(args.feature,
|
|
3353
|
+
passArgs.push(args.feature, appDir);
|
|
3505
3354
|
} else {
|
|
3506
3355
|
passArgs.push("--help");
|
|
3507
3356
|
}
|
|
3508
3357
|
execSync3(`node ${JSON.stringify(script)} ${passArgs.map((a) => JSON.stringify(a)).join(" ")}`, {
|
|
3509
3358
|
stdio: "inherit",
|
|
3510
|
-
cwd:
|
|
3359
|
+
cwd: appDir
|
|
3511
3360
|
});
|
|
3512
3361
|
}
|
|
3513
3362
|
});
|
|
@@ -3520,17 +3369,17 @@ import { join as join13 } from "path";
|
|
|
3520
3369
|
import { homedir as homedir5 } from "os";
|
|
3521
3370
|
var SKILL_REPO = "deepdotspace/deepspace-skill";
|
|
3522
3371
|
function runSkillsAdd(extraArgs) {
|
|
3523
|
-
return new Promise((
|
|
3372
|
+
return new Promise((resolve10) => {
|
|
3524
3373
|
const child = spawn2(
|
|
3525
3374
|
"npx",
|
|
3526
3375
|
["-y", "skills@latest", "add", SKILL_REPO, ...extraArgs],
|
|
3527
3376
|
{ stdio: "inherit" }
|
|
3528
3377
|
);
|
|
3529
|
-
child.on("close", (code) =>
|
|
3378
|
+
child.on("close", (code) => resolve10(code ?? 1));
|
|
3530
3379
|
child.on("error", (err) => {
|
|
3531
3380
|
console.error(`Failed to invoke npx: ${err.message}`);
|
|
3532
3381
|
console.error("Is Node.js installed and on PATH?");
|
|
3533
|
-
|
|
3382
|
+
resolve10(1);
|
|
3534
3383
|
});
|
|
3535
3384
|
});
|
|
3536
3385
|
}
|
|
@@ -3593,6 +3442,13 @@ var skills_default = defineCommand12({
|
|
|
3593
3442
|
import { defineCommand as defineCommand13 } from "citty";
|
|
3594
3443
|
|
|
3595
3444
|
// src/cli/lib/api.ts
|
|
3445
|
+
var ApiError = class extends Error {
|
|
3446
|
+
constructor(message, status3, code) {
|
|
3447
|
+
super(message);
|
|
3448
|
+
this.status = status3;
|
|
3449
|
+
this.code = code;
|
|
3450
|
+
}
|
|
3451
|
+
};
|
|
3596
3452
|
async function apiFetch(baseUrl, token, path, init) {
|
|
3597
3453
|
const res = await fetch(`${baseUrl}${path}`, {
|
|
3598
3454
|
...init,
|
|
@@ -3605,18 +3461,21 @@ async function apiFetch(baseUrl, token, path, init) {
|
|
|
3605
3461
|
const text2 = await res.text();
|
|
3606
3462
|
if (!res.ok) {
|
|
3607
3463
|
let msg = text2;
|
|
3464
|
+
let code;
|
|
3608
3465
|
try {
|
|
3609
|
-
|
|
3466
|
+
const body = JSON.parse(text2);
|
|
3467
|
+
msg = body.error ?? text2;
|
|
3468
|
+
code = body.code;
|
|
3610
3469
|
} catch {
|
|
3611
3470
|
}
|
|
3612
|
-
throw new
|
|
3471
|
+
throw new ApiError(`API ${path} (${res.status}): ${msg}`, res.status, code);
|
|
3613
3472
|
}
|
|
3614
|
-
return JSON.parse(text2);
|
|
3473
|
+
return text2 ? JSON.parse(text2) : {};
|
|
3615
3474
|
}
|
|
3616
3475
|
|
|
3617
3476
|
// src/cli/commands/domain.ts
|
|
3618
|
-
var
|
|
3619
|
-
var api = (token, path, init) => apiFetch(
|
|
3477
|
+
var API_URL3 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
3478
|
+
var api = (token, path, init) => apiFetch(API_URL3, token, path, init);
|
|
3620
3479
|
function scrubInternal(value) {
|
|
3621
3480
|
if (Array.isArray(value)) return value.map(scrubInternal);
|
|
3622
3481
|
if (value && typeof value === "object") {
|
|
@@ -3642,11 +3501,11 @@ async function confirm3(message) {
|
|
|
3642
3501
|
throw new Error(`${message} \u2014 pass --yes to confirm non-interactively`);
|
|
3643
3502
|
}
|
|
3644
3503
|
process.stdout.write(`${message} [y/N] `);
|
|
3645
|
-
return new Promise((
|
|
3504
|
+
return new Promise((resolve10) => {
|
|
3646
3505
|
process.stdin.setEncoding("utf-8");
|
|
3647
3506
|
process.stdin.once("data", (data) => {
|
|
3648
3507
|
const answer = data.toString().trim().toLowerCase();
|
|
3649
|
-
|
|
3508
|
+
resolve10(answer === "y" || answer === "yes");
|
|
3650
3509
|
});
|
|
3651
3510
|
});
|
|
3652
3511
|
}
|
|
@@ -3723,7 +3582,7 @@ var buy = defineCommand13({
|
|
|
3723
3582
|
}
|
|
3724
3583
|
const checkout = await api(token, "/api/domains/checkout", {
|
|
3725
3584
|
method: "POST",
|
|
3726
|
-
body: JSON.stringify({ domain: args.domain, appName })
|
|
3585
|
+
body: JSON.stringify({ domain: args.domain, appId: appName })
|
|
3727
3586
|
});
|
|
3728
3587
|
if (args.json) {
|
|
3729
3588
|
process.stdout.write(
|
|
@@ -3867,7 +3726,7 @@ var attach = defineCommand13({
|
|
|
3867
3726
|
`/api/domains/${found.id}/reattach`,
|
|
3868
3727
|
{
|
|
3869
3728
|
method: "POST",
|
|
3870
|
-
body: JSON.stringify({
|
|
3729
|
+
body: JSON.stringify({ appId: targetApp })
|
|
3871
3730
|
}
|
|
3872
3731
|
);
|
|
3873
3732
|
if (args.json) {
|
|
@@ -3950,36 +3809,19 @@ function isTestAccountEmail(email) {
|
|
|
3950
3809
|
}
|
|
3951
3810
|
|
|
3952
3811
|
// src/cli/commands/collaborators.ts
|
|
3953
|
-
var
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
...init,
|
|
3957
|
-
headers: {
|
|
3958
|
-
Authorization: `Bearer ${token}`,
|
|
3959
|
-
"Content-Type": "application/json",
|
|
3960
|
-
...init?.headers ?? {}
|
|
3961
|
-
}
|
|
3962
|
-
});
|
|
3963
|
-
const text2 = await res.text();
|
|
3964
|
-
if (!res.ok) {
|
|
3965
|
-
let msg = text2;
|
|
3966
|
-
try {
|
|
3967
|
-
msg = JSON.parse(text2).error ?? text2;
|
|
3968
|
-
} catch {
|
|
3969
|
-
}
|
|
3970
|
-
throw new Error(`API ${path} (${res.status}): ${msg}`);
|
|
3971
|
-
}
|
|
3972
|
-
return text2 ? JSON.parse(text2) : {};
|
|
3812
|
+
var API_URL4 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
3813
|
+
function api2(token, path, init) {
|
|
3814
|
+
return apiFetch(API_URL4, token, path, init);
|
|
3973
3815
|
}
|
|
3974
3816
|
var list3 = defineCommand14({
|
|
3975
3817
|
meta: { name: "list", description: "List collaborators on your app" },
|
|
3976
3818
|
args: {
|
|
3977
|
-
app: { type: "string", description: "App
|
|
3819
|
+
app: { type: "string", alias: "a", description: "App id (defaults to ./wrangler.toml)" },
|
|
3978
3820
|
json: { type: "boolean", description: "Emit JSON" }
|
|
3979
3821
|
},
|
|
3980
3822
|
async run({ args }) {
|
|
3981
3823
|
const token = await ensureToken();
|
|
3982
|
-
const app =
|
|
3824
|
+
const app = requireAppIdArg(args.app);
|
|
3983
3825
|
const { collaborators } = await api2(
|
|
3984
3826
|
token,
|
|
3985
3827
|
`/api/app-collaborators/${encodeURIComponent(app)}`
|
|
@@ -4005,12 +3847,12 @@ var add2 = defineCommand14({
|
|
|
4005
3847
|
description: "Collaborator email (must already be a DeepSpace user)",
|
|
4006
3848
|
required: true
|
|
4007
3849
|
},
|
|
4008
|
-
app: { type: "string", description: "App
|
|
3850
|
+
app: { type: "string", alias: "a", description: "App id (defaults to ./wrangler.toml)" },
|
|
4009
3851
|
json: { type: "boolean", description: "Emit JSON" }
|
|
4010
3852
|
},
|
|
4011
3853
|
async run({ args }) {
|
|
4012
3854
|
const token = await ensureToken();
|
|
4013
|
-
const app =
|
|
3855
|
+
const app = requireAppIdArg(args.app);
|
|
4014
3856
|
if (isTestAccountEmail(args.email)) {
|
|
4015
3857
|
console.error(
|
|
4016
3858
|
"Test accounts (@deepspace.test) cannot be added as collaborators. Use a real DeepSpace account."
|
|
@@ -4033,12 +3875,12 @@ var remove = defineCommand14({
|
|
|
4033
3875
|
meta: { name: "remove", description: "Remove a collaborator from your app" },
|
|
4034
3876
|
args: {
|
|
4035
3877
|
email: { type: "positional", description: "Collaborator email", required: true },
|
|
4036
|
-
app: { type: "string", description: "App
|
|
3878
|
+
app: { type: "string", alias: "a", description: "App id (defaults to ./wrangler.toml)" },
|
|
4037
3879
|
json: { type: "boolean", description: "Emit JSON" }
|
|
4038
3880
|
},
|
|
4039
3881
|
async run({ args }) {
|
|
4040
3882
|
const token = await ensureToken();
|
|
4041
|
-
const app =
|
|
3883
|
+
const app = requireAppIdArg(args.app);
|
|
4042
3884
|
const { collaborators } = await api2(
|
|
4043
3885
|
token,
|
|
4044
3886
|
`/api/app-collaborators/${encodeURIComponent(app)}`
|
|
@@ -4066,12 +3908,86 @@ var collaborators_default = defineCommand14({
|
|
|
4066
3908
|
subCommands: { list: list3, add: add2, remove }
|
|
4067
3909
|
});
|
|
4068
3910
|
|
|
4069
|
-
// src/cli/commands/
|
|
3911
|
+
// src/cli/commands/transfer.ts
|
|
4070
3912
|
import { defineCommand as defineCommand15 } from "citty";
|
|
3913
|
+
var API_URL5 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
3914
|
+
var DEPLOY_URL5 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
3915
|
+
var offer = defineCommand15({
|
|
3916
|
+
meta: { name: "offer", description: "Offer this app to another DeepSpace user" },
|
|
3917
|
+
args: {
|
|
3918
|
+
email: { type: "positional", description: "Recipient email", required: true },
|
|
3919
|
+
app: { type: "string", alias: "a", description: "App id (defaults to ./wrangler.toml)" }
|
|
3920
|
+
},
|
|
3921
|
+
async run({ args }) {
|
|
3922
|
+
const token = await ensureToken();
|
|
3923
|
+
const app = requireAppIdArg(args.app);
|
|
3924
|
+
const { expiresAt } = await apiFetch(
|
|
3925
|
+
API_URL5,
|
|
3926
|
+
token,
|
|
3927
|
+
`/api/app-transfers/${encodeURIComponent(app)}`,
|
|
3928
|
+
{ method: "POST", body: JSON.stringify({ email: args.email }) }
|
|
3929
|
+
);
|
|
3930
|
+
console.log(
|
|
3931
|
+
`\u2713 Offered ${app} to ${args.email} (expires ${expiresAt}).
|
|
3932
|
+
They accept with: deepspace transfer accept --app ${app}`
|
|
3933
|
+
);
|
|
3934
|
+
}
|
|
3935
|
+
});
|
|
3936
|
+
var status2 = defineCommand15({
|
|
3937
|
+
meta: { name: "status", description: "Show the pending transfer offer, if any" },
|
|
3938
|
+
args: {
|
|
3939
|
+
app: { type: "string", alias: "a", description: "App id (defaults to ./wrangler.toml)" }
|
|
3940
|
+
},
|
|
3941
|
+
async run({ args }) {
|
|
3942
|
+
const token = await ensureToken();
|
|
3943
|
+
const app = requireAppIdArg(args.app);
|
|
3944
|
+
const { transfer } = await apiFetch(API_URL5, token, `/api/app-transfers/${encodeURIComponent(app)}`);
|
|
3945
|
+
if (!transfer) {
|
|
3946
|
+
console.log(`No pending transfer for ${app}.`);
|
|
3947
|
+
return;
|
|
3948
|
+
}
|
|
3949
|
+
console.log(`Pending: ${app} \u2192 ${transfer.toEmailDisplay} (expires ${transfer.expiresAt}).`);
|
|
3950
|
+
}
|
|
3951
|
+
});
|
|
3952
|
+
var accept = defineCommand15({
|
|
3953
|
+
meta: { name: "accept", description: "Accept a transfer offered to you" },
|
|
3954
|
+
args: {
|
|
3955
|
+
app: { type: "string", alias: "a", description: "App id of the offered app", required: true }
|
|
3956
|
+
},
|
|
3957
|
+
async run({ args }) {
|
|
3958
|
+
const token = await ensureToken();
|
|
3959
|
+
const app = requireAppIdArg(args.app);
|
|
3960
|
+
await apiFetch(DEPLOY_URL5, token, `/api/apps/${encodeURIComponent(app)}/transfer/accept`, {
|
|
3961
|
+
method: "POST"
|
|
3962
|
+
});
|
|
3963
|
+
console.log(
|
|
3964
|
+
`\u2713 You now own ${app}. Run \`deepspace init\` in a fresh clone (or set DEEPSPACE_APP_ID = "${app}" in wrangler.toml) and deploy.`
|
|
3965
|
+
);
|
|
3966
|
+
}
|
|
3967
|
+
});
|
|
3968
|
+
var cancel4 = defineCommand15({
|
|
3969
|
+
meta: { name: "cancel", description: "Cancel/decline the pending offer (either party)" },
|
|
3970
|
+
args: {
|
|
3971
|
+
app: { type: "string", alias: "a", description: "App id (defaults to ./wrangler.toml)" }
|
|
3972
|
+
},
|
|
3973
|
+
async run({ args }) {
|
|
3974
|
+
const token = await ensureToken();
|
|
3975
|
+
const app = requireAppIdArg(args.app);
|
|
3976
|
+
await apiFetch(API_URL5, token, `/api/app-transfers/${encodeURIComponent(app)}`, { method: "DELETE" });
|
|
3977
|
+
console.log(`\u2713 Transfer offer for ${app} cancelled.`);
|
|
3978
|
+
}
|
|
3979
|
+
});
|
|
3980
|
+
var transfer_default = defineCommand15({
|
|
3981
|
+
meta: { name: "transfer", description: "Transfer app ownership to another user" },
|
|
3982
|
+
subCommands: { offer, status: status2, accept, cancel: cancel4 }
|
|
3983
|
+
});
|
|
3984
|
+
|
|
3985
|
+
// src/cli/commands/integrations.ts
|
|
3986
|
+
import { defineCommand as defineCommand16 } from "citty";
|
|
4071
3987
|
|
|
4072
3988
|
// src/cli/commands/_invoke-impl.ts
|
|
4073
3989
|
import { readFileSync as readFileSync10 } from "fs";
|
|
4074
|
-
var
|
|
3990
|
+
var API_URL6 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
4075
3991
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
4076
3992
|
function parseTarget(target) {
|
|
4077
3993
|
if (!target || typeof target !== "string") {
|
|
@@ -4113,7 +4029,7 @@ function flushAndExit(code) {
|
|
|
4113
4029
|
});
|
|
4114
4030
|
}
|
|
4115
4031
|
async function fetchCatalog(opts = {}) {
|
|
4116
|
-
const url = opts.summary ? `${
|
|
4032
|
+
const url = opts.summary ? `${API_URL6}/api/integrations?summary=1` : `${API_URL6}/api/integrations`;
|
|
4117
4033
|
const res = await fetch(url);
|
|
4118
4034
|
if (!res.ok) {
|
|
4119
4035
|
throw new Error(`Failed to fetch integration catalog (${res.status})`);
|
|
@@ -4201,7 +4117,7 @@ async function runInvoke(args) {
|
|
|
4201
4117
|
const startedAt = Date.now();
|
|
4202
4118
|
let res;
|
|
4203
4119
|
try {
|
|
4204
|
-
res = await fetch(`${
|
|
4120
|
+
res = await fetch(`${API_URL6}/api/integrations/${integration}/${endpoint}`, {
|
|
4205
4121
|
method: "POST",
|
|
4206
4122
|
headers: {
|
|
4207
4123
|
"Content-Type": "application/json",
|
|
@@ -4252,7 +4168,7 @@ async function runInvoke(args) {
|
|
|
4252
4168
|
}
|
|
4253
4169
|
|
|
4254
4170
|
// src/cli/commands/integrations.ts
|
|
4255
|
-
var list4 =
|
|
4171
|
+
var list4 = defineCommand16({
|
|
4256
4172
|
meta: {
|
|
4257
4173
|
name: "list",
|
|
4258
4174
|
description: "List all available integrations"
|
|
@@ -4273,7 +4189,7 @@ var list4 = defineCommand15({
|
|
|
4273
4189
|
}
|
|
4274
4190
|
}
|
|
4275
4191
|
});
|
|
4276
|
-
var info =
|
|
4192
|
+
var info = defineCommand16({
|
|
4277
4193
|
meta: {
|
|
4278
4194
|
name: "info",
|
|
4279
4195
|
description: "Print schema + example body for an integration endpoint"
|
|
@@ -4299,7 +4215,7 @@ var info = defineCommand15({
|
|
|
4299
4215
|
}
|
|
4300
4216
|
}
|
|
4301
4217
|
});
|
|
4302
|
-
var invoke =
|
|
4218
|
+
var invoke = defineCommand16({
|
|
4303
4219
|
meta: {
|
|
4304
4220
|
name: "invoke",
|
|
4305
4221
|
description: "Invoke a platform integration as the logged-in user"
|
|
@@ -4352,7 +4268,7 @@ var invoke = defineCommand15({
|
|
|
4352
4268
|
}
|
|
4353
4269
|
}
|
|
4354
4270
|
});
|
|
4355
|
-
var integrations_default =
|
|
4271
|
+
var integrations_default = defineCommand16({
|
|
4356
4272
|
meta: {
|
|
4357
4273
|
name: "integrations",
|
|
4358
4274
|
description: "List, inspect, and invoke platform integrations"
|
|
@@ -4365,8 +4281,8 @@ var integrations_default = defineCommand15({
|
|
|
4365
4281
|
});
|
|
4366
4282
|
|
|
4367
4283
|
// src/cli/commands/invoke.ts
|
|
4368
|
-
import { defineCommand as
|
|
4369
|
-
var invoke_default =
|
|
4284
|
+
import { defineCommand as defineCommand17 } from "citty";
|
|
4285
|
+
var invoke_default = defineCommand17({
|
|
4370
4286
|
meta: {
|
|
4371
4287
|
name: "invoke",
|
|
4372
4288
|
description: "Invoke a platform integration as the logged-in user"
|
|
@@ -4439,13 +4355,13 @@ var invoke_default = defineCommand16({
|
|
|
4439
4355
|
});
|
|
4440
4356
|
|
|
4441
4357
|
// src/cli/commands/library.ts
|
|
4442
|
-
import { defineCommand as
|
|
4358
|
+
import { defineCommand as defineCommand18 } from "citty";
|
|
4443
4359
|
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
4444
4360
|
import { join as join14, resolve as resolve9 } from "path";
|
|
4445
|
-
import { parse as
|
|
4361
|
+
import { parse as parseToml4 } from "smol-toml";
|
|
4446
4362
|
import * as p6 from "@clack/prompts";
|
|
4447
4363
|
var DEFAULT_LIBRARY_APP = "deepdotspace-site";
|
|
4448
|
-
var publish =
|
|
4364
|
+
var publish = defineCommand18({
|
|
4449
4365
|
meta: {
|
|
4450
4366
|
name: "publish",
|
|
4451
4367
|
description: "Publish the current app to the DeepSpace library"
|
|
@@ -4518,12 +4434,12 @@ var publish = defineCommand17({
|
|
|
4518
4434
|
intro6("Publishing to the DeepSpace library");
|
|
4519
4435
|
let appName = typeof args.app === "string" && args.app.trim() ? args.app.trim() : void 0;
|
|
4520
4436
|
if (!appName) {
|
|
4521
|
-
const
|
|
4522
|
-
const wranglerPath = join14(
|
|
4437
|
+
const appDir = resolve9(typeof args.dir === "string" && args.dir ? args.dir : ".");
|
|
4438
|
+
const wranglerPath = join14(appDir, "wrangler.toml");
|
|
4523
4439
|
if (!existsSync12(wranglerPath)) {
|
|
4524
4440
|
return die("No --app provided and no wrangler.toml found. Either pass --app <name> or run from your app's directory.");
|
|
4525
4441
|
}
|
|
4526
|
-
const cfg =
|
|
4442
|
+
const cfg = parseToml4(readFileSync11(wranglerPath, "utf-8"));
|
|
4527
4443
|
const nameRes = resolveAppName(cfg.name);
|
|
4528
4444
|
if (!nameRes.ok) return die(`wrangler.toml: ${nameRes.reason}`);
|
|
4529
4445
|
appName = nameRes.name;
|
|
@@ -4584,7 +4500,7 @@ var publish = defineCommand17({
|
|
|
4584
4500
|
p6.outro("Done");
|
|
4585
4501
|
}
|
|
4586
4502
|
});
|
|
4587
|
-
var unpublish =
|
|
4503
|
+
var unpublish = defineCommand18({
|
|
4588
4504
|
meta: {
|
|
4589
4505
|
name: "unpublish",
|
|
4590
4506
|
description: "Remove a library entry (owner or admin only)"
|
|
@@ -4643,7 +4559,7 @@ var unpublish = defineCommand17({
|
|
|
4643
4559
|
p6.outro("Done");
|
|
4644
4560
|
}
|
|
4645
4561
|
});
|
|
4646
|
-
var library_default =
|
|
4562
|
+
var library_default = defineCommand18({
|
|
4647
4563
|
meta: {
|
|
4648
4564
|
name: "library",
|
|
4649
4565
|
description: "Manage your DeepSpace library entries"
|
|
@@ -4655,13 +4571,13 @@ var library_default = defineCommand17({
|
|
|
4655
4571
|
});
|
|
4656
4572
|
|
|
4657
4573
|
// src/cli/commands/feedback.ts
|
|
4658
|
-
import { defineCommand as
|
|
4574
|
+
import { defineCommand as defineCommand19 } from "citty";
|
|
4659
4575
|
import * as p7 from "@clack/prompts";
|
|
4660
4576
|
import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
|
|
4661
4577
|
import { dirname as dirname3, join as join15 } from "path";
|
|
4662
4578
|
import { fileURLToPath } from "url";
|
|
4663
4579
|
import { release } from "os";
|
|
4664
|
-
var
|
|
4580
|
+
var API_URL7 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
4665
4581
|
var TYPES = ["bug", "feature", "other"];
|
|
4666
4582
|
var TYPE_LABELS = {
|
|
4667
4583
|
bug: "Bug report",
|
|
@@ -4671,7 +4587,7 @@ var TYPE_LABELS = {
|
|
|
4671
4587
|
var MAX_TITLE_LEN = 200;
|
|
4672
4588
|
var MAX_BODY_LEN = 1e4;
|
|
4673
4589
|
var MAX_CONTEXT_LEN = 200;
|
|
4674
|
-
var api3 = (token, path, init) => apiFetch(
|
|
4590
|
+
var api3 = (token, path, init) => apiFetch(API_URL7, token, path, init);
|
|
4675
4591
|
function normalizeType(raw) {
|
|
4676
4592
|
const value = (raw ?? "bug").toLowerCase();
|
|
4677
4593
|
if (TYPES.includes(value)) return value;
|
|
@@ -4733,7 +4649,7 @@ function buildFeedbackPayload(input) {
|
|
|
4733
4649
|
...input.context
|
|
4734
4650
|
};
|
|
4735
4651
|
}
|
|
4736
|
-
var feedback_default =
|
|
4652
|
+
var feedback_default = defineCommand19({
|
|
4737
4653
|
meta: {
|
|
4738
4654
|
name: "feedback",
|
|
4739
4655
|
description: "Submit a bug report, feature request, or feedback to DeepSpace"
|
|
@@ -4769,7 +4685,7 @@ var feedback_default = defineCommand18({
|
|
|
4769
4685
|
},
|
|
4770
4686
|
async run({ args }) {
|
|
4771
4687
|
const interactive = process.stdin.isTTY && !args.json;
|
|
4772
|
-
const
|
|
4688
|
+
const fail2 = (message) => {
|
|
4773
4689
|
if (args.json) {
|
|
4774
4690
|
process.stdout.write(JSON.stringify({ ok: false, error: message }) + "\n");
|
|
4775
4691
|
} else {
|
|
@@ -4781,12 +4697,12 @@ var feedback_default = defineCommand18({
|
|
|
4781
4697
|
try {
|
|
4782
4698
|
type = normalizeType(args.type ? String(args.type) : void 0);
|
|
4783
4699
|
} catch (err) {
|
|
4784
|
-
return
|
|
4700
|
+
return fail2(err.message);
|
|
4785
4701
|
}
|
|
4786
4702
|
let title = args.title ? String(args.title) : "";
|
|
4787
4703
|
let body = args.message ? String(args.message) : "";
|
|
4788
4704
|
if ((!title || !body) && !interactive) {
|
|
4789
|
-
return
|
|
4705
|
+
return fail2(
|
|
4790
4706
|
'Provide a title and --message to submit non-interactively, e.g. `deepspace feedback "Title" --message "Details"`.'
|
|
4791
4707
|
);
|
|
4792
4708
|
}
|
|
@@ -4848,7 +4764,7 @@ var feedback_default = defineCommand18({
|
|
|
4848
4764
|
context: collectContext()
|
|
4849
4765
|
});
|
|
4850
4766
|
} catch (err) {
|
|
4851
|
-
return
|
|
4767
|
+
return fail2(err.message);
|
|
4852
4768
|
}
|
|
4853
4769
|
if (interactive && !args.yes) {
|
|
4854
4770
|
p7.note(
|
|
@@ -4861,1011 +4777,395 @@ Context: deepspace ${payload.cliVersion} \xB7 ${payload.nodeVersion} \xB7 ${payl
|
|
|
4861
4777
|
"Review"
|
|
4862
4778
|
);
|
|
4863
4779
|
const ok = await p7.confirm({ message: "Send this to the DeepSpace team?" });
|
|
4864
|
-
if (p7.isCancel(ok) || !ok) {
|
|
4865
|
-
p7.cancel("Not sent.");
|
|
4866
|
-
process.exit(0);
|
|
4867
|
-
}
|
|
4868
|
-
}
|
|
4869
|
-
let token;
|
|
4870
|
-
try {
|
|
4871
|
-
token = await ensureToken();
|
|
4872
|
-
} catch (err) {
|
|
4873
|
-
return fail(err.message ?? "Not signed in. Run `deepspace login`.");
|
|
4874
|
-
}
|
|
4875
|
-
let result;
|
|
4876
|
-
try {
|
|
4877
|
-
result = await api3(token, "/api/feedback", {
|
|
4878
|
-
method: "POST",
|
|
4879
|
-
body: JSON.stringify(payload)
|
|
4880
|
-
});
|
|
4881
|
-
} catch (err) {
|
|
4882
|
-
return fail(err.message);
|
|
4883
|
-
}
|
|
4884
|
-
if (args.json) {
|
|
4885
|
-
process.stdout.write(JSON.stringify({ ok: true, report: result.report }) + "\n");
|
|
4886
|
-
return;
|
|
4887
|
-
}
|
|
4888
|
-
if (interactive) {
|
|
4889
|
-
p7.outro(`Thanks! Submitted as ${result.report.id}.`);
|
|
4890
|
-
} else {
|
|
4891
|
-
console.log(`Submitted feedback ${result.report.id} (${result.report.type}).`);
|
|
4892
|
-
}
|
|
4893
|
-
console.log(`Track triage status at ${DASHBOARD_URL}.`);
|
|
4894
|
-
}
|
|
4895
|
-
});
|
|
4896
|
-
|
|
4897
|
-
// src/cli/commands/secrets.ts
|
|
4898
|
-
import { defineCommand as defineCommand19 } from "citty";
|
|
4899
|
-
import { existsSync as existsSync14, readFileSync as readFileSync13, rmSync as rmSync4 } from "fs";
|
|
4900
|
-
import { join as join16, resolve as resolve10 } from "path";
|
|
4901
|
-
var DEPLOY_URL5 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
4902
|
-
function appDir(args) {
|
|
4903
|
-
const dir = typeof args.dir === "string" && args.dir ? args.dir : typeof args.scope === "string" && args.scope ? args.scope : ".";
|
|
4904
|
-
return resolve10(dir);
|
|
4905
|
-
}
|
|
4906
|
-
function wranglerEnv(args) {
|
|
4907
|
-
return typeof args.env === "string" && args.env.trim() ? args.env.trim() : void 0;
|
|
4908
|
-
}
|
|
4909
|
-
function targetFromArgs(args) {
|
|
4910
|
-
const dir = appDir(args);
|
|
4911
|
-
const target = resolveSecretsTarget(dir, {
|
|
4912
|
-
project: typeof args.project === "string" && args.project ? args.project : void 0,
|
|
4913
|
-
config: typeof args.config === "string" && args.config ? args.config : void 0,
|
|
4914
|
-
wranglerEnv: wranglerEnv(args)
|
|
4915
|
-
});
|
|
4916
|
-
return { appDir: dir, target };
|
|
4917
|
-
}
|
|
4918
|
-
function scopedArgs() {
|
|
4919
|
-
return {
|
|
4920
|
-
dir: { type: "string", description: "App directory (default: current directory)", required: false },
|
|
4921
|
-
scope: { type: "string", description: "Directory scope; Doppler-compatible alias for --dir", required: false },
|
|
4922
|
-
env: { type: "string", alias: "e", description: "Advanced: wrangler.toml [env.<name>] cache/link slot", required: false },
|
|
4923
|
-
json: { type: "boolean", description: "Emit JSON", default: false }
|
|
4924
|
-
};
|
|
4925
|
-
}
|
|
4926
|
-
function commonArgs() {
|
|
4927
|
-
return {
|
|
4928
|
-
...scopedArgs(),
|
|
4929
|
-
project: { type: "string", alias: "p", description: "Secrets project (default: linked project or wrangler name; does not link)", required: false },
|
|
4930
|
-
config: { type: "string", alias: "c", description: "Secrets config (Doppler-compatible; default: linked config or prd)", required: false }
|
|
4931
|
-
};
|
|
4932
|
-
}
|
|
4933
|
-
function setupArgs() {
|
|
4934
|
-
return {
|
|
4935
|
-
...scopedArgs(),
|
|
4936
|
-
project: { type: "string", alias: "p", description: "Secrets project to link (default: linked project or wrangler name)", required: false },
|
|
4937
|
-
config: { type: "string", alias: "c", description: "Secrets config to link (default: linked config or prd)", required: false },
|
|
4938
|
-
"no-interactive": { type: "boolean", description: "Accepted for Doppler CLI compatibility; DeepSpace setup never prompts", default: false },
|
|
4939
|
-
"no-save-token": { type: "boolean", description: "Accepted for Doppler CLI compatibility; DeepSpace does not save a token here", default: false }
|
|
4940
|
-
};
|
|
4941
|
-
}
|
|
4942
|
-
async function refreshCache(token, dir, target) {
|
|
4943
|
-
const values = await fetchSecretsValues(
|
|
4944
|
-
DEPLOY_URL5,
|
|
4945
|
-
token,
|
|
4946
|
-
target.projectName,
|
|
4947
|
-
target.configName,
|
|
4948
|
-
appScopedSecretsApiOptions(dir, target)
|
|
4949
|
-
);
|
|
4950
|
-
writeSecretsCacheFile(dir, values, target);
|
|
4951
|
-
return Object.keys(values).length;
|
|
4952
|
-
}
|
|
4953
|
-
async function refreshCacheIfLinked(token, dir, target) {
|
|
4954
|
-
if (!secretsTargetMatchesLink(dir, target)) return null;
|
|
4955
|
-
return refreshCache(token, dir, target);
|
|
4956
|
-
}
|
|
4957
|
-
function writeLinkForTarget(dir, target, projectOverride) {
|
|
4958
|
-
writeWranglerSecretsLink(dir, target, { projectOverride });
|
|
4959
|
-
}
|
|
4960
|
-
function redeployReminder(target) {
|
|
4961
|
-
const envFlag = target.wranglerEnv ? ` --env ${target.wranglerEnv}` : "";
|
|
4962
|
-
console.log(`Redeploy for production to receive these secrets: npx deepspace deploy${envFlag}`);
|
|
4963
|
-
}
|
|
4964
|
-
function unlinkedTargetNotice(target) {
|
|
4965
|
-
const envFlag = target.wranglerEnv ? ` --env ${target.wranglerEnv}` : "";
|
|
4966
|
-
console.log(
|
|
4967
|
-
`Not linked for dev/test/deploy; run npx deepspace secrets setup --project ${target.projectName} --config ${target.configName}${envFlag}.`
|
|
4968
|
-
);
|
|
4969
|
-
}
|
|
4970
|
-
function cacheJson(count) {
|
|
4971
|
-
return { linked: count !== null, cachedSecrets: count };
|
|
4972
|
-
}
|
|
4973
|
-
function updateRequiredSecretsIfTopLevelLinked(dir, target, keys, opts = {}) {
|
|
4974
|
-
if (target.wranglerEnv || !secretsTargetMatchesLink(dir, target)) return null;
|
|
4975
|
-
return updateWranglerRequiredSecrets(dir, keys, opts);
|
|
4976
|
-
}
|
|
4977
|
-
function removeRequiredSecretsIfTopLevelLinked(dir, target, keys) {
|
|
4978
|
-
if (target.wranglerEnv || !secretsTargetMatchesLink(dir, target)) return null;
|
|
4979
|
-
return removeWranglerRequiredSecrets(dir, keys);
|
|
4980
|
-
}
|
|
4981
|
-
function requiredSecretsJson(keys) {
|
|
4982
|
-
return keys ? { requiredSecrets: keys } : {};
|
|
4983
|
-
}
|
|
4984
|
-
function requiredSecretsNotice(keys) {
|
|
4985
|
-
if (!keys) return;
|
|
4986
|
-
console.log(`Updated wrangler.toml detected-secret advisory (${keys.length} name${keys.length === 1 ? "" : "s"}).`);
|
|
4987
|
-
}
|
|
4988
|
-
async function ensureLinkedRemoteTarget(dir, target) {
|
|
4989
|
-
let token;
|
|
4990
|
-
try {
|
|
4991
|
-
token = await ensureToken();
|
|
4992
|
-
} catch (err) {
|
|
4993
|
-
return {
|
|
4994
|
-
status: "unchecked",
|
|
4995
|
-
message: err instanceof Error ? err.message : String(err)
|
|
4996
|
-
};
|
|
4997
|
-
}
|
|
4998
|
-
try {
|
|
4999
|
-
const configs2 = await listConfigs(
|
|
5000
|
-
DEPLOY_URL5,
|
|
5001
|
-
token,
|
|
5002
|
-
target.projectName,
|
|
5003
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5004
|
-
);
|
|
5005
|
-
if (configs2.some((config) => config.name === target.configName)) {
|
|
5006
|
-
return { status: "ok" };
|
|
5007
|
-
}
|
|
5008
|
-
} catch (err) {
|
|
5009
|
-
if (!(err instanceof SecretsApiError && err.status === 404)) {
|
|
5010
|
-
return {
|
|
5011
|
-
status: "unchecked",
|
|
5012
|
-
message: err instanceof Error ? err.message : String(err)
|
|
5013
|
-
};
|
|
5014
|
-
}
|
|
5015
|
-
}
|
|
5016
|
-
try {
|
|
5017
|
-
await createConfig(
|
|
5018
|
-
DEPLOY_URL5,
|
|
5019
|
-
token,
|
|
5020
|
-
target.projectName,
|
|
5021
|
-
target.configName,
|
|
5022
|
-
void 0,
|
|
5023
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5024
|
-
);
|
|
5025
|
-
return { status: "created" };
|
|
5026
|
-
} catch (err) {
|
|
5027
|
-
return {
|
|
5028
|
-
status: "unchecked",
|
|
5029
|
-
message: `Remote config ${target.projectName}/${target.configName} was not found and could not be created: ${err instanceof Error ? err.message : String(err)}`
|
|
5030
|
-
};
|
|
5031
|
-
}
|
|
5032
|
-
}
|
|
5033
|
-
async function bestEffortSetServerSecretsLink(dir, target) {
|
|
5034
|
-
try {
|
|
5035
|
-
const token = await ensureToken();
|
|
5036
|
-
await setServerSecretsLink(
|
|
5037
|
-
DEPLOY_URL5,
|
|
5038
|
-
token,
|
|
5039
|
-
resolveSecretsAppName(dir, target.wranglerEnv),
|
|
5040
|
-
target.projectName
|
|
5041
|
-
);
|
|
5042
|
-
} catch {
|
|
5043
|
-
}
|
|
5044
|
-
}
|
|
5045
|
-
async function bestEffortClearServerSecretsLink(dir, targetEnv) {
|
|
5046
|
-
try {
|
|
5047
|
-
const token = await ensureToken();
|
|
5048
|
-
await clearServerSecretsLink(DEPLOY_URL5, token, resolveSecretsAppName(dir, targetEnv));
|
|
5049
|
-
} catch {
|
|
5050
|
-
}
|
|
5051
|
-
}
|
|
5052
|
-
async function importLegacyDevVarsSecrets(dir, target, remoteCheck) {
|
|
5053
|
-
if (remoteCheck.status === "unchecked") return null;
|
|
5054
|
-
const legacyPath = target.wranglerEnv ? join16(dir, `.dev.vars.${target.wranglerEnv}`) : join16(dir, ".dev.vars");
|
|
5055
|
-
if (!existsSync14(legacyPath)) return null;
|
|
5056
|
-
try {
|
|
5057
|
-
const custom = extractCustomDevVars(
|
|
5058
|
-
stripGeneratedSecretsCache2(readFileSync13(legacyPath, "utf-8"))
|
|
5059
|
-
);
|
|
5060
|
-
if (!custom.trim()) return null;
|
|
5061
|
-
const parsed = parseDevVars(custom);
|
|
5062
|
-
const secrets = {};
|
|
5063
|
-
const skipped = [];
|
|
5064
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
5065
|
-
try {
|
|
5066
|
-
validateSecretName(key);
|
|
5067
|
-
validateSecretValue(key, value);
|
|
5068
|
-
secrets[key] = value;
|
|
5069
|
-
} catch {
|
|
5070
|
-
skipped.push(key);
|
|
5071
|
-
}
|
|
5072
|
-
}
|
|
5073
|
-
const keys = Object.keys(secrets).sort();
|
|
5074
|
-
if (keys.length === 0) return null;
|
|
5075
|
-
const token = await ensureToken();
|
|
5076
|
-
if (remoteCheck.status === "ok") {
|
|
5077
|
-
const existing = await listSecrets(
|
|
5078
|
-
DEPLOY_URL5,
|
|
5079
|
-
token,
|
|
5080
|
-
target.projectName,
|
|
5081
|
-
target.configName,
|
|
5082
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5083
|
-
);
|
|
5084
|
-
if (existing.secrets.length > 0) {
|
|
5085
|
-
return { status: "config-not-empty", existingCount: existing.secrets.length, keys };
|
|
5086
|
-
}
|
|
5087
|
-
}
|
|
5088
|
-
await uploadSecrets(
|
|
5089
|
-
DEPLOY_URL5,
|
|
5090
|
-
token,
|
|
5091
|
-
target.projectName,
|
|
5092
|
-
target.configName,
|
|
5093
|
-
secrets,
|
|
5094
|
-
false,
|
|
5095
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5096
|
-
);
|
|
5097
|
-
let cleanedLegacyFile = false;
|
|
5098
|
-
try {
|
|
5099
|
-
const cleaned = removeDevVarsKeys(
|
|
5100
|
-
stripGeneratedSecretsCache2(readFileSync13(legacyPath, "utf-8")),
|
|
5101
|
-
new Set(keys)
|
|
5102
|
-
);
|
|
5103
|
-
if (target.wranglerEnv && !extractCustomDevVars(cleaned).trim()) {
|
|
5104
|
-
rmSync4(legacyPath, { force: true });
|
|
5105
|
-
} else {
|
|
5106
|
-
writeSecretFileSync(legacyPath, cleaned ? `${cleaned}
|
|
5107
|
-
` : "");
|
|
4780
|
+
if (p7.isCancel(ok) || !ok) {
|
|
4781
|
+
p7.cancel("Not sent.");
|
|
4782
|
+
process.exit(0);
|
|
5108
4783
|
}
|
|
5109
|
-
cleanedLegacyFile = true;
|
|
5110
|
-
} catch {
|
|
5111
4784
|
}
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
}
|
|
5118
|
-
}
|
|
5119
|
-
function printLegacySecretsImport(result, target) {
|
|
5120
|
-
if (!result) return;
|
|
5121
|
-
const configRef = `${target.projectName}/${target.configName}`;
|
|
5122
|
-
if (result.status === "imported") {
|
|
5123
|
-
console.log(
|
|
5124
|
-
`Imported ${result.keys.length} legacy secret${result.keys.length === 1 ? "" : "s"} from .dev.vars into ${configRef}: ${result.keys.join(", ")}`
|
|
5125
|
-
);
|
|
5126
|
-
if (result.cleanedLegacyFile) {
|
|
5127
|
-
console.log("Removed the migrated lines from the local file; the remote store owns them now.");
|
|
4785
|
+
let token;
|
|
4786
|
+
try {
|
|
4787
|
+
token = await ensureToken();
|
|
4788
|
+
} catch (err) {
|
|
4789
|
+
return fail2(err.message ?? "Not signed in. Run `deepspace login`.");
|
|
5128
4790
|
}
|
|
5129
|
-
|
|
5130
|
-
|
|
4791
|
+
let result;
|
|
4792
|
+
try {
|
|
4793
|
+
result = await api3(token, "/api/feedback", {
|
|
4794
|
+
method: "POST",
|
|
4795
|
+
body: JSON.stringify(payload)
|
|
4796
|
+
});
|
|
4797
|
+
} catch (err) {
|
|
4798
|
+
return fail2(err.message);
|
|
5131
4799
|
}
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
if (result.status === "config-not-empty") {
|
|
5136
|
-
console.log(
|
|
5137
|
-
`Found ${result.keys.length} legacy secret${result.keys.length === 1 ? "" : "s"} in .dev.vars, but ${configRef} already has ${result.existingCount} \u2014 not imported. Merge manually with npx deepspace secrets upload.`
|
|
5138
|
-
);
|
|
5139
|
-
return;
|
|
5140
|
-
}
|
|
5141
|
-
console.warn(`Warning: legacy .dev.vars secrets were not imported: ${result.message}`);
|
|
5142
|
-
}
|
|
5143
|
-
function printLinkRemoteWarning(check) {
|
|
5144
|
-
if (check.status !== "unchecked") return;
|
|
5145
|
-
console.warn(`Warning: Linked locally, but the remote config was not checked: ${check.message}`);
|
|
5146
|
-
}
|
|
5147
|
-
function positionals(args, firstName) {
|
|
5148
|
-
const raw = Array.isArray(args._) ? args._ : [];
|
|
5149
|
-
if (raw.length > 0) return raw.map(String);
|
|
5150
|
-
const first = args[firstName];
|
|
5151
|
-
return typeof first === "string" && first ? [first] : [];
|
|
5152
|
-
}
|
|
5153
|
-
function rawPositionals(rawArgs, argsDef) {
|
|
5154
|
-
const camel = (name) => name.replace(/-([a-z0-9])/g, (_, ch) => ch.toUpperCase());
|
|
5155
|
-
const stringFlags = /* @__PURE__ */ new Set();
|
|
5156
|
-
const booleanFlags = /* @__PURE__ */ new Set();
|
|
5157
|
-
for (const [name, def] of Object.entries(argsDef)) {
|
|
5158
|
-
if (def.type === "positional") continue;
|
|
5159
|
-
const flags = def.type === "boolean" ? booleanFlags : stringFlags;
|
|
5160
|
-
flags.add(name);
|
|
5161
|
-
flags.add(camel(name));
|
|
5162
|
-
for (const alias of Array.isArray(def.alias) ? def.alias : def.alias ? [def.alias] : []) {
|
|
5163
|
-
flags.add(alias);
|
|
5164
|
-
flags.add(camel(alias));
|
|
4800
|
+
if (args.json) {
|
|
4801
|
+
process.stdout.write(JSON.stringify({ ok: true, report: result.report }) + "\n");
|
|
4802
|
+
return;
|
|
5165
4803
|
}
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
if (token === "--") {
|
|
5171
|
-
out.push(...rawArgs.slice(i + 1));
|
|
5172
|
-
break;
|
|
5173
|
-
}
|
|
5174
|
-
if (token.length > 1 && token.startsWith("-")) {
|
|
5175
|
-
const body = token.startsWith("--") ? token.slice(2) : token.slice(1);
|
|
5176
|
-
const eq = body.indexOf("=");
|
|
5177
|
-
const name = eq === -1 ? body : body.slice(0, eq);
|
|
5178
|
-
if (booleanFlags.has(name) || name.startsWith("no-") && booleanFlags.has(name.slice(3))) {
|
|
5179
|
-
continue;
|
|
5180
|
-
}
|
|
5181
|
-
if (stringFlags.has(name)) {
|
|
5182
|
-
if (eq === -1) i++;
|
|
5183
|
-
continue;
|
|
5184
|
-
}
|
|
4804
|
+
if (interactive) {
|
|
4805
|
+
p7.outro(`Thanks! Submitted as ${result.report.id}.`);
|
|
4806
|
+
} else {
|
|
4807
|
+
console.log(`Submitted feedback ${result.report.id} (${result.report.type}).`);
|
|
5185
4808
|
}
|
|
5186
|
-
|
|
5187
|
-
}
|
|
5188
|
-
return out;
|
|
5189
|
-
}
|
|
5190
|
-
function hasRawFlag(rawArgs, name) {
|
|
5191
|
-
if (!Array.isArray(rawArgs)) return false;
|
|
5192
|
-
for (const token of rawArgs) {
|
|
5193
|
-
if (token === "--") return false;
|
|
5194
|
-
if (token === `--${name}`) return true;
|
|
4809
|
+
console.log(`Track triage status at ${DASHBOARD_URL}.`);
|
|
5195
4810
|
}
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
function resolveConfigureUnsetArgs(args) {
|
|
5221
|
-
const names = positionals(args, "name");
|
|
5222
|
-
if (names.length === 0) {
|
|
5223
|
-
throw new Error("At least one setting is required. Use `npx deepspace secrets configure unset project config`.");
|
|
4811
|
+
});
|
|
4812
|
+
|
|
4813
|
+
// src/cli/commands/secrets.ts
|
|
4814
|
+
import { defineCommand as defineCommand20 } from "citty";
|
|
4815
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
4816
|
+
var DEPLOY_URL6 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
4817
|
+
var COMMON_ARGS = {
|
|
4818
|
+
app: {
|
|
4819
|
+
type: "string",
|
|
4820
|
+
alias: "a",
|
|
4821
|
+
description: "App id (default: DEEPSPACE_APP_ID from the nearest wrangler.toml)",
|
|
4822
|
+
required: false
|
|
4823
|
+
},
|
|
4824
|
+
config: {
|
|
4825
|
+
type: "string",
|
|
4826
|
+
alias: "c",
|
|
4827
|
+
description: 'Config name (default: the --env name, or "prd")',
|
|
4828
|
+
required: false
|
|
4829
|
+
},
|
|
4830
|
+
env: {
|
|
4831
|
+
type: "string",
|
|
4832
|
+
alias: "e",
|
|
4833
|
+
description: "wrangler.toml [env.<name>] slot \u2014 selects that env\u2019s app id and config",
|
|
4834
|
+
required: false
|
|
5224
4835
|
}
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
4836
|
+
};
|
|
4837
|
+
async function resolveTarget(args) {
|
|
4838
|
+
const wranglerEnv = args.env?.trim() || void 0;
|
|
4839
|
+
let appId = args.app?.trim();
|
|
4840
|
+
if (appId && !APP_ID_RE.test(appId)) {
|
|
4841
|
+
throw new Error(`Invalid app id "${appId}" \u2014 expected app_<26 chars>.`);
|
|
4842
|
+
}
|
|
4843
|
+
if (!appId) {
|
|
4844
|
+
const appDir = findAppDir();
|
|
4845
|
+
appId = appDir && readAppId(appDir, wranglerEnv) || void 0;
|
|
4846
|
+
if (!appId) {
|
|
4847
|
+
throw new Error(
|
|
4848
|
+
"No app id. Run from an app directory whose wrangler.toml carries DEEPSPACE_APP_ID (`deepspace init` mints one), or pass --app <appId>."
|
|
4849
|
+
);
|
|
5229
4850
|
}
|
|
5230
|
-
if (!unset.includes(name)) unset.push(name);
|
|
5231
4851
|
}
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
4852
|
+
const configName = validateConfigName(args.config?.trim() || defaultConfigNameForEnv(wranglerEnv));
|
|
4853
|
+
const token = await ensureToken();
|
|
4854
|
+
return { appId, configName, token };
|
|
4855
|
+
}
|
|
4856
|
+
function fail(err) {
|
|
4857
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
4858
|
+
process.exit(1);
|
|
5237
4859
|
}
|
|
5238
|
-
var list5 =
|
|
4860
|
+
var list5 = defineCommand20({
|
|
5239
4861
|
meta: { name: "list", description: "List masked secrets in a config" },
|
|
5240
4862
|
args: {
|
|
5241
|
-
...
|
|
5242
|
-
"only-names": {
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
4863
|
+
...COMMON_ARGS,
|
|
4864
|
+
"only-names": {
|
|
4865
|
+
type: "boolean",
|
|
4866
|
+
description: "Only print secret names; omit values and metadata",
|
|
4867
|
+
default: false
|
|
4868
|
+
},
|
|
4869
|
+
json: { type: "boolean", description: "Machine-readable output", default: false }
|
|
5246
4870
|
},
|
|
5247
4871
|
async run({ args }) {
|
|
5248
4872
|
try {
|
|
5249
|
-
const
|
|
5250
|
-
const {
|
|
5251
|
-
const result = await listSecrets(
|
|
5252
|
-
DEPLOY_URL5,
|
|
5253
|
-
token,
|
|
5254
|
-
target.projectName,
|
|
5255
|
-
target.configName,
|
|
5256
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5257
|
-
);
|
|
4873
|
+
const t = await resolveTarget(args);
|
|
4874
|
+
const { secrets } = await listSecrets(DEPLOY_URL6, t.token, t.appId, t.configName);
|
|
5258
4875
|
if (args.json) {
|
|
5259
|
-
|
|
4876
|
+
console.log(JSON.stringify({ appId: t.appId, config: t.configName, secrets }, null, 2));
|
|
5260
4877
|
return;
|
|
5261
4878
|
}
|
|
5262
|
-
if (
|
|
5263
|
-
console.log(`No secrets in ${
|
|
4879
|
+
if (secrets.length === 0) {
|
|
4880
|
+
console.log(`No secrets in ${t.configName}.`);
|
|
5264
4881
|
return;
|
|
5265
4882
|
}
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
return;
|
|
5269
|
-
}
|
|
5270
|
-
for (const item of result.secrets) {
|
|
5271
|
-
console.log(`${item.key.padEnd(32)} ${item.masked} v${item.version} ${item.updatedAt}`);
|
|
4883
|
+
for (const s of secrets) {
|
|
4884
|
+
console.log(args["only-names"] ? s.key : `${s.key} (v${s.version}, ${s.valueBytes}B, ${s.updatedAt})`);
|
|
5272
4885
|
}
|
|
5273
4886
|
} catch (err) {
|
|
5274
|
-
|
|
5275
|
-
process.exit(1);
|
|
4887
|
+
fail(err);
|
|
5276
4888
|
}
|
|
5277
4889
|
}
|
|
5278
4890
|
});
|
|
5279
|
-
var
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
};
|
|
5287
|
-
var set = defineCommand19({
|
|
5288
|
-
meta: { name: "set", description: "Set remote secrets from KEY=value or KEY value" },
|
|
5289
|
-
args: SET_ARGS,
|
|
5290
|
-
async run({ args, rawArgs }) {
|
|
4891
|
+
var set = defineCommand20({
|
|
4892
|
+
meta: { name: "set", description: "Set secrets from KEY=value pairs" },
|
|
4893
|
+
args: {
|
|
4894
|
+
...COMMON_ARGS,
|
|
4895
|
+
secret: { type: "positional", description: "KEY=value (repeatable)", required: true }
|
|
4896
|
+
},
|
|
4897
|
+
async run({ args }) {
|
|
5291
4898
|
try {
|
|
5292
|
-
const
|
|
5293
|
-
const
|
|
5294
|
-
|
|
5295
|
-
const secrets = parseSetInputs(inputs);
|
|
5296
|
-
await uploadSecrets(
|
|
5297
|
-
DEPLOY_URL5,
|
|
5298
|
-
token,
|
|
5299
|
-
target.projectName,
|
|
5300
|
-
target.configName,
|
|
5301
|
-
secrets,
|
|
5302
|
-
false,
|
|
5303
|
-
appScopedSecretsApiOptions(dir, target)
|
|
4899
|
+
const t = await resolveTarget(args);
|
|
4900
|
+
const pairs = [args.secret, ...args._ ?? []].filter(
|
|
4901
|
+
(v) => typeof v === "string" && v.length > 0
|
|
5304
4902
|
);
|
|
5305
|
-
const
|
|
5306
|
-
const
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
4903
|
+
const secrets = {};
|
|
4904
|
+
for (const pair of pairs) {
|
|
4905
|
+
const eq = pair.indexOf("=");
|
|
4906
|
+
if (eq <= 0) throw new Error(`Expected KEY=value, got "${pair}"`);
|
|
4907
|
+
const key = validateSecretName(pair.slice(0, eq));
|
|
4908
|
+
const value = pair.slice(eq + 1);
|
|
4909
|
+
validateSecretValue(key, value);
|
|
4910
|
+
secrets[key] = value;
|
|
5311
4911
|
}
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
unlinkedTargetNotice(target);
|
|
4912
|
+
if (Object.keys(secrets).length === 1) {
|
|
4913
|
+
const [[k, v]] = Object.entries(secrets);
|
|
4914
|
+
await setSecret(DEPLOY_URL6, t.token, t.appId, t.configName, k, v);
|
|
5316
4915
|
} else {
|
|
5317
|
-
|
|
5318
|
-
redeployReminder(target);
|
|
4916
|
+
await uploadSecrets(DEPLOY_URL6, t.token, t.appId, t.configName, secrets, false);
|
|
5319
4917
|
}
|
|
4918
|
+
console.log(`Set ${Object.keys(secrets).length} secret${Object.keys(secrets).length === 1 ? "" : "s"} in ${t.configName}.`);
|
|
5320
4919
|
} catch (err) {
|
|
5321
|
-
|
|
5322
|
-
process.exit(1);
|
|
4920
|
+
fail(err);
|
|
5323
4921
|
}
|
|
5324
4922
|
}
|
|
5325
4923
|
});
|
|
5326
|
-
var get =
|
|
5327
|
-
meta: { name: "get", description: "Get
|
|
4924
|
+
var get = defineCommand20({
|
|
4925
|
+
meta: { name: "get", description: "Get a secret; pass --plain to print the value" },
|
|
5328
4926
|
args: {
|
|
5329
|
-
...
|
|
5330
|
-
key: { type: "positional", description: "Secret
|
|
5331
|
-
plain: { type: "boolean", description: "Print plaintext value", default: false }
|
|
5332
|
-
"no-exit-on-missing-secret": { type: "boolean", description: "Do not exit when a requested secret is missing", default: false },
|
|
5333
|
-
raw: { type: "boolean", description: "Accepted for Doppler CLI compatibility; DeepSpace has no variable interpolation", default: false },
|
|
5334
|
-
copy: { type: "boolean", description: "Accepted for Doppler CLI compatibility; clipboard copy is not implemented", default: false },
|
|
5335
|
-
type: { type: "boolean", description: "Accepted for Doppler CLI compatibility", default: false },
|
|
5336
|
-
visibility: { type: "boolean", description: "Accepted for Doppler CLI compatibility", default: false }
|
|
4927
|
+
...COMMON_ARGS,
|
|
4928
|
+
key: { type: "positional", description: "Secret name", required: true },
|
|
4929
|
+
plain: { type: "boolean", description: "Print the plaintext value", default: false }
|
|
5337
4930
|
},
|
|
5338
4931
|
async run({ args }) {
|
|
5339
4932
|
try {
|
|
5340
|
-
const
|
|
5341
|
-
const
|
|
5342
|
-
const keys = positionals(args, "key").map((key) => validateSecretName(key));
|
|
4933
|
+
const t = await resolveTarget(args);
|
|
4934
|
+
const key = validateSecretName(args.key);
|
|
5343
4935
|
if (args.plain) {
|
|
5344
|
-
const
|
|
5345
|
-
|
|
5346
|
-
for (const key of keys) {
|
|
5347
|
-
try {
|
|
5348
|
-
values[key] = await getSecretPlain(
|
|
5349
|
-
DEPLOY_URL5,
|
|
5350
|
-
token,
|
|
5351
|
-
target.projectName,
|
|
5352
|
-
target.configName,
|
|
5353
|
-
key,
|
|
5354
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5355
|
-
);
|
|
5356
|
-
} catch (err) {
|
|
5357
|
-
if (!args.noExitOnMissingSecret) throw err;
|
|
5358
|
-
missing2.push(key);
|
|
5359
|
-
}
|
|
5360
|
-
}
|
|
5361
|
-
if (args.json) {
|
|
5362
|
-
process.stdout.write(JSON.stringify({ secrets: values, missing: missing2 }, null, 2) + "\n");
|
|
5363
|
-
return;
|
|
5364
|
-
}
|
|
5365
|
-
for (const key of keys) if (Object.prototype.hasOwnProperty.call(values, key)) process.stdout.write(values[key] + "\n");
|
|
5366
|
-
return;
|
|
5367
|
-
}
|
|
5368
|
-
const result = await listSecrets(
|
|
5369
|
-
DEPLOY_URL5,
|
|
5370
|
-
token,
|
|
5371
|
-
target.projectName,
|
|
5372
|
-
target.configName,
|
|
5373
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5374
|
-
);
|
|
5375
|
-
const found = result.secrets.filter((item) => keys.includes(item.key));
|
|
5376
|
-
const foundKeys = new Set(found.map((item) => item.key));
|
|
5377
|
-
const missing = keys.filter((key) => !foundKeys.has(key));
|
|
5378
|
-
if (missing.length > 0 && !args.noExitOnMissingSecret) {
|
|
5379
|
-
throw new Error(`Secret "${missing[0]}" not found in ${target.projectName}/${target.configName}.`);
|
|
5380
|
-
}
|
|
5381
|
-
if (args.json) {
|
|
5382
|
-
process.stdout.write(JSON.stringify({ secrets: found, missing }, null, 2) + "\n");
|
|
4936
|
+
const { value } = await getSecretPlain(DEPLOY_URL6, t.token, t.appId, t.configName, key);
|
|
4937
|
+
process.stdout.write(value + "\n");
|
|
5383
4938
|
return;
|
|
5384
4939
|
}
|
|
5385
|
-
|
|
5386
|
-
|
|
4940
|
+
const { secrets } = await listSecrets(DEPLOY_URL6, t.token, t.appId, t.configName);
|
|
4941
|
+
const item = secrets.find((s) => s.key === key);
|
|
4942
|
+
if (!item) fail(new Error(`Secret "${key}" not found in ${t.configName}`));
|
|
4943
|
+
console.log(`${item.key} (v${item.version}, ${item.valueBytes}B, ${item.updatedAt})`);
|
|
5387
4944
|
} catch (err) {
|
|
5388
|
-
|
|
5389
|
-
process.exit(1);
|
|
4945
|
+
fail(err);
|
|
5390
4946
|
}
|
|
5391
4947
|
}
|
|
5392
4948
|
});
|
|
5393
|
-
var del2 =
|
|
5394
|
-
meta: { name: "delete", description: "Delete one or more
|
|
4949
|
+
var del2 = defineCommand20({
|
|
4950
|
+
meta: { name: "delete", description: "Delete one or more secrets" },
|
|
5395
4951
|
args: {
|
|
5396
|
-
...
|
|
5397
|
-
key: { type: "positional", description: "Secret
|
|
5398
|
-
yes: { type: "boolean", alias: "y", description: "Proceed without confirmation; accepted for Doppler CLI compatibility", default: false },
|
|
5399
|
-
raw: { type: "boolean", description: "Accepted for Doppler CLI compatibility; DeepSpace has no variable interpolation", default: false }
|
|
4952
|
+
...COMMON_ARGS,
|
|
4953
|
+
key: { type: "positional", description: "Secret name (repeatable)", required: true }
|
|
5400
4954
|
},
|
|
5401
4955
|
async run({ args }) {
|
|
5402
4956
|
try {
|
|
5403
|
-
const
|
|
5404
|
-
const
|
|
5405
|
-
|
|
5406
|
-
for (const key of keys) {
|
|
5407
|
-
await deleteSecret(
|
|
5408
|
-
DEPLOY_URL5,
|
|
5409
|
-
token,
|
|
5410
|
-
target.projectName,
|
|
5411
|
-
target.configName,
|
|
5412
|
-
key,
|
|
5413
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5414
|
-
);
|
|
5415
|
-
}
|
|
5416
|
-
const count = await refreshCacheIfLinked(token, dir, target);
|
|
5417
|
-
const requiredSecrets = removeRequiredSecretsIfTopLevelLinked(dir, target, keys);
|
|
5418
|
-
if (args.json) {
|
|
5419
|
-
process.stdout.write(JSON.stringify({ ok: true, deleted: keys, target, ...cacheJson(count), ...requiredSecretsJson(requiredSecrets) }) + "\n");
|
|
5420
|
-
return;
|
|
5421
|
-
}
|
|
5422
|
-
console.log(`Deleted ${keys.length} secret${keys.length === 1 ? "" : "s"} from ${target.projectName}/${target.configName}.`);
|
|
5423
|
-
requiredSecretsNotice(requiredSecrets);
|
|
5424
|
-
if (count === null) {
|
|
5425
|
-
unlinkedTargetNotice(target);
|
|
5426
|
-
} else {
|
|
5427
|
-
console.log(`Refreshed ${count} cached secret${count === 1 ? "" : "s"}.`);
|
|
5428
|
-
redeployReminder(target);
|
|
5429
|
-
}
|
|
5430
|
-
} catch (err) {
|
|
5431
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
5432
|
-
process.exit(1);
|
|
5433
|
-
}
|
|
5434
|
-
}
|
|
5435
|
-
});
|
|
5436
|
-
var pull = defineCommand19({
|
|
5437
|
-
meta: { name: "pull", description: "Refresh .dev.vars* only when the selected config is linked" },
|
|
5438
|
-
args: commonArgs(),
|
|
5439
|
-
async run({ args }) {
|
|
5440
|
-
try {
|
|
5441
|
-
const token = await ensureToken();
|
|
5442
|
-
const { appDir: dir, target } = targetFromArgs(args);
|
|
5443
|
-
const values = await fetchSecretsValues(
|
|
5444
|
-
DEPLOY_URL5,
|
|
5445
|
-
token,
|
|
5446
|
-
target.projectName,
|
|
5447
|
-
target.configName,
|
|
5448
|
-
appScopedSecretsApiOptions(dir, target)
|
|
4957
|
+
const t = await resolveTarget(args);
|
|
4958
|
+
const keys = [args.key, ...args._ ?? []].filter(
|
|
4959
|
+
(v) => typeof v === "string" && v.length > 0
|
|
5449
4960
|
);
|
|
5450
|
-
const
|
|
5451
|
-
|
|
5452
|
-
if (linked) writeSecretsCacheFile(dir, values, target);
|
|
5453
|
-
if (args.json) {
|
|
5454
|
-
process.stdout.write(JSON.stringify({ ok: true, target, linked, cachedSecrets: linked ? count : null }) + "\n");
|
|
5455
|
-
return;
|
|
4961
|
+
for (const key of keys) {
|
|
4962
|
+
await deleteSecret(DEPLOY_URL6, t.token, t.appId, t.configName, validateSecretName(key));
|
|
5456
4963
|
}
|
|
5457
|
-
console.log(
|
|
5458
|
-
if (!linked) unlinkedTargetNotice(target);
|
|
4964
|
+
console.log(`Deleted ${keys.length} secret${keys.length === 1 ? "" : "s"} from ${t.configName}.`);
|
|
5459
4965
|
} catch (err) {
|
|
5460
|
-
|
|
5461
|
-
process.exit(1);
|
|
4966
|
+
fail(err);
|
|
5462
4967
|
}
|
|
5463
4968
|
}
|
|
5464
4969
|
});
|
|
5465
|
-
var
|
|
5466
|
-
meta: { name: "
|
|
4970
|
+
var upload = defineCommand20({
|
|
4971
|
+
meta: { name: "upload", description: "Upload secrets from a dotenv or JSON file" },
|
|
5467
4972
|
args: {
|
|
5468
|
-
...
|
|
5469
|
-
file: { type: "positional", description: "
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
"no-fallback": { type: "boolean", description: "Accepted for Doppler CLI compatibility; DeepSpace does not use fallback files", default: false },
|
|
5475
|
-
offline: { type: "boolean", description: "Accepted for Doppler CLI compatibility; offline fallback files are not supported", default: false }
|
|
5476
|
-
},
|
|
5477
|
-
async run({ args, rawArgs }) {
|
|
5478
|
-
try {
|
|
5479
|
-
const token = await ensureToken();
|
|
5480
|
-
const { appDir: dir, target } = targetFromArgs(args);
|
|
5481
|
-
const values = await fetchSecretsValues(
|
|
5482
|
-
DEPLOY_URL5,
|
|
5483
|
-
token,
|
|
5484
|
-
target.projectName,
|
|
5485
|
-
target.configName,
|
|
5486
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5487
|
-
);
|
|
5488
|
-
const output = formatSecretsDownload(values, downloadFormat(args.format));
|
|
5489
|
-
const file = typeof args.file === "string" && args.file ? args.file : void 0;
|
|
5490
|
-
const noFile = Boolean(args.noFile) || hasRawFlag(rawArgs, "no-file");
|
|
5491
|
-
if (noFile || !file) {
|
|
5492
|
-
if (!noFile && !file) {
|
|
5493
|
-
throw new Error("Provide an output filepath or pass --no-file to print to stdout.");
|
|
5494
|
-
}
|
|
5495
|
-
process.stdout.write(output);
|
|
5496
|
-
return;
|
|
5497
|
-
}
|
|
5498
|
-
writeSecretFileSync(resolve10(file), output);
|
|
5499
|
-
if (args.json) {
|
|
5500
|
-
process.stdout.write(JSON.stringify({ ok: true, path: resolve10(file), target, secretCount: Object.keys(values).length }) + "\n");
|
|
5501
|
-
return;
|
|
5502
|
-
}
|
|
5503
|
-
console.log(`Downloaded ${Object.keys(values).length} secret${Object.keys(values).length === 1 ? "" : "s"} to ${file}.`);
|
|
5504
|
-
} catch (err) {
|
|
5505
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
5506
|
-
process.exit(1);
|
|
4973
|
+
...COMMON_ARGS,
|
|
4974
|
+
file: { type: "positional", description: "Path to a dotenv or JSON file (- for stdin)", required: true },
|
|
4975
|
+
replace: {
|
|
4976
|
+
type: "boolean",
|
|
4977
|
+
description: "Replace the whole config (delete keys missing from the file)",
|
|
4978
|
+
default: false
|
|
5507
4979
|
}
|
|
5508
|
-
}
|
|
5509
|
-
});
|
|
5510
|
-
var upload = defineCommand19({
|
|
5511
|
-
meta: { name: "upload", description: "Upload remote secrets from a dotenv or JSON file" },
|
|
5512
|
-
args: {
|
|
5513
|
-
...commonArgs(),
|
|
5514
|
-
file: { type: "positional", description: "Path to dotenv/JSON file", required: true },
|
|
5515
|
-
replace: { type: "boolean", description: "Delete secrets absent from the upload", default: false },
|
|
5516
|
-
raw: { type: "boolean", description: "Accepted for Doppler CLI compatibility; DeepSpace has no variable interpolation", default: false }
|
|
5517
4980
|
},
|
|
5518
4981
|
async run({ args }) {
|
|
5519
4982
|
try {
|
|
5520
|
-
const
|
|
5521
|
-
const
|
|
5522
|
-
const file = String(args.file);
|
|
5523
|
-
const content = readExistingFile(file);
|
|
4983
|
+
const t = await resolveTarget(args);
|
|
4984
|
+
const content = args.file === "-" ? readFileSync13(0, "utf-8") : readFileSync13(args.file, "utf-8");
|
|
5524
4985
|
const secrets = parseSecretsUpload(content);
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
target.projectName,
|
|
5529
|
-
target.configName,
|
|
5530
|
-
secrets,
|
|
5531
|
-
!!args.replace,
|
|
5532
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5533
|
-
);
|
|
5534
|
-
const count = await refreshCacheIfLinked(token, dir, target);
|
|
5535
|
-
const requiredSecrets = updateRequiredSecretsIfTopLevelLinked(dir, target, Object.keys(secrets), {
|
|
5536
|
-
replace: !!args.replace
|
|
5537
|
-
});
|
|
5538
|
-
if (args.json) {
|
|
5539
|
-
process.stdout.write(JSON.stringify({ ok: true, uploaded: Object.keys(secrets), target, ...cacheJson(count), ...requiredSecretsJson(requiredSecrets) }) + "\n");
|
|
5540
|
-
return;
|
|
5541
|
-
}
|
|
5542
|
-
console.log(`Uploaded ${Object.keys(secrets).length} secret${Object.keys(secrets).length === 1 ? "" : "s"} to ${target.projectName}/${target.configName}.`);
|
|
5543
|
-
requiredSecretsNotice(requiredSecrets);
|
|
5544
|
-
if (count === null) {
|
|
5545
|
-
unlinkedTargetNotice(target);
|
|
5546
|
-
} else {
|
|
5547
|
-
console.log(`Refreshed ${count} cached secret${count === 1 ? "" : "s"}.`);
|
|
5548
|
-
redeployReminder(target);
|
|
5549
|
-
}
|
|
5550
|
-
} catch (err) {
|
|
5551
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
5552
|
-
process.exit(1);
|
|
5553
|
-
}
|
|
5554
|
-
}
|
|
5555
|
-
});
|
|
5556
|
-
var configsList = defineCommand19({
|
|
5557
|
-
meta: { name: "list", description: "List configs in a secrets project" },
|
|
5558
|
-
args: commonArgs(),
|
|
5559
|
-
async run({ args }) {
|
|
5560
|
-
try {
|
|
5561
|
-
const token = await ensureToken();
|
|
5562
|
-
const { appDir: dir, target } = targetFromArgs(args);
|
|
5563
|
-
const configs2 = await listConfigs(
|
|
5564
|
-
DEPLOY_URL5,
|
|
5565
|
-
token,
|
|
5566
|
-
target.projectName,
|
|
5567
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5568
|
-
);
|
|
5569
|
-
if (args.json) {
|
|
5570
|
-
process.stdout.write(JSON.stringify({ projectName: target.projectName, configs: configs2 }, null, 2) + "\n");
|
|
5571
|
-
return;
|
|
5572
|
-
}
|
|
5573
|
-
if (configs2.length === 0) {
|
|
5574
|
-
console.log(`No configs in ${target.projectName}.`);
|
|
5575
|
-
return;
|
|
5576
|
-
}
|
|
5577
|
-
for (const cfg of configs2) {
|
|
5578
|
-
console.log(`${cfg.name.padEnd(24)} ${cfg.secretCount ?? 0} secret${cfg.secretCount === 1 ? "" : "s"} ${cfg.updatedAt}`);
|
|
5579
|
-
}
|
|
4986
|
+
if (Object.keys(secrets).length === 0) fail(new Error("No secrets found in the input."));
|
|
4987
|
+
await uploadSecrets(DEPLOY_URL6, t.token, t.appId, t.configName, secrets, args.replace);
|
|
4988
|
+
console.log(`Uploaded ${Object.keys(secrets).length} secrets to ${t.configName}.`);
|
|
5580
4989
|
} catch (err) {
|
|
5581
|
-
|
|
5582
|
-
process.exit(1);
|
|
4990
|
+
fail(err);
|
|
5583
4991
|
}
|
|
5584
4992
|
}
|
|
5585
4993
|
});
|
|
5586
|
-
var
|
|
5587
|
-
meta: { name: "
|
|
4994
|
+
var download = defineCommand20({
|
|
4995
|
+
meta: { name: "download", description: "Download a config\u2019s secrets (dotenv/json/shell)" },
|
|
5588
4996
|
args: {
|
|
5589
|
-
...
|
|
5590
|
-
|
|
5591
|
-
|
|
4997
|
+
...COMMON_ARGS,
|
|
4998
|
+
format: {
|
|
4999
|
+
type: "string",
|
|
5000
|
+
description: "dotenv (default) | json | shell",
|
|
5001
|
+
default: "dotenv"
|
|
5002
|
+
}
|
|
5592
5003
|
},
|
|
5593
5004
|
async run({ args }) {
|
|
5594
5005
|
try {
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
const dir = appDir(args);
|
|
5600
|
-
const projectName = validateProjectName(args.project || resolveSecretsTarget(dir, { wranglerEnv: wranglerEnv(args) }).projectName);
|
|
5601
|
-
const rawName = typeof args.config === "string" && args.config ? args.config : typeof args.configName === "string" && args.configName ? args.configName : args.name;
|
|
5602
|
-
if (typeof rawName !== "string" || !rawName) throw new Error("Config name required");
|
|
5603
|
-
const configName = validateConfigName(rawName);
|
|
5604
|
-
const target = { projectName, configName, wranglerEnv: wranglerEnv(args) };
|
|
5605
|
-
const result = await createConfig(
|
|
5606
|
-
DEPLOY_URL5,
|
|
5607
|
-
token,
|
|
5608
|
-
projectName,
|
|
5609
|
-
configName,
|
|
5610
|
-
void 0,
|
|
5611
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5612
|
-
);
|
|
5613
|
-
const count = await refreshCacheIfLinked(token, dir, target);
|
|
5614
|
-
if (args.json) {
|
|
5615
|
-
process.stdout.write(JSON.stringify({ ...result, target, ...cacheJson(count) }) + "\n");
|
|
5616
|
-
return;
|
|
5617
|
-
}
|
|
5618
|
-
if (result.created) {
|
|
5619
|
-
console.log(`Created ${projectName}/${configName}.`);
|
|
5620
|
-
} else {
|
|
5621
|
-
console.log(`Config ${projectName}/${configName} already exists.`);
|
|
5622
|
-
}
|
|
5623
|
-
if (count === null) {
|
|
5624
|
-
unlinkedTargetNotice(target);
|
|
5625
|
-
} else {
|
|
5626
|
-
console.log(`Refreshed ${count} cached secret${count === 1 ? "" : "s"}.`);
|
|
5006
|
+
const t = await resolveTarget(args);
|
|
5007
|
+
const format = args.format;
|
|
5008
|
+
if (!["dotenv", "json", "shell"].includes(format)) {
|
|
5009
|
+
fail(new Error(`Unknown format "${args.format}" \u2014 use dotenv, json, or shell.`));
|
|
5627
5010
|
}
|
|
5011
|
+
const { secrets } = await fetchSecretsValues(DEPLOY_URL6, t.token, t.appId, t.configName);
|
|
5012
|
+
process.stdout.write(formatSecretsDownload(secrets, format));
|
|
5628
5013
|
} catch (err) {
|
|
5629
|
-
|
|
5630
|
-
process.exit(1);
|
|
5014
|
+
fail(err);
|
|
5631
5015
|
}
|
|
5632
5016
|
}
|
|
5633
5017
|
});
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
project: typeof args.project === "string" && args.project ? args.project : void 0,
|
|
5638
|
-
wranglerEnv: wranglerEnv(args)
|
|
5639
|
-
});
|
|
5640
|
-
const sources = positionals(args, "sourceConfig");
|
|
5641
|
-
if (sources.length > 1) throw new Error("Only one source config can be provided.");
|
|
5642
|
-
const sourceConfigName = validateConfigName(
|
|
5643
|
-
sources[0] ?? (typeof args.config === "string" && args.config ? args.config : baseTarget.configName)
|
|
5644
|
-
);
|
|
5645
|
-
if (typeof args.name !== "string" || !args.name) {
|
|
5646
|
-
throw new Error("Destination config name required; pass --name <new-config>.");
|
|
5647
|
-
}
|
|
5648
|
-
const target = {
|
|
5649
|
-
projectName: baseTarget.projectName,
|
|
5650
|
-
configName: validateConfigName(args.name),
|
|
5651
|
-
wranglerEnv: baseTarget.wranglerEnv
|
|
5652
|
-
};
|
|
5653
|
-
return { appDir: dir, sourceConfigName, target };
|
|
5654
|
-
}
|
|
5655
|
-
var configsClone = defineCommand19({
|
|
5656
|
-
meta: { name: "clone", description: "Create a remote config by copying another config server-side" },
|
|
5657
|
-
args: {
|
|
5658
|
-
...commonArgs(),
|
|
5659
|
-
sourceConfig: { type: "positional", description: "Source config name (default: --config or linked/default config)", required: false },
|
|
5660
|
-
name: { type: "string", description: "Destination config name", required: true }
|
|
5661
|
-
},
|
|
5018
|
+
var pull = defineCommand20({
|
|
5019
|
+
meta: { name: "pull", description: "Refresh the .dev.vars cache from the app store" },
|
|
5020
|
+
args: { ...COMMON_ARGS },
|
|
5662
5021
|
async run({ args }) {
|
|
5663
5022
|
try {
|
|
5664
|
-
const
|
|
5665
|
-
const
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5673
|
-
);
|
|
5674
|
-
const count = await refreshCacheIfLinked(token, dir, target);
|
|
5675
|
-
if (args.json) {
|
|
5676
|
-
process.stdout.write(JSON.stringify({ ...result, target, sourceConfigName, ...cacheJson(count) }) + "\n");
|
|
5023
|
+
const wranglerEnv = args.env?.trim() || void 0;
|
|
5024
|
+
const appDir = findAppDir();
|
|
5025
|
+
if (!appDir) fail(new Error("Run from a DeepSpace app directory (one containing wrangler.toml)."));
|
|
5026
|
+
const t = await resolveTarget(args);
|
|
5027
|
+
const ownerId = decodeJwtPayload(t.token).sub;
|
|
5028
|
+
const pulled = await pullAppSecretsCache(DEPLOY_URL6, t.token, t.appId, t.configName);
|
|
5029
|
+
if (!pulled) {
|
|
5030
|
+
console.log(`No secrets in ${t.configName} yet \u2014 nothing to pull.`);
|
|
5677
5031
|
return;
|
|
5678
5032
|
}
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
if (count === null) {
|
|
5688
|
-
unlinkedTargetNotice(target);
|
|
5689
|
-
} else {
|
|
5690
|
-
console.log(`Refreshed ${count} cached secret${count === 1 ? "" : "s"}.`);
|
|
5691
|
-
}
|
|
5033
|
+
await writeDevVars(appDir, ownerId, t.token, wranglerEnv, {
|
|
5034
|
+
appId: t.appId,
|
|
5035
|
+
generatedSecretsCache: renderSecretsCache(pulled.values, pulled),
|
|
5036
|
+
sharedDevVarsCache: true
|
|
5037
|
+
});
|
|
5038
|
+
console.log(
|
|
5039
|
+
`Pulled ${Object.keys(pulled.values).length} secrets (${t.configName}) into .dev.vars.`
|
|
5040
|
+
);
|
|
5692
5041
|
} catch (err) {
|
|
5693
|
-
|
|
5694
|
-
process.exit(1);
|
|
5042
|
+
fail(err);
|
|
5695
5043
|
}
|
|
5696
5044
|
}
|
|
5697
5045
|
});
|
|
5698
|
-
var
|
|
5699
|
-
meta: { name: "
|
|
5700
|
-
args: {
|
|
5701
|
-
...commonArgs(),
|
|
5702
|
-
name: { type: "positional", description: "Config name", required: false }
|
|
5703
|
-
},
|
|
5046
|
+
var configsList = defineCommand20({
|
|
5047
|
+
meta: { name: "list", description: "List the app\u2019s configs" },
|
|
5048
|
+
args: { ...COMMON_ARGS },
|
|
5704
5049
|
async run({ args }) {
|
|
5705
5050
|
try {
|
|
5706
|
-
const
|
|
5707
|
-
const
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
if (typeof rawName !== "string" || !rawName) throw new Error("Config name required");
|
|
5711
|
-
const configName = validateConfigName(rawName);
|
|
5712
|
-
const link = resolveSecretsLink(dir, target.wranglerEnv);
|
|
5713
|
-
const deletingLinked = link.linked && link.projectName === target.projectName && link.configName === configName;
|
|
5714
|
-
await deleteConfig(
|
|
5715
|
-
DEPLOY_URL5,
|
|
5716
|
-
token,
|
|
5717
|
-
target.projectName,
|
|
5718
|
-
configName,
|
|
5719
|
-
appScopedSecretsApiOptions(dir, target)
|
|
5720
|
-
);
|
|
5721
|
-
if (deletingLinked) {
|
|
5722
|
-
removeWranglerSecretsLink(dir, target.wranglerEnv);
|
|
5723
|
-
clearSecretsCacheFile(dir);
|
|
5724
|
-
}
|
|
5725
|
-
if (args.json) {
|
|
5726
|
-
process.stdout.write(JSON.stringify({
|
|
5727
|
-
ok: true,
|
|
5728
|
-
deleted: configName,
|
|
5729
|
-
projectName: target.projectName,
|
|
5730
|
-
unlinked: deletingLinked,
|
|
5731
|
-
cachedSecrets: deletingLinked ? 0 : null
|
|
5732
|
-
}) + "\n");
|
|
5051
|
+
const t = await resolveTarget(args);
|
|
5052
|
+
const { configs: configs2 } = await listConfigs(DEPLOY_URL6, t.token, t.appId);
|
|
5053
|
+
if (configs2.length === 0) {
|
|
5054
|
+
console.log("No configs yet \u2014 the first `secrets set` creates one.");
|
|
5733
5055
|
return;
|
|
5734
5056
|
}
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
console.log("Deleted config was linked; removed the local link and cleared generated cache.");
|
|
5738
|
-
redeployReminder({ ...target, configName });
|
|
5057
|
+
for (const cfg of configs2) {
|
|
5058
|
+
console.log(`${cfg.name} (${cfg.secretCount ?? 0} secrets, updated ${cfg.updatedAt})`);
|
|
5739
5059
|
}
|
|
5740
5060
|
} catch (err) {
|
|
5741
|
-
|
|
5742
|
-
process.exit(1);
|
|
5061
|
+
fail(err);
|
|
5743
5062
|
}
|
|
5744
5063
|
}
|
|
5745
5064
|
});
|
|
5746
|
-
var
|
|
5747
|
-
meta: { name: "
|
|
5748
|
-
args:
|
|
5065
|
+
var configsCreate = defineCommand20({
|
|
5066
|
+
meta: { name: "create", description: "Create a config (optionally copying another)" },
|
|
5067
|
+
args: {
|
|
5068
|
+
...COMMON_ARGS,
|
|
5069
|
+
name: { type: "positional", description: "Config name", required: true },
|
|
5070
|
+
"copy-from": { type: "string", description: "Copy secrets from this config", required: false }
|
|
5071
|
+
},
|
|
5749
5072
|
async run({ args }) {
|
|
5750
5073
|
try {
|
|
5751
|
-
const
|
|
5752
|
-
|
|
5753
|
-
await
|
|
5754
|
-
|
|
5755
|
-
const migration = await importLegacyDevVarsSecrets(dir, target, remoteCheck);
|
|
5756
|
-
if (args.json) {
|
|
5757
|
-
process.stdout.write(
|
|
5758
|
-
JSON.stringify({ ok: true, target, remoteCheck, ...migration && { migration } }) + "\n"
|
|
5759
|
-
);
|
|
5760
|
-
return;
|
|
5761
|
-
}
|
|
5762
|
-
console.log(`Linked ${target.wranglerEnv ? `[env.${target.wranglerEnv}]` : "top-level app"} to ${target.projectName}/${target.configName}.`);
|
|
5763
|
-
if (remoteCheck.status === "created") {
|
|
5764
|
-
console.log(`Created remote config ${target.projectName}/${target.configName}.`);
|
|
5765
|
-
}
|
|
5766
|
-
printLegacySecretsImport(migration, target);
|
|
5767
|
-
printLinkRemoteWarning(remoteCheck);
|
|
5074
|
+
const t = await resolveTarget(args);
|
|
5075
|
+
const name = validateConfigName(args.name);
|
|
5076
|
+
await createConfig(DEPLOY_URL6, t.token, t.appId, name, args["copy-from"] || void 0);
|
|
5077
|
+
console.log(`Created ${name}.`);
|
|
5768
5078
|
} catch (err) {
|
|
5769
|
-
|
|
5770
|
-
process.exit(1);
|
|
5079
|
+
fail(err);
|
|
5771
5080
|
}
|
|
5772
5081
|
}
|
|
5773
5082
|
});
|
|
5774
|
-
var
|
|
5775
|
-
meta: { name: "
|
|
5083
|
+
var configsDelete = defineCommand20({
|
|
5084
|
+
meta: { name: "delete", description: "Delete a config and all of its secrets" },
|
|
5776
5085
|
args: {
|
|
5777
|
-
...
|
|
5778
|
-
name: { type: "positional", description: "
|
|
5086
|
+
...COMMON_ARGS,
|
|
5087
|
+
name: { type: "positional", description: "Config name", required: true }
|
|
5779
5088
|
},
|
|
5780
5089
|
async run({ args }) {
|
|
5781
5090
|
try {
|
|
5782
|
-
const
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
await bestEffortClearServerSecretsLink(dir, targetEnv);
|
|
5787
|
-
}
|
|
5788
|
-
if (args.json) {
|
|
5789
|
-
process.stdout.write(JSON.stringify({ ok: true, unset, env: targetEnv ?? null, cachedSecrets: 0 }) + "\n");
|
|
5790
|
-
return;
|
|
5791
|
-
}
|
|
5792
|
-
console.log(`Unset ${unset.join(", ")} for ${targetEnv ? `[env.${targetEnv}]` : "top-level app"} secrets config.`);
|
|
5793
|
-
console.log("Cleared generated secrets cache for that target.");
|
|
5091
|
+
const t = await resolveTarget(args);
|
|
5092
|
+
const name = validateConfigName(args.name);
|
|
5093
|
+
await deleteConfig(DEPLOY_URL6, t.token, t.appId, name);
|
|
5094
|
+
console.log(`Deleted ${name}.`);
|
|
5794
5095
|
} catch (err) {
|
|
5795
|
-
|
|
5796
|
-
process.exit(1);
|
|
5096
|
+
fail(err);
|
|
5797
5097
|
}
|
|
5798
5098
|
}
|
|
5799
5099
|
});
|
|
5800
|
-
var
|
|
5801
|
-
meta: { name: "
|
|
5802
|
-
subCommands: {
|
|
5803
|
-
unset: configureUnset
|
|
5804
|
-
}
|
|
5805
|
-
});
|
|
5806
|
-
var configs = defineCommand19({
|
|
5807
|
-
meta: { name: "configs", description: "Manage remote secrets configs" },
|
|
5808
|
-
subCommands: {
|
|
5809
|
-
list: configsList,
|
|
5810
|
-
create: configsCreate,
|
|
5811
|
-
clone: configsClone,
|
|
5812
|
-
delete: configsDelete
|
|
5813
|
-
}
|
|
5100
|
+
var configs = defineCommand20({
|
|
5101
|
+
meta: { name: "configs", description: "Manage the app\u2019s secrets configs" },
|
|
5102
|
+
subCommands: { list: configsList, create: configsCreate, delete: configsDelete }
|
|
5814
5103
|
});
|
|
5815
|
-
var secrets_default =
|
|
5816
|
-
meta: { name: "secrets", description: "Manage
|
|
5104
|
+
var secrets_default = defineCommand20({
|
|
5105
|
+
meta: { name: "secrets", description: "Manage the app\u2019s secrets store" },
|
|
5817
5106
|
subCommands: {
|
|
5818
5107
|
list: list5,
|
|
5819
5108
|
set,
|
|
5820
5109
|
get,
|
|
5821
5110
|
delete: del2,
|
|
5822
|
-
pull,
|
|
5823
|
-
download,
|
|
5824
5111
|
upload,
|
|
5825
|
-
|
|
5826
|
-
|
|
5112
|
+
download,
|
|
5113
|
+
pull,
|
|
5827
5114
|
configs
|
|
5828
5115
|
}
|
|
5829
5116
|
});
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5117
|
+
|
|
5118
|
+
// src/cli/commands/init.ts
|
|
5119
|
+
import { defineCommand as defineCommand21 } from "citty";
|
|
5120
|
+
var init_default = defineCommand21({
|
|
5121
|
+
meta: {
|
|
5122
|
+
name: "init",
|
|
5123
|
+
description: "Mint this app\u2019s immutable DEEPSPACE_APP_ID into wrangler.toml"
|
|
5124
|
+
},
|
|
5125
|
+
args: {
|
|
5126
|
+
"new-id": {
|
|
5127
|
+
type: "boolean",
|
|
5128
|
+
description: "Replace the existing id \u2014 forks this repo as a SEPARATE app (new data, new secrets, new registration). The original app keeps running.",
|
|
5129
|
+
default: false
|
|
5130
|
+
},
|
|
5131
|
+
env: {
|
|
5132
|
+
type: "string",
|
|
5133
|
+
alias: "e",
|
|
5134
|
+
description: "wrangler.toml [env.<name>] block to stamp (each env is its own app)",
|
|
5135
|
+
required: false
|
|
5136
|
+
}
|
|
5137
|
+
},
|
|
5138
|
+
async run({ args }) {
|
|
5139
|
+
const appDir = findAppDir();
|
|
5140
|
+
if (!appDir) {
|
|
5141
|
+
console.error("Run from a DeepSpace app directory (one containing wrangler.toml).");
|
|
5142
|
+
process.exit(1);
|
|
5143
|
+
}
|
|
5144
|
+
const envName = args.env || void 0;
|
|
5145
|
+
const existing = readAppId(appDir, envName);
|
|
5146
|
+
if (existing && !args["new-id"]) {
|
|
5147
|
+
console.log(`Already initialized: ${existing}${envName ? ` (env: ${envName})` : ""}`);
|
|
5148
|
+
return;
|
|
5149
|
+
}
|
|
5150
|
+
const appId = mintAppId();
|
|
5151
|
+
writeAppId(appDir, appId, { wranglerEnv: envName, force: args["new-id"] });
|
|
5152
|
+
if (existing) {
|
|
5153
|
+
console.log(`Forked: ${existing} \u2192 ${appId}${envName ? ` (env: ${envName})` : ""}`);
|
|
5154
|
+
console.log("The next deploy registers this as a NEW app; the original is untouched.");
|
|
5155
|
+
} else {
|
|
5156
|
+
console.log(`Minted ${appId}${envName ? ` (env: ${envName})` : ""} \u2014 commit wrangler.toml.`);
|
|
5157
|
+
console.log("The first deploy registers it and claims the `name` subdomain.");
|
|
5840
5158
|
}
|
|
5841
|
-
return out;
|
|
5842
|
-
}
|
|
5843
|
-
if (inputs.length === 1) {
|
|
5844
|
-
const raw = inputs[0];
|
|
5845
|
-
throw new Error(`Secret value required for "${raw}". Use KEY=value or KEY value.`);
|
|
5846
|
-
}
|
|
5847
|
-
if (inputs.length === 2 && !inputs[0].includes("=")) {
|
|
5848
|
-
const key = validateSecretName(inputs[0]);
|
|
5849
|
-
const value = inputs[1];
|
|
5850
|
-
validateSecretValue(key, value);
|
|
5851
|
-
out[key] = value;
|
|
5852
|
-
return out;
|
|
5853
5159
|
}
|
|
5854
|
-
|
|
5855
|
-
}
|
|
5856
|
-
function readExistingFile(path) {
|
|
5857
|
-
const full = resolve10(path);
|
|
5858
|
-
if (!existsSync14(full)) throw new Error(`File not found: ${path}`);
|
|
5859
|
-
return readFileSync13(full, "utf-8");
|
|
5860
|
-
}
|
|
5160
|
+
});
|
|
5861
5161
|
|
|
5862
5162
|
// src/cli/cli.ts
|
|
5863
5163
|
var __dirname = dirname4(fileURLToPath2(import.meta.url));
|
|
5864
|
-
var pkg = JSON.parse(readFileSync14(
|
|
5865
|
-
if (process.platform === "linux" && !process.env.SSL_CERT_FILE &&
|
|
5164
|
+
var pkg = JSON.parse(readFileSync14(join16(__dirname, "..", "package.json"), "utf-8"));
|
|
5165
|
+
if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync14("/etc/ssl/certs/ca-certificates.crt")) {
|
|
5866
5166
|
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
5867
5167
|
}
|
|
5868
|
-
var create2 =
|
|
5168
|
+
var create2 = defineCommand22({
|
|
5869
5169
|
meta: {
|
|
5870
5170
|
name: "create",
|
|
5871
5171
|
description: "Create a new DeepSpace app"
|
|
@@ -5884,13 +5184,14 @@ var create2 = defineCommand20({
|
|
|
5884
5184
|
execSync4(`npm create deepspace@latest${name}`, { stdio: "inherit" });
|
|
5885
5185
|
}
|
|
5886
5186
|
});
|
|
5887
|
-
var main =
|
|
5187
|
+
var main = defineCommand22({
|
|
5888
5188
|
meta: {
|
|
5889
5189
|
name: "deepspace",
|
|
5890
5190
|
version: pkg.version,
|
|
5891
5191
|
description: "DeepSpace SDK CLI"
|
|
5892
5192
|
},
|
|
5893
5193
|
subCommands: {
|
|
5194
|
+
init: init_default,
|
|
5894
5195
|
create: create2,
|
|
5895
5196
|
login: login_default,
|
|
5896
5197
|
logout: logout_default,
|
|
@@ -5908,6 +5209,7 @@ var main = defineCommand20({
|
|
|
5908
5209
|
undeploy: undeploy_default,
|
|
5909
5210
|
domain: domain_default,
|
|
5910
5211
|
collaborators: collaborators_default,
|
|
5212
|
+
transfer: transfer_default,
|
|
5911
5213
|
library: library_default,
|
|
5912
5214
|
feedback: feedback_default,
|
|
5913
5215
|
secrets: secrets_default
|