@kryd/cli 0.3.0 → 0.4.1
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 +168 -15
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -14666,6 +14666,23 @@ function date4(params) {
|
|
|
14666
14666
|
config(en_default());
|
|
14667
14667
|
|
|
14668
14668
|
// ../../packages/shared-types/dist/usage.js
|
|
14669
|
+
var USAGE_OUTCOME = {
|
|
14670
|
+
ok: "ok",
|
|
14671
|
+
/** Refused by the request-rate limiter. */
|
|
14672
|
+
throttledRate: "throttled_rate",
|
|
14673
|
+
/** Refused by the daily token cap. */
|
|
14674
|
+
throttledCap: "throttled_cap"
|
|
14675
|
+
};
|
|
14676
|
+
var USAGE_OUTCOMES = [
|
|
14677
|
+
USAGE_OUTCOME.ok,
|
|
14678
|
+
USAGE_OUTCOME.throttledRate,
|
|
14679
|
+
USAGE_OUTCOME.throttledCap
|
|
14680
|
+
];
|
|
14681
|
+
var STORAGE_FRESHNESS_MS = 3 * 60 * 60 * 1e3;
|
|
14682
|
+
var USAGE_THROTTLED_OUTCOMES = [
|
|
14683
|
+
USAGE_OUTCOME.throttledRate,
|
|
14684
|
+
USAGE_OUTCOME.throttledCap
|
|
14685
|
+
];
|
|
14669
14686
|
var usageEventSchema = external_exports.object({
|
|
14670
14687
|
// Gateway-assigned id (stable across POST retries) → the ingest inserts `onConflictDoNothing` on
|
|
14671
14688
|
// it, so a re-sent batch after a lost ACK never double-counts (KRYD-35 review).
|
|
@@ -14688,7 +14705,7 @@ var usageEventSchema = external_exports.object({
|
|
|
14688
14705
|
// fail the whole batch — so one malformed outcome can't discard up to 999 valid usage events in the
|
|
14689
14706
|
// same POST (the batch-loss failure mode the FK-free `account_id` decision already guards, KRYD-35).
|
|
14690
14707
|
// Because only in-set values are ever persisted, the DB column needs no CHECK constraint.
|
|
14691
|
-
outcome: external_exports.enum(
|
|
14708
|
+
outcome: external_exports.enum(USAGE_OUTCOMES).default(USAGE_OUTCOME.ok).catch(USAGE_OUTCOME.ok)
|
|
14692
14709
|
});
|
|
14693
14710
|
var usageIngestBatchSchema = external_exports.object({
|
|
14694
14711
|
events: external_exports.array(usageEventSchema).min(1).max(1e3)
|
|
@@ -15327,6 +15344,15 @@ async function getProject(apiUrl, token, projectId) {
|
|
|
15327
15344
|
}
|
|
15328
15345
|
return await res.json();
|
|
15329
15346
|
}
|
|
15347
|
+
async function listProjects(apiUrl, token) {
|
|
15348
|
+
const res = await fetch(`${apiUrl}/projects`, {
|
|
15349
|
+
headers: { authorization: `Bearer ${token}` }
|
|
15350
|
+
});
|
|
15351
|
+
if (!res.ok) {
|
|
15352
|
+
throw new ApiError(`Listing projects failed (${res.status})`, await parseEnvelope(res));
|
|
15353
|
+
}
|
|
15354
|
+
return (await res.json()).items;
|
|
15355
|
+
}
|
|
15330
15356
|
async function deleteProject(apiUrl, token, projectId) {
|
|
15331
15357
|
const res = await fetch(`${apiUrl}/projects/${encodeURIComponent(projectId)}`, {
|
|
15332
15358
|
method: "DELETE",
|
|
@@ -15741,6 +15767,29 @@ var VITE_META_FRAMEWORKS = [
|
|
|
15741
15767
|
"vike"
|
|
15742
15768
|
];
|
|
15743
15769
|
var SERVER_FRAMEWORKS = ["hono", "express", "fastify", "koa"];
|
|
15770
|
+
var NON_NODE_START_TOOLS = [
|
|
15771
|
+
"vite",
|
|
15772
|
+
"next",
|
|
15773
|
+
"astro",
|
|
15774
|
+
"nuxt",
|
|
15775
|
+
"remix",
|
|
15776
|
+
"react-router",
|
|
15777
|
+
"svelte",
|
|
15778
|
+
"solid-start",
|
|
15779
|
+
"qwik",
|
|
15780
|
+
"vike",
|
|
15781
|
+
"parcel",
|
|
15782
|
+
"webpack",
|
|
15783
|
+
"rollup",
|
|
15784
|
+
"serve",
|
|
15785
|
+
"http-server"
|
|
15786
|
+
];
|
|
15787
|
+
function startScriptLooksLikePlainNode(start) {
|
|
15788
|
+
const s = start.toLowerCase();
|
|
15789
|
+
return !NON_NODE_START_TOOLS.some(
|
|
15790
|
+
(tool) => new RegExp(`(^|[\\s/"'\`=])${tool}([\\s@/"'\`]|$)`).test(s)
|
|
15791
|
+
);
|
|
15792
|
+
}
|
|
15744
15793
|
function readPackageJson(cwd) {
|
|
15745
15794
|
try {
|
|
15746
15795
|
return JSON.parse(readFileSync2(join2(cwd, "package.json"), "utf8"));
|
|
@@ -15758,6 +15807,8 @@ function frameworkFrom(pkg, cwd) {
|
|
|
15758
15807
|
if (VITE_META_FRAMEWORKS.some(has)) return null;
|
|
15759
15808
|
if (SERVER_FRAMEWORKS.some(has)) return "node";
|
|
15760
15809
|
if (has("vite")) return "vite-spa";
|
|
15810
|
+
const start = pkg?.scripts?.start?.trim();
|
|
15811
|
+
if (start && startScriptLooksLikePlainNode(start)) return "node";
|
|
15761
15812
|
return null;
|
|
15762
15813
|
}
|
|
15763
15814
|
function nameFrom(pkg, cwd) {
|
|
@@ -15876,16 +15927,34 @@ function configureGitRemote(cwd, remote, url2) {
|
|
|
15876
15927
|
}
|
|
15877
15928
|
try {
|
|
15878
15929
|
const remotes = run(["remote"]).split(/\s+/).filter(Boolean);
|
|
15879
|
-
|
|
15880
|
-
|
|
15881
|
-
|
|
15882
|
-
}
|
|
15883
|
-
run(["remote", "add", remote, url2]);
|
|
15884
|
-
return { status: "added", remote };
|
|
15930
|
+
const status = remotes.includes(remote) ? "updated" : "added";
|
|
15931
|
+
if (status === "updated") run(["remote", "set-url", remote, url2]);
|
|
15932
|
+
else run(["remote", "add", remote, url2]);
|
|
15933
|
+
return { status, remote, tracking: setUpstreamIfUnset(run, remote) };
|
|
15885
15934
|
} catch {
|
|
15886
15935
|
return { status: "unavailable" };
|
|
15887
15936
|
}
|
|
15888
15937
|
}
|
|
15938
|
+
function setUpstreamIfUnset(run, remote) {
|
|
15939
|
+
let branch;
|
|
15940
|
+
try {
|
|
15941
|
+
branch = run(["symbolic-ref", "--short", "HEAD"]);
|
|
15942
|
+
} catch {
|
|
15943
|
+
return "detached";
|
|
15944
|
+
}
|
|
15945
|
+
if (!branch) return "detached";
|
|
15946
|
+
try {
|
|
15947
|
+
if (run(["config", "--get", `branch.${branch}.remote`])) return "kept-existing";
|
|
15948
|
+
} catch {
|
|
15949
|
+
}
|
|
15950
|
+
try {
|
|
15951
|
+
run(["config", `branch.${branch}.remote`, remote]);
|
|
15952
|
+
run(["config", `branch.${branch}.merge`, `refs/heads/${branch}`]);
|
|
15953
|
+
return "set";
|
|
15954
|
+
} catch {
|
|
15955
|
+
return "unavailable";
|
|
15956
|
+
}
|
|
15957
|
+
}
|
|
15889
15958
|
function probe(cwd, args) {
|
|
15890
15959
|
try {
|
|
15891
15960
|
const value = execFileSync("git", args, {
|
|
@@ -16022,7 +16091,10 @@ async function runInit(opts) {
|
|
|
16022
16091
|
// First `kryd init` for the account claims the tenant slug (the vanity routing key in
|
|
16023
16092
|
// every deploy URL); later inits omit it (the account already has one). Story 3.8.
|
|
16024
16093
|
// Built conditionally (exactOptionalPropertyTypes — don't pass an explicit undefined).
|
|
16025
|
-
...opts.tenant ? { tenantSlug: opts.tenant } : {}
|
|
16094
|
+
...opts.tenant ? { tenantSlug: opts.tenant } : {},
|
|
16095
|
+
// KRYD-184a: only sent when the flag is present. The API defaults to private and sends
|
|
16096
|
+
// `private` explicitly to the forge on every path, so omitting this is never "public".
|
|
16097
|
+
...opts.public ? { public: true } : {}
|
|
16026
16098
|
});
|
|
16027
16099
|
let linkNote = "";
|
|
16028
16100
|
try {
|
|
@@ -16051,7 +16123,9 @@ async function runInit(opts) {
|
|
|
16051
16123
|
case "added":
|
|
16052
16124
|
case "updated":
|
|
16053
16125
|
nextStep = `${remote.status === "added" ? "Added" : "Updated"} git remote "${remote.remote}" (with your push credential).
|
|
16054
|
-
|
|
16126
|
+
` + (remote.tracking === "set" ? `Tracking set: \`git push\` and \`kryd push\` both deploy this branch.
|
|
16127
|
+
` : remote.tracking === "kept-existing" ? `This branch already tracks another remote, so it was left alone \u2014 deploy with \`kryd push\`.
|
|
16128
|
+
` : "") + `Next: kryd push # push to deploy (same as \`git push ${remote.remote} ${branch}\`)
|
|
16055
16129
|
`;
|
|
16056
16130
|
break;
|
|
16057
16131
|
case "not-a-repo":
|
|
@@ -16522,6 +16596,37 @@ function reportDetachResult(result, opts) {
|
|
|
16522
16596
|
process.exitCode = 1;
|
|
16523
16597
|
}
|
|
16524
16598
|
}
|
|
16599
|
+
function isProjectId(arg) {
|
|
16600
|
+
return arg.startsWith("proj_");
|
|
16601
|
+
}
|
|
16602
|
+
async function resolveProjectIdByName(apiUrl, token, name) {
|
|
16603
|
+
let projects;
|
|
16604
|
+
try {
|
|
16605
|
+
projects = await listProjects(apiUrl, token);
|
|
16606
|
+
} catch (err) {
|
|
16607
|
+
reportError(err);
|
|
16608
|
+
return null;
|
|
16609
|
+
}
|
|
16610
|
+
const matches = projects.filter((p) => p.name === name);
|
|
16611
|
+
if (matches.length === 0) {
|
|
16612
|
+
process.stderr.write(
|
|
16613
|
+
`No project named ${name} on this account. Run \`kryd project list\` to see the names.
|
|
16614
|
+
`
|
|
16615
|
+
);
|
|
16616
|
+
process.exitCode = 1;
|
|
16617
|
+
return null;
|
|
16618
|
+
}
|
|
16619
|
+
if (matches.length === 1) return matches[0].id;
|
|
16620
|
+
const listed = matches.map((p) => ` ${p.id} ${p.status}
|
|
16621
|
+
`).join("");
|
|
16622
|
+
process.stderr.write(
|
|
16623
|
+
`${matches.length} projects are named ${name}:
|
|
16624
|
+
${listed}Re-run \`kryd project rm <id>\` with the one you mean.
|
|
16625
|
+
`
|
|
16626
|
+
);
|
|
16627
|
+
process.exitCode = 1;
|
|
16628
|
+
return null;
|
|
16629
|
+
}
|
|
16525
16630
|
async function runProjectRemove(opts) {
|
|
16526
16631
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
16527
16632
|
const token = loadConfig().token;
|
|
@@ -16530,9 +16635,15 @@ async function runProjectRemove(opts) {
|
|
|
16530
16635
|
process.exitCode = 1;
|
|
16531
16636
|
return;
|
|
16532
16637
|
}
|
|
16533
|
-
|
|
16638
|
+
let projectId;
|
|
16639
|
+
if (opts.project !== void 0 && !isProjectId(opts.project)) {
|
|
16640
|
+
projectId = await resolveProjectIdByName(apiUrl, token, opts.project);
|
|
16641
|
+
if (!projectId) return;
|
|
16642
|
+
} else {
|
|
16643
|
+
projectId = resolveProjectId(opts.project, opts.cwd);
|
|
16644
|
+
}
|
|
16534
16645
|
if (!projectId) {
|
|
16535
|
-
reportNotLinked("kryd project rm <
|
|
16646
|
+
reportNotLinked("kryd project rm <name|id>");
|
|
16536
16647
|
return;
|
|
16537
16648
|
}
|
|
16538
16649
|
let project2;
|
|
@@ -16540,8 +16651,10 @@ async function runProjectRemove(opts) {
|
|
|
16540
16651
|
try {
|
|
16541
16652
|
project2 = await getProject(apiUrl, token, projectId);
|
|
16542
16653
|
if (!project2) {
|
|
16543
|
-
process.stderr.write(
|
|
16544
|
-
`
|
|
16654
|
+
process.stderr.write(
|
|
16655
|
+
`No project with id ${projectId} on this account. Run \`kryd project list\` to see your projects.
|
|
16656
|
+
`
|
|
16657
|
+
);
|
|
16545
16658
|
process.exitCode = 1;
|
|
16546
16659
|
return;
|
|
16547
16660
|
}
|
|
@@ -16738,6 +16851,40 @@ function envAnnotation(environments) {
|
|
|
16738
16851
|
if (preview && !prod) return " \u2192 preview only";
|
|
16739
16852
|
return "";
|
|
16740
16853
|
}
|
|
16854
|
+
async function runProjectList(opts) {
|
|
16855
|
+
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
16856
|
+
const token = loadConfig().token;
|
|
16857
|
+
if (!token) {
|
|
16858
|
+
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
16859
|
+
process.exitCode = 1;
|
|
16860
|
+
return;
|
|
16861
|
+
}
|
|
16862
|
+
let projects;
|
|
16863
|
+
try {
|
|
16864
|
+
projects = await listProjects(apiUrl, token);
|
|
16865
|
+
} catch (err) {
|
|
16866
|
+
reportError(err);
|
|
16867
|
+
return;
|
|
16868
|
+
}
|
|
16869
|
+
if (projects.length === 0) {
|
|
16870
|
+
process.stdout.write("No projects yet \u2014 run `kryd init` in a project directory.\n");
|
|
16871
|
+
return;
|
|
16872
|
+
}
|
|
16873
|
+
const linkedId = resolveProjectId(void 0, opts.cwd);
|
|
16874
|
+
const nameWidth = projects.reduce((max, p) => Math.max(max, p.name.length), 0);
|
|
16875
|
+
const subWidth = projects.reduce((max, p) => Math.max(max, p.subdomain.length), 0);
|
|
16876
|
+
const statusWidth = projects.reduce((max, p) => Math.max(max, p.status.length), 0);
|
|
16877
|
+
for (const p of projects) {
|
|
16878
|
+
const mark = p.id === linkedId ? "*" : " ";
|
|
16879
|
+
process.stdout.write(
|
|
16880
|
+
`${mark} ${p.name.padEnd(nameWidth)} ${p.subdomain.padEnd(subWidth)} ${p.status.padEnd(statusWidth)} ${p.id}
|
|
16881
|
+
`
|
|
16882
|
+
);
|
|
16883
|
+
}
|
|
16884
|
+
if (projects.some((p) => p.id === linkedId)) {
|
|
16885
|
+
process.stdout.write("\n* linked to this directory\n");
|
|
16886
|
+
}
|
|
16887
|
+
}
|
|
16741
16888
|
function renderEnvGroup(heading, vars, opts) {
|
|
16742
16889
|
const width = vars.reduce((max, v) => Math.max(max, v.key.length), 0);
|
|
16743
16890
|
const dates = opts.showUpdated ? vars.map((v) => isoDate(v.updatedAt)) : [];
|
|
@@ -17127,7 +17274,7 @@ Run \`kryd env list\` to see what is set. (If you meant the runtime variable of
|
|
|
17127
17274
|
reportError(err);
|
|
17128
17275
|
}
|
|
17129
17276
|
}
|
|
17130
|
-
var CLI_VERSION = true ? "0.
|
|
17277
|
+
var CLI_VERSION = true ? "0.4.1" : "0.0.0-dev";
|
|
17131
17278
|
var program = new Command();
|
|
17132
17279
|
program.name("kryd").description("Kryd CLI").version(CLI_VERSION);
|
|
17133
17280
|
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(
|
|
@@ -17145,6 +17292,10 @@ program.command("init").description("Link this project's repo + register its pus
|
|
|
17145
17292
|
// has always assigned a slug, so the flag could only agree with it or 409. It now changes the
|
|
17146
17293
|
// slug, and only while nothing derives from it.
|
|
17147
17294
|
"your tenant slug \u2014 appears in every deploy URL; changeable until your first project"
|
|
17295
|
+
).option(
|
|
17296
|
+
"--public",
|
|
17297
|
+
// KRYD-184a. Chosen at creation; there is no command to flip it afterwards yet (KRYD-341).
|
|
17298
|
+
"create the repository PUBLIC \u2014 readable by anyone signed in to the forge (default: private). Set at creation only"
|
|
17148
17299
|
).option("--api-url <url>", "control-plane API base URL").action((opts) => runInit(opts));
|
|
17149
17300
|
program.command("logs [target]").description(
|
|
17150
17301
|
"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)."
|
|
@@ -17170,8 +17321,9 @@ program.command("redeploy [project]").description(
|
|
|
17170
17321
|
"Redeploy the current live commit (no rebuild) to apply config/env changes, and follow it live"
|
|
17171
17322
|
).option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runRedeploy({ ...opts, project: project2 }));
|
|
17172
17323
|
var project = program.command("project").description("Manage your projects");
|
|
17324
|
+
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));
|
|
17173
17325
|
project.command("rm [project]").description(
|
|
17174
|
-
"Permanently delete a project and everything Kryd created for it (asks you to type its name)"
|
|
17326
|
+
"Permanently delete a project and everything Kryd created for it (by name or id; asks you to type its name)"
|
|
17175
17327
|
).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 }));
|
|
17176
17328
|
var db = program.command("db").description("Manage project databases");
|
|
17177
17329
|
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(
|
|
@@ -17237,6 +17389,7 @@ export {
|
|
|
17237
17389
|
runLogin,
|
|
17238
17390
|
runLogout,
|
|
17239
17391
|
runLogs,
|
|
17392
|
+
runProjectList,
|
|
17240
17393
|
runProjectRemove,
|
|
17241
17394
|
runPush,
|
|
17242
17395
|
runRedeploy,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kryd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
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",
|