@kryd/cli 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +420 -83
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -88,6 +88,69 @@ var PRODUCTION_TLD = "kryd.cloud";
|
|
|
88
88
|
var PREVIEW_TLD = "kryd.dev";
|
|
89
89
|
var TENANT_SLUG_RE = new RegExp(`^[a-z0-9](?:[a-z0-9-]{0,${MAX_TENANT_SLUG - 2}}[a-z0-9])?$`);
|
|
90
90
|
|
|
91
|
+
// ../../packages/shared-types/dist/reserved-slugs.js
|
|
92
|
+
var FORGEJO_RESERVED_USERNAMES = [
|
|
93
|
+
".",
|
|
94
|
+
"..",
|
|
95
|
+
"-",
|
|
96
|
+
".well-known",
|
|
97
|
+
"api",
|
|
98
|
+
"metrics",
|
|
99
|
+
"v2",
|
|
100
|
+
"assets",
|
|
101
|
+
"attachments",
|
|
102
|
+
"avatar",
|
|
103
|
+
"avatars",
|
|
104
|
+
"repo-avatars",
|
|
105
|
+
"captcha",
|
|
106
|
+
"login",
|
|
107
|
+
"org",
|
|
108
|
+
"repo",
|
|
109
|
+
"user",
|
|
110
|
+
"admin",
|
|
111
|
+
"explore",
|
|
112
|
+
"issues",
|
|
113
|
+
"pulls",
|
|
114
|
+
"milestones",
|
|
115
|
+
"notifications",
|
|
116
|
+
"report_abuse",
|
|
117
|
+
"favicon.ico",
|
|
118
|
+
"manifest.json",
|
|
119
|
+
"robots.txt",
|
|
120
|
+
"sitemap.xml",
|
|
121
|
+
"ssh_info",
|
|
122
|
+
"swagger.v1.json",
|
|
123
|
+
"ghost",
|
|
124
|
+
"gitea-actions",
|
|
125
|
+
"forgejo-actions"
|
|
126
|
+
];
|
|
127
|
+
var PLATFORM_RESERVED_SLUGS = [
|
|
128
|
+
"kryd",
|
|
129
|
+
"krydhq",
|
|
130
|
+
"www",
|
|
131
|
+
"api",
|
|
132
|
+
"app",
|
|
133
|
+
"ai",
|
|
134
|
+
"docs",
|
|
135
|
+
"forge",
|
|
136
|
+
"admin",
|
|
137
|
+
"console",
|
|
138
|
+
"status",
|
|
139
|
+
"mail",
|
|
140
|
+
"support",
|
|
141
|
+
"billing",
|
|
142
|
+
"account",
|
|
143
|
+
"accounts",
|
|
144
|
+
"static",
|
|
145
|
+
"assets",
|
|
146
|
+
"cdn",
|
|
147
|
+
"dashboard"
|
|
148
|
+
];
|
|
149
|
+
var RESERVED = /* @__PURE__ */ new Set([
|
|
150
|
+
...FORGEJO_RESERVED_USERNAMES,
|
|
151
|
+
...PLATFORM_RESERVED_SLUGS
|
|
152
|
+
]);
|
|
153
|
+
|
|
91
154
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
92
155
|
var external_exports = {};
|
|
93
156
|
__export(external_exports, {
|
|
@@ -14806,6 +14869,7 @@ import {
|
|
|
14806
14869
|
existsSync,
|
|
14807
14870
|
mkdirSync,
|
|
14808
14871
|
readFileSync,
|
|
14872
|
+
rmSync,
|
|
14809
14873
|
writeFileSync
|
|
14810
14874
|
} from "node:fs";
|
|
14811
14875
|
import { homedir } from "node:os";
|
|
@@ -14922,6 +14986,18 @@ function writeSecretFile(path, contents) {
|
|
|
14922
14986
|
writeFileSync(path, contents, { mode: 384 });
|
|
14923
14987
|
chmodSync(path, 384);
|
|
14924
14988
|
}
|
|
14989
|
+
function clearProjectLinkIfMatches(projectId, cwd = process.cwd()) {
|
|
14990
|
+
const root = findProjectLinkDir(cwd);
|
|
14991
|
+
if (!root) return false;
|
|
14992
|
+
const link = loadProjectLink(cwd);
|
|
14993
|
+
if (link?.projectId !== projectId) return false;
|
|
14994
|
+
try {
|
|
14995
|
+
rmSync(join(root, PROJECT_LINK_DIR, PROJECT_LINK_FILE));
|
|
14996
|
+
return true;
|
|
14997
|
+
} catch {
|
|
14998
|
+
return false;
|
|
14999
|
+
}
|
|
15000
|
+
}
|
|
14925
15001
|
function resolveProjectId(explicit, cwd = process.cwd()) {
|
|
14926
15002
|
if (explicit) return explicit;
|
|
14927
15003
|
return loadProjectLink(cwd)?.projectId ?? null;
|
|
@@ -15241,6 +15317,58 @@ async function detachDatabase(apiUrl, token, projectId) {
|
|
|
15241
15317
|
);
|
|
15242
15318
|
}
|
|
15243
15319
|
}
|
|
15320
|
+
async function getProject(apiUrl, token, projectId) {
|
|
15321
|
+
const res = await fetch(`${apiUrl}/projects/${encodeURIComponent(projectId)}`, {
|
|
15322
|
+
headers: { authorization: `Bearer ${token}` }
|
|
15323
|
+
});
|
|
15324
|
+
if (res.status === 404) return null;
|
|
15325
|
+
if (!res.ok) {
|
|
15326
|
+
throw new ApiError(`Reading the project failed (${res.status})`, await parseEnvelope(res));
|
|
15327
|
+
}
|
|
15328
|
+
return await res.json();
|
|
15329
|
+
}
|
|
15330
|
+
async function listProjects(apiUrl, token) {
|
|
15331
|
+
const res = await fetch(`${apiUrl}/projects`, {
|
|
15332
|
+
headers: { authorization: `Bearer ${token}` }
|
|
15333
|
+
});
|
|
15334
|
+
if (!res.ok) {
|
|
15335
|
+
throw new ApiError(`Listing projects failed (${res.status})`, await parseEnvelope(res));
|
|
15336
|
+
}
|
|
15337
|
+
return (await res.json()).items;
|
|
15338
|
+
}
|
|
15339
|
+
async function deleteProject(apiUrl, token, projectId) {
|
|
15340
|
+
const res = await fetch(`${apiUrl}/projects/${encodeURIComponent(projectId)}`, {
|
|
15341
|
+
method: "DELETE",
|
|
15342
|
+
headers: { authorization: `Bearer ${token}` }
|
|
15343
|
+
});
|
|
15344
|
+
if (!res.ok) {
|
|
15345
|
+
throw new ApiError(`Project delete failed (${res.status})`, await parseEnvelope(res));
|
|
15346
|
+
}
|
|
15347
|
+
return await res.json();
|
|
15348
|
+
}
|
|
15349
|
+
async function pollProjectDeleted(read, opts) {
|
|
15350
|
+
const intervalMs = opts?.intervalMs ?? 2e3;
|
|
15351
|
+
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
15352
|
+
const maxConsecutiveErrors = opts?.maxConsecutiveErrors ?? 5;
|
|
15353
|
+
const sleep = opts?.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
15354
|
+
const started = Date.now();
|
|
15355
|
+
let consecutiveErrors = 0;
|
|
15356
|
+
let last = null;
|
|
15357
|
+
for (; ; ) {
|
|
15358
|
+
try {
|
|
15359
|
+
const project2 = await read();
|
|
15360
|
+
consecutiveErrors = 0;
|
|
15361
|
+
if (project2 === null) return { gone: true, status: null };
|
|
15362
|
+
last = project2.status;
|
|
15363
|
+
opts?.onTick?.(project2.status);
|
|
15364
|
+
if (project2.status === "delete_failed") return { gone: false, status: last };
|
|
15365
|
+
} catch (err) {
|
|
15366
|
+
if (++consecutiveErrors >= maxConsecutiveErrors) throw err;
|
|
15367
|
+
}
|
|
15368
|
+
if (Date.now() - started >= timeoutMs) return { gone: false, status: last };
|
|
15369
|
+
await sleep(intervalMs);
|
|
15370
|
+
}
|
|
15371
|
+
}
|
|
15244
15372
|
async function detachStorage(apiUrl, token, projectId) {
|
|
15245
15373
|
const res = await fetch(`${apiUrl}/storage/${encodeURIComponent(projectId)}`, {
|
|
15246
15374
|
method: "DELETE",
|
|
@@ -15711,6 +15839,18 @@ async function promptHidden(question, io) {
|
|
|
15711
15839
|
io.output.write("\n");
|
|
15712
15840
|
}
|
|
15713
15841
|
}
|
|
15842
|
+
async function promptLine(question, io) {
|
|
15843
|
+
const rl = createInterface({ input: io.input, output: io.output });
|
|
15844
|
+
try {
|
|
15845
|
+
return await new Promise((resolve2) => {
|
|
15846
|
+
rl.once("SIGINT", () => resolve2(null));
|
|
15847
|
+
rl.once("close", () => resolve2(null));
|
|
15848
|
+
rl.question(question, (answer) => resolve2(answer));
|
|
15849
|
+
});
|
|
15850
|
+
} finally {
|
|
15851
|
+
rl.close();
|
|
15852
|
+
}
|
|
15853
|
+
}
|
|
15714
15854
|
async function promptConfirm(question, io) {
|
|
15715
15855
|
const rl = createInterface({ input: io.input, output: io.output });
|
|
15716
15856
|
try {
|
|
@@ -15885,17 +16025,20 @@ async function runInit(opts) {
|
|
|
15885
16025
|
}
|
|
15886
16026
|
const name = opts.name ?? detected.name;
|
|
15887
16027
|
try {
|
|
15888
|
-
const { project, repo, pushToken } = await linkProject(apiUrl, token, {
|
|
16028
|
+
const { project: project2, repo, pushToken } = await linkProject(apiUrl, token, {
|
|
15889
16029
|
name,
|
|
15890
16030
|
framework,
|
|
15891
16031
|
// First `kryd init` for the account claims the tenant slug (the vanity routing key in
|
|
15892
16032
|
// every deploy URL); later inits omit it (the account already has one). Story 3.8.
|
|
15893
16033
|
// Built conditionally (exactOptionalPropertyTypes — don't pass an explicit undefined).
|
|
15894
|
-
...opts.tenant ? { tenantSlug: opts.tenant } : {}
|
|
16034
|
+
...opts.tenant ? { tenantSlug: opts.tenant } : {},
|
|
16035
|
+
// KRYD-184a: only sent when the flag is present. The API defaults to private and sends
|
|
16036
|
+
// `private` explicitly to the forge on every path, so omitting this is never "public".
|
|
16037
|
+
...opts.public ? { public: true } : {}
|
|
15895
16038
|
});
|
|
15896
16039
|
let linkNote = "";
|
|
15897
16040
|
try {
|
|
15898
|
-
saveProjectLink(cwd, { projectId:
|
|
16041
|
+
saveProjectLink(cwd, { projectId: project2.id });
|
|
15899
16042
|
linkNote = "Linked this folder \u2192 .kryd/project.json \u2014 `kryd deploy` / `kryd logs` now work here with no id.\n";
|
|
15900
16043
|
try {
|
|
15901
16044
|
ensureKrydIgnored(cwd);
|
|
@@ -15907,7 +16050,7 @@ async function runInit(opts) {
|
|
|
15907
16050
|
}
|
|
15908
16051
|
} catch (linkErr) {
|
|
15909
16052
|
process.stderr.write(
|
|
15910
|
-
`warning: could not write .kryd/project.json (${linkErr instanceof Error ? linkErr.message : String(linkErr)}) \u2014 pass the project id explicitly, e.g. \`kryd deploy ${
|
|
16053
|
+
`warning: could not write .kryd/project.json (${linkErr instanceof Error ? linkErr.message : String(linkErr)}) \u2014 pass the project id explicitly, e.g. \`kryd deploy ${project2.id}\`.
|
|
15911
16054
|
`
|
|
15912
16055
|
);
|
|
15913
16056
|
}
|
|
@@ -15940,9 +16083,9 @@ Install git (https://git-scm.com/downloads), then re-run \`kryd init\` here.
|
|
|
15940
16083
|
break;
|
|
15941
16084
|
}
|
|
15942
16085
|
process.stdout.write(
|
|
15943
|
-
`Linked "${name}" (${
|
|
15944
|
-
Production URL: https://${
|
|
15945
|
-
Preview branches deploy to https://<branch>-<hash>-${
|
|
16086
|
+
`Linked "${name}" (${project2.framework}) \u2192 ${repo.htmlUrl}
|
|
16087
|
+
Production URL: https://${project2.subdomain}.${PRODUCTION_TLD}
|
|
16088
|
+
Preview branches deploy to https://<branch>-<hash>-${project2.subdomain}.${PREVIEW_TLD}
|
|
15946
16089
|
${linkNote}
|
|
15947
16090
|
${nextStep}`
|
|
15948
16091
|
);
|
|
@@ -16060,13 +16203,13 @@ async function runDeploy(opts) {
|
|
|
16060
16203
|
process.exitCode = 1;
|
|
16061
16204
|
return;
|
|
16062
16205
|
}
|
|
16063
|
-
const
|
|
16064
|
-
if (!
|
|
16206
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16207
|
+
if (!project2) {
|
|
16065
16208
|
reportNotLinked("kryd deploy <projectId>");
|
|
16066
16209
|
return;
|
|
16067
16210
|
}
|
|
16068
16211
|
try {
|
|
16069
|
-
const deploymentId = await triggerDeploy(apiUrl, token,
|
|
16212
|
+
const deploymentId = await triggerDeploy(apiUrl, token, project2);
|
|
16070
16213
|
process.stdout.write(`Triggered deploy ${deploymentId}
|
|
16071
16214
|
`);
|
|
16072
16215
|
await followDeploy(apiUrl, token, deploymentId);
|
|
@@ -16211,8 +16354,8 @@ async function runRollback(opts) {
|
|
|
16211
16354
|
process.exitCode = 1;
|
|
16212
16355
|
return;
|
|
16213
16356
|
}
|
|
16214
|
-
const
|
|
16215
|
-
if (!
|
|
16357
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16358
|
+
if (!project2) {
|
|
16216
16359
|
reportNotLinked("kryd rollback <projectId> [deployment]");
|
|
16217
16360
|
return;
|
|
16218
16361
|
}
|
|
@@ -16220,7 +16363,7 @@ async function runRollback(opts) {
|
|
|
16220
16363
|
const { deploymentId, rolledBackTo, note } = await rollbackDeploy(
|
|
16221
16364
|
apiUrl,
|
|
16222
16365
|
token,
|
|
16223
|
-
|
|
16366
|
+
project2,
|
|
16224
16367
|
opts.deployment
|
|
16225
16368
|
);
|
|
16226
16369
|
process.stdout.write(
|
|
@@ -16241,8 +16384,8 @@ async function runRedeploy(opts) {
|
|
|
16241
16384
|
process.exitCode = 1;
|
|
16242
16385
|
return;
|
|
16243
16386
|
}
|
|
16244
|
-
const
|
|
16245
|
-
if (!
|
|
16387
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16388
|
+
if (!project2) {
|
|
16246
16389
|
reportNotLinked("kryd redeploy <projectId>");
|
|
16247
16390
|
return;
|
|
16248
16391
|
}
|
|
@@ -16250,7 +16393,7 @@ async function runRedeploy(opts) {
|
|
|
16250
16393
|
const { deploymentId, redeployedCommit, note } = await redeployProject(
|
|
16251
16394
|
apiUrl,
|
|
16252
16395
|
token,
|
|
16253
|
-
|
|
16396
|
+
project2
|
|
16254
16397
|
);
|
|
16255
16398
|
process.stdout.write(
|
|
16256
16399
|
`Redeploying ${redeployedCommit.slice(0, 7)} \u2192 ${deploymentId}
|
|
@@ -16270,8 +16413,8 @@ async function runDbCreate(opts) {
|
|
|
16270
16413
|
process.exitCode = 1;
|
|
16271
16414
|
return;
|
|
16272
16415
|
}
|
|
16273
|
-
const
|
|
16274
|
-
if (!
|
|
16416
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16417
|
+
if (!project2) {
|
|
16275
16418
|
reportNotLinked("kryd db create <projectId>");
|
|
16276
16419
|
return;
|
|
16277
16420
|
}
|
|
@@ -16299,29 +16442,29 @@ async function runDbCreate(opts) {
|
|
|
16299
16442
|
}
|
|
16300
16443
|
try {
|
|
16301
16444
|
const db2 = await attachDatabase(apiUrl, token, {
|
|
16302
|
-
projectId:
|
|
16445
|
+
projectId: project2,
|
|
16303
16446
|
mode,
|
|
16304
16447
|
...connectionString ? { connectionString } : {}
|
|
16305
16448
|
});
|
|
16306
16449
|
if (db2.status === "ready") {
|
|
16307
16450
|
process.stdout.write(
|
|
16308
|
-
`Attached ${db2.mode} database ${db2.id} to ${
|
|
16451
|
+
`Attached ${db2.mode} database ${db2.id} to ${project2}.
|
|
16309
16452
|
The connection string will be injected as DATABASE_URL on your next deploy.
|
|
16310
16453
|
`
|
|
16311
16454
|
);
|
|
16312
16455
|
return;
|
|
16313
16456
|
}
|
|
16314
|
-
process.stdout.write(`Provisioning a shared database for ${
|
|
16457
|
+
process.stdout.write(`Provisioning a shared database for ${project2}\u2026
|
|
16315
16458
|
`);
|
|
16316
16459
|
const result = await pollResourceUntilTerminal(
|
|
16317
|
-
() => getDatabaseStatus(apiUrl, token,
|
|
16460
|
+
() => getDatabaseStatus(apiUrl, token, project2)
|
|
16318
16461
|
);
|
|
16319
16462
|
reportSettleResult(result, {
|
|
16320
|
-
readyMsg: `Attached shared database ${db2.id} to ${
|
|
16463
|
+
readyMsg: `Attached shared database ${db2.id} to ${project2}.
|
|
16321
16464
|
The connection string will be injected as DATABASE_URL on your next deploy.
|
|
16322
16465
|
`,
|
|
16323
16466
|
failedLabel: "Database provisioning",
|
|
16324
|
-
retryCmd: `kryd db create ${
|
|
16467
|
+
retryCmd: `kryd db create ${project2}`
|
|
16325
16468
|
});
|
|
16326
16469
|
} catch (err) {
|
|
16327
16470
|
reportError(err);
|
|
@@ -16351,24 +16494,24 @@ async function runStorageCreate(opts) {
|
|
|
16351
16494
|
process.exitCode = 1;
|
|
16352
16495
|
return;
|
|
16353
16496
|
}
|
|
16354
|
-
const
|
|
16355
|
-
if (!
|
|
16497
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16498
|
+
if (!project2) {
|
|
16356
16499
|
reportNotLinked("kryd storage create <projectId>");
|
|
16357
16500
|
return;
|
|
16358
16501
|
}
|
|
16359
16502
|
try {
|
|
16360
|
-
const bucket = await createStorage(apiUrl, token, { projectId:
|
|
16361
|
-
process.stdout.write(`Provisioning object storage for ${
|
|
16503
|
+
const bucket = await createStorage(apiUrl, token, { projectId: project2 });
|
|
16504
|
+
process.stdout.write(`Provisioning object storage for ${project2}\u2026
|
|
16362
16505
|
`);
|
|
16363
16506
|
const result = await pollResourceUntilTerminal(
|
|
16364
|
-
() => getStorageStatus(apiUrl, token,
|
|
16507
|
+
() => getStorageStatus(apiUrl, token, project2)
|
|
16365
16508
|
);
|
|
16366
16509
|
reportSettleResult(result, {
|
|
16367
|
-
readyMsg: `Created object storage ${bucket.id} for ${
|
|
16510
|
+
readyMsg: `Created object storage ${bucket.id} for ${project2}.
|
|
16368
16511
|
Its S3 credentials will be injected on your next deploy.
|
|
16369
16512
|
`,
|
|
16370
16513
|
failedLabel: "Storage provisioning",
|
|
16371
|
-
retryCmd: `kryd storage create ${
|
|
16514
|
+
retryCmd: `kryd storage create ${project2}`
|
|
16372
16515
|
});
|
|
16373
16516
|
} catch (err) {
|
|
16374
16517
|
reportError(err);
|
|
@@ -16391,6 +16534,152 @@ function reportDetachResult(result, opts) {
|
|
|
16391
16534
|
process.exitCode = 1;
|
|
16392
16535
|
}
|
|
16393
16536
|
}
|
|
16537
|
+
function isProjectId(arg) {
|
|
16538
|
+
return arg.startsWith("proj_");
|
|
16539
|
+
}
|
|
16540
|
+
async function resolveProjectIdByName(apiUrl, token, name) {
|
|
16541
|
+
let projects;
|
|
16542
|
+
try {
|
|
16543
|
+
projects = await listProjects(apiUrl, token);
|
|
16544
|
+
} catch (err) {
|
|
16545
|
+
reportError(err);
|
|
16546
|
+
return null;
|
|
16547
|
+
}
|
|
16548
|
+
const matches = projects.filter((p) => p.name === name);
|
|
16549
|
+
if (matches.length === 0) {
|
|
16550
|
+
process.stderr.write(
|
|
16551
|
+
`No project named ${name} on this account. Run \`kryd project list\` to see the names.
|
|
16552
|
+
`
|
|
16553
|
+
);
|
|
16554
|
+
process.exitCode = 1;
|
|
16555
|
+
return null;
|
|
16556
|
+
}
|
|
16557
|
+
if (matches.length === 1) return matches[0].id;
|
|
16558
|
+
const listed = matches.map((p) => ` ${p.id} ${p.status}
|
|
16559
|
+
`).join("");
|
|
16560
|
+
process.stderr.write(
|
|
16561
|
+
`${matches.length} projects are named ${name}:
|
|
16562
|
+
${listed}Re-run \`kryd project rm <id>\` with the one you mean.
|
|
16563
|
+
`
|
|
16564
|
+
);
|
|
16565
|
+
process.exitCode = 1;
|
|
16566
|
+
return null;
|
|
16567
|
+
}
|
|
16568
|
+
async function runProjectRemove(opts) {
|
|
16569
|
+
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
16570
|
+
const token = loadConfig().token;
|
|
16571
|
+
if (!token) {
|
|
16572
|
+
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
16573
|
+
process.exitCode = 1;
|
|
16574
|
+
return;
|
|
16575
|
+
}
|
|
16576
|
+
let projectId;
|
|
16577
|
+
if (opts.project !== void 0 && !isProjectId(opts.project)) {
|
|
16578
|
+
projectId = await resolveProjectIdByName(apiUrl, token, opts.project);
|
|
16579
|
+
if (!projectId) return;
|
|
16580
|
+
} else {
|
|
16581
|
+
projectId = resolveProjectId(opts.project, opts.cwd);
|
|
16582
|
+
}
|
|
16583
|
+
if (!projectId) {
|
|
16584
|
+
reportNotLinked("kryd project rm <name|id>");
|
|
16585
|
+
return;
|
|
16586
|
+
}
|
|
16587
|
+
let project2;
|
|
16588
|
+
let resources;
|
|
16589
|
+
try {
|
|
16590
|
+
project2 = await getProject(apiUrl, token, projectId);
|
|
16591
|
+
if (!project2) {
|
|
16592
|
+
process.stderr.write(
|
|
16593
|
+
`No project with id ${projectId} on this account. Run \`kryd project list\` to see your projects.
|
|
16594
|
+
`
|
|
16595
|
+
);
|
|
16596
|
+
process.exitCode = 1;
|
|
16597
|
+
return;
|
|
16598
|
+
}
|
|
16599
|
+
if (project2.status === "deleting") {
|
|
16600
|
+
process.stdout.write(`${project2.name} is already being deleted \u2014 following it.
|
|
16601
|
+
`);
|
|
16602
|
+
return followProjectDeletion(apiUrl, token, projectId, project2.name, opts);
|
|
16603
|
+
}
|
|
16604
|
+
resources = project2.status === "delete_failed" ? ["whatever the previous attempt did not manage to remove"] : await describeProjectResources(apiUrl, token, projectId);
|
|
16605
|
+
} catch (err) {
|
|
16606
|
+
reportError(err);
|
|
16607
|
+
return;
|
|
16608
|
+
}
|
|
16609
|
+
if (!opts.yes) {
|
|
16610
|
+
const io = opts.io ?? defaultPromptIO();
|
|
16611
|
+
if (!isInteractive(io)) {
|
|
16612
|
+
process.stderr.write(
|
|
16613
|
+
`Refusing to delete ${project2.name} without confirmation. Re-run with --yes to confirm non-interactively.
|
|
16614
|
+
`
|
|
16615
|
+
);
|
|
16616
|
+
process.exitCode = 1;
|
|
16617
|
+
return;
|
|
16618
|
+
}
|
|
16619
|
+
io.output.write(
|
|
16620
|
+
`This permanently deletes ${project2.name} and:
|
|
16621
|
+
` + resources.map((r) => ` \u2022 ${r}
|
|
16622
|
+
`).join("") + "This cannot be undone.\n"
|
|
16623
|
+
);
|
|
16624
|
+
const typed = await promptLine(
|
|
16625
|
+
`Type ${project2.name} to confirm: `,
|
|
16626
|
+
io
|
|
16627
|
+
);
|
|
16628
|
+
if (typed !== project2.name) {
|
|
16629
|
+
process.stdout.write(`Left ${project2.name} in place.
|
|
16630
|
+
`);
|
|
16631
|
+
return;
|
|
16632
|
+
}
|
|
16633
|
+
}
|
|
16634
|
+
try {
|
|
16635
|
+
const accepted = await deleteProject(apiUrl, token, projectId);
|
|
16636
|
+
process.stdout.write(
|
|
16637
|
+
accepted.alreadyInProgress ? `${project2.name} was already being deleted \u2014 following it.
|
|
16638
|
+
` : `Deleting ${project2.name}\u2026
|
|
16639
|
+
`
|
|
16640
|
+
);
|
|
16641
|
+
await followProjectDeletion(apiUrl, token, projectId, project2.name, opts);
|
|
16642
|
+
} catch (err) {
|
|
16643
|
+
reportError(err);
|
|
16644
|
+
}
|
|
16645
|
+
}
|
|
16646
|
+
async function followProjectDeletion(apiUrl, token, projectId, name, opts) {
|
|
16647
|
+
const result = await pollProjectDeleted(
|
|
16648
|
+
() => getProject(apiUrl, token, projectId),
|
|
16649
|
+
opts.pollOpts
|
|
16650
|
+
);
|
|
16651
|
+
if (result.gone) {
|
|
16652
|
+
const cleared = clearProjectLinkIfMatches(projectId, opts.cwd);
|
|
16653
|
+
process.stdout.write(
|
|
16654
|
+
`Deleted ${name}.
|
|
16655
|
+
` + (cleared ? "Removed the local .kryd/project.json link.\n" : "")
|
|
16656
|
+
);
|
|
16657
|
+
return;
|
|
16658
|
+
}
|
|
16659
|
+
if (result.status === "delete_failed") {
|
|
16660
|
+
process.stderr.write(
|
|
16661
|
+
`Deleting ${name} failed part-way. Your project is still listed, but some of its resources may already be gone \u2014 the teardown removes them in order and stops where it failed.
|
|
16662
|
+
Re-run \`kryd project rm ${projectId}\` to resume from where it stopped.
|
|
16663
|
+
`
|
|
16664
|
+
);
|
|
16665
|
+
process.exitCode = 1;
|
|
16666
|
+
return;
|
|
16667
|
+
}
|
|
16668
|
+
process.stderr.write(
|
|
16669
|
+
`Could not confirm that ${name} is gone \u2014 it is still being deleted (or the worker stalled). Re-run \`kryd project rm ${projectId}\` to check.
|
|
16670
|
+
`
|
|
16671
|
+
);
|
|
16672
|
+
process.exitCode = 1;
|
|
16673
|
+
}
|
|
16674
|
+
async function describeProjectResources(apiUrl, token, projectId) {
|
|
16675
|
+
const out = ["its deployed app, its containers and its built images"];
|
|
16676
|
+
const db2 = await getDatabaseStatus(apiUrl, token, projectId).catch(() => null);
|
|
16677
|
+
if (db2) out.push("its Postgres database \u2014 and all the data in it");
|
|
16678
|
+
const storage2 = await getStorageStatus(apiUrl, token, projectId).catch(() => null);
|
|
16679
|
+
if (storage2) out.push("its object-storage bucket \u2014 and every object in it");
|
|
16680
|
+
out.push("its Git repository on the forge, and its push token");
|
|
16681
|
+
return out;
|
|
16682
|
+
}
|
|
16394
16683
|
async function runDbDetach(opts) {
|
|
16395
16684
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
16396
16685
|
const token = loadConfig().token;
|
|
@@ -16399,23 +16688,23 @@ async function runDbDetach(opts) {
|
|
|
16399
16688
|
process.exitCode = 1;
|
|
16400
16689
|
return;
|
|
16401
16690
|
}
|
|
16402
|
-
const
|
|
16403
|
-
if (!
|
|
16691
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16692
|
+
if (!project2) {
|
|
16404
16693
|
reportNotLinked("kryd db detach <projectId>");
|
|
16405
16694
|
return;
|
|
16406
16695
|
}
|
|
16407
16696
|
try {
|
|
16408
|
-
await detachDatabase(apiUrl, token,
|
|
16409
|
-
process.stdout.write(`Detaching the database from ${
|
|
16697
|
+
await detachDatabase(apiUrl, token, project2);
|
|
16698
|
+
process.stdout.write(`Detaching the database from ${project2}\u2026
|
|
16410
16699
|
`);
|
|
16411
16700
|
const result = await pollResourceDetached(
|
|
16412
|
-
() => getDatabaseStatus(apiUrl, token,
|
|
16701
|
+
() => getDatabaseStatus(apiUrl, token, project2)
|
|
16413
16702
|
);
|
|
16414
16703
|
reportDetachResult(result, {
|
|
16415
|
-
goneMsg: `Detached the database from ${
|
|
16704
|
+
goneMsg: `Detached the database from ${project2}.
|
|
16416
16705
|
`,
|
|
16417
16706
|
failedLabel: "Database detach",
|
|
16418
|
-
retryCmd: `kryd db detach ${
|
|
16707
|
+
retryCmd: `kryd db detach ${project2}`
|
|
16419
16708
|
});
|
|
16420
16709
|
} catch (err) {
|
|
16421
16710
|
reportError(err);
|
|
@@ -16429,15 +16718,15 @@ async function runAiEnable(opts) {
|
|
|
16429
16718
|
process.exitCode = 1;
|
|
16430
16719
|
return;
|
|
16431
16720
|
}
|
|
16432
|
-
const
|
|
16433
|
-
if (!
|
|
16721
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16722
|
+
if (!project2) {
|
|
16434
16723
|
reportNotLinked("kryd ai enable <projectId>");
|
|
16435
16724
|
return;
|
|
16436
16725
|
}
|
|
16437
16726
|
try {
|
|
16438
|
-
const status = await enableAi(apiUrl, token, { projectId:
|
|
16727
|
+
const status = await enableAi(apiUrl, token, { projectId: project2 });
|
|
16439
16728
|
process.stdout.write(
|
|
16440
|
-
`AI enabled for ${
|
|
16729
|
+
`AI enabled for ${project2}.
|
|
16441
16730
|
` + (status.url ? `Endpoint: ${status.url} (OpenAI-compatible base URL \u2014 use it as-is)
|
|
16442
16731
|
` : "") + `AI_GATEWAY_URL + AI_GATEWAY_TOKEN will be injected on your next deploy (kryd deploy).
|
|
16443
16732
|
`
|
|
@@ -16454,23 +16743,23 @@ async function runStorageDetach(opts) {
|
|
|
16454
16743
|
process.exitCode = 1;
|
|
16455
16744
|
return;
|
|
16456
16745
|
}
|
|
16457
|
-
const
|
|
16458
|
-
if (!
|
|
16746
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16747
|
+
if (!project2) {
|
|
16459
16748
|
reportNotLinked("kryd storage detach <projectId>");
|
|
16460
16749
|
return;
|
|
16461
16750
|
}
|
|
16462
16751
|
try {
|
|
16463
|
-
await detachStorage(apiUrl, token,
|
|
16464
|
-
process.stdout.write(`Detaching object storage from ${
|
|
16752
|
+
await detachStorage(apiUrl, token, project2);
|
|
16753
|
+
process.stdout.write(`Detaching object storage from ${project2}\u2026
|
|
16465
16754
|
`);
|
|
16466
16755
|
const result = await pollResourceDetached(
|
|
16467
|
-
() => getStorageStatus(apiUrl, token,
|
|
16756
|
+
() => getStorageStatus(apiUrl, token, project2)
|
|
16468
16757
|
);
|
|
16469
16758
|
reportDetachResult(result, {
|
|
16470
|
-
goneMsg: `Detached object storage from ${
|
|
16759
|
+
goneMsg: `Detached object storage from ${project2}.
|
|
16471
16760
|
`,
|
|
16472
16761
|
failedLabel: "Storage detach",
|
|
16473
|
-
retryCmd: `kryd storage detach ${
|
|
16762
|
+
retryCmd: `kryd storage detach ${project2}`
|
|
16474
16763
|
});
|
|
16475
16764
|
} catch (err) {
|
|
16476
16765
|
reportError(err);
|
|
@@ -16500,6 +16789,40 @@ function envAnnotation(environments) {
|
|
|
16500
16789
|
if (preview && !prod) return " \u2192 preview only";
|
|
16501
16790
|
return "";
|
|
16502
16791
|
}
|
|
16792
|
+
async function runProjectList(opts) {
|
|
16793
|
+
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
16794
|
+
const token = loadConfig().token;
|
|
16795
|
+
if (!token) {
|
|
16796
|
+
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
16797
|
+
process.exitCode = 1;
|
|
16798
|
+
return;
|
|
16799
|
+
}
|
|
16800
|
+
let projects;
|
|
16801
|
+
try {
|
|
16802
|
+
projects = await listProjects(apiUrl, token);
|
|
16803
|
+
} catch (err) {
|
|
16804
|
+
reportError(err);
|
|
16805
|
+
return;
|
|
16806
|
+
}
|
|
16807
|
+
if (projects.length === 0) {
|
|
16808
|
+
process.stdout.write("No projects yet \u2014 run `kryd init` in a project directory.\n");
|
|
16809
|
+
return;
|
|
16810
|
+
}
|
|
16811
|
+
const linkedId = resolveProjectId(void 0, opts.cwd);
|
|
16812
|
+
const nameWidth = projects.reduce((max, p) => Math.max(max, p.name.length), 0);
|
|
16813
|
+
const subWidth = projects.reduce((max, p) => Math.max(max, p.subdomain.length), 0);
|
|
16814
|
+
const statusWidth = projects.reduce((max, p) => Math.max(max, p.status.length), 0);
|
|
16815
|
+
for (const p of projects) {
|
|
16816
|
+
const mark = p.id === linkedId ? "*" : " ";
|
|
16817
|
+
process.stdout.write(
|
|
16818
|
+
`${mark} ${p.name.padEnd(nameWidth)} ${p.subdomain.padEnd(subWidth)} ${p.status.padEnd(statusWidth)} ${p.id}
|
|
16819
|
+
`
|
|
16820
|
+
);
|
|
16821
|
+
}
|
|
16822
|
+
if (projects.some((p) => p.id === linkedId)) {
|
|
16823
|
+
process.stdout.write("\n* linked to this directory\n");
|
|
16824
|
+
}
|
|
16825
|
+
}
|
|
16503
16826
|
function renderEnvGroup(heading, vars, opts) {
|
|
16504
16827
|
const width = vars.reduce((max, v) => Math.max(max, v.key.length), 0);
|
|
16505
16828
|
const dates = opts.showUpdated ? vars.map((v) => isoDate(v.updatedAt)) : [];
|
|
@@ -16523,16 +16846,16 @@ async function runEnvList(opts) {
|
|
|
16523
16846
|
process.exitCode = 1;
|
|
16524
16847
|
return;
|
|
16525
16848
|
}
|
|
16526
|
-
const
|
|
16527
|
-
if (!
|
|
16849
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16850
|
+
if (!project2) {
|
|
16528
16851
|
reportNotLinked("kryd env list <projectId>");
|
|
16529
16852
|
return;
|
|
16530
16853
|
}
|
|
16531
16854
|
try {
|
|
16532
|
-
const vars = await listEnvVars(apiUrl, token,
|
|
16855
|
+
const vars = await listEnvVars(apiUrl, token, project2);
|
|
16533
16856
|
if (vars.length === 0) {
|
|
16534
16857
|
process.stdout.write(
|
|
16535
|
-
`No environment variables for ${
|
|
16858
|
+
`No environment variables for ${project2} yet.
|
|
16536
16859
|
Set one with \`kryd env set KEY --stdin\`, or attach a database, storage or the AI gateway.
|
|
16537
16860
|
`
|
|
16538
16861
|
);
|
|
@@ -16605,8 +16928,8 @@ async function runEnvPull(opts) {
|
|
|
16605
16928
|
process.exitCode = 1;
|
|
16606
16929
|
return;
|
|
16607
16930
|
}
|
|
16608
|
-
const
|
|
16609
|
-
if (!
|
|
16931
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16932
|
+
if (!project2) {
|
|
16610
16933
|
reportNotLinked("kryd env pull <projectId>");
|
|
16611
16934
|
return;
|
|
16612
16935
|
}
|
|
@@ -16624,11 +16947,11 @@ async function runEnvPull(opts) {
|
|
|
16624
16947
|
return;
|
|
16625
16948
|
}
|
|
16626
16949
|
try {
|
|
16627
|
-
const { values } = await pullEnvValues(apiUrl, token,
|
|
16950
|
+
const { values } = await pullEnvValues(apiUrl, token, project2, environment);
|
|
16628
16951
|
const entries = Object.entries(values).sort(
|
|
16629
16952
|
([a], [b]) => a.localeCompare(b)
|
|
16630
16953
|
);
|
|
16631
|
-
const header = `# Written by \`kryd env pull\` \u2014 your customer-set variables for ${
|
|
16954
|
+
const header = `# Written by \`kryd env pull\` \u2014 your customer-set variables for ${project2} (${environment}).
|
|
16632
16955
|
# Regenerate with \`kryd env pull\`. Do not commit this file.
|
|
16633
16956
|
`;
|
|
16634
16957
|
const body = entries.map(([k, v]) => toDotenvLine(k, v)).join("\n");
|
|
@@ -16640,7 +16963,7 @@ ${body}
|
|
|
16640
16963
|
` : header);
|
|
16641
16964
|
const count = entries.length;
|
|
16642
16965
|
process.stdout.write(
|
|
16643
|
-
count === 0 ? `No customer environment variables for ${
|
|
16966
|
+
count === 0 ? `No customer environment variables for ${project2} (${environment}) \u2014 wrote an empty ${outRel}.
|
|
16644
16967
|
` : `Wrote ${count} variable${count === 1 ? "" : "s"} to ${outRel} (${environment}), mode 0600.
|
|
16645
16968
|
`
|
|
16646
16969
|
);
|
|
@@ -16706,8 +17029,8 @@ async function runEnvSet(opts) {
|
|
|
16706
17029
|
process.exitCode = 1;
|
|
16707
17030
|
return;
|
|
16708
17031
|
}
|
|
16709
|
-
const
|
|
16710
|
-
if (!
|
|
17032
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
17033
|
+
if (!project2) {
|
|
16711
17034
|
reportNotLinked("kryd env set KEY=VALUE <projectId>");
|
|
16712
17035
|
return;
|
|
16713
17036
|
}
|
|
@@ -16773,7 +17096,7 @@ async function runEnvSet(opts) {
|
|
|
16773
17096
|
const written = await setEnvVar(
|
|
16774
17097
|
apiUrl,
|
|
16775
17098
|
token,
|
|
16776
|
-
|
|
17099
|
+
project2,
|
|
16777
17100
|
key,
|
|
16778
17101
|
resolved.value,
|
|
16779
17102
|
scope,
|
|
@@ -16781,7 +17104,7 @@ async function runEnvSet(opts) {
|
|
|
16781
17104
|
);
|
|
16782
17105
|
const envTag = environment === "preview" ? " (preview)" : "";
|
|
16783
17106
|
process.stdout.write(
|
|
16784
|
-
`${written.created ? "Set" : "Updated"} ${written.key}${scope === "build" ? " (build-time)" : envTag} for ${
|
|
17107
|
+
`${written.created ? "Set" : "Updated"} ${written.key}${scope === "build" ? " (build-time)" : envTag} for ${project2}.
|
|
16785
17108
|
` + // The public-bundle warning comes FIRST, before the applies-when note. Someone who reads one
|
|
16786
17109
|
// line of output should read the consequential one.
|
|
16787
17110
|
(scope === "build" ? `${BUILD_ENV_PUBLIC_WARNING}
|
|
@@ -16801,8 +17124,8 @@ async function runEnvRemove(opts) {
|
|
|
16801
17124
|
process.exitCode = 1;
|
|
16802
17125
|
return;
|
|
16803
17126
|
}
|
|
16804
|
-
const
|
|
16805
|
-
if (!
|
|
17127
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
17128
|
+
if (!project2) {
|
|
16806
17129
|
reportNotLinked("kryd env rm KEY <projectId>");
|
|
16807
17130
|
return;
|
|
16808
17131
|
}
|
|
@@ -16837,10 +17160,10 @@ If it comes from a resource you attached, detach that instead (\`kryd db detach\
|
|
|
16837
17160
|
}
|
|
16838
17161
|
if (scope === "build") {
|
|
16839
17162
|
try {
|
|
16840
|
-
const vars = await listEnvVars(apiUrl, token,
|
|
17163
|
+
const vars = await listEnvVars(apiUrl, token, project2, "build");
|
|
16841
17164
|
if (!vars.some((v) => v.key === key && v.scope === "build")) {
|
|
16842
17165
|
process.stderr.write(
|
|
16843
|
-
`${key} is not set as a build-time variable on ${
|
|
17166
|
+
`${key} is not set as a build-time variable on ${project2}.
|
|
16844
17167
|
Run \`kryd env list\` to see what is set. (If you meant the runtime variable of that name, drop --build.)
|
|
16845
17168
|
`
|
|
16846
17169
|
);
|
|
@@ -16863,7 +17186,7 @@ Run \`kryd env list\` to see what is set. (If you meant the runtime variable of
|
|
|
16863
17186
|
return;
|
|
16864
17187
|
}
|
|
16865
17188
|
const confirmed = await promptConfirm(
|
|
16866
|
-
scope === "build" ? `Remove the build-time variable ${key} from ${
|
|
17189
|
+
scope === "build" ? `Remove the build-time variable ${key} from ${project2}? Your next build will not have it (the current image still does).` : environment === "preview" ? `Remove the preview override for ${key} on ${project2}? Previews will revert to inheriting the production value.` : `Remove ${key} from ${project2}? Its stored value (production and any preview override) is deleted and cannot be recovered.`,
|
|
16867
17190
|
io
|
|
16868
17191
|
);
|
|
16869
17192
|
if (!confirmed) {
|
|
@@ -16873,10 +17196,10 @@ Run \`kryd env list\` to see what is set. (If you meant the runtime variable of
|
|
|
16873
17196
|
}
|
|
16874
17197
|
}
|
|
16875
17198
|
try {
|
|
16876
|
-
const removed = await removeEnvVar(apiUrl, token,
|
|
17199
|
+
const removed = await removeEnvVar(apiUrl, token, project2, key, scope, environment);
|
|
16877
17200
|
const envTag = environment === "preview" ? " (preview override)" : "";
|
|
16878
17201
|
process.stdout.write(
|
|
16879
|
-
`Removed ${removed.key}${scope === "build" ? " (build-time)" : envTag} from ${
|
|
17202
|
+
`Removed ${removed.key}${scope === "build" ? " (build-time)" : envTag} from ${project2}.
|
|
16880
17203
|
` + // The build-scope warning is deliberately bleaker than the runtime one, because the truth is.
|
|
16881
17204
|
// A runtime value is gone from the container at the next deploy. A build-time value is
|
|
16882
17205
|
// COMPILED INTO the live image and into every bundle already downloaded from it — removing
|
|
@@ -16889,7 +17212,7 @@ Run \`kryd env list\` to see what is set. (If you meant the runtime variable of
|
|
|
16889
17212
|
reportError(err);
|
|
16890
17213
|
}
|
|
16891
17214
|
}
|
|
16892
|
-
var CLI_VERSION = true ? "0.
|
|
17215
|
+
var CLI_VERSION = true ? "0.4.0" : "0.0.0-dev";
|
|
16893
17216
|
var program = new Command();
|
|
16894
17217
|
program.name("kryd").description("Kryd CLI").version(CLI_VERSION);
|
|
16895
17218
|
program.command("login").description("Sign in via the browser (default) and store a token").option("--email <email>", "account email (with --password; non-interactive escape hatch)").option("--password <password>", "account password (with --email; visible in `ps` \u2014 prefer the browser flow)").option(
|
|
@@ -16903,7 +17226,14 @@ program.command("init").description("Link this project's repo + register its pus
|
|
|
16903
17226
|
"react-router | nextjs | vite-spa | node (auto-detected if omitted)"
|
|
16904
17227
|
).option(
|
|
16905
17228
|
"--tenant <slug>",
|
|
16906
|
-
|
|
17229
|
+
// KRYD-265 / KRYD-188: this used to say "claimed once on first init", which was false — signup
|
|
17230
|
+
// has always assigned a slug, so the flag could only agree with it or 409. It now changes the
|
|
17231
|
+
// slug, and only while nothing derives from it.
|
|
17232
|
+
"your tenant slug \u2014 appears in every deploy URL; changeable until your first project"
|
|
17233
|
+
).option(
|
|
17234
|
+
"--public",
|
|
17235
|
+
// KRYD-184a. Chosen at creation; there is no command to flip it afterwards yet (KRYD-341).
|
|
17236
|
+
"create the repository PUBLIC \u2014 readable by anyone signed in to the forge (default: private). Set at creation only"
|
|
16907
17237
|
).option("--api-url <url>", "control-plane API base URL").action((opts) => runInit(opts));
|
|
16908
17238
|
program.command("logs [target]").description(
|
|
16909
17239
|
"Follow logs. Default: a deploy's build/deploy logs ([target]=deployment id, latest if omitted). With --runtime: a container's stdout/stderr \u2014 [target]=a project id tails its live app, a deployment id (dpl_\u2026) tails THAT deploy's container (incl. a failed one, to see why it crashed)."
|
|
@@ -16921,24 +17251,29 @@ program.command("push [branch]").description(
|
|
|
16921
17251
|
).option("--api-url <url>", "control-plane API base URL").action(
|
|
16922
17252
|
(branch, opts, cmd) => runPush({ ...opts, branch, gitArgs: cmd.args.slice(1) })
|
|
16923
17253
|
);
|
|
16924
|
-
program.command("deploy [project]").description("Trigger a deploy of the project's production branch and follow it live").option("--api-url <url>", "control-plane API base URL").action((
|
|
17254
|
+
program.command("deploy [project]").description("Trigger a deploy of the project's production branch and follow it live").option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runDeploy({ ...opts, project: project2 }));
|
|
16925
17255
|
program.command("rollback [project] [deployment]").description("Roll back to a previous successful deploy (no rebuild) and follow it live").option("--api-url <url>", "control-plane API base URL").action(
|
|
16926
|
-
(
|
|
17256
|
+
(project2, deployment, opts) => runRollback({ ...opts, project: project2, deployment })
|
|
16927
17257
|
);
|
|
16928
17258
|
program.command("redeploy [project]").description(
|
|
16929
17259
|
"Redeploy the current live commit (no rebuild) to apply config/env changes, and follow it live"
|
|
16930
|
-
).option("--api-url <url>", "control-plane API base URL").action((
|
|
17260
|
+
).option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runRedeploy({ ...opts, project: project2 }));
|
|
17261
|
+
var project = program.command("project").description("Manage your projects");
|
|
17262
|
+
project.command("list").description("List your projects \u2014 name, subdomain, status and id").option("--api-url <url>", "control-plane API base URL").action((opts) => runProjectList(opts));
|
|
17263
|
+
project.command("rm [project]").description(
|
|
17264
|
+
"Permanently delete a project and everything Kryd created for it (by name or id; asks you to type its name)"
|
|
17265
|
+
).option("--yes", "skip the confirmation (for non-interactive use)").option("--api-url <url>", "control-plane API base URL").action((projectArg, opts) => runProjectRemove({ ...opts, project: projectArg }));
|
|
16931
17266
|
var db = program.command("db").description("Manage project databases");
|
|
16932
17267
|
db.command("create [project]").description("Attach a shared or bring-your-own Postgres database to a project").option("--shared", "attach a shared managed database (the default)").option(
|
|
16933
17268
|
"--connection-string <url>",
|
|
16934
17269
|
"attach an existing database (BYO) by pasting its connection string"
|
|
16935
|
-
).option("--api-url <url>", "control-plane API base URL").action((
|
|
16936
|
-
db.command("detach [project]").description("Tear down the project's database and reclaim its credential").option("--api-url <url>", "control-plane API base URL").action((
|
|
17270
|
+
).option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runDbCreate({ ...opts, project: project2 }));
|
|
17271
|
+
db.command("detach [project]").description("Tear down the project's database and reclaim its credential").option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runDbDetach({ ...opts, project: project2 }));
|
|
16937
17272
|
var storage = program.command("storage").description("Manage project object storage");
|
|
16938
|
-
storage.command("create [project]").description("Create an S3-compatible object-storage bucket for a project").option("--api-url <url>", "control-plane API base URL").action((
|
|
16939
|
-
storage.command("detach [project]").description("Tear down the project's object storage and reclaim its credentials").option("--api-url <url>", "control-plane API base URL").action((
|
|
17273
|
+
storage.command("create [project]").description("Create an S3-compatible object-storage bucket for a project").option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runStorageCreate({ ...opts, project: project2 }));
|
|
17274
|
+
storage.command("detach [project]").description("Tear down the project's object storage and reclaim its credentials").option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runStorageDetach({ ...opts, project: project2 }));
|
|
16940
17275
|
var env = program.command("env").description("Manage your app's environment variables");
|
|
16941
|
-
env.command("list [project]").description("List the project's environment variables (names and origin \u2014 never values)").option("--api-url <url>", "control-plane API base URL").action((
|
|
17276
|
+
env.command("list [project]").description("List the project's environment variables (names and origin \u2014 never values)").option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runEnvList({ ...opts, project: project2 }));
|
|
16942
17277
|
env.command("set <key> [project]").description(
|
|
16943
17278
|
"Set one of your environment variables \u2014 `KEY=VALUE`, or `KEY` with --stdin / a prompt"
|
|
16944
17279
|
).option(
|
|
@@ -16950,14 +17285,14 @@ env.command("set <key> [project]").description(
|
|
|
16950
17285
|
).option(
|
|
16951
17286
|
"--preview",
|
|
16952
17287
|
"set the value for PREVIEW deploys only \u2014 an override that shadows production for previews (without it, a set targets production, which previews inherit)"
|
|
16953
|
-
).option("--api-url <url>", "control-plane API base URL").action((key,
|
|
17288
|
+
).option("--api-url <url>", "control-plane API base URL").action((key, project2, opts) => runEnvSet({ ...opts, spec: key, project: project2 }));
|
|
16954
17289
|
env.command("rm <key> [project]").description("Remove one of your environment variables (asks for confirmation)").option("--yes", "skip the confirmation prompt (required when there is no terminal)").option(
|
|
16955
17290
|
"--build",
|
|
16956
17291
|
"remove the build-time variable of this name rather than the runtime one (the same name can exist as both)"
|
|
16957
17292
|
).option(
|
|
16958
17293
|
"--preview",
|
|
16959
17294
|
"remove only the PREVIEW override, reverting previews to inheriting production (without it, the whole key is removed \u2014 production and any preview override)"
|
|
16960
|
-
).option("--api-url <url>", "control-plane API base URL").action((key,
|
|
17295
|
+
).option("--api-url <url>", "control-plane API base URL").action((key, project2, opts) => runEnvRemove({ ...opts, key, project: project2 }));
|
|
16961
17296
|
env.command("pull [project]").description(
|
|
16962
17297
|
"Write your customer-set environment variables (values included) to a local .env file for development"
|
|
16963
17298
|
).option(
|
|
@@ -16966,9 +17301,9 @@ env.command("pull [project]").description(
|
|
|
16966
17301
|
).option(
|
|
16967
17302
|
"--out <path>",
|
|
16968
17303
|
"file to write, relative to the project root (default: .env.kryd \u2014 never your hand-maintained .env)"
|
|
16969
|
-
).option("--force", "overwrite the --out file if it already exists").option("--api-url <url>", "control-plane API base URL").action((
|
|
17304
|
+
).option("--force", "overwrite the --out file if it already exists").option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runEnvPull({ ...opts, project: project2 }));
|
|
16970
17305
|
var ai = program.command("ai").description("Manage the app's EU AI gateway");
|
|
16971
|
-
ai.command("enable [project]").description("Give the project an authenticated EU AI endpoint (injects on next deploy)").option("--api-url <url>", "control-plane API base URL").action((
|
|
17306
|
+
ai.command("enable [project]").description("Give the project an authenticated EU AI endpoint (injects on next deploy)").option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runAiEnable({ ...opts, project: project2 }));
|
|
16972
17307
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
16973
17308
|
program.parseAsync().catch((err) => {
|
|
16974
17309
|
process.stderr.write(
|
|
@@ -16992,6 +17327,8 @@ export {
|
|
|
16992
17327
|
runLogin,
|
|
16993
17328
|
runLogout,
|
|
16994
17329
|
runLogs,
|
|
17330
|
+
runProjectList,
|
|
17331
|
+
runProjectRemove,
|
|
16995
17332
|
runPush,
|
|
16996
17333
|
runRedeploy,
|
|
16997
17334
|
runRollback,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kryd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Kryd CLI — push a React / Vite / Next.js app to the European cloud for your AI: git push → live with SSL, one-click managed Postgres & object storage, and an EU-hosted AI gateway already wired in. Your code and your model calls stay in the EU.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kryd",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"typescript": "^5.6.3",
|
|
51
51
|
"vitest": "^2.1.8",
|
|
52
52
|
"@kryd/shared-types": "0.0.0",
|
|
53
|
-
"@kryd/config-
|
|
54
|
-
"@kryd/config-
|
|
53
|
+
"@kryd/config-ts": "0.0.0",
|
|
54
|
+
"@kryd/config-eslint": "0.0.0"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --external:commander --define:__KRYD_VERSION__=\\\"$npm_package_version\\\" --outfile=dist/index.js",
|