@kryd/cli 0.13.0 → 0.15.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 +8 -0
- package/dist/index.js +199 -62
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ Once you've run `kryd init` in a directory, the commands below work **arg-less**
|
|
|
43
43
|
| `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. |
|
|
44
44
|
| `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. |
|
|
45
45
|
| `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. |
|
|
46
|
+
| `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, inside your account, 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. |
|
|
46
47
|
| `kryd support new \| list \| show \| reply` | Ask Kryd support a question, report a bug or a wish, and read the answer. Non-interactive (every input is a flag) and `--json` prints one JSON document on stdout — so an agent can use it too. In a linked folder, `new` adds the project and its last production deploy; when that deploy failed, the last lines of its log are sent to Kryd support too, with secret-looking values masked. `--no-context` leaves all of it out. |
|
|
47
48
|
|
|
48
49
|
For an agent, for example:
|
|
@@ -52,6 +53,13 @@ kryd support new --type bug --subject "Deploy hangs on build" --message "Every d
|
|
|
52
53
|
kryd support show T-1042 --json
|
|
53
54
|
```
|
|
54
55
|
|
|
56
|
+
In CI, no `kryd login` and no config file: set a deploy token as the secret `KRYD_TOKEN` and every command uses it (it wins over a stored login).
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
kryd token create github-actions # prints kryd_… once — store it as the CI secret KRYD_TOKEN
|
|
60
|
+
KRYD_TOKEN=kryd_… kryd deploy <project>
|
|
61
|
+
```
|
|
62
|
+
|
|
55
63
|
Run `kryd <command> --help` for options. Every project-scoped command accepts an explicit `<project>` id, or resolves it from the `.kryd` link (walking up from the current directory, git-style).
|
|
56
64
|
|
|
57
65
|
## Sovereignty
|
package/dist/index.js
CHANGED
|
@@ -14963,25 +14963,6 @@ async function parseEnvelope(res) {
|
|
|
14963
14963
|
return void 0;
|
|
14964
14964
|
}
|
|
14965
14965
|
}
|
|
14966
|
-
async function login(apiUrl, creds) {
|
|
14967
|
-
const res = await fetch(`${apiUrl}/api/auth/sign-in/email`, {
|
|
14968
|
-
method: "POST",
|
|
14969
|
-
headers: { "content-type": "application/json", origin: apiUrl },
|
|
14970
|
-
body: JSON.stringify(creds)
|
|
14971
|
-
});
|
|
14972
|
-
if (!res.ok) {
|
|
14973
|
-
throw new ApiError(
|
|
14974
|
-
`Login failed (${res.status})`,
|
|
14975
|
-
await parseEnvelope(res),
|
|
14976
|
-
res.status
|
|
14977
|
-
);
|
|
14978
|
-
}
|
|
14979
|
-
const token = res.headers.get("set-auth-token");
|
|
14980
|
-
if (!token) {
|
|
14981
|
-
throw new ApiError("Login succeeded but the API returned no token.");
|
|
14982
|
-
}
|
|
14983
|
-
return token;
|
|
14984
|
-
}
|
|
14985
14966
|
async function whoami(apiUrl, token) {
|
|
14986
14967
|
const res = await fetch(`${apiUrl}/me`, {
|
|
14987
14968
|
headers: { authorization: `Bearer ${token}` }
|
|
@@ -15950,6 +15931,10 @@ function loadConfig() {
|
|
|
15950
15931
|
return {};
|
|
15951
15932
|
}
|
|
15952
15933
|
}
|
|
15934
|
+
function resolveToken() {
|
|
15935
|
+
const fromEnv = process.env.KRYD_TOKEN?.trim();
|
|
15936
|
+
return fromEnv ? fromEnv : loadConfig().token;
|
|
15937
|
+
}
|
|
15953
15938
|
function writeConfig(config2) {
|
|
15954
15939
|
const path = configPath();
|
|
15955
15940
|
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
@@ -16175,7 +16160,7 @@ function reportSupportError(err) {
|
|
|
16175
16160
|
process.exitCode = 1;
|
|
16176
16161
|
}
|
|
16177
16162
|
function requireToken() {
|
|
16178
|
-
const token =
|
|
16163
|
+
const token = resolveToken();
|
|
16179
16164
|
if (!token) usage("Not logged in \u2014 run `kryd login` first.");
|
|
16180
16165
|
return token ?? null;
|
|
16181
16166
|
}
|
|
@@ -16327,6 +16312,154 @@ async function runSupportReply(raw, opts) {
|
|
|
16327
16312
|
}
|
|
16328
16313
|
}
|
|
16329
16314
|
|
|
16315
|
+
// src/tokens.ts
|
|
16316
|
+
var TOKEN_EXPIRY_CHOICES = ["30", "90", "365", "never"];
|
|
16317
|
+
var DEFAULT_TOKEN_EXPIRY = "90";
|
|
16318
|
+
var MAX_TOKEN_NAME = 32;
|
|
16319
|
+
function expiresInSeconds(choice) {
|
|
16320
|
+
if (choice === "never") return null;
|
|
16321
|
+
if (!TOKEN_EXPIRY_CHOICES.includes(choice)) return void 0;
|
|
16322
|
+
return Number(choice) * 24 * 60 * 60;
|
|
16323
|
+
}
|
|
16324
|
+
async function tokenFetch(apiUrl, token, path, what, body) {
|
|
16325
|
+
const res = await fetch(`${apiUrl}/api/auth/api-key/${path}`, {
|
|
16326
|
+
method: body === void 0 ? "GET" : "POST",
|
|
16327
|
+
headers: {
|
|
16328
|
+
authorization: `Bearer ${token}`,
|
|
16329
|
+
...body !== void 0 ? { "content-type": "application/json" } : {}
|
|
16330
|
+
},
|
|
16331
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
16332
|
+
});
|
|
16333
|
+
if (!res.ok) {
|
|
16334
|
+
const parsed = await res.json().catch(() => void 0);
|
|
16335
|
+
const message = typeof parsed?.message === "string" ? parsed.message : `${what} failed (${res.status})`;
|
|
16336
|
+
const code = typeof parsed?.code === "string" ? parsed.code : `http.${res.status}`;
|
|
16337
|
+
throw new ApiError(`${what} failed (${res.status})`, { error: { code, message } }, res.status);
|
|
16338
|
+
}
|
|
16339
|
+
try {
|
|
16340
|
+
return await res.json();
|
|
16341
|
+
} catch {
|
|
16342
|
+
throw new ApiError(`The API returned an unexpected answer to "${what.toLowerCase()}".`);
|
|
16343
|
+
}
|
|
16344
|
+
}
|
|
16345
|
+
var isToken = (v) => {
|
|
16346
|
+
const t = v;
|
|
16347
|
+
return typeof t?.id === "string" && typeof t?.createdAt === "string";
|
|
16348
|
+
};
|
|
16349
|
+
async function createDeployToken(apiUrl, token, input) {
|
|
16350
|
+
const body = await tokenFetch(apiUrl, token, "create", "Creating the token", input);
|
|
16351
|
+
if (!isToken(body) || typeof body.key !== "string" || body.key.length === 0) {
|
|
16352
|
+
throw new ApiError("The API returned an unexpected answer to creating the token.");
|
|
16353
|
+
}
|
|
16354
|
+
return body;
|
|
16355
|
+
}
|
|
16356
|
+
async function listDeployTokens(apiUrl, token) {
|
|
16357
|
+
const body = await tokenFetch(apiUrl, token, "list", "Listing the tokens");
|
|
16358
|
+
if (!Array.isArray(body?.apiKeys) || !body.apiKeys.every(isToken)) {
|
|
16359
|
+
throw new ApiError("The API returned an unexpected answer to listing the tokens.");
|
|
16360
|
+
}
|
|
16361
|
+
return body.apiKeys;
|
|
16362
|
+
}
|
|
16363
|
+
async function revokeDeployToken(apiUrl, token, id) {
|
|
16364
|
+
await tokenFetch(apiUrl, token, "delete", "Revoking the token", { keyId: id });
|
|
16365
|
+
}
|
|
16366
|
+
function fail(message) {
|
|
16367
|
+
process.stderr.write(`${message}
|
|
16368
|
+
`);
|
|
16369
|
+
process.exitCode = 1;
|
|
16370
|
+
}
|
|
16371
|
+
function reportTokenError(err) {
|
|
16372
|
+
if (err instanceof ApiError && err.status === 401) {
|
|
16373
|
+
fail("Not logged in, or the session expired \u2014 run `kryd login`.");
|
|
16374
|
+
} else if (err instanceof ApiError && err.status === 403 && resolveToken()?.startsWith("kryd_")) {
|
|
16375
|
+
fail(
|
|
16376
|
+
"A deploy token cannot manage tokens. Sign in with `kryd login` (and unset KRYD_TOKEN for this command)."
|
|
16377
|
+
);
|
|
16378
|
+
} else if (err instanceof ApiError && err.status === 404) {
|
|
16379
|
+
fail("No such token on your account. `kryd token list` shows their ids.");
|
|
16380
|
+
} else if (err instanceof ApiError && err.envelope) {
|
|
16381
|
+
fail(err.envelope.error.message);
|
|
16382
|
+
} else {
|
|
16383
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
16384
|
+
}
|
|
16385
|
+
}
|
|
16386
|
+
function requireToken2() {
|
|
16387
|
+
const token = resolveToken();
|
|
16388
|
+
if (!token) fail("Not logged in \u2014 run `kryd login` first.");
|
|
16389
|
+
return token ?? null;
|
|
16390
|
+
}
|
|
16391
|
+
var day = (iso) => iso ? iso.slice(0, 10) : "\u2014";
|
|
16392
|
+
async function runTokenCreate(name, opts) {
|
|
16393
|
+
const trimmed = name.trim();
|
|
16394
|
+
if (trimmed.length === 0 || trimmed.length > MAX_TOKEN_NAME) {
|
|
16395
|
+
return fail(`A token name is 1 to ${MAX_TOKEN_NAME} characters.`);
|
|
16396
|
+
}
|
|
16397
|
+
const choice = opts.expires ?? DEFAULT_TOKEN_EXPIRY;
|
|
16398
|
+
const expiresIn = expiresInSeconds(choice);
|
|
16399
|
+
if (expiresIn === void 0) {
|
|
16400
|
+
return fail(`--expires is one of ${TOKEN_EXPIRY_CHOICES.join(", ")} (days, or never).`);
|
|
16401
|
+
}
|
|
16402
|
+
const token = requireToken2();
|
|
16403
|
+
if (!token) return;
|
|
16404
|
+
try {
|
|
16405
|
+
const created = await createDeployToken(resolveApiUrl(opts.apiUrl), token, { name: trimmed, expiresIn });
|
|
16406
|
+
process.stdout.write(`${created.key}
|
|
16407
|
+
`);
|
|
16408
|
+
process.stderr.write(
|
|
16409
|
+
`Created deploy token "${trimmed}" (${created.id}), ${created.expiresAt ? `valid until ${day(created.expiresAt)}` : "without expiry"}.
|
|
16410
|
+
This token will not be shown again. Store it now, e.g. as the CI secret KRYD_TOKEN.
|
|
16411
|
+
`
|
|
16412
|
+
);
|
|
16413
|
+
} catch (err) {
|
|
16414
|
+
reportTokenError(err);
|
|
16415
|
+
}
|
|
16416
|
+
}
|
|
16417
|
+
async function runTokenList(opts) {
|
|
16418
|
+
const token = requireToken2();
|
|
16419
|
+
if (!token) return;
|
|
16420
|
+
try {
|
|
16421
|
+
const list = await listDeployTokens(resolveApiUrl(opts.apiUrl), token);
|
|
16422
|
+
if (opts.json) {
|
|
16423
|
+
process.stdout.write(`${JSON.stringify(list, null, 2)}
|
|
16424
|
+
`);
|
|
16425
|
+
return;
|
|
16426
|
+
}
|
|
16427
|
+
if (list.length === 0) {
|
|
16428
|
+
process.stdout.write("No deploy tokens. Create one with `kryd token create <name>`.\n");
|
|
16429
|
+
return;
|
|
16430
|
+
}
|
|
16431
|
+
const rows = [
|
|
16432
|
+
["ID", "NAME", "STARTS WITH", "CREATED", "LAST USED", "EXPIRES"],
|
|
16433
|
+
...list.map((t) => [
|
|
16434
|
+
t.id,
|
|
16435
|
+
t.name ?? "\u2014",
|
|
16436
|
+
t.start ? `${t.start}\u2026` : "\u2014",
|
|
16437
|
+
day(t.createdAt),
|
|
16438
|
+
day(t.lastRequest),
|
|
16439
|
+
t.expiresAt ? day(t.expiresAt) : "never"
|
|
16440
|
+
])
|
|
16441
|
+
];
|
|
16442
|
+
const widths = rows[0].map((_, i) => Math.max(...rows.map((r) => r[i].length)));
|
|
16443
|
+
for (const r of rows) {
|
|
16444
|
+
process.stdout.write(`${r.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd()}
|
|
16445
|
+
`);
|
|
16446
|
+
}
|
|
16447
|
+
} catch (err) {
|
|
16448
|
+
reportTokenError(err);
|
|
16449
|
+
}
|
|
16450
|
+
}
|
|
16451
|
+
async function runTokenRevoke(id, opts) {
|
|
16452
|
+
const token = requireToken2();
|
|
16453
|
+
if (!token) return;
|
|
16454
|
+
try {
|
|
16455
|
+
await revokeDeployToken(resolveApiUrl(opts.apiUrl), token, id);
|
|
16456
|
+
process.stdout.write(`Revoked ${id}. Anything still using it now gets 401.
|
|
16457
|
+
`);
|
|
16458
|
+
} catch (err) {
|
|
16459
|
+
reportTokenError(err);
|
|
16460
|
+
}
|
|
16461
|
+
}
|
|
16462
|
+
|
|
16330
16463
|
// src/browser-login.ts
|
|
16331
16464
|
import { spawn } from "node:child_process";
|
|
16332
16465
|
import { randomUUID } from "node:crypto";
|
|
@@ -16415,7 +16548,7 @@ async function runGitCredential(operation, opts = {}) {
|
|
|
16415
16548
|
const input = parseCredentialInput(opts.stdin ?? "");
|
|
16416
16549
|
if (input.protocol && input.protocol !== "https") return;
|
|
16417
16550
|
const cwd = opts.cwd ?? process.cwd();
|
|
16418
|
-
const token =
|
|
16551
|
+
const token = resolveToken();
|
|
16419
16552
|
if (!token) return;
|
|
16420
16553
|
const projectId = resolveProjectId(void 0, cwd);
|
|
16421
16554
|
if (!projectId) return;
|
|
@@ -17626,7 +17759,7 @@ async function explainMissingProject(err) {
|
|
|
17626
17759
|
if (err.status !== 404) return "";
|
|
17627
17760
|
const resolution = resolvedLink();
|
|
17628
17761
|
if (!resolution) return "";
|
|
17629
|
-
const token =
|
|
17762
|
+
const token = resolveToken();
|
|
17630
17763
|
if (!token) return "";
|
|
17631
17764
|
if (await projectIsVisible(token, resolution.link.projectId) !== false) return "";
|
|
17632
17765
|
let session;
|
|
@@ -17654,7 +17787,7 @@ async function backfillLinkAccount() {
|
|
|
17654
17787
|
const resolution = resolvedLink();
|
|
17655
17788
|
if (!resolution) return;
|
|
17656
17789
|
if (resolution.link.accountId && resolution.link.accountSlug) return;
|
|
17657
|
-
const token =
|
|
17790
|
+
const token = resolveToken();
|
|
17658
17791
|
if (!token) return;
|
|
17659
17792
|
if (await projectIsVisible(token, resolution.link.projectId) !== true) return;
|
|
17660
17793
|
try {
|
|
@@ -17679,25 +17812,17 @@ async function runLogin(opts) {
|
|
|
17679
17812
|
if (pastedToken) {
|
|
17680
17813
|
saveToken(pastedToken, apiUrl);
|
|
17681
17814
|
process.stdout.write("Token saved.\n");
|
|
17815
|
+
warnIfEnvTokenOverrides();
|
|
17682
17816
|
return;
|
|
17683
17817
|
}
|
|
17684
|
-
if (opts.email && opts.password) {
|
|
17685
|
-
const token2 = await login(apiUrl, { email: opts.email, password: opts.password });
|
|
17686
|
-
saveToken(token2, apiUrl);
|
|
17687
|
-
process.stdout.write(`Logged in as ${opts.email}.
|
|
17688
|
-
`);
|
|
17689
|
-
return;
|
|
17690
|
-
}
|
|
17691
|
-
if (opts.email || opts.password) {
|
|
17692
|
-
throw new Error("Provide BOTH --email and --password, or omit them to log in via the browser.");
|
|
17693
|
-
}
|
|
17694
17818
|
const { token, apiUrl: learnedApiUrl } = await browserLogin(resolveDashboardUrl(opts.dashboardUrl));
|
|
17695
17819
|
saveToken(token, learnedApiUrl ?? apiUrl);
|
|
17696
17820
|
process.stdout.write("Logged in.\n");
|
|
17821
|
+
warnIfEnvTokenOverrides();
|
|
17697
17822
|
}
|
|
17698
17823
|
async function runWhoami(opts = {}) {
|
|
17699
17824
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
17700
|
-
const token =
|
|
17825
|
+
const token = resolveToken() ?? "";
|
|
17701
17826
|
try {
|
|
17702
17827
|
const me = await whoami(apiUrl, token);
|
|
17703
17828
|
process.stdout.write(
|
|
@@ -17708,14 +17833,22 @@ async function runWhoami(opts = {}) {
|
|
|
17708
17833
|
await reportError(err);
|
|
17709
17834
|
}
|
|
17710
17835
|
}
|
|
17836
|
+
function warnIfEnvTokenOverrides() {
|
|
17837
|
+
if (process.env.KRYD_TOKEN?.trim()) {
|
|
17838
|
+
process.stderr.write(
|
|
17839
|
+
"KRYD_TOKEN is set in this shell and overrides the stored login; commands keep using it until you unset it.\n"
|
|
17840
|
+
);
|
|
17841
|
+
}
|
|
17842
|
+
}
|
|
17711
17843
|
function runLogout() {
|
|
17712
17844
|
clearToken();
|
|
17713
17845
|
process.stdout.write("Logged out.\n");
|
|
17846
|
+
warnIfEnvTokenOverrides();
|
|
17714
17847
|
}
|
|
17715
17848
|
async function runInit(opts) {
|
|
17716
17849
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
17717
17850
|
const cwd = opts.cwd ?? process.cwd();
|
|
17718
|
-
const token =
|
|
17851
|
+
const token = resolveToken();
|
|
17719
17852
|
if (!token) {
|
|
17720
17853
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
17721
17854
|
process.exitCode = 1;
|
|
@@ -17938,7 +18071,7 @@ async function replayStoredLog(apiUrl, token, deploymentId) {
|
|
|
17938
18071
|
}
|
|
17939
18072
|
async function runLogs(opts) {
|
|
17940
18073
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
17941
|
-
const token =
|
|
18074
|
+
const token = resolveToken();
|
|
17942
18075
|
if (!token) {
|
|
17943
18076
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
17944
18077
|
process.exitCode = 1;
|
|
@@ -17990,7 +18123,7 @@ async function runLogs(opts) {
|
|
|
17990
18123
|
}
|
|
17991
18124
|
async function runRuntimeLogs(opts) {
|
|
17992
18125
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
17993
|
-
const token =
|
|
18126
|
+
const token = resolveToken();
|
|
17994
18127
|
if (!token) {
|
|
17995
18128
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
17996
18129
|
process.exitCode = 1;
|
|
@@ -18047,7 +18180,7 @@ async function runRuntimeLogs(opts) {
|
|
|
18047
18180
|
}
|
|
18048
18181
|
async function runDeploy(opts) {
|
|
18049
18182
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18050
|
-
const token =
|
|
18183
|
+
const token = resolveToken();
|
|
18051
18184
|
if (!token) {
|
|
18052
18185
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18053
18186
|
process.exitCode = 1;
|
|
@@ -18083,7 +18216,7 @@ function renderFor(logs) {
|
|
|
18083
18216
|
var LOGS_FLAG_HELP = "stream the full build log line by line instead of the progress display";
|
|
18084
18217
|
async function runPush(opts) {
|
|
18085
18218
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18086
|
-
const token =
|
|
18219
|
+
const token = resolveToken();
|
|
18087
18220
|
if (!token) {
|
|
18088
18221
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18089
18222
|
process.exitCode = 1;
|
|
@@ -18235,7 +18368,7 @@ async function followApps(apiUrl, token, deployments, render, estimateFrom) {
|
|
|
18235
18368
|
}
|
|
18236
18369
|
async function runRollback(opts) {
|
|
18237
18370
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18238
|
-
const token =
|
|
18371
|
+
const token = resolveToken();
|
|
18239
18372
|
if (!token) {
|
|
18240
18373
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18241
18374
|
process.exitCode = 1;
|
|
@@ -18266,7 +18399,7 @@ async function runRollback(opts) {
|
|
|
18266
18399
|
}
|
|
18267
18400
|
async function runRedeploy(opts) {
|
|
18268
18401
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18269
|
-
const token =
|
|
18402
|
+
const token = resolveToken();
|
|
18270
18403
|
if (!token) {
|
|
18271
18404
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18272
18405
|
process.exitCode = 1;
|
|
@@ -18296,7 +18429,7 @@ ${note}
|
|
|
18296
18429
|
}
|
|
18297
18430
|
async function runDbAdd(opts) {
|
|
18298
18431
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18299
|
-
const token =
|
|
18432
|
+
const token = resolveToken();
|
|
18300
18433
|
if (!token) {
|
|
18301
18434
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18302
18435
|
process.exitCode = 1;
|
|
@@ -18377,7 +18510,7 @@ function reportSettleResult(result, opts) {
|
|
|
18377
18510
|
}
|
|
18378
18511
|
async function runStorageAdd(opts) {
|
|
18379
18512
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18380
|
-
const token =
|
|
18513
|
+
const token = resolveToken();
|
|
18381
18514
|
if (!token) {
|
|
18382
18515
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18383
18516
|
process.exitCode = 1;
|
|
@@ -18471,7 +18604,7 @@ ${listed}Re-run \`kryd project rm <id>\` with the one you mean.
|
|
|
18471
18604
|
}
|
|
18472
18605
|
async function runProjectRemove(opts) {
|
|
18473
18606
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18474
|
-
const token =
|
|
18607
|
+
const token = resolveToken();
|
|
18475
18608
|
if (!token) {
|
|
18476
18609
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18477
18610
|
process.exitCode = 1;
|
|
@@ -18586,7 +18719,7 @@ async function describeProjectResources(apiUrl, token, projectId) {
|
|
|
18586
18719
|
}
|
|
18587
18720
|
async function runDbRemove(opts) {
|
|
18588
18721
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18589
|
-
const token =
|
|
18722
|
+
const token = resolveToken();
|
|
18590
18723
|
if (!token) {
|
|
18591
18724
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18592
18725
|
process.exitCode = 1;
|
|
@@ -18625,7 +18758,7 @@ async function runDbRemove(opts) {
|
|
|
18625
18758
|
}
|
|
18626
18759
|
async function runAiAdd(opts) {
|
|
18627
18760
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18628
|
-
const token =
|
|
18761
|
+
const token = resolveToken();
|
|
18629
18762
|
if (!token) {
|
|
18630
18763
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18631
18764
|
process.exitCode = 1;
|
|
@@ -18650,7 +18783,7 @@ async function runAiAdd(opts) {
|
|
|
18650
18783
|
}
|
|
18651
18784
|
async function runAiRemove(opts) {
|
|
18652
18785
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18653
|
-
const token =
|
|
18786
|
+
const token = resolveToken();
|
|
18654
18787
|
if (!token) {
|
|
18655
18788
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18656
18789
|
process.exitCode = 1;
|
|
@@ -18717,7 +18850,7 @@ Remove it before attaching another.
|
|
|
18717
18850
|
}
|
|
18718
18851
|
async function runDbStatus(opts) {
|
|
18719
18852
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18720
|
-
const token =
|
|
18853
|
+
const token = resolveToken();
|
|
18721
18854
|
if (!token) {
|
|
18722
18855
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18723
18856
|
process.exitCode = 1;
|
|
@@ -18737,7 +18870,7 @@ async function runDbStatus(opts) {
|
|
|
18737
18870
|
}
|
|
18738
18871
|
async function runStorageStatus(opts) {
|
|
18739
18872
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18740
|
-
const token =
|
|
18873
|
+
const token = resolveToken();
|
|
18741
18874
|
if (!token) {
|
|
18742
18875
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18743
18876
|
process.exitCode = 1;
|
|
@@ -18757,7 +18890,7 @@ async function runStorageStatus(opts) {
|
|
|
18757
18890
|
}
|
|
18758
18891
|
async function runAiStatus(opts) {
|
|
18759
18892
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18760
|
-
const token =
|
|
18893
|
+
const token = resolveToken();
|
|
18761
18894
|
if (!token) {
|
|
18762
18895
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18763
18896
|
process.exitCode = 1;
|
|
@@ -18793,7 +18926,7 @@ function daysUntil(iso) {
|
|
|
18793
18926
|
var TOKEN_EXPIRY_WARNING_DAYS = 30;
|
|
18794
18927
|
async function runWorkflowAdd(opts) {
|
|
18795
18928
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18796
|
-
const token =
|
|
18929
|
+
const token = resolveToken();
|
|
18797
18930
|
if (!token) {
|
|
18798
18931
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18799
18932
|
process.exitCode = 1;
|
|
@@ -18826,7 +18959,7 @@ HATCHET_CLIENT_TOKEN will be injected on your next production deploy (kryd deplo
|
|
|
18826
18959
|
}
|
|
18827
18960
|
async function runWorkflowRemove(opts) {
|
|
18828
18961
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18829
|
-
const token =
|
|
18962
|
+
const token = resolveToken();
|
|
18830
18963
|
if (!token) {
|
|
18831
18964
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18832
18965
|
process.exitCode = 1;
|
|
@@ -18871,7 +19004,7 @@ Workflow history, crons and schedules stay until the project is deleted.
|
|
|
18871
19004
|
}
|
|
18872
19005
|
async function runWorkflowStatus(opts) {
|
|
18873
19006
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18874
|
-
const token =
|
|
19007
|
+
const token = resolveToken();
|
|
18875
19008
|
if (!token) {
|
|
18876
19009
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18877
19010
|
process.exitCode = 1;
|
|
@@ -18930,7 +19063,7 @@ function formatDomainRecords(records) {
|
|
|
18930
19063
|
}
|
|
18931
19064
|
function domainContext(opts, usage2) {
|
|
18932
19065
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18933
|
-
const token =
|
|
19066
|
+
const token = resolveToken();
|
|
18934
19067
|
if (!token) {
|
|
18935
19068
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18936
19069
|
process.exitCode = 1;
|
|
@@ -19337,7 +19470,7 @@ HATCHET_CLIENT_TOKEN is injected into your app and your worker on production dep
|
|
|
19337
19470
|
}
|
|
19338
19471
|
async function runStorageRemove(opts) {
|
|
19339
19472
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19340
|
-
const token =
|
|
19473
|
+
const token = resolveToken();
|
|
19341
19474
|
if (!token) {
|
|
19342
19475
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19343
19476
|
process.exitCode = 1;
|
|
@@ -19400,7 +19533,7 @@ function envAnnotation(environments) {
|
|
|
19400
19533
|
}
|
|
19401
19534
|
async function runProjectList(opts) {
|
|
19402
19535
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19403
|
-
const token =
|
|
19536
|
+
const token = resolveToken();
|
|
19404
19537
|
if (!token) {
|
|
19405
19538
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19406
19539
|
process.exitCode = 1;
|
|
@@ -19451,7 +19584,7 @@ ${rows}`;
|
|
|
19451
19584
|
}
|
|
19452
19585
|
async function runEnvList(opts) {
|
|
19453
19586
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19454
|
-
const token =
|
|
19587
|
+
const token = resolveToken();
|
|
19455
19588
|
if (!token) {
|
|
19456
19589
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19457
19590
|
process.exitCode = 1;
|
|
@@ -19533,7 +19666,7 @@ function toDotenvLine(key, value) {
|
|
|
19533
19666
|
}
|
|
19534
19667
|
async function runEnvPull(opts) {
|
|
19535
19668
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19536
|
-
const token =
|
|
19669
|
+
const token = resolveToken();
|
|
19537
19670
|
if (!token) {
|
|
19538
19671
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19539
19672
|
process.exitCode = 1;
|
|
@@ -19634,7 +19767,7 @@ async function runEnvSet(opts) {
|
|
|
19634
19767
|
const scope = opts.build ? "build" : "runtime";
|
|
19635
19768
|
const environment = opts.preview ? "preview" : "production";
|
|
19636
19769
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19637
|
-
const token =
|
|
19770
|
+
const token = resolveToken();
|
|
19638
19771
|
if (!token) {
|
|
19639
19772
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19640
19773
|
process.exitCode = 1;
|
|
@@ -19729,7 +19862,7 @@ async function runEnvRemove(opts) {
|
|
|
19729
19862
|
const scope = opts.build ? "build" : "runtime";
|
|
19730
19863
|
const environment = opts.preview ? "preview" : "production";
|
|
19731
19864
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19732
|
-
const token =
|
|
19865
|
+
const token = resolveToken();
|
|
19733
19866
|
if (!token) {
|
|
19734
19867
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19735
19868
|
process.exitCode = 1;
|
|
@@ -19823,13 +19956,13 @@ Run \`kryd env list\` to see what is set. (If you meant the runtime variable of
|
|
|
19823
19956
|
await reportError(err);
|
|
19824
19957
|
}
|
|
19825
19958
|
}
|
|
19826
|
-
var CLI_VERSION = true ? "0.
|
|
19959
|
+
var CLI_VERSION = true ? "0.15.0" : "0.0.0-dev";
|
|
19827
19960
|
var program = new Command();
|
|
19828
19961
|
program.name("kryd").description("Kryd CLI").version(CLI_VERSION);
|
|
19829
19962
|
program.hook("postAction", async () => {
|
|
19830
19963
|
await backfillLinkAccount();
|
|
19831
19964
|
});
|
|
19832
|
-
program.command("login").description("Sign in via the browser (default) and store a token").option(
|
|
19965
|
+
program.command("login").description("Sign in via the browser (default) and store a token").option(
|
|
19833
19966
|
"--token <token>",
|
|
19834
19967
|
"supply a token instead of the browser flow (prefer $KRYD_TOKEN \u2014 flags are visible in `ps`)"
|
|
19835
19968
|
).option("--api-url <url>", "control-plane API base URL").option("--dashboard-url <url>", "dashboard base URL for the browser approve page (default app.kryd.eu)").action((opts) => runLogin(opts));
|
|
@@ -19971,6 +20104,10 @@ support.command("new").description(
|
|
|
19971
20104
|
support.command("list").description("Your account's tickets, with their status").option("--json", "print { items } as JSON on stdout").option("--api-url <url>", "control-plane API base URL").action((opts) => runSupportList(opts));
|
|
19972
20105
|
support.command("show <ticket>").description("One ticket and its conversation, e.g. `kryd support show T-1042`").option("--json", "print the ticket and its messages as JSON on stdout").option("--api-url <url>", "control-plane API base URL").action((ticket, opts) => runSupportShow(ticket, opts));
|
|
19973
20106
|
support.command("reply <ticket>").description("Answer in a ticket's conversation \u2014 the ticket goes back to Kryd support (status open)").option("--message <text>", "your reply (required)").option("--json", "print the updated ticket as JSON on stdout").option("--api-url <url>", "control-plane API base URL").action((ticket, opts) => runSupportReply(ticket, opts));
|
|
20107
|
+
var tokenCommand = program.command("token").description("Deploy tokens for CI and scripts: set one as KRYD_TOKEN and every command uses it, no login needed");
|
|
20108
|
+
tokenCommand.command("create <name>").description("Create a deploy token and print it once \u2014 it acts as you, inside your account").option("--expires <days>", "30 | 90 | 365 | never", DEFAULT_TOKEN_EXPIRY).option("--api-url <url>", "control-plane API base URL").action((name, opts) => runTokenCreate(name, opts));
|
|
20109
|
+
tokenCommand.command("list").description("Your deploy tokens: name, first characters, created, last used, expiry \u2014 never the value").option("--json", "print the tokens as JSON on stdout").option("--api-url <url>", "control-plane API base URL").action((opts) => runTokenList(opts));
|
|
20110
|
+
tokenCommand.command("revoke <id>").description("Revoke a deploy token; anything still using it gets 401").option("--api-url <url>", "control-plane API base URL").action((id, opts) => runTokenRevoke(id, opts));
|
|
19974
20111
|
function invokedDirectly() {
|
|
19975
20112
|
const entry = process.argv[1];
|
|
19976
20113
|
if (!entry) return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kryd/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.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",
|