@kryd/cli 0.18.0 → 0.19.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/README.md +3 -1
- package/dist/index.js +72 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -44,6 +44,7 @@ Once you've run `kryd init` in a directory, the commands below work **arg-less**
|
|
|
44
44
|
| `kryd storage add \| remove \| status [project]` | Attach, tear down or inspect S3-compatible object storage. `remove` destroys every object and asks first. |
|
|
45
45
|
| `kryd env list \| set \| rm [project]` | Manage your own environment variables. `set KEY --stdin` (or a prompt) keeps a secret out of `ps` and your shell history; values are never printed back. Add `--build` for build-time variables (`VITE_*`, `NEXT_PUBLIC_*`) — these are compiled into your public bundle, so they must never be secrets, and they take effect at your next build rather than your next deploy. |
|
|
46
46
|
| `kryd ai add \| remove \| status [project]` | Give the project an authenticated EU AI-gateway endpoint (injected on the next deploy), take it away again, or show what it has. |
|
|
47
|
+
| `kryd ai models` | List the AI models Kryd offers right now — the same catalogue the dashboard's AI tab shows, in the same order (popular ones marked `*`). A request's `model` field carries the `MODEL` value. The list is the same for every account, but the call runs in your current account; needs a login, no project. |
|
|
47
48
|
| `kryd workflow add \| remove \| status [project]` | Give the project its own durable-workflow tenant — `HATCHET_CLIENT_TOKEN` is injected on the next deploy — revoke that token again, or show whether it has one and when it expires. `remove` keeps the tenant's history, crons and schedules until the project is deleted. |
|
|
48
49
|
| `kryd workflow init --language <typescript\|python\|go>` | Scaffold a workflow worker here and link it as a new project. The worker ships with the small HTTP health listener Kryd's deploy gate needs — without it a deploy is torn down with "nothing listening yet" — plus a `kryd.json` that tells `kryd init` what this project is. `--no-link` writes the files only. |
|
|
49
50
|
| `kryd token create \| list \| revoke` | Deploy tokens for CI and scripts. `create <name> [--expires 30\|90\|365\|never]` (default 90 days) prints the token once; it acts as you, with your role in the **current account**, is bound to that one account for good, and cannot create tokens itself. Managing tokens needs `kryd login`. A token can do what you can on your projects — read env values and push — so after a leak, revoke it and also rotate the secrets it could read. |
|
|
@@ -56,7 +57,7 @@ kryd support new --type bug --subject "Deploy hangs on build" --message "Every d
|
|
|
56
57
|
kryd support show T-1042 --json
|
|
57
58
|
```
|
|
58
59
|
|
|
59
|
-
**`--json` on the read commands.** `kryd project list`, `whoami`, `db status`, `storage status`, `ai status`, `workflow status`, `domain status [host]` and `env list` take `--json` too, with the same contract: exactly one JSON document on stdout; an error is text on stderr with exit 1 and an empty stdout. "Nothing attached" is an answer, not an error (exit 0). The shapes:
|
|
60
|
+
**`--json` on the read commands.** `kryd project list`, `whoami`, `db status`, `storage status`, `ai status`, `ai models`, `workflow status`, `domain status [host]` and `env list` take `--json` too, with the same contract: exactly one JSON document on stdout; an error is text on stderr with exit 1 and an empty stdout. "Nothing attached" is an answer, not an error (exit 0). The shapes:
|
|
60
61
|
|
|
61
62
|
| Command | `--json` prints |
|
|
62
63
|
|---|---|
|
|
@@ -65,6 +66,7 @@ kryd support show T-1042 --json
|
|
|
65
66
|
| `kryd db status` | `{ production, preview }`, each `{ attached, status, failureReason }`. When production is not attached the preview is not read, and is reported as not attached |
|
|
66
67
|
| `kryd storage status` | `{ attached, status, failureReason }` |
|
|
67
68
|
| `kryd ai status` | `{ enabled, url }` |
|
|
69
|
+
| `kryd ai models` | `{ items }`: the AI catalogue rows as the API returns them (`id`, `model`, `provider`, `tasks`, `stage`, `popular`, `sortOrder`), in its order. An empty catalogue is `{ "items": [] }` |
|
|
68
70
|
| `kryd workflow status` | `{ enabled, tokenExpiresAt, tokenExpiresInDays }` — both `null` without an expiry; the days are negative once the token has expired |
|
|
69
71
|
| `kryd domain status` | `{ projectHost, domains }`; each domain is `{ id, host, app?, redirectTo?, status, detail, checkedAt, createdAt, records }`, with `status` the raw value (`pending`, `verified`, `active`, `failed`), not the text-mode wording |
|
|
70
72
|
| `kryd domain status <host>` | that one domain, the same fields, its DNS `records` included |
|
package/dist/index.js
CHANGED
|
@@ -15364,6 +15364,28 @@ async function getAiStatus(apiUrl, token, projectId) {
|
|
|
15364
15364
|
}
|
|
15365
15365
|
return { enabled: parsed.enabled, url: parsed.url };
|
|
15366
15366
|
}
|
|
15367
|
+
async function getAiModels(apiUrl, token) {
|
|
15368
|
+
const res = await fetch(`${apiUrl}/ai-models`, {
|
|
15369
|
+
headers: { ...authHeaders(token) }
|
|
15370
|
+
});
|
|
15371
|
+
if (!res.ok) {
|
|
15372
|
+
throw new ApiError(
|
|
15373
|
+
`AI catalogue read failed (${res.status})`,
|
|
15374
|
+
await parseEnvelope(res),
|
|
15375
|
+
res.status
|
|
15376
|
+
);
|
|
15377
|
+
}
|
|
15378
|
+
const parsed = await res.json().catch(() => void 0);
|
|
15379
|
+
const isRow = (row) => {
|
|
15380
|
+
if (!row || typeof row !== "object") return false;
|
|
15381
|
+
const r = row;
|
|
15382
|
+
return typeof r.model === "string" && typeof r.provider === "string" && Array.isArray(r.tasks) && r.tasks.every((t) => typeof t === "string") && typeof r.stage === "string" && typeof r.popular === "boolean";
|
|
15383
|
+
};
|
|
15384
|
+
if (!Array.isArray(parsed) || !parsed.every(isRow)) {
|
|
15385
|
+
throw new ApiError("The API returned an unexpected AI-catalogue response.");
|
|
15386
|
+
}
|
|
15387
|
+
return parsed;
|
|
15388
|
+
}
|
|
15367
15389
|
async function removeAi(apiUrl, token, projectId) {
|
|
15368
15390
|
const res = await fetch(`${apiUrl}/ai/${encodeURIComponent(projectId)}`, {
|
|
15369
15391
|
method: "DELETE",
|
|
@@ -16812,7 +16834,7 @@ This project deploys to Kryd, a European cloud platform. Use what it already pro
|
|
|
16812
16834
|
- **Git is the deploy.** The repository lives on Kryd's own git server in the EU (remote \`kryd\`). \`git push kryd <branch>\` or \`kryd push\` builds and deploys; every other branch gets its own preview URL. Don't add CI deploy pipelines, hosting Dockerfiles or another platform's config.
|
|
16813
16835
|
- **Database:** managed Postgres \u2014 \`kryd db add\`, then use \`DATABASE_URL\`. Preview deploys can get a database of their own: \`kryd db add --preview\`. Don't add a database container or SQLite for production data.
|
|
16814
16836
|
- **File storage:** S3-compatible object storage \u2014 \`kryd storage add\`.
|
|
16815
|
-
- **AI:** an EU-hosted, OpenAI-compatible gateway with several models \u2014 \`kryd ai add\`. Don't add provider API keys.
|
|
16837
|
+
- **AI:** an EU-hosted, OpenAI-compatible gateway with several models \u2014 \`kryd ai add\`; list the current models with \`kryd ai models\`. Don't add provider API keys.
|
|
16816
16838
|
- **Long-running or background work** \u2014 jobs, retries, schedules \u2014 belongs in a workflow (\`kryd workflow init\`), not in a request handler, a timer loop or a separate queue.
|
|
16817
16839
|
- **Incoming webhooks** (Stripe, GitHub, \u2026) go to a workflow webhook, created in the Kryd dashboard under Workflows, so every delivery is stored and retried instead of handled inside a web request.
|
|
16818
16840
|
- **Your own domain:** \`kryd domain add <host>\` prints the DNS records to create; the HTTPS certificate is automatic.
|
|
@@ -16827,7 +16849,7 @@ This project deploys to Kryd, a European cloud platform. Use what it already pro
|
|
|
16827
16849
|
- **Workflows:** \`kryd workflow init --language typescript|python|go\` scaffolds a worker, declared in \`kryd.json\` (\`"web": false\` = the project is a worker only). Make every task safe to run twice: a worker stops within seconds on SIGTERM, and the engine re-runs the task.
|
|
16828
16850
|
- **Login:** without a terminal there is no login \u2014 ask the user to run \`kryd init\` or \`kryd login\`. A deploy token (\`KRYD_TOKEN\`) can deploy but cannot create a project.
|
|
16829
16851
|
- **Destructive commands** ask for confirmation; without a terminal they need \`--yes\`.
|
|
16830
|
-
- **Read commands take \`--json\`** for one JSON document on stdout \u2014 \`kryd project list --json\`, \`kryd whoami --json\`, \`kryd db status --json\`, \`kryd storage status --json\`, \`kryd ai status --json\`, \`kryd workflow status --json\`, \`kryd domain status --json\`, \`kryd env list --json\`. "Nothing attached" is an answer (exit 0); an error is text on stderr with exit 1 and an empty stdout.
|
|
16852
|
+
- **Read commands take \`--json\`** for one JSON document on stdout \u2014 \`kryd project list --json\`, \`kryd whoami --json\`, \`kryd db status --json\`, \`kryd storage status --json\`, \`kryd ai status --json\`, \`kryd ai models --json\`, \`kryd workflow status --json\`, \`kryd domain status --json\`, \`kryd env list --json\`. "Nothing attached" is an answer (exit 0); an error is text on stderr with exit 1 and an empty stdout.
|
|
16831
16853
|
`;
|
|
16832
16854
|
var AGENTS_FOOTER = "Run `kryd <command> --help` for details. After updating the CLI, refresh this block with `kryd init --agents`.\n";
|
|
16833
16855
|
function isHidden(cmd) {
|
|
@@ -19592,6 +19614,50 @@ async function runAiStatus(opts) {
|
|
|
19592
19614
|
await reportError(err);
|
|
19593
19615
|
}
|
|
19594
19616
|
}
|
|
19617
|
+
async function runAiModels(opts) {
|
|
19618
|
+
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19619
|
+
const token = resolveToken();
|
|
19620
|
+
if (!token) {
|
|
19621
|
+
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19622
|
+
process.exitCode = 1;
|
|
19623
|
+
return;
|
|
19624
|
+
}
|
|
19625
|
+
let models;
|
|
19626
|
+
try {
|
|
19627
|
+
models = await getAiModels(apiUrl, token);
|
|
19628
|
+
} catch (err) {
|
|
19629
|
+
await reportError(err);
|
|
19630
|
+
return;
|
|
19631
|
+
}
|
|
19632
|
+
if (opts.json) {
|
|
19633
|
+
printJson2({ items: models });
|
|
19634
|
+
return;
|
|
19635
|
+
}
|
|
19636
|
+
if (models.length === 0) {
|
|
19637
|
+
process.stdout.write("No models in the AI catalogue right now.\n");
|
|
19638
|
+
return;
|
|
19639
|
+
}
|
|
19640
|
+
const rows = models.map((m) => ({
|
|
19641
|
+
mark: m.popular ? "*" : " ",
|
|
19642
|
+
model: m.model,
|
|
19643
|
+
provider: m.provider,
|
|
19644
|
+
tasks: m.tasks.join(","),
|
|
19645
|
+
stage: m.stage
|
|
19646
|
+
}));
|
|
19647
|
+
const width = (label, pick2) => rows.reduce((max, r) => Math.max(max, pick2(r).length), label.length);
|
|
19648
|
+
const modelW = width("MODEL", (r) => r.model);
|
|
19649
|
+
const providerW = width("PROVIDER", (r) => r.provider);
|
|
19650
|
+
const tasksW = width("TASKS", (r) => r.tasks);
|
|
19651
|
+
const line = (mark, model, provider, tasks, stage) => `${mark} ${model.padEnd(modelW)} ${provider.padEnd(providerW)} ${tasks.padEnd(tasksW)} ${stage}
|
|
19652
|
+
`;
|
|
19653
|
+
process.stdout.write(line(" ", "MODEL", "PROVIDER", "TASKS", "STAGE"));
|
|
19654
|
+
for (const r of rows) process.stdout.write(line(r.mark, r.model, r.provider, r.tasks, r.stage));
|
|
19655
|
+
process.stdout.write("\n");
|
|
19656
|
+
if (rows.some((r) => r.mark === "*")) process.stdout.write("* popular\n");
|
|
19657
|
+
process.stdout.write(
|
|
19658
|
+
"Use one by putting its MODEL value in the request's `model` field (after `kryd ai add`).\n"
|
|
19659
|
+
);
|
|
19660
|
+
}
|
|
19595
19661
|
function isoDay(iso) {
|
|
19596
19662
|
return iso.slice(0, 10);
|
|
19597
19663
|
}
|
|
@@ -20670,7 +20736,7 @@ Run \`kryd env list\` to see what is set. (If you meant the runtime variable of
|
|
|
20670
20736
|
await reportError(err);
|
|
20671
20737
|
}
|
|
20672
20738
|
}
|
|
20673
|
-
var CLI_VERSION = true ? "0.
|
|
20739
|
+
var CLI_VERSION = true ? "0.19.0" : "0.0.0-dev";
|
|
20674
20740
|
var program = new Command();
|
|
20675
20741
|
program.name("kryd").description("Kryd CLI").version(CLI_VERSION);
|
|
20676
20742
|
program.hook("postAction", async () => {
|
|
@@ -20800,10 +20866,11 @@ env.command("pull [project]").description(
|
|
|
20800
20866
|
"--out <path>",
|
|
20801
20867
|
"file to write, relative to the project root (default: .env.kryd \u2014 never your hand-maintained .env)"
|
|
20802
20868
|
).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 }));
|
|
20803
|
-
var ai = program.command("ai").description("Manage the app's EU AI gateway");
|
|
20869
|
+
var ai = program.command("ai").description("Manage the app's EU AI gateway and list its models");
|
|
20804
20870
|
ai.command("add [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) => runAiAdd({ ...opts, project: project2 }));
|
|
20805
20871
|
ai.command("remove [project]").description("Stop injecting the project's AI endpoint (asks for confirmation)").option("--yes", "skip the confirmation prompt").option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runAiRemove({ ...opts, project: project2 }));
|
|
20806
20872
|
ai.command("status [project]").description("Show whether the project has an AI endpoint, and what it is").option("--json", "print { enabled, url } as JSON on stdout").option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runAiStatus({ ...opts, project: project2 }));
|
|
20873
|
+
ai.command("models").description("List the AI models Kryd offers right now (the catalogue the dashboard shows)").option("--json", "print { items } as JSON on stdout").option("--api-url <url>", "control-plane API base URL").action((opts) => runAiModels(opts));
|
|
20807
20874
|
var workflow = program.command("workflow").description("Manage the project's durable workflows (its own workflow tenant)");
|
|
20808
20875
|
workflow.command("add [project]").description("Give the project a workflow tenant and inject HATCHET_CLIENT_TOKEN on next deploy").option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runWorkflowAdd({ ...opts, project: project2 }));
|
|
20809
20876
|
workflow.command("remove [project]").description("Revoke the project's workflow token and stop injecting it (asks for confirmation)").option("--yes", "skip the confirmation prompt").option("--api-url <url>", "control-plane API base URL").action((project2, opts) => runWorkflowRemove({ ...opts, project: project2 }));
|
|
@@ -20856,6 +20923,7 @@ export {
|
|
|
20856
20923
|
backfillLinkAccount,
|
|
20857
20924
|
program,
|
|
20858
20925
|
runAiAdd,
|
|
20926
|
+
runAiModels,
|
|
20859
20927
|
runAiRemove,
|
|
20860
20928
|
runAiStatus,
|
|
20861
20929
|
runDbAdd,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kryd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Kryd CLI — git push → live on the European cloud for your AI: SSL, managed Postgres & object storage, and an EU-hosted AI gateway. Your code and model calls stay in the EU. Beta: sign up at https://kryd.eu/beta",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kryd",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"typescript": "^5.6.3",
|
|
43
43
|
"vitest": "^2.1.8",
|
|
44
44
|
"@kryd/shared-types": "0.0.0",
|
|
45
|
-
"@kryd/config-
|
|
46
|
-
"@kryd/config-
|
|
45
|
+
"@kryd/config-ts": "0.0.0",
|
|
46
|
+
"@kryd/config-eslint": "0.0.0"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --external:commander --define:__KRYD_VERSION__=\\\"$npm_package_version\\\" --outfile=dist/index.js",
|