@kryd/cli 0.2.1 → 0.3.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 +328 -82
- package/package.json +1 -1
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,49 @@ 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 deleteProject(apiUrl, token, projectId) {
|
|
15331
|
+
const res = await fetch(`${apiUrl}/projects/${encodeURIComponent(projectId)}`, {
|
|
15332
|
+
method: "DELETE",
|
|
15333
|
+
headers: { authorization: `Bearer ${token}` }
|
|
15334
|
+
});
|
|
15335
|
+
if (!res.ok) {
|
|
15336
|
+
throw new ApiError(`Project delete failed (${res.status})`, await parseEnvelope(res));
|
|
15337
|
+
}
|
|
15338
|
+
return await res.json();
|
|
15339
|
+
}
|
|
15340
|
+
async function pollProjectDeleted(read, opts) {
|
|
15341
|
+
const intervalMs = opts?.intervalMs ?? 2e3;
|
|
15342
|
+
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
15343
|
+
const maxConsecutiveErrors = opts?.maxConsecutiveErrors ?? 5;
|
|
15344
|
+
const sleep = opts?.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
15345
|
+
const started = Date.now();
|
|
15346
|
+
let consecutiveErrors = 0;
|
|
15347
|
+
let last = null;
|
|
15348
|
+
for (; ; ) {
|
|
15349
|
+
try {
|
|
15350
|
+
const project2 = await read();
|
|
15351
|
+
consecutiveErrors = 0;
|
|
15352
|
+
if (project2 === null) return { gone: true, status: null };
|
|
15353
|
+
last = project2.status;
|
|
15354
|
+
opts?.onTick?.(project2.status);
|
|
15355
|
+
if (project2.status === "delete_failed") return { gone: false, status: last };
|
|
15356
|
+
} catch (err) {
|
|
15357
|
+
if (++consecutiveErrors >= maxConsecutiveErrors) throw err;
|
|
15358
|
+
}
|
|
15359
|
+
if (Date.now() - started >= timeoutMs) return { gone: false, status: last };
|
|
15360
|
+
await sleep(intervalMs);
|
|
15361
|
+
}
|
|
15362
|
+
}
|
|
15244
15363
|
async function detachStorage(apiUrl, token, projectId) {
|
|
15245
15364
|
const res = await fetch(`${apiUrl}/storage/${encodeURIComponent(projectId)}`, {
|
|
15246
15365
|
method: "DELETE",
|
|
@@ -15711,6 +15830,18 @@ async function promptHidden(question, io) {
|
|
|
15711
15830
|
io.output.write("\n");
|
|
15712
15831
|
}
|
|
15713
15832
|
}
|
|
15833
|
+
async function promptLine(question, io) {
|
|
15834
|
+
const rl = createInterface({ input: io.input, output: io.output });
|
|
15835
|
+
try {
|
|
15836
|
+
return await new Promise((resolve2) => {
|
|
15837
|
+
rl.once("SIGINT", () => resolve2(null));
|
|
15838
|
+
rl.once("close", () => resolve2(null));
|
|
15839
|
+
rl.question(question, (answer) => resolve2(answer));
|
|
15840
|
+
});
|
|
15841
|
+
} finally {
|
|
15842
|
+
rl.close();
|
|
15843
|
+
}
|
|
15844
|
+
}
|
|
15714
15845
|
async function promptConfirm(question, io) {
|
|
15715
15846
|
const rl = createInterface({ input: io.input, output: io.output });
|
|
15716
15847
|
try {
|
|
@@ -15885,7 +16016,7 @@ async function runInit(opts) {
|
|
|
15885
16016
|
}
|
|
15886
16017
|
const name = opts.name ?? detected.name;
|
|
15887
16018
|
try {
|
|
15888
|
-
const { project, repo, pushToken } = await linkProject(apiUrl, token, {
|
|
16019
|
+
const { project: project2, repo, pushToken } = await linkProject(apiUrl, token, {
|
|
15889
16020
|
name,
|
|
15890
16021
|
framework,
|
|
15891
16022
|
// First `kryd init` for the account claims the tenant slug (the vanity routing key in
|
|
@@ -15895,7 +16026,7 @@ async function runInit(opts) {
|
|
|
15895
16026
|
});
|
|
15896
16027
|
let linkNote = "";
|
|
15897
16028
|
try {
|
|
15898
|
-
saveProjectLink(cwd, { projectId:
|
|
16029
|
+
saveProjectLink(cwd, { projectId: project2.id });
|
|
15899
16030
|
linkNote = "Linked this folder \u2192 .kryd/project.json \u2014 `kryd deploy` / `kryd logs` now work here with no id.\n";
|
|
15900
16031
|
try {
|
|
15901
16032
|
ensureKrydIgnored(cwd);
|
|
@@ -15907,7 +16038,7 @@ async function runInit(opts) {
|
|
|
15907
16038
|
}
|
|
15908
16039
|
} catch (linkErr) {
|
|
15909
16040
|
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 ${
|
|
16041
|
+
`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
16042
|
`
|
|
15912
16043
|
);
|
|
15913
16044
|
}
|
|
@@ -15940,9 +16071,9 @@ Install git (https://git-scm.com/downloads), then re-run \`kryd init\` here.
|
|
|
15940
16071
|
break;
|
|
15941
16072
|
}
|
|
15942
16073
|
process.stdout.write(
|
|
15943
|
-
`Linked "${name}" (${
|
|
15944
|
-
Production URL: https://${
|
|
15945
|
-
Preview branches deploy to https://<branch>-<hash>-${
|
|
16074
|
+
`Linked "${name}" (${project2.framework}) \u2192 ${repo.htmlUrl}
|
|
16075
|
+
Production URL: https://${project2.subdomain}.${PRODUCTION_TLD}
|
|
16076
|
+
Preview branches deploy to https://<branch>-<hash>-${project2.subdomain}.${PREVIEW_TLD}
|
|
15946
16077
|
${linkNote}
|
|
15947
16078
|
${nextStep}`
|
|
15948
16079
|
);
|
|
@@ -16060,13 +16191,13 @@ async function runDeploy(opts) {
|
|
|
16060
16191
|
process.exitCode = 1;
|
|
16061
16192
|
return;
|
|
16062
16193
|
}
|
|
16063
|
-
const
|
|
16064
|
-
if (!
|
|
16194
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16195
|
+
if (!project2) {
|
|
16065
16196
|
reportNotLinked("kryd deploy <projectId>");
|
|
16066
16197
|
return;
|
|
16067
16198
|
}
|
|
16068
16199
|
try {
|
|
16069
|
-
const deploymentId = await triggerDeploy(apiUrl, token,
|
|
16200
|
+
const deploymentId = await triggerDeploy(apiUrl, token, project2);
|
|
16070
16201
|
process.stdout.write(`Triggered deploy ${deploymentId}
|
|
16071
16202
|
`);
|
|
16072
16203
|
await followDeploy(apiUrl, token, deploymentId);
|
|
@@ -16211,8 +16342,8 @@ async function runRollback(opts) {
|
|
|
16211
16342
|
process.exitCode = 1;
|
|
16212
16343
|
return;
|
|
16213
16344
|
}
|
|
16214
|
-
const
|
|
16215
|
-
if (!
|
|
16345
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16346
|
+
if (!project2) {
|
|
16216
16347
|
reportNotLinked("kryd rollback <projectId> [deployment]");
|
|
16217
16348
|
return;
|
|
16218
16349
|
}
|
|
@@ -16220,7 +16351,7 @@ async function runRollback(opts) {
|
|
|
16220
16351
|
const { deploymentId, rolledBackTo, note } = await rollbackDeploy(
|
|
16221
16352
|
apiUrl,
|
|
16222
16353
|
token,
|
|
16223
|
-
|
|
16354
|
+
project2,
|
|
16224
16355
|
opts.deployment
|
|
16225
16356
|
);
|
|
16226
16357
|
process.stdout.write(
|
|
@@ -16241,8 +16372,8 @@ async function runRedeploy(opts) {
|
|
|
16241
16372
|
process.exitCode = 1;
|
|
16242
16373
|
return;
|
|
16243
16374
|
}
|
|
16244
|
-
const
|
|
16245
|
-
if (!
|
|
16375
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16376
|
+
if (!project2) {
|
|
16246
16377
|
reportNotLinked("kryd redeploy <projectId>");
|
|
16247
16378
|
return;
|
|
16248
16379
|
}
|
|
@@ -16250,7 +16381,7 @@ async function runRedeploy(opts) {
|
|
|
16250
16381
|
const { deploymentId, redeployedCommit, note } = await redeployProject(
|
|
16251
16382
|
apiUrl,
|
|
16252
16383
|
token,
|
|
16253
|
-
|
|
16384
|
+
project2
|
|
16254
16385
|
);
|
|
16255
16386
|
process.stdout.write(
|
|
16256
16387
|
`Redeploying ${redeployedCommit.slice(0, 7)} \u2192 ${deploymentId}
|
|
@@ -16270,8 +16401,8 @@ async function runDbCreate(opts) {
|
|
|
16270
16401
|
process.exitCode = 1;
|
|
16271
16402
|
return;
|
|
16272
16403
|
}
|
|
16273
|
-
const
|
|
16274
|
-
if (!
|
|
16404
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16405
|
+
if (!project2) {
|
|
16275
16406
|
reportNotLinked("kryd db create <projectId>");
|
|
16276
16407
|
return;
|
|
16277
16408
|
}
|
|
@@ -16299,29 +16430,29 @@ async function runDbCreate(opts) {
|
|
|
16299
16430
|
}
|
|
16300
16431
|
try {
|
|
16301
16432
|
const db2 = await attachDatabase(apiUrl, token, {
|
|
16302
|
-
projectId:
|
|
16433
|
+
projectId: project2,
|
|
16303
16434
|
mode,
|
|
16304
16435
|
...connectionString ? { connectionString } : {}
|
|
16305
16436
|
});
|
|
16306
16437
|
if (db2.status === "ready") {
|
|
16307
16438
|
process.stdout.write(
|
|
16308
|
-
`Attached ${db2.mode} database ${db2.id} to ${
|
|
16439
|
+
`Attached ${db2.mode} database ${db2.id} to ${project2}.
|
|
16309
16440
|
The connection string will be injected as DATABASE_URL on your next deploy.
|
|
16310
16441
|
`
|
|
16311
16442
|
);
|
|
16312
16443
|
return;
|
|
16313
16444
|
}
|
|
16314
|
-
process.stdout.write(`Provisioning a shared database for ${
|
|
16445
|
+
process.stdout.write(`Provisioning a shared database for ${project2}\u2026
|
|
16315
16446
|
`);
|
|
16316
16447
|
const result = await pollResourceUntilTerminal(
|
|
16317
|
-
() => getDatabaseStatus(apiUrl, token,
|
|
16448
|
+
() => getDatabaseStatus(apiUrl, token, project2)
|
|
16318
16449
|
);
|
|
16319
16450
|
reportSettleResult(result, {
|
|
16320
|
-
readyMsg: `Attached shared database ${db2.id} to ${
|
|
16451
|
+
readyMsg: `Attached shared database ${db2.id} to ${project2}.
|
|
16321
16452
|
The connection string will be injected as DATABASE_URL on your next deploy.
|
|
16322
16453
|
`,
|
|
16323
16454
|
failedLabel: "Database provisioning",
|
|
16324
|
-
retryCmd: `kryd db create ${
|
|
16455
|
+
retryCmd: `kryd db create ${project2}`
|
|
16325
16456
|
});
|
|
16326
16457
|
} catch (err) {
|
|
16327
16458
|
reportError(err);
|
|
@@ -16351,24 +16482,24 @@ async function runStorageCreate(opts) {
|
|
|
16351
16482
|
process.exitCode = 1;
|
|
16352
16483
|
return;
|
|
16353
16484
|
}
|
|
16354
|
-
const
|
|
16355
|
-
if (!
|
|
16485
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16486
|
+
if (!project2) {
|
|
16356
16487
|
reportNotLinked("kryd storage create <projectId>");
|
|
16357
16488
|
return;
|
|
16358
16489
|
}
|
|
16359
16490
|
try {
|
|
16360
|
-
const bucket = await createStorage(apiUrl, token, { projectId:
|
|
16361
|
-
process.stdout.write(`Provisioning object storage for ${
|
|
16491
|
+
const bucket = await createStorage(apiUrl, token, { projectId: project2 });
|
|
16492
|
+
process.stdout.write(`Provisioning object storage for ${project2}\u2026
|
|
16362
16493
|
`);
|
|
16363
16494
|
const result = await pollResourceUntilTerminal(
|
|
16364
|
-
() => getStorageStatus(apiUrl, token,
|
|
16495
|
+
() => getStorageStatus(apiUrl, token, project2)
|
|
16365
16496
|
);
|
|
16366
16497
|
reportSettleResult(result, {
|
|
16367
|
-
readyMsg: `Created object storage ${bucket.id} for ${
|
|
16498
|
+
readyMsg: `Created object storage ${bucket.id} for ${project2}.
|
|
16368
16499
|
Its S3 credentials will be injected on your next deploy.
|
|
16369
16500
|
`,
|
|
16370
16501
|
failedLabel: "Storage provisioning",
|
|
16371
|
-
retryCmd: `kryd storage create ${
|
|
16502
|
+
retryCmd: `kryd storage create ${project2}`
|
|
16372
16503
|
});
|
|
16373
16504
|
} catch (err) {
|
|
16374
16505
|
reportError(err);
|
|
@@ -16391,6 +16522,113 @@ function reportDetachResult(result, opts) {
|
|
|
16391
16522
|
process.exitCode = 1;
|
|
16392
16523
|
}
|
|
16393
16524
|
}
|
|
16525
|
+
async function runProjectRemove(opts) {
|
|
16526
|
+
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
16527
|
+
const token = loadConfig().token;
|
|
16528
|
+
if (!token) {
|
|
16529
|
+
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
16530
|
+
process.exitCode = 1;
|
|
16531
|
+
return;
|
|
16532
|
+
}
|
|
16533
|
+
const projectId = resolveProjectId(opts.project, opts.cwd);
|
|
16534
|
+
if (!projectId) {
|
|
16535
|
+
reportNotLinked("kryd project rm <projectId>");
|
|
16536
|
+
return;
|
|
16537
|
+
}
|
|
16538
|
+
let project2;
|
|
16539
|
+
let resources;
|
|
16540
|
+
try {
|
|
16541
|
+
project2 = await getProject(apiUrl, token, projectId);
|
|
16542
|
+
if (!project2) {
|
|
16543
|
+
process.stderr.write(`No project ${projectId} on this account.
|
|
16544
|
+
`);
|
|
16545
|
+
process.exitCode = 1;
|
|
16546
|
+
return;
|
|
16547
|
+
}
|
|
16548
|
+
if (project2.status === "deleting") {
|
|
16549
|
+
process.stdout.write(`${project2.name} is already being deleted \u2014 following it.
|
|
16550
|
+
`);
|
|
16551
|
+
return followProjectDeletion(apiUrl, token, projectId, project2.name, opts);
|
|
16552
|
+
}
|
|
16553
|
+
resources = project2.status === "delete_failed" ? ["whatever the previous attempt did not manage to remove"] : await describeProjectResources(apiUrl, token, projectId);
|
|
16554
|
+
} catch (err) {
|
|
16555
|
+
reportError(err);
|
|
16556
|
+
return;
|
|
16557
|
+
}
|
|
16558
|
+
if (!opts.yes) {
|
|
16559
|
+
const io = opts.io ?? defaultPromptIO();
|
|
16560
|
+
if (!isInteractive(io)) {
|
|
16561
|
+
process.stderr.write(
|
|
16562
|
+
`Refusing to delete ${project2.name} without confirmation. Re-run with --yes to confirm non-interactively.
|
|
16563
|
+
`
|
|
16564
|
+
);
|
|
16565
|
+
process.exitCode = 1;
|
|
16566
|
+
return;
|
|
16567
|
+
}
|
|
16568
|
+
io.output.write(
|
|
16569
|
+
`This permanently deletes ${project2.name} and:
|
|
16570
|
+
` + resources.map((r) => ` \u2022 ${r}
|
|
16571
|
+
`).join("") + "This cannot be undone.\n"
|
|
16572
|
+
);
|
|
16573
|
+
const typed = await promptLine(
|
|
16574
|
+
`Type ${project2.name} to confirm: `,
|
|
16575
|
+
io
|
|
16576
|
+
);
|
|
16577
|
+
if (typed !== project2.name) {
|
|
16578
|
+
process.stdout.write(`Left ${project2.name} in place.
|
|
16579
|
+
`);
|
|
16580
|
+
return;
|
|
16581
|
+
}
|
|
16582
|
+
}
|
|
16583
|
+
try {
|
|
16584
|
+
const accepted = await deleteProject(apiUrl, token, projectId);
|
|
16585
|
+
process.stdout.write(
|
|
16586
|
+
accepted.alreadyInProgress ? `${project2.name} was already being deleted \u2014 following it.
|
|
16587
|
+
` : `Deleting ${project2.name}\u2026
|
|
16588
|
+
`
|
|
16589
|
+
);
|
|
16590
|
+
await followProjectDeletion(apiUrl, token, projectId, project2.name, opts);
|
|
16591
|
+
} catch (err) {
|
|
16592
|
+
reportError(err);
|
|
16593
|
+
}
|
|
16594
|
+
}
|
|
16595
|
+
async function followProjectDeletion(apiUrl, token, projectId, name, opts) {
|
|
16596
|
+
const result = await pollProjectDeleted(
|
|
16597
|
+
() => getProject(apiUrl, token, projectId),
|
|
16598
|
+
opts.pollOpts
|
|
16599
|
+
);
|
|
16600
|
+
if (result.gone) {
|
|
16601
|
+
const cleared = clearProjectLinkIfMatches(projectId, opts.cwd);
|
|
16602
|
+
process.stdout.write(
|
|
16603
|
+
`Deleted ${name}.
|
|
16604
|
+
` + (cleared ? "Removed the local .kryd/project.json link.\n" : "")
|
|
16605
|
+
);
|
|
16606
|
+
return;
|
|
16607
|
+
}
|
|
16608
|
+
if (result.status === "delete_failed") {
|
|
16609
|
+
process.stderr.write(
|
|
16610
|
+
`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.
|
|
16611
|
+
Re-run \`kryd project rm ${projectId}\` to resume from where it stopped.
|
|
16612
|
+
`
|
|
16613
|
+
);
|
|
16614
|
+
process.exitCode = 1;
|
|
16615
|
+
return;
|
|
16616
|
+
}
|
|
16617
|
+
process.stderr.write(
|
|
16618
|
+
`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.
|
|
16619
|
+
`
|
|
16620
|
+
);
|
|
16621
|
+
process.exitCode = 1;
|
|
16622
|
+
}
|
|
16623
|
+
async function describeProjectResources(apiUrl, token, projectId) {
|
|
16624
|
+
const out = ["its deployed app, its containers and its built images"];
|
|
16625
|
+
const db2 = await getDatabaseStatus(apiUrl, token, projectId).catch(() => null);
|
|
16626
|
+
if (db2) out.push("its Postgres database \u2014 and all the data in it");
|
|
16627
|
+
const storage2 = await getStorageStatus(apiUrl, token, projectId).catch(() => null);
|
|
16628
|
+
if (storage2) out.push("its object-storage bucket \u2014 and every object in it");
|
|
16629
|
+
out.push("its Git repository on the forge, and its push token");
|
|
16630
|
+
return out;
|
|
16631
|
+
}
|
|
16394
16632
|
async function runDbDetach(opts) {
|
|
16395
16633
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
16396
16634
|
const token = loadConfig().token;
|
|
@@ -16399,23 +16637,23 @@ async function runDbDetach(opts) {
|
|
|
16399
16637
|
process.exitCode = 1;
|
|
16400
16638
|
return;
|
|
16401
16639
|
}
|
|
16402
|
-
const
|
|
16403
|
-
if (!
|
|
16640
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16641
|
+
if (!project2) {
|
|
16404
16642
|
reportNotLinked("kryd db detach <projectId>");
|
|
16405
16643
|
return;
|
|
16406
16644
|
}
|
|
16407
16645
|
try {
|
|
16408
|
-
await detachDatabase(apiUrl, token,
|
|
16409
|
-
process.stdout.write(`Detaching the database from ${
|
|
16646
|
+
await detachDatabase(apiUrl, token, project2);
|
|
16647
|
+
process.stdout.write(`Detaching the database from ${project2}\u2026
|
|
16410
16648
|
`);
|
|
16411
16649
|
const result = await pollResourceDetached(
|
|
16412
|
-
() => getDatabaseStatus(apiUrl, token,
|
|
16650
|
+
() => getDatabaseStatus(apiUrl, token, project2)
|
|
16413
16651
|
);
|
|
16414
16652
|
reportDetachResult(result, {
|
|
16415
|
-
goneMsg: `Detached the database from ${
|
|
16653
|
+
goneMsg: `Detached the database from ${project2}.
|
|
16416
16654
|
`,
|
|
16417
16655
|
failedLabel: "Database detach",
|
|
16418
|
-
retryCmd: `kryd db detach ${
|
|
16656
|
+
retryCmd: `kryd db detach ${project2}`
|
|
16419
16657
|
});
|
|
16420
16658
|
} catch (err) {
|
|
16421
16659
|
reportError(err);
|
|
@@ -16429,15 +16667,15 @@ async function runAiEnable(opts) {
|
|
|
16429
16667
|
process.exitCode = 1;
|
|
16430
16668
|
return;
|
|
16431
16669
|
}
|
|
16432
|
-
const
|
|
16433
|
-
if (!
|
|
16670
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16671
|
+
if (!project2) {
|
|
16434
16672
|
reportNotLinked("kryd ai enable <projectId>");
|
|
16435
16673
|
return;
|
|
16436
16674
|
}
|
|
16437
16675
|
try {
|
|
16438
|
-
const status = await enableAi(apiUrl, token, { projectId:
|
|
16676
|
+
const status = await enableAi(apiUrl, token, { projectId: project2 });
|
|
16439
16677
|
process.stdout.write(
|
|
16440
|
-
`AI enabled for ${
|
|
16678
|
+
`AI enabled for ${project2}.
|
|
16441
16679
|
` + (status.url ? `Endpoint: ${status.url} (OpenAI-compatible base URL \u2014 use it as-is)
|
|
16442
16680
|
` : "") + `AI_GATEWAY_URL + AI_GATEWAY_TOKEN will be injected on your next deploy (kryd deploy).
|
|
16443
16681
|
`
|
|
@@ -16454,23 +16692,23 @@ async function runStorageDetach(opts) {
|
|
|
16454
16692
|
process.exitCode = 1;
|
|
16455
16693
|
return;
|
|
16456
16694
|
}
|
|
16457
|
-
const
|
|
16458
|
-
if (!
|
|
16695
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16696
|
+
if (!project2) {
|
|
16459
16697
|
reportNotLinked("kryd storage detach <projectId>");
|
|
16460
16698
|
return;
|
|
16461
16699
|
}
|
|
16462
16700
|
try {
|
|
16463
|
-
await detachStorage(apiUrl, token,
|
|
16464
|
-
process.stdout.write(`Detaching object storage from ${
|
|
16701
|
+
await detachStorage(apiUrl, token, project2);
|
|
16702
|
+
process.stdout.write(`Detaching object storage from ${project2}\u2026
|
|
16465
16703
|
`);
|
|
16466
16704
|
const result = await pollResourceDetached(
|
|
16467
|
-
() => getStorageStatus(apiUrl, token,
|
|
16705
|
+
() => getStorageStatus(apiUrl, token, project2)
|
|
16468
16706
|
);
|
|
16469
16707
|
reportDetachResult(result, {
|
|
16470
|
-
goneMsg: `Detached object storage from ${
|
|
16708
|
+
goneMsg: `Detached object storage from ${project2}.
|
|
16471
16709
|
`,
|
|
16472
16710
|
failedLabel: "Storage detach",
|
|
16473
|
-
retryCmd: `kryd storage detach ${
|
|
16711
|
+
retryCmd: `kryd storage detach ${project2}`
|
|
16474
16712
|
});
|
|
16475
16713
|
} catch (err) {
|
|
16476
16714
|
reportError(err);
|
|
@@ -16523,16 +16761,16 @@ async function runEnvList(opts) {
|
|
|
16523
16761
|
process.exitCode = 1;
|
|
16524
16762
|
return;
|
|
16525
16763
|
}
|
|
16526
|
-
const
|
|
16527
|
-
if (!
|
|
16764
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16765
|
+
if (!project2) {
|
|
16528
16766
|
reportNotLinked("kryd env list <projectId>");
|
|
16529
16767
|
return;
|
|
16530
16768
|
}
|
|
16531
16769
|
try {
|
|
16532
|
-
const vars = await listEnvVars(apiUrl, token,
|
|
16770
|
+
const vars = await listEnvVars(apiUrl, token, project2);
|
|
16533
16771
|
if (vars.length === 0) {
|
|
16534
16772
|
process.stdout.write(
|
|
16535
|
-
`No environment variables for ${
|
|
16773
|
+
`No environment variables for ${project2} yet.
|
|
16536
16774
|
Set one with \`kryd env set KEY --stdin\`, or attach a database, storage or the AI gateway.
|
|
16537
16775
|
`
|
|
16538
16776
|
);
|
|
@@ -16605,8 +16843,8 @@ async function runEnvPull(opts) {
|
|
|
16605
16843
|
process.exitCode = 1;
|
|
16606
16844
|
return;
|
|
16607
16845
|
}
|
|
16608
|
-
const
|
|
16609
|
-
if (!
|
|
16846
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16847
|
+
if (!project2) {
|
|
16610
16848
|
reportNotLinked("kryd env pull <projectId>");
|
|
16611
16849
|
return;
|
|
16612
16850
|
}
|
|
@@ -16624,11 +16862,11 @@ async function runEnvPull(opts) {
|
|
|
16624
16862
|
return;
|
|
16625
16863
|
}
|
|
16626
16864
|
try {
|
|
16627
|
-
const { values } = await pullEnvValues(apiUrl, token,
|
|
16865
|
+
const { values } = await pullEnvValues(apiUrl, token, project2, environment);
|
|
16628
16866
|
const entries = Object.entries(values).sort(
|
|
16629
16867
|
([a], [b]) => a.localeCompare(b)
|
|
16630
16868
|
);
|
|
16631
|
-
const header = `# Written by \`kryd env pull\` \u2014 your customer-set variables for ${
|
|
16869
|
+
const header = `# Written by \`kryd env pull\` \u2014 your customer-set variables for ${project2} (${environment}).
|
|
16632
16870
|
# Regenerate with \`kryd env pull\`. Do not commit this file.
|
|
16633
16871
|
`;
|
|
16634
16872
|
const body = entries.map(([k, v]) => toDotenvLine(k, v)).join("\n");
|
|
@@ -16640,7 +16878,7 @@ ${body}
|
|
|
16640
16878
|
` : header);
|
|
16641
16879
|
const count = entries.length;
|
|
16642
16880
|
process.stdout.write(
|
|
16643
|
-
count === 0 ? `No customer environment variables for ${
|
|
16881
|
+
count === 0 ? `No customer environment variables for ${project2} (${environment}) \u2014 wrote an empty ${outRel}.
|
|
16644
16882
|
` : `Wrote ${count} variable${count === 1 ? "" : "s"} to ${outRel} (${environment}), mode 0600.
|
|
16645
16883
|
`
|
|
16646
16884
|
);
|
|
@@ -16706,8 +16944,8 @@ async function runEnvSet(opts) {
|
|
|
16706
16944
|
process.exitCode = 1;
|
|
16707
16945
|
return;
|
|
16708
16946
|
}
|
|
16709
|
-
const
|
|
16710
|
-
if (!
|
|
16947
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
16948
|
+
if (!project2) {
|
|
16711
16949
|
reportNotLinked("kryd env set KEY=VALUE <projectId>");
|
|
16712
16950
|
return;
|
|
16713
16951
|
}
|
|
@@ -16773,7 +17011,7 @@ async function runEnvSet(opts) {
|
|
|
16773
17011
|
const written = await setEnvVar(
|
|
16774
17012
|
apiUrl,
|
|
16775
17013
|
token,
|
|
16776
|
-
|
|
17014
|
+
project2,
|
|
16777
17015
|
key,
|
|
16778
17016
|
resolved.value,
|
|
16779
17017
|
scope,
|
|
@@ -16781,7 +17019,7 @@ async function runEnvSet(opts) {
|
|
|
16781
17019
|
);
|
|
16782
17020
|
const envTag = environment === "preview" ? " (preview)" : "";
|
|
16783
17021
|
process.stdout.write(
|
|
16784
|
-
`${written.created ? "Set" : "Updated"} ${written.key}${scope === "build" ? " (build-time)" : envTag} for ${
|
|
17022
|
+
`${written.created ? "Set" : "Updated"} ${written.key}${scope === "build" ? " (build-time)" : envTag} for ${project2}.
|
|
16785
17023
|
` + // The public-bundle warning comes FIRST, before the applies-when note. Someone who reads one
|
|
16786
17024
|
// line of output should read the consequential one.
|
|
16787
17025
|
(scope === "build" ? `${BUILD_ENV_PUBLIC_WARNING}
|
|
@@ -16801,8 +17039,8 @@ async function runEnvRemove(opts) {
|
|
|
16801
17039
|
process.exitCode = 1;
|
|
16802
17040
|
return;
|
|
16803
17041
|
}
|
|
16804
|
-
const
|
|
16805
|
-
if (!
|
|
17042
|
+
const project2 = resolveProjectId(opts.project, opts.cwd);
|
|
17043
|
+
if (!project2) {
|
|
16806
17044
|
reportNotLinked("kryd env rm KEY <projectId>");
|
|
16807
17045
|
return;
|
|
16808
17046
|
}
|
|
@@ -16837,10 +17075,10 @@ If it comes from a resource you attached, detach that instead (\`kryd db detach\
|
|
|
16837
17075
|
}
|
|
16838
17076
|
if (scope === "build") {
|
|
16839
17077
|
try {
|
|
16840
|
-
const vars = await listEnvVars(apiUrl, token,
|
|
17078
|
+
const vars = await listEnvVars(apiUrl, token, project2, "build");
|
|
16841
17079
|
if (!vars.some((v) => v.key === key && v.scope === "build")) {
|
|
16842
17080
|
process.stderr.write(
|
|
16843
|
-
`${key} is not set as a build-time variable on ${
|
|
17081
|
+
`${key} is not set as a build-time variable on ${project2}.
|
|
16844
17082
|
Run \`kryd env list\` to see what is set. (If you meant the runtime variable of that name, drop --build.)
|
|
16845
17083
|
`
|
|
16846
17084
|
);
|
|
@@ -16863,7 +17101,7 @@ Run \`kryd env list\` to see what is set. (If you meant the runtime variable of
|
|
|
16863
17101
|
return;
|
|
16864
17102
|
}
|
|
16865
17103
|
const confirmed = await promptConfirm(
|
|
16866
|
-
scope === "build" ? `Remove the build-time variable ${key} from ${
|
|
17104
|
+
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
17105
|
io
|
|
16868
17106
|
);
|
|
16869
17107
|
if (!confirmed) {
|
|
@@ -16873,10 +17111,10 @@ Run \`kryd env list\` to see what is set. (If you meant the runtime variable of
|
|
|
16873
17111
|
}
|
|
16874
17112
|
}
|
|
16875
17113
|
try {
|
|
16876
|
-
const removed = await removeEnvVar(apiUrl, token,
|
|
17114
|
+
const removed = await removeEnvVar(apiUrl, token, project2, key, scope, environment);
|
|
16877
17115
|
const envTag = environment === "preview" ? " (preview override)" : "";
|
|
16878
17116
|
process.stdout.write(
|
|
16879
|
-
`Removed ${removed.key}${scope === "build" ? " (build-time)" : envTag} from ${
|
|
17117
|
+
`Removed ${removed.key}${scope === "build" ? " (build-time)" : envTag} from ${project2}.
|
|
16880
17118
|
` + // The build-scope warning is deliberately bleaker than the runtime one, because the truth is.
|
|
16881
17119
|
// A runtime value is gone from the container at the next deploy. A build-time value is
|
|
16882
17120
|
// COMPILED INTO the live image and into every bundle already downloaded from it — removing
|
|
@@ -16889,7 +17127,7 @@ Run \`kryd env list\` to see what is set. (If you meant the runtime variable of
|
|
|
16889
17127
|
reportError(err);
|
|
16890
17128
|
}
|
|
16891
17129
|
}
|
|
16892
|
-
var CLI_VERSION = true ? "0.
|
|
17130
|
+
var CLI_VERSION = true ? "0.3.0" : "0.0.0-dev";
|
|
16893
17131
|
var program = new Command();
|
|
16894
17132
|
program.name("kryd").description("Kryd CLI").version(CLI_VERSION);
|
|
16895
17133
|
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 +17141,10 @@ program.command("init").description("Link this project's repo + register its pus
|
|
|
16903
17141
|
"react-router | nextjs | vite-spa | node (auto-detected if omitted)"
|
|
16904
17142
|
).option(
|
|
16905
17143
|
"--tenant <slug>",
|
|
16906
|
-
|
|
17144
|
+
// KRYD-265 / KRYD-188: this used to say "claimed once on first init", which was false — signup
|
|
17145
|
+
// has always assigned a slug, so the flag could only agree with it or 409. It now changes the
|
|
17146
|
+
// slug, and only while nothing derives from it.
|
|
17147
|
+
"your tenant slug \u2014 appears in every deploy URL; changeable until your first project"
|
|
16907
17148
|
).option("--api-url <url>", "control-plane API base URL").action((opts) => runInit(opts));
|
|
16908
17149
|
program.command("logs [target]").description(
|
|
16909
17150
|
"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 +17162,28 @@ program.command("push [branch]").description(
|
|
|
16921
17162
|
).option("--api-url <url>", "control-plane API base URL").action(
|
|
16922
17163
|
(branch, opts, cmd) => runPush({ ...opts, branch, gitArgs: cmd.args.slice(1) })
|
|
16923
17164
|
);
|
|
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((
|
|
17165
|
+
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
17166
|
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
|
-
(
|
|
17167
|
+
(project2, deployment, opts) => runRollback({ ...opts, project: project2, deployment })
|
|
16927
17168
|
);
|
|
16928
17169
|
program.command("redeploy [project]").description(
|
|
16929
17170
|
"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((
|
|
17171
|
+
).option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runRedeploy({ ...opts, project: project2 }));
|
|
17172
|
+
var project = program.command("project").description("Manage your projects");
|
|
17173
|
+
project.command("rm [project]").description(
|
|
17174
|
+
"Permanently delete a project and everything Kryd created for it (asks you to type its name)"
|
|
17175
|
+
).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
17176
|
var db = program.command("db").description("Manage project databases");
|
|
16932
17177
|
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
17178
|
"--connection-string <url>",
|
|
16934
17179
|
"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((
|
|
17180
|
+
).option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runDbCreate({ ...opts, project: project2 }));
|
|
17181
|
+
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
17182
|
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((
|
|
17183
|
+
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 }));
|
|
17184
|
+
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
17185
|
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((
|
|
17186
|
+
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
17187
|
env.command("set <key> [project]").description(
|
|
16943
17188
|
"Set one of your environment variables \u2014 `KEY=VALUE`, or `KEY` with --stdin / a prompt"
|
|
16944
17189
|
).option(
|
|
@@ -16950,14 +17195,14 @@ env.command("set <key> [project]").description(
|
|
|
16950
17195
|
).option(
|
|
16951
17196
|
"--preview",
|
|
16952
17197
|
"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,
|
|
17198
|
+
).option("--api-url <url>", "control-plane API base URL").action((key, project2, opts) => runEnvSet({ ...opts, spec: key, project: project2 }));
|
|
16954
17199
|
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
17200
|
"--build",
|
|
16956
17201
|
"remove the build-time variable of this name rather than the runtime one (the same name can exist as both)"
|
|
16957
17202
|
).option(
|
|
16958
17203
|
"--preview",
|
|
16959
17204
|
"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,
|
|
17205
|
+
).option("--api-url <url>", "control-plane API base URL").action((key, project2, opts) => runEnvRemove({ ...opts, key, project: project2 }));
|
|
16961
17206
|
env.command("pull [project]").description(
|
|
16962
17207
|
"Write your customer-set environment variables (values included) to a local .env file for development"
|
|
16963
17208
|
).option(
|
|
@@ -16966,9 +17211,9 @@ env.command("pull [project]").description(
|
|
|
16966
17211
|
).option(
|
|
16967
17212
|
"--out <path>",
|
|
16968
17213
|
"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((
|
|
17214
|
+
).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
17215
|
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((
|
|
17216
|
+
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
17217
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
16973
17218
|
program.parseAsync().catch((err) => {
|
|
16974
17219
|
process.stderr.write(
|
|
@@ -16992,6 +17237,7 @@ export {
|
|
|
16992
17237
|
runLogin,
|
|
16993
17238
|
runLogout,
|
|
16994
17239
|
runLogs,
|
|
17240
|
+
runProjectRemove,
|
|
16995
17241
|
runPush,
|
|
16996
17242
|
runRedeploy,
|
|
16997
17243
|
runRollback,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kryd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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",
|