deepspace 0.5.3 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +386 -331
- package/dist/cli.js.map +1 -1
- package/dist/testing.d.ts +115 -0
- package/features/cron/feature.json +1 -2
- package/features/landing/feature.json +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/cli/cli.ts
|
|
4
4
|
import { defineCommand as defineCommand22, runMain } from "citty";
|
|
5
5
|
import { execSync as execSync4 } from "child_process";
|
|
6
|
-
import { readFileSync as
|
|
6
|
+
import { readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
|
|
7
7
|
import { dirname as dirname4, join as join16 } from "path";
|
|
8
8
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9
9
|
|
|
@@ -77,8 +77,215 @@ function createSpinner() {
|
|
|
77
77
|
// src/cli/env.ts
|
|
78
78
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
79
79
|
|
|
80
|
+
// src/server/rooms/binding-manifest.ts
|
|
81
|
+
var AUTO_PROVISION_SENTINEL = "auto";
|
|
82
|
+
var VECTORIZE_METRICS = /* @__PURE__ */ new Set(["cosine", "euclidean", "dot-product"]);
|
|
83
|
+
var ALLOWED_BINDING_TYPES = /* @__PURE__ */ new Set([
|
|
84
|
+
"vectorize",
|
|
85
|
+
"ai",
|
|
86
|
+
"r2_bucket",
|
|
87
|
+
"kv_namespace",
|
|
88
|
+
"d1",
|
|
89
|
+
"queue",
|
|
90
|
+
"browser_rendering",
|
|
91
|
+
"analytics_engine",
|
|
92
|
+
"hyperdrive"
|
|
93
|
+
]);
|
|
94
|
+
var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
|
|
95
|
+
"ASSETS",
|
|
96
|
+
"PLATFORM_WORKER",
|
|
97
|
+
"API_WORKER",
|
|
98
|
+
"APP_NAME",
|
|
99
|
+
"OWNER_USER_ID",
|
|
100
|
+
"AUTH_JWT_PUBLIC_KEY",
|
|
101
|
+
"AUTH_JWT_ISSUER",
|
|
102
|
+
"AUTH_WORKER_URL",
|
|
103
|
+
"APP_IDENTITY_TOKEN",
|
|
104
|
+
"APP_OWNER_JWT",
|
|
105
|
+
"USAGE_EVENTS"
|
|
106
|
+
]);
|
|
107
|
+
function validateBindingManifest(manifest) {
|
|
108
|
+
if (!Array.isArray(manifest)) {
|
|
109
|
+
return { valid: false, errors: [{ reason: "Manifest must be an array" }] };
|
|
110
|
+
}
|
|
111
|
+
const errors = [];
|
|
112
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
113
|
+
for (const entry of manifest) {
|
|
114
|
+
if (!entry || typeof entry !== "object") {
|
|
115
|
+
errors.push({ reason: `Entry must be an object (got ${entry === null ? "null" : typeof entry})` });
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const e = entry;
|
|
119
|
+
const type = e.type;
|
|
120
|
+
const name = e.name;
|
|
121
|
+
if (typeof type !== "string" || !ALLOWED_BINDING_TYPES.has(type)) {
|
|
122
|
+
errors.push({
|
|
123
|
+
reason: `Disallowed binding type: ${typeof type === "string" ? type : `(${typeof type})`}. Allowed: ${[...ALLOWED_BINDING_TYPES].sort().join(", ")}`
|
|
124
|
+
});
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (typeof name !== "string" || !name) {
|
|
128
|
+
errors.push({ reason: `Binding of type "${type}" missing 'name' (string)` });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (RESERVED_BINDING_NAMES.has(name)) {
|
|
132
|
+
errors.push({ reason: `Binding name "${name}" is reserved by the SDK` });
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (seenNames.has(name)) {
|
|
136
|
+
errors.push({ reason: `Duplicate binding name "${name}"` });
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
seenNames.add(name);
|
|
140
|
+
const binding = entry;
|
|
141
|
+
const fieldErr = requiredFieldError(binding);
|
|
142
|
+
if (fieldErr) errors.push({ binding, reason: fieldErr });
|
|
143
|
+
}
|
|
144
|
+
if (errors.length) return { valid: false, errors };
|
|
145
|
+
return { valid: true, bindings: manifest };
|
|
146
|
+
}
|
|
147
|
+
function requiredFieldError(b) {
|
|
148
|
+
switch (b.type) {
|
|
149
|
+
case "vectorize": {
|
|
150
|
+
if (!b.index_name) return `vectorize binding "${b.name}" missing index_name (or "auto")`;
|
|
151
|
+
if (b.index_name === AUTO_PROVISION_SENTINEL) {
|
|
152
|
+
if (typeof b.dimensions !== "number" || b.dimensions <= 0) {
|
|
153
|
+
return `vectorize binding "${b.name}" with index_name="auto" requires "dimensions" (positive number)`;
|
|
154
|
+
}
|
|
155
|
+
if (!b.metric || !VECTORIZE_METRICS.has(b.metric)) {
|
|
156
|
+
return `vectorize binding "${b.name}" with index_name="auto" requires "metric" (one of: ${[...VECTORIZE_METRICS].join(", ")})`;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
case "r2_bucket":
|
|
162
|
+
return b.bucket_name ? null : `r2_bucket binding "${b.name}" missing bucket_name (or "auto")`;
|
|
163
|
+
case "kv_namespace": {
|
|
164
|
+
if (!b.namespace_id) return `kv_namespace binding "${b.name}" missing namespace_id (or "auto")`;
|
|
165
|
+
if (b.namespace_id === AUTO_PROVISION_SENTINEL && !b.title) {
|
|
166
|
+
return `kv_namespace binding "${b.name}" with namespace_id="auto" requires "title"`;
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
case "d1": {
|
|
171
|
+
if (!b.id) return `d1 binding "${b.name}" missing id (or "auto")`;
|
|
172
|
+
if (b.id === AUTO_PROVISION_SENTINEL && !b.database_name) {
|
|
173
|
+
return `d1 binding "${b.name}" with id="auto" requires "database_name"`;
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
case "queue":
|
|
178
|
+
return b.queue_name ? null : `queue binding "${b.name}" missing queue_name (or "auto")`;
|
|
179
|
+
case "hyperdrive":
|
|
180
|
+
if (!b.id) return `hyperdrive binding "${b.name}" missing id`;
|
|
181
|
+
if (b.id === AUTO_PROVISION_SENTINEL) {
|
|
182
|
+
return `hyperdrive binding "${b.name}": auto-provisioning not yet supported (provide a real id)`;
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
case "ai":
|
|
186
|
+
case "browser_rendering":
|
|
187
|
+
case "analytics_engine":
|
|
188
|
+
return null;
|
|
189
|
+
// no required fields beyond `name`
|
|
190
|
+
default: {
|
|
191
|
+
const _exhaustive = b;
|
|
192
|
+
void _exhaustive;
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function bindingManifestFromOutputConfig(outputConfig) {
|
|
198
|
+
const out = [];
|
|
199
|
+
const aiBinding = pluckString(outputConfig.ai, "binding");
|
|
200
|
+
if (aiBinding) out.push({ type: "ai", name: aiBinding });
|
|
201
|
+
for (const v of asObjectArray(outputConfig.vectorize)) {
|
|
202
|
+
const name = pluckString(v, "binding");
|
|
203
|
+
const index_name = pluckString(v, "index_name");
|
|
204
|
+
if (!name || !index_name) continue;
|
|
205
|
+
const dimensions = pluckNumber(v, "dimensions");
|
|
206
|
+
const metric = pluckString(v, "metric");
|
|
207
|
+
out.push({
|
|
208
|
+
type: "vectorize",
|
|
209
|
+
name,
|
|
210
|
+
index_name,
|
|
211
|
+
...dimensions != null && { dimensions },
|
|
212
|
+
...metric && (metric === "cosine" || metric === "euclidean" || metric === "dot-product") && { metric }
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
for (const r of asObjectArray(outputConfig.r2_buckets)) {
|
|
216
|
+
const name = pluckString(r, "binding");
|
|
217
|
+
const bucket_name = pluckString(r, "bucket_name");
|
|
218
|
+
if (name && bucket_name) out.push({ type: "r2_bucket", name, bucket_name });
|
|
219
|
+
}
|
|
220
|
+
for (const k of asObjectArray(outputConfig.kv_namespaces)) {
|
|
221
|
+
const name = pluckString(k, "binding");
|
|
222
|
+
const namespace_id = pluckString(k, "id");
|
|
223
|
+
if (!name || !namespace_id) continue;
|
|
224
|
+
const title = pluckString(k, "title");
|
|
225
|
+
out.push({
|
|
226
|
+
type: "kv_namespace",
|
|
227
|
+
name,
|
|
228
|
+
namespace_id,
|
|
229
|
+
...title && { title }
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
for (const d of asObjectArray(outputConfig.d1_databases)) {
|
|
233
|
+
const name = pluckString(d, "binding");
|
|
234
|
+
const id = pluckString(d, "database_id");
|
|
235
|
+
if (!name || !id) continue;
|
|
236
|
+
const database_name = pluckString(d, "database_name");
|
|
237
|
+
out.push({
|
|
238
|
+
type: "d1",
|
|
239
|
+
name,
|
|
240
|
+
id,
|
|
241
|
+
...database_name && { database_name }
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
const queues = isObject(outputConfig.queues) ? outputConfig.queues : null;
|
|
245
|
+
for (const q of asObjectArray(queues?.producers)) {
|
|
246
|
+
const name = pluckString(q, "binding");
|
|
247
|
+
const queue_name = pluckString(q, "queue");
|
|
248
|
+
if (name && queue_name) out.push({ type: "queue", name, queue_name });
|
|
249
|
+
}
|
|
250
|
+
const browserBinding = pluckString(outputConfig.browser, "binding");
|
|
251
|
+
if (browserBinding) out.push({ type: "browser_rendering", name: browserBinding });
|
|
252
|
+
for (const a of asObjectArray(outputConfig.analytics_engine_datasets)) {
|
|
253
|
+
const name = pluckString(a, "binding");
|
|
254
|
+
const dataset = pluckString(a, "dataset");
|
|
255
|
+
if (name) out.push({ type: "analytics_engine", name, dataset: dataset ?? void 0 });
|
|
256
|
+
}
|
|
257
|
+
for (const h of asObjectArray(outputConfig.hyperdrive)) {
|
|
258
|
+
const name = pluckString(h, "binding");
|
|
259
|
+
const id = pluckString(h, "id");
|
|
260
|
+
if (name && id) out.push({ type: "hyperdrive", name, id });
|
|
261
|
+
}
|
|
262
|
+
return out;
|
|
263
|
+
}
|
|
264
|
+
function isObject(v) {
|
|
265
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
266
|
+
}
|
|
267
|
+
function pluckString(v, key) {
|
|
268
|
+
if (!isObject(v)) return null;
|
|
269
|
+
const val = v[key];
|
|
270
|
+
return typeof val === "string" && val.length > 0 ? val : null;
|
|
271
|
+
}
|
|
272
|
+
function pluckNumber(v, key) {
|
|
273
|
+
if (!isObject(v)) return null;
|
|
274
|
+
const val = v[key];
|
|
275
|
+
return typeof val === "number" && Number.isFinite(val) ? val : null;
|
|
276
|
+
}
|
|
277
|
+
function asObjectArray(v) {
|
|
278
|
+
if (!Array.isArray(v)) return [];
|
|
279
|
+
return v.filter(isObject);
|
|
280
|
+
}
|
|
281
|
+
|
|
80
282
|
// src/cli/lib/secrets.ts
|
|
81
283
|
var GENERATED_SECRETS_DIVIDER = "# --- DeepSpace secrets cache: generated by `npx deepspace secrets`; do not edit manually ---";
|
|
284
|
+
var RESERVED_SECRET_NAMES = /* @__PURE__ */ new Set([
|
|
285
|
+
...RESERVED_BINDING_NAMES,
|
|
286
|
+
"API_WORKER_URL",
|
|
287
|
+
"PLATFORM_WORKER_URL"
|
|
288
|
+
]);
|
|
82
289
|
var SECRET_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
83
290
|
var CONFIG_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
84
291
|
var MAX_SECRET_VALUE_BYTES = 32 * 1024;
|
|
@@ -96,6 +303,11 @@ function validateSecretName(name) {
|
|
|
96
303
|
`Invalid secret name "${name}". Use letters, numbers, and underscores; start with a letter or underscore.`
|
|
97
304
|
);
|
|
98
305
|
}
|
|
306
|
+
if (RESERVED_SECRET_NAMES.has(name)) {
|
|
307
|
+
throw new Error(
|
|
308
|
+
`"${name}" is managed by the DeepSpace SDK and can't be set as an app secret \u2014 the platform injects it at deploy time.`
|
|
309
|
+
);
|
|
310
|
+
}
|
|
99
311
|
return name;
|
|
100
312
|
}
|
|
101
313
|
function validateSecretValue(key, value) {
|
|
@@ -117,7 +329,10 @@ async function secretsApi(deployUrl, token, path, init = {}) {
|
|
|
117
329
|
});
|
|
118
330
|
const body = await res.json().catch(() => ({}));
|
|
119
331
|
if (!res.ok) {
|
|
120
|
-
|
|
332
|
+
const err = new Error(body.error ?? `Secrets request failed (${res.status})`);
|
|
333
|
+
err.status = res.status;
|
|
334
|
+
err.apiPath = path;
|
|
335
|
+
throw err;
|
|
121
336
|
}
|
|
122
337
|
return body;
|
|
123
338
|
}
|
|
@@ -177,8 +392,7 @@ async function pullAppSecretsCache(deployUrl, token, appId, configName) {
|
|
|
177
392
|
const { secrets } = await fetchSecretsValues(deployUrl, token, appId, configName);
|
|
178
393
|
return { appId, configName, values: secrets };
|
|
179
394
|
} catch (err) {
|
|
180
|
-
|
|
181
|
-
if (msg.includes("(404)")) return null;
|
|
395
|
+
if (err?.status === 404) return null;
|
|
182
396
|
throw err;
|
|
183
397
|
}
|
|
184
398
|
}
|
|
@@ -230,6 +444,7 @@ function parseSecretsUpload(content) {
|
|
|
230
444
|
if (eq <= 0) continue;
|
|
231
445
|
const key = line.slice(0, eq).trim();
|
|
232
446
|
if (!SECRET_NAME_RE.test(key)) continue;
|
|
447
|
+
validateSecretName(key);
|
|
233
448
|
let value = line.slice(eq + 1).trim();
|
|
234
449
|
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
235
450
|
value = unescapeDotenv(value.slice(1, -1));
|
|
@@ -251,13 +466,16 @@ function unescapeDotenv(v) {
|
|
|
251
466
|
}
|
|
252
467
|
return out;
|
|
253
468
|
}
|
|
469
|
+
function shellSingleQuote(value) {
|
|
470
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
471
|
+
}
|
|
254
472
|
function formatSecretsDownload(secrets, format) {
|
|
255
473
|
const entries = Object.entries(secrets).sort(([a], [b]) => a.localeCompare(b));
|
|
256
474
|
if (format === "json") {
|
|
257
475
|
return JSON.stringify(Object.fromEntries(entries), null, 2) + "\n";
|
|
258
476
|
}
|
|
259
477
|
if (format === "shell") {
|
|
260
|
-
return entries.map(([k, v]) => `export ${k}=${
|
|
478
|
+
return entries.map(([k, v]) => `export ${k}=${shellSingleQuote(v)}`).join("\n") + "\n";
|
|
261
479
|
}
|
|
262
480
|
return entries.map(([k, v]) => `${k}=${quoteDotenvValue(v)}`).join("\n") + "\n";
|
|
263
481
|
}
|
|
@@ -277,7 +495,7 @@ async function refreshSecretsCache(deployUrl, token, appId, wranglerEnv) {
|
|
|
277
495
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
278
496
|
import { join, resolve } from "path";
|
|
279
497
|
import { parse as parseToml } from "smol-toml";
|
|
280
|
-
var APP_ID_RE = /^app_[0-9A-HJKMNP-TV-Z]{26}$/;
|
|
498
|
+
var APP_ID_RE = /^(app_[0-9A-HJKMNP-TV-Z]{26}|[a-z0-9][a-z0-9_-]{0,63})$/;
|
|
281
499
|
var CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
282
500
|
function mintAppId(now = Date.now()) {
|
|
283
501
|
let ts = "";
|
|
@@ -690,38 +908,6 @@ function extractCustomDevVars(content) {
|
|
|
690
908
|
while (out.length && out[out.length - 1].trim() === "") out.pop();
|
|
691
909
|
return out.join("\n");
|
|
692
910
|
}
|
|
693
|
-
function removeDevVarsKeys(content, keys) {
|
|
694
|
-
const lines = content.split("\n");
|
|
695
|
-
const out = [];
|
|
696
|
-
let i = 0;
|
|
697
|
-
while (i < lines.length) {
|
|
698
|
-
const line = lines[i];
|
|
699
|
-
const eq = line.indexOf("=");
|
|
700
|
-
const keyMatch = eq >= 0 ? line.slice(0, eq).match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*$/) : null;
|
|
701
|
-
if (!keyMatch) {
|
|
702
|
-
out.push(line);
|
|
703
|
-
i++;
|
|
704
|
-
continue;
|
|
705
|
-
}
|
|
706
|
-
const key = keyMatch[1];
|
|
707
|
-
const valStart = line.slice(eq + 1);
|
|
708
|
-
const block = [line];
|
|
709
|
-
if (valStart.startsWith('"') && !hasUnescapedQuote(valStart.slice(1))) {
|
|
710
|
-
i++;
|
|
711
|
-
while (i < lines.length) {
|
|
712
|
-
block.push(lines[i]);
|
|
713
|
-
const closed = hasUnescapedQuote(lines[i]);
|
|
714
|
-
i++;
|
|
715
|
-
if (closed) break;
|
|
716
|
-
}
|
|
717
|
-
} else {
|
|
718
|
-
i++;
|
|
719
|
-
}
|
|
720
|
-
if (!keys.has(key)) out.push(...block);
|
|
721
|
-
}
|
|
722
|
-
while (out.length && out[out.length - 1].trim() === "") out.pop();
|
|
723
|
-
return out.join("\n");
|
|
724
|
-
}
|
|
725
911
|
function hasUnescapedQuote(s) {
|
|
726
912
|
let escaped = false;
|
|
727
913
|
for (const ch of s) {
|
|
@@ -820,8 +1006,10 @@ async function writeDevVars(appDir, ownerId, callerJwt, wranglerEnv, opts = {})
|
|
|
820
1006
|
`OWNER_USER_ID=${ownerId}`,
|
|
821
1007
|
`APP_OWNER_JWT=${appOwnerJwt}`,
|
|
822
1008
|
...appIdentityToken ? [`APP_IDENTITY_TOKEN=${appIdentityToken}`] : [],
|
|
823
|
-
// Open /api/debug/* (proxied to RecordRoom)
|
|
824
|
-
//
|
|
1009
|
+
// Open /api/debug/* (proxied to RecordRoom) locally. This is the SDK's
|
|
1010
|
+
// dev-only auto-value; the deploy ships the remote store, not this
|
|
1011
|
+
// `.dev.vars` section, so it never reaches prod. To enable it on a deployed
|
|
1012
|
+
// env, `deepspace secrets set ALLOW_DEBUG_ROUTES=true`.
|
|
825
1013
|
`ALLOW_DEBUG_ROUTES=true`
|
|
826
1014
|
].join("\n");
|
|
827
1015
|
const useSharedDevVarsCache = opts.sharedDevVarsCache ?? opts.generatedSecretsCache !== void 0;
|
|
@@ -1544,6 +1732,7 @@ var dev_default = defineCommand3({
|
|
|
1544
1732
|
// src/cli/commands/kill.ts
|
|
1545
1733
|
import { defineCommand as defineCommand4 } from "citty";
|
|
1546
1734
|
import { spawnSync } from "child_process";
|
|
1735
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync3, readlinkSync } from "fs";
|
|
1547
1736
|
import { setTimeout as wait } from "timers/promises";
|
|
1548
1737
|
var SIGTERM_GRACE_MS = 1500;
|
|
1549
1738
|
var IS_WIN = process.platform === "win32";
|
|
@@ -1614,7 +1803,55 @@ function listenerPids(port) {
|
|
|
1614
1803
|
`Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess`
|
|
1615
1804
|
);
|
|
1616
1805
|
}
|
|
1617
|
-
|
|
1806
|
+
const viaLsof = run(["lsof", "-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"]);
|
|
1807
|
+
if (viaLsof.length) return viaLsof;
|
|
1808
|
+
return listenerPidsViaProc(port);
|
|
1809
|
+
}
|
|
1810
|
+
function listenerPidsViaProc(port) {
|
|
1811
|
+
try {
|
|
1812
|
+
const hexPort = port.toString(16).toUpperCase().padStart(4, "0");
|
|
1813
|
+
const inodes = /* @__PURE__ */ new Set();
|
|
1814
|
+
for (const f of ["/proc/net/tcp", "/proc/net/tcp6"]) {
|
|
1815
|
+
let data;
|
|
1816
|
+
try {
|
|
1817
|
+
data = readFileSync8(f, "utf-8");
|
|
1818
|
+
} catch {
|
|
1819
|
+
continue;
|
|
1820
|
+
}
|
|
1821
|
+
for (const line of data.split("\n").slice(1)) {
|
|
1822
|
+
const cols = line.trim().split(/\s+/);
|
|
1823
|
+
if (cols[3] !== "0A") continue;
|
|
1824
|
+
if (cols[1]?.split(":")[1] === hexPort && cols[9]) inodes.add(cols[9]);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
if (!inodes.size) return [];
|
|
1828
|
+
const pids = /* @__PURE__ */ new Set();
|
|
1829
|
+
for (const pidDir of readdirSync3("/proc")) {
|
|
1830
|
+
if (!/^\d+$/.test(pidDir)) continue;
|
|
1831
|
+
let fds;
|
|
1832
|
+
try {
|
|
1833
|
+
fds = readdirSync3(`/proc/${pidDir}/fd`);
|
|
1834
|
+
} catch {
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1837
|
+
for (const fd of fds) {
|
|
1838
|
+
let link;
|
|
1839
|
+
try {
|
|
1840
|
+
link = readlinkSync(`/proc/${pidDir}/fd/${fd}`);
|
|
1841
|
+
} catch {
|
|
1842
|
+
continue;
|
|
1843
|
+
}
|
|
1844
|
+
const m = link.match(/^socket:\[(\d+)\]$/);
|
|
1845
|
+
if (m && inodes.has(m[1])) {
|
|
1846
|
+
pids.add(Number(pidDir));
|
|
1847
|
+
break;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
return [...pids];
|
|
1852
|
+
} catch {
|
|
1853
|
+
return [];
|
|
1854
|
+
}
|
|
1618
1855
|
}
|
|
1619
1856
|
function childPids(parentPid) {
|
|
1620
1857
|
if (IS_WIN) {
|
|
@@ -1935,7 +2172,7 @@ var screenshot_default = defineCommand6({
|
|
|
1935
2172
|
// src/cli/commands/test-accounts.ts
|
|
1936
2173
|
import { defineCommand as defineCommand7 } from "citty";
|
|
1937
2174
|
import * as p3 from "@clack/prompts";
|
|
1938
|
-
import { readFileSync as
|
|
2175
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
|
|
1939
2176
|
import { join as join10 } from "path";
|
|
1940
2177
|
import { homedir as homedir4 } from "os";
|
|
1941
2178
|
var SESSION_COOKIE2 = "__Secure-better-auth.session_token";
|
|
@@ -1945,7 +2182,7 @@ var ACCOUNTS_PATH = join10(DIR3, "test-accounts.json");
|
|
|
1945
2182
|
function loadAccounts() {
|
|
1946
2183
|
if (!existsSync9(ACCOUNTS_PATH)) return [];
|
|
1947
2184
|
try {
|
|
1948
|
-
return JSON.parse(
|
|
2185
|
+
return JSON.parse(readFileSync9(ACCOUNTS_PATH, "utf-8"));
|
|
1949
2186
|
} catch {
|
|
1950
2187
|
return [];
|
|
1951
2188
|
}
|
|
@@ -1955,7 +2192,7 @@ function saveAccounts(accounts) {
|
|
|
1955
2192
|
writeFileSync8(ACCOUNTS_PATH, JSON.stringify(accounts, null, 2), { mode: 384 });
|
|
1956
2193
|
}
|
|
1957
2194
|
function sessionCookie() {
|
|
1958
|
-
const token =
|
|
2195
|
+
const token = readFileSync9(SESSION_PATH2, "utf-8").trim();
|
|
1959
2196
|
return `${SESSION_COOKIE2}=${encodeURIComponent(token)}`;
|
|
1960
2197
|
}
|
|
1961
2198
|
async function fetchRemoteAccounts() {
|
|
@@ -2225,214 +2462,10 @@ var test_accounts_default = defineCommand7({
|
|
|
2225
2462
|
import { defineCommand as defineCommand8 } from "citty";
|
|
2226
2463
|
import { execSync as execSync2 } from "child_process";
|
|
2227
2464
|
import { randomUUID } from "crypto";
|
|
2228
|
-
import { readFileSync as
|
|
2465
|
+
import { readFileSync as readFileSync10, readdirSync as readdirSync4, existsSync as existsSync10 } from "fs";
|
|
2229
2466
|
import { join as join11, resolve as resolve7, dirname as dirname2 } from "path";
|
|
2230
2467
|
import { pathToFileURL } from "url";
|
|
2231
2468
|
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
|
-
}
|
|
2434
|
-
|
|
2435
|
-
// src/cli/commands/deploy.ts
|
|
2436
2469
|
var DEPLOY_URL3 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
2437
2470
|
var API_URL2 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
2438
2471
|
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.";
|
|
@@ -2603,11 +2636,26 @@ var deploy_default = defineCommand8({
|
|
|
2603
2636
|
);
|
|
2604
2637
|
}
|
|
2605
2638
|
p4.log.info(`Id: ${appId}`);
|
|
2639
|
+
const readCustomDevVarsAt = (path) => {
|
|
2640
|
+
if (!existsSync10(path)) return {};
|
|
2641
|
+
const section = extractCustomDevVars(stripGeneratedSecretsCache(readFileSync10(path, "utf-8")));
|
|
2642
|
+
if (!section) return {};
|
|
2643
|
+
try {
|
|
2644
|
+
return parseDevVars(section);
|
|
2645
|
+
} catch (err) {
|
|
2646
|
+
p4.cancel(`.dev.vars: ${errMessage(err)}`);
|
|
2647
|
+
process.exit(1);
|
|
2648
|
+
}
|
|
2649
|
+
};
|
|
2606
2650
|
let linkedSecrets = null;
|
|
2651
|
+
let localOnlyDevVars = {};
|
|
2607
2652
|
try {
|
|
2608
2653
|
const refreshed = await refreshSecretsCache(DEPLOY_URL3, token, appId, envName);
|
|
2609
2654
|
if (refreshed) {
|
|
2610
2655
|
linkedSecrets = refreshed.pulled;
|
|
2656
|
+
localOnlyDevVars = readCustomDevVarsAt(
|
|
2657
|
+
devVarsPathFor(appDir, envName, { sharedDevVarsCache: true })
|
|
2658
|
+
);
|
|
2611
2659
|
await writeDevVars(appDir, ownerId, token, envName, {
|
|
2612
2660
|
appId,
|
|
2613
2661
|
generatedSecretsCache: refreshed.rendered,
|
|
@@ -2616,7 +2664,14 @@ var deploy_default = defineCommand8({
|
|
|
2616
2664
|
p4.log.info(refreshed.summary);
|
|
2617
2665
|
}
|
|
2618
2666
|
} catch (err) {
|
|
2619
|
-
|
|
2667
|
+
const msg = errMessage(err);
|
|
2668
|
+
if (err?.status === 403) {
|
|
2669
|
+
p4.cancel(
|
|
2670
|
+
"You're not authorized to deploy this app \u2014 you must be its owner or a current collaborator. If you were a collaborator, your access may have been revoked."
|
|
2671
|
+
);
|
|
2672
|
+
} else {
|
|
2673
|
+
p4.cancel(`Failed to refresh app secrets before deploy: ${msg}`);
|
|
2674
|
+
}
|
|
2620
2675
|
process.exit(1);
|
|
2621
2676
|
}
|
|
2622
2677
|
const junk = removeMacosJunk(appDir);
|
|
@@ -2648,13 +2703,13 @@ var deploy_default = defineCommand8({
|
|
|
2648
2703
|
p4.cancel("Build output config not found at .wrangler/deploy/config.json");
|
|
2649
2704
|
process.exit(1);
|
|
2650
2705
|
}
|
|
2651
|
-
const deployConfig = JSON.parse(
|
|
2706
|
+
const deployConfig = JSON.parse(readFileSync10(deployConfigPath, "utf-8"));
|
|
2652
2707
|
const outputWranglerPath = resolve7(dirname2(deployConfigPath), deployConfig.configPath);
|
|
2653
2708
|
if (!existsSync10(outputWranglerPath)) {
|
|
2654
2709
|
p4.cancel(`Output wrangler.json not found at ${outputWranglerPath}`);
|
|
2655
2710
|
process.exit(1);
|
|
2656
2711
|
}
|
|
2657
|
-
const outputConfig = JSON.parse(
|
|
2712
|
+
const outputConfig = JSON.parse(readFileSync10(outputWranglerPath, "utf-8"));
|
|
2658
2713
|
if (outputConfig.name && outputConfig.name !== appName) {
|
|
2659
2714
|
const envHint = envName ? ` (--env ${envName})` : "";
|
|
2660
2715
|
p4.cancel(
|
|
@@ -2676,7 +2731,7 @@ var deploy_default = defineCommand8({
|
|
|
2676
2731
|
s.start("Collecting assets...");
|
|
2677
2732
|
const assets = collectAssets(clientDir);
|
|
2678
2733
|
s.stop(`Collected ${assets.length} assets`);
|
|
2679
|
-
const workerJs =
|
|
2734
|
+
const workerJs = readFileSync10(workerBundlePath, "utf-8");
|
|
2680
2735
|
const doBindings = outputConfig.durable_objects?.bindings;
|
|
2681
2736
|
const sqliteClasses = new Set(
|
|
2682
2737
|
outputConfig.migrations?.flatMap(
|
|
@@ -2708,18 +2763,13 @@ var deploy_default = defineCommand8({
|
|
|
2708
2763
|
const devVarsPath = devVarsPathFor(appDir, envName, {
|
|
2709
2764
|
sharedDevVarsCache: linkedSecrets !== null
|
|
2710
2765
|
});
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
} catch (err) {
|
|
2719
|
-
p4.cancel(`.dev.vars: ${errMessage(err)}`);
|
|
2720
|
-
process.exit(1);
|
|
2721
|
-
}
|
|
2722
|
-
}
|
|
2766
|
+
const userSecrets = linkedSecrets ? { ...linkedSecrets.values } : {};
|
|
2767
|
+
const handEditedDevVars = linkedSecrets ? localOnlyDevVars : readCustomDevVarsAt(devVarsPath);
|
|
2768
|
+
const strayDevVars = Object.keys(handEditedDevVars).filter((k) => !(k in userSecrets));
|
|
2769
|
+
if (strayDevVars.length) {
|
|
2770
|
+
p4.log.warn(
|
|
2771
|
+
`.dev.vars has ${strayDevVars.join(", ")} not in the app store \u2014 ${strayDevVars.length === 1 ? "it is" : "they are"} NOT deployed. \`.dev.vars\` is a local dev cache, not deploy config. Move ${strayDevVars.length === 1 ? "it" : "them"} into the store with \`deepspace secrets set KEY=value\` (or \`deepspace secrets upload .dev.vars\`), then redeploy.`
|
|
2772
|
+
);
|
|
2723
2773
|
}
|
|
2724
2774
|
const userSecretNames = Object.keys(userSecrets);
|
|
2725
2775
|
const declaredBindingNames = /* @__PURE__ */ new Set([
|
|
@@ -2729,22 +2779,19 @@ var deploy_default = defineCommand8({
|
|
|
2729
2779
|
for (const name of userSecretNames) {
|
|
2730
2780
|
if (RESERVED_BINDING_NAMES.has(name)) {
|
|
2731
2781
|
p4.cancel(
|
|
2732
|
-
|
|
2782
|
+
`App secret "${name}" is a reserved binding name \u2014 remove it with \`deepspace secrets delete ${name}\`.`
|
|
2733
2783
|
);
|
|
2734
2784
|
process.exit(1);
|
|
2735
2785
|
}
|
|
2736
2786
|
if (declaredBindingNames.has(name)) {
|
|
2737
2787
|
p4.cancel(
|
|
2738
|
-
|
|
2788
|
+
`App secret "${name}" collides with a binding declared in wrangler.toml \u2014 rename one or the other.`
|
|
2739
2789
|
);
|
|
2740
2790
|
process.exit(1);
|
|
2741
2791
|
}
|
|
2742
2792
|
}
|
|
2743
2793
|
if (userSecretNames.length) {
|
|
2744
|
-
p4.log.info(`App secrets
|
|
2745
|
-
if (!linkedSecrets) {
|
|
2746
|
-
p4.log.info("Not in the app store yet \u2014 migrating after this deploy.");
|
|
2747
|
-
}
|
|
2794
|
+
p4.log.info(`App secrets: ${userSecretNames.join(", ")}`);
|
|
2748
2795
|
}
|
|
2749
2796
|
s.start(`Deploying to ${appName}.app.space...`);
|
|
2750
2797
|
const extraRoutes = extractRunWorkerFirst(wranglerConfig);
|
|
@@ -2874,16 +2921,6 @@ var deploy_default = defineCommand8({
|
|
|
2874
2921
|
"First requests may briefly hit the assets transitional page; retry in a minute."
|
|
2875
2922
|
);
|
|
2876
2923
|
p4.log.success(`Live at: ${body.url}`);
|
|
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
2924
|
await syncSubscriptionPlans(appDir, appId, token);
|
|
2888
2925
|
await syncOneTimeProducts(appDir, appId, token);
|
|
2889
2926
|
p4.outro("Done");
|
|
@@ -2892,41 +2929,11 @@ var deploy_default = defineCommand8({
|
|
|
2892
2929
|
}
|
|
2893
2930
|
s.stop("Deployed!");
|
|
2894
2931
|
p4.log.success(`Live at: ${body.url}`);
|
|
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
2932
|
await syncSubscriptionPlans(appDir, appId, token);
|
|
2906
2933
|
await syncOneTimeProducts(appDir, appId, token);
|
|
2907
2934
|
p4.outro("Done");
|
|
2908
2935
|
}
|
|
2909
2936
|
});
|
|
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
2937
|
async function syncSubscriptionPlans(appDir, appName, token) {
|
|
2931
2938
|
const subsPath = join11(appDir, "src", "subscriptions.ts");
|
|
2932
2939
|
if (!existsSync10(subsPath)) return;
|
|
@@ -3155,7 +3162,7 @@ async function waitForAssetsReady(url, timeoutMs) {
|
|
|
3155
3162
|
function collectAssets(dir) {
|
|
3156
3163
|
const assets = [];
|
|
3157
3164
|
function walk(d, prefix) {
|
|
3158
|
-
for (const entry of
|
|
3165
|
+
for (const entry of readdirSync4(d, { withFileTypes: true })) {
|
|
3159
3166
|
if (entry.name === ".assetsignore") continue;
|
|
3160
3167
|
const full = join11(d, entry.name);
|
|
3161
3168
|
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
@@ -3164,7 +3171,7 @@ function collectAssets(dir) {
|
|
|
3164
3171
|
} else {
|
|
3165
3172
|
assets.push({
|
|
3166
3173
|
path: "/" + rel,
|
|
3167
|
-
contentBase64:
|
|
3174
|
+
contentBase64: readFileSync10(full).toString("base64")
|
|
3168
3175
|
});
|
|
3169
3176
|
}
|
|
3170
3177
|
}
|
|
@@ -3986,7 +3993,7 @@ var transfer_default = defineCommand15({
|
|
|
3986
3993
|
import { defineCommand as defineCommand16 } from "citty";
|
|
3987
3994
|
|
|
3988
3995
|
// src/cli/commands/_invoke-impl.ts
|
|
3989
|
-
import { readFileSync as
|
|
3996
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
3990
3997
|
var API_URL6 = process.env.DEEPSPACE_API_URL ?? PLATFORM_URLS.api;
|
|
3991
3998
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
3992
3999
|
function parseTarget(target) {
|
|
@@ -4011,7 +4018,7 @@ function resolveBody(opts) {
|
|
|
4011
4018
|
if (opts.body != null) {
|
|
4012
4019
|
raw = opts.body;
|
|
4013
4020
|
} else if (opts.bodyFile != null) {
|
|
4014
|
-
raw = opts.bodyFile === "-" ?
|
|
4021
|
+
raw = opts.bodyFile === "-" ? readFileSync11(0, "utf-8") : readFileSync11(opts.bodyFile, "utf-8");
|
|
4015
4022
|
} else {
|
|
4016
4023
|
return "{}";
|
|
4017
4024
|
}
|
|
@@ -4356,7 +4363,7 @@ var invoke_default = defineCommand17({
|
|
|
4356
4363
|
|
|
4357
4364
|
// src/cli/commands/library.ts
|
|
4358
4365
|
import { defineCommand as defineCommand18 } from "citty";
|
|
4359
|
-
import { existsSync as existsSync12, readFileSync as
|
|
4366
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
|
|
4360
4367
|
import { join as join14, resolve as resolve9 } from "path";
|
|
4361
4368
|
import { parse as parseToml4 } from "smol-toml";
|
|
4362
4369
|
import * as p6 from "@clack/prompts";
|
|
@@ -4439,7 +4446,7 @@ var publish = defineCommand18({
|
|
|
4439
4446
|
if (!existsSync12(wranglerPath)) {
|
|
4440
4447
|
return die("No --app provided and no wrangler.toml found. Either pass --app <name> or run from your app's directory.");
|
|
4441
4448
|
}
|
|
4442
|
-
const cfg = parseToml4(
|
|
4449
|
+
const cfg = parseToml4(readFileSync12(wranglerPath, "utf-8"));
|
|
4443
4450
|
const nameRes = resolveAppName(cfg.name);
|
|
4444
4451
|
if (!nameRes.ok) return die(`wrangler.toml: ${nameRes.reason}`);
|
|
4445
4452
|
appName = nameRes.name;
|
|
@@ -4573,7 +4580,7 @@ var library_default = defineCommand18({
|
|
|
4573
4580
|
// src/cli/commands/feedback.ts
|
|
4574
4581
|
import { defineCommand as defineCommand19 } from "citty";
|
|
4575
4582
|
import * as p7 from "@clack/prompts";
|
|
4576
|
-
import { existsSync as existsSync13, readFileSync as
|
|
4583
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13 } from "fs";
|
|
4577
4584
|
import { dirname as dirname3, join as join15 } from "path";
|
|
4578
4585
|
import { fileURLToPath } from "url";
|
|
4579
4586
|
import { release } from "os";
|
|
@@ -4602,7 +4609,7 @@ function readCliVersion() {
|
|
|
4602
4609
|
];
|
|
4603
4610
|
for (const path of candidates) {
|
|
4604
4611
|
if (!existsSync13(path)) continue;
|
|
4605
|
-
const pkg2 = JSON.parse(
|
|
4612
|
+
const pkg2 = JSON.parse(readFileSync13(path, "utf-8"));
|
|
4606
4613
|
if (pkg2.name === "deepspace" && pkg2.version) return pkg2.version;
|
|
4607
4614
|
}
|
|
4608
4615
|
} catch {
|
|
@@ -4812,7 +4819,7 @@ Context: deepspace ${payload.cliVersion} \xB7 ${payload.nodeVersion} \xB7 ${payl
|
|
|
4812
4819
|
|
|
4813
4820
|
// src/cli/commands/secrets.ts
|
|
4814
4821
|
import { defineCommand as defineCommand20 } from "citty";
|
|
4815
|
-
import { readFileSync as
|
|
4822
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
4816
4823
|
var DEPLOY_URL6 = process.env.DEEPSPACE_DEPLOY_URL ?? PLATFORM_URLS.deploy;
|
|
4817
4824
|
var COMMON_ARGS = {
|
|
4818
4825
|
app: {
|
|
@@ -4844,6 +4851,11 @@ async function resolveTarget(args) {
|
|
|
4844
4851
|
const appDir = findAppDir();
|
|
4845
4852
|
appId = appDir && readAppId(appDir, wranglerEnv) || void 0;
|
|
4846
4853
|
if (!appId) {
|
|
4854
|
+
if (wranglerEnv && appDir && readAppId(appDir, void 0)) {
|
|
4855
|
+
throw new Error(
|
|
4856
|
+
`No app id for env "${wranglerEnv}" \u2014 wrangler.toml has no [env.${wranglerEnv}] block with its own DEEPSPACE_APP_ID. To target the "${wranglerEnv}" config on this app, use \`-c ${wranglerEnv}\` instead of \`--env ${wranglerEnv}\`.`
|
|
4857
|
+
);
|
|
4858
|
+
}
|
|
4847
4859
|
throw new Error(
|
|
4848
4860
|
"No app id. Run from an app directory whose wrangler.toml carries DEEPSPACE_APP_ID (`deepspace init` mints one), or pass --app <appId>."
|
|
4849
4861
|
);
|
|
@@ -4857,6 +4869,14 @@ function fail(err) {
|
|
|
4857
4869
|
console.error(err instanceof Error ? err.message : String(err));
|
|
4858
4870
|
process.exit(1);
|
|
4859
4871
|
}
|
|
4872
|
+
function warnIfDebugRoutesEnabled(secrets) {
|
|
4873
|
+
if (secrets.ALLOW_DEBUG_ROUTES !== "true") return false;
|
|
4874
|
+
console.warn(
|
|
4875
|
+
"\n\u26A0 ALLOW_DEBUG_ROUTES=true exposes an UNAUTHENTICATED debug API on the deployed app \u2014\n anyone who can reach /api/debug/* can read and mutate any record. Redeploy to apply;\n `deepspace secrets delete ALLOW_DEBUG_ROUTES` (then redeploy) turns it back off."
|
|
4876
|
+
);
|
|
4877
|
+
return true;
|
|
4878
|
+
}
|
|
4879
|
+
var APPLY_HINT = "Run `deepspace deploy` to apply \u2014 secrets take effect at deploy time.";
|
|
4860
4880
|
var list5 = defineCommand20({
|
|
4861
4881
|
meta: { name: "list", description: "List masked secrets in a config" },
|
|
4862
4882
|
args: {
|
|
@@ -4881,7 +4901,7 @@ var list5 = defineCommand20({
|
|
|
4881
4901
|
return;
|
|
4882
4902
|
}
|
|
4883
4903
|
for (const s of secrets) {
|
|
4884
|
-
console.log(args["only-names"] ? s.key : `${s.key} (v${s.version}, ${s.
|
|
4904
|
+
console.log(args["only-names"] ? s.key : `${s.key} (v${s.version}, ${s.updatedAt})`);
|
|
4885
4905
|
}
|
|
4886
4906
|
} catch (err) {
|
|
4887
4907
|
fail(err);
|
|
@@ -4897,18 +4917,25 @@ var set = defineCommand20({
|
|
|
4897
4917
|
async run({ args }) {
|
|
4898
4918
|
try {
|
|
4899
4919
|
const t = await resolveTarget(args);
|
|
4900
|
-
const pairs = [args.secret, ...args._ ?? []].filter(
|
|
4920
|
+
const pairs = [.../* @__PURE__ */ new Set([args.secret, ...args._ ?? []])].filter(
|
|
4901
4921
|
(v) => typeof v === "string" && v.length > 0
|
|
4902
4922
|
);
|
|
4903
4923
|
const secrets = {};
|
|
4924
|
+
const dupes = [];
|
|
4904
4925
|
for (const pair of pairs) {
|
|
4905
4926
|
const eq = pair.indexOf("=");
|
|
4906
4927
|
if (eq <= 0) throw new Error(`Expected KEY=value, got "${pair}"`);
|
|
4907
4928
|
const key = validateSecretName(pair.slice(0, eq));
|
|
4908
4929
|
const value = pair.slice(eq + 1);
|
|
4909
4930
|
validateSecretValue(key, value);
|
|
4931
|
+
if (key in secrets && !dupes.includes(key)) dupes.push(key);
|
|
4910
4932
|
secrets[key] = value;
|
|
4911
4933
|
}
|
|
4934
|
+
if (dupes.length) {
|
|
4935
|
+
console.warn(
|
|
4936
|
+
`Warning: ${dupes.join(", ")} given more than once \u2014 kept the last value for each.`
|
|
4937
|
+
);
|
|
4938
|
+
}
|
|
4912
4939
|
if (Object.keys(secrets).length === 1) {
|
|
4913
4940
|
const [[k, v]] = Object.entries(secrets);
|
|
4914
4941
|
await setSecret(DEPLOY_URL6, t.token, t.appId, t.configName, k, v);
|
|
@@ -4916,6 +4943,7 @@ var set = defineCommand20({
|
|
|
4916
4943
|
await uploadSecrets(DEPLOY_URL6, t.token, t.appId, t.configName, secrets, false);
|
|
4917
4944
|
}
|
|
4918
4945
|
console.log(`Set ${Object.keys(secrets).length} secret${Object.keys(secrets).length === 1 ? "" : "s"} in ${t.configName}.`);
|
|
4946
|
+
if (!warnIfDebugRoutesEnabled(secrets)) console.log(APPLY_HINT);
|
|
4919
4947
|
} catch (err) {
|
|
4920
4948
|
fail(err);
|
|
4921
4949
|
}
|
|
@@ -4934,13 +4962,13 @@ var get = defineCommand20({
|
|
|
4934
4962
|
const key = validateSecretName(args.key);
|
|
4935
4963
|
if (args.plain) {
|
|
4936
4964
|
const { value } = await getSecretPlain(DEPLOY_URL6, t.token, t.appId, t.configName, key);
|
|
4937
|
-
process.stdout.write(value + "\n");
|
|
4965
|
+
process.stdout.write(process.stdout.isTTY ? value + "\n" : value);
|
|
4938
4966
|
return;
|
|
4939
4967
|
}
|
|
4940
4968
|
const { secrets } = await listSecrets(DEPLOY_URL6, t.token, t.appId, t.configName);
|
|
4941
4969
|
const item = secrets.find((s) => s.key === key);
|
|
4942
4970
|
if (!item) fail(new Error(`Secret "${key}" not found in ${t.configName}`));
|
|
4943
|
-
console.log(`${item.key} (v${item.version}, ${item.
|
|
4971
|
+
console.log(`${item.key} (v${item.version}, ${item.updatedAt})`);
|
|
4944
4972
|
} catch (err) {
|
|
4945
4973
|
fail(err);
|
|
4946
4974
|
}
|
|
@@ -4955,13 +4983,24 @@ var del2 = defineCommand20({
|
|
|
4955
4983
|
async run({ args }) {
|
|
4956
4984
|
try {
|
|
4957
4985
|
const t = await resolveTarget(args);
|
|
4958
|
-
const keys = [args.key, ...args._ ?? []].filter(
|
|
4986
|
+
const keys = [.../* @__PURE__ */ new Set([args.key, ...args._ ?? []])].filter(
|
|
4959
4987
|
(v) => typeof v === "string" && v.length > 0
|
|
4960
4988
|
);
|
|
4989
|
+
let deleted = 0;
|
|
4990
|
+
let absent = 0;
|
|
4961
4991
|
for (const key of keys) {
|
|
4962
|
-
|
|
4992
|
+
const name = validateSecretName(key);
|
|
4993
|
+
try {
|
|
4994
|
+
await deleteSecret(DEPLOY_URL6, t.token, t.appId, t.configName, name);
|
|
4995
|
+
deleted++;
|
|
4996
|
+
} catch (err) {
|
|
4997
|
+
if (err?.status === 404) absent++;
|
|
4998
|
+
else throw err;
|
|
4999
|
+
}
|
|
4963
5000
|
}
|
|
4964
|
-
|
|
5001
|
+
const note3 = absent > 0 ? ` (${absent} already absent)` : "";
|
|
5002
|
+
console.log(`Deleted ${deleted} secret${deleted === 1 ? "" : "s"} from ${t.configName}.${note3}`);
|
|
5003
|
+
if (deleted > 0) console.log(APPLY_HINT);
|
|
4965
5004
|
} catch (err) {
|
|
4966
5005
|
fail(err);
|
|
4967
5006
|
}
|
|
@@ -4981,11 +5020,12 @@ var upload = defineCommand20({
|
|
|
4981
5020
|
async run({ args }) {
|
|
4982
5021
|
try {
|
|
4983
5022
|
const t = await resolveTarget(args);
|
|
4984
|
-
const content = args.file === "-" ?
|
|
5023
|
+
const content = args.file === "-" ? readFileSync14(0, "utf-8") : readFileSync14(args.file, "utf-8");
|
|
4985
5024
|
const secrets = parseSecretsUpload(content);
|
|
4986
5025
|
if (Object.keys(secrets).length === 0) fail(new Error("No secrets found in the input."));
|
|
4987
5026
|
await uploadSecrets(DEPLOY_URL6, t.token, t.appId, t.configName, secrets, args.replace);
|
|
4988
5027
|
console.log(`Uploaded ${Object.keys(secrets).length} secrets to ${t.configName}.`);
|
|
5028
|
+
if (!warnIfDebugRoutesEnabled(secrets)) console.log(APPLY_HINT);
|
|
4989
5029
|
} catch (err) {
|
|
4990
5030
|
fail(err);
|
|
4991
5031
|
}
|
|
@@ -5073,7 +5113,20 @@ var configsCreate = defineCommand20({
|
|
|
5073
5113
|
try {
|
|
5074
5114
|
const t = await resolveTarget(args);
|
|
5075
5115
|
const name = validateConfigName(args.name);
|
|
5076
|
-
|
|
5116
|
+
const copyFrom = args["copy-from"] || void 0;
|
|
5117
|
+
const { configs: configs2 } = await listConfigs(DEPLOY_URL6, t.token, t.appId);
|
|
5118
|
+
if (configs2.some((c) => c.name === name)) {
|
|
5119
|
+
if (copyFrom) {
|
|
5120
|
+
fail(
|
|
5121
|
+
new Error(
|
|
5122
|
+
`Config "${name}" already exists \u2014 refusing to copy "${copyFrom}" over it. Delete it first, or pick a new name.`
|
|
5123
|
+
)
|
|
5124
|
+
);
|
|
5125
|
+
}
|
|
5126
|
+
console.log(`Config ${name} already exists.`);
|
|
5127
|
+
return;
|
|
5128
|
+
}
|
|
5129
|
+
await createConfig(DEPLOY_URL6, t.token, t.appId, name, copyFrom);
|
|
5077
5130
|
console.log(`Created ${name}.`);
|
|
5078
5131
|
} catch (err) {
|
|
5079
5132
|
fail(err);
|
|
@@ -5138,7 +5191,9 @@ var init_default = defineCommand21({
|
|
|
5138
5191
|
async run({ args }) {
|
|
5139
5192
|
const appDir = findAppDir();
|
|
5140
5193
|
if (!appDir) {
|
|
5141
|
-
console.error(
|
|
5194
|
+
console.error(
|
|
5195
|
+
"No wrangler.toml here \u2014 `deepspace init` stamps an app id into an existing DeepSpace app. To create a new app, run `deepspace create <name>` (or `npm create deepspace@latest <name>`), then run this from inside it."
|
|
5196
|
+
);
|
|
5142
5197
|
process.exit(1);
|
|
5143
5198
|
}
|
|
5144
5199
|
const envName = args.env || void 0;
|
|
@@ -5161,7 +5216,7 @@ var init_default = defineCommand21({
|
|
|
5161
5216
|
|
|
5162
5217
|
// src/cli/cli.ts
|
|
5163
5218
|
var __dirname = dirname4(fileURLToPath2(import.meta.url));
|
|
5164
|
-
var pkg = JSON.parse(
|
|
5219
|
+
var pkg = JSON.parse(readFileSync15(join16(__dirname, "..", "package.json"), "utf-8"));
|
|
5165
5220
|
if (process.platform === "linux" && !process.env.SSL_CERT_FILE && existsSync14("/etc/ssl/certs/ca-certificates.crt")) {
|
|
5166
5221
|
process.env.SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt";
|
|
5167
5222
|
}
|