@kryd/cli 0.14.0 → 0.16.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 +12 -1
- package/dist/index.js +616 -244
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -8,6 +8,22 @@ var __export = (target, all) => {
|
|
|
8
8
|
// src/index.ts
|
|
9
9
|
import { Command } from "commander";
|
|
10
10
|
|
|
11
|
+
// ../../packages/shared-types/dist/api.js
|
|
12
|
+
var ERROR_CODES = {
|
|
13
|
+
validationFailed: "validation.failed",
|
|
14
|
+
unauthorized: "auth.unauthorized",
|
|
15
|
+
forbidden: "auth.forbidden",
|
|
16
|
+
notFound: "resource.not_found",
|
|
17
|
+
conflict: "resource.conflict",
|
|
18
|
+
unprocessable: "request.unprocessable",
|
|
19
|
+
badRequest: "request.bad",
|
|
20
|
+
internal: "internal.error",
|
|
21
|
+
// KRYD-516b: a request that names no account while the caller is in several (400), and a member
|
|
22
|
+
// whose role lacks the route's permission (403, `details.permission` names it).
|
|
23
|
+
accountRequired: "account.required",
|
|
24
|
+
permissionRequired: "auth.permission_required"
|
|
25
|
+
};
|
|
26
|
+
|
|
11
27
|
// ../../packages/shared-types/dist/db-url.js
|
|
12
28
|
function isValidPostgresConnectionString(value) {
|
|
13
29
|
if (value.length === 0 || /\s/.test(value))
|
|
@@ -14875,6 +14891,42 @@ function parseTicketNumber(raw) {
|
|
|
14875
14891
|
var TICKET_ATTACHMENT_MAX_BYTES = 10 * 1024 * 1024;
|
|
14876
14892
|
var TICKET_ATTACHMENT_MAX_SIZE_LABEL = `${TICKET_ATTACHMENT_MAX_BYTES / (1024 * 1024)} MB`;
|
|
14877
14893
|
|
|
14894
|
+
// ../../packages/shared-types/dist/permissions.js
|
|
14895
|
+
var PERMISSIONS = [
|
|
14896
|
+
/** Open the site of a private preview or a closed production (enforced from KRYD-575). */
|
|
14897
|
+
"app:view",
|
|
14898
|
+
/** See the account itself: name, slug. */
|
|
14899
|
+
"account:view",
|
|
14900
|
+
/** Rename the account and change its slug. */
|
|
14901
|
+
"account:manage",
|
|
14902
|
+
/** See who is a member, and the invites still open. */
|
|
14903
|
+
"members:view",
|
|
14904
|
+
/** Invite people, change a member's role, remove a member. */
|
|
14905
|
+
"members:manage",
|
|
14906
|
+
/** See usage and costs. */
|
|
14907
|
+
"usage:view",
|
|
14908
|
+
/** See projects, deploys, domains, databases, storage, AI and workflows. */
|
|
14909
|
+
"projects:view",
|
|
14910
|
+
/** Change a project: env vars, domains, databases, storage, AI, workflows, remove it. */
|
|
14911
|
+
"projects:manage",
|
|
14912
|
+
/** Read env var VALUES (secrets), not only their names. */
|
|
14913
|
+
"env:read",
|
|
14914
|
+
/** Link a repository, deploy, roll back, get a push credential, manage deploy hooks. */
|
|
14915
|
+
"deploy",
|
|
14916
|
+
/** Read build and runtime logs. */
|
|
14917
|
+
"logs:view",
|
|
14918
|
+
/** Trigger, cancel and replay workflow runs; add and remove crons and schedules. */
|
|
14919
|
+
"workflows:run",
|
|
14920
|
+
/** Open and answer support tickets. */
|
|
14921
|
+
"support"
|
|
14922
|
+
];
|
|
14923
|
+
var ROLES = {
|
|
14924
|
+
owner: PERMISSIONS,
|
|
14925
|
+
member: PERMISSIONS.filter((p) => p !== "members:manage" && p !== "account:manage"),
|
|
14926
|
+
viewer: ["app:view"]
|
|
14927
|
+
};
|
|
14928
|
+
var ACCOUNT_HEADER = "Kryd-Account";
|
|
14929
|
+
|
|
14878
14930
|
// ../../packages/shared-types/dist/workflow-run.js
|
|
14879
14931
|
var MAX_WEBHOOK_NAME = 32;
|
|
14880
14932
|
var WEBHOOK_NAME_RE = new RegExp(`^[a-z0-9](?:[a-z0-9-]{0,${MAX_WEBHOOK_NAME - 2}}[a-z0-9])?$`);
|
|
@@ -14936,6 +14988,209 @@ import {
|
|
|
14936
14988
|
import { basename as basename2, dirname as dirname2, isAbsolute, join as join3, relative, resolve as resolve2 } from "node:path";
|
|
14937
14989
|
import { pathToFileURL } from "node:url";
|
|
14938
14990
|
|
|
14991
|
+
// src/config.ts
|
|
14992
|
+
import {
|
|
14993
|
+
appendFileSync,
|
|
14994
|
+
chmodSync,
|
|
14995
|
+
existsSync,
|
|
14996
|
+
mkdirSync,
|
|
14997
|
+
readFileSync,
|
|
14998
|
+
rmSync,
|
|
14999
|
+
writeFileSync
|
|
15000
|
+
} from "node:fs";
|
|
15001
|
+
import { homedir } from "node:os";
|
|
15002
|
+
import { dirname, join } from "node:path";
|
|
15003
|
+
var DEFAULT_API_URL = "http://localhost:3000";
|
|
15004
|
+
function configPath() {
|
|
15005
|
+
const dir = process.env.KRYD_CONFIG_DIR ?? join(homedir(), ".kryd");
|
|
15006
|
+
return join(dir, "config.json");
|
|
15007
|
+
}
|
|
15008
|
+
function loadConfig() {
|
|
15009
|
+
let raw;
|
|
15010
|
+
try {
|
|
15011
|
+
raw = readFileSync(configPath(), "utf8");
|
|
15012
|
+
} catch {
|
|
15013
|
+
return {};
|
|
15014
|
+
}
|
|
15015
|
+
try {
|
|
15016
|
+
return JSON.parse(raw);
|
|
15017
|
+
} catch {
|
|
15018
|
+
process.stderr.write(
|
|
15019
|
+
`warning: ${configPath()} is not valid JSON \u2014 ignoring it.
|
|
15020
|
+
`
|
|
15021
|
+
);
|
|
15022
|
+
return {};
|
|
15023
|
+
}
|
|
15024
|
+
}
|
|
15025
|
+
function resolveToken() {
|
|
15026
|
+
const fromEnv = process.env.KRYD_TOKEN?.trim();
|
|
15027
|
+
return fromEnv ? fromEnv : loadConfig().token;
|
|
15028
|
+
}
|
|
15029
|
+
function writeConfig(config2) {
|
|
15030
|
+
const path = configPath();
|
|
15031
|
+
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
15032
|
+
writeFileSync(path, JSON.stringify(config2, null, 2) + "\n", { mode: 384 });
|
|
15033
|
+
chmodSync(path, 384);
|
|
15034
|
+
}
|
|
15035
|
+
function saveToken(token, apiUrl) {
|
|
15036
|
+
const config2 = loadConfig();
|
|
15037
|
+
config2.token = token;
|
|
15038
|
+
if (apiUrl) config2.apiUrl = apiUrl;
|
|
15039
|
+
writeConfig(config2);
|
|
15040
|
+
}
|
|
15041
|
+
function clearToken() {
|
|
15042
|
+
const config2 = loadConfig();
|
|
15043
|
+
delete config2.token;
|
|
15044
|
+
delete config2.account;
|
|
15045
|
+
delete config2.accountSlug;
|
|
15046
|
+
writeConfig(config2);
|
|
15047
|
+
}
|
|
15048
|
+
function saveAccount(account) {
|
|
15049
|
+
const config2 = loadConfig();
|
|
15050
|
+
config2.account = account.accountId;
|
|
15051
|
+
if (account.slug) config2.accountSlug = account.slug;
|
|
15052
|
+
else delete config2.accountSlug;
|
|
15053
|
+
writeConfig(config2);
|
|
15054
|
+
}
|
|
15055
|
+
function clearAccount() {
|
|
15056
|
+
const config2 = loadConfig();
|
|
15057
|
+
delete config2.account;
|
|
15058
|
+
delete config2.accountSlug;
|
|
15059
|
+
writeConfig(config2);
|
|
15060
|
+
}
|
|
15061
|
+
var DEPLOY_TOKEN_PREFIX = "kryd_";
|
|
15062
|
+
function accountHeader(token) {
|
|
15063
|
+
if (!token || token.startsWith(DEPLOY_TOKEN_PREFIX)) return {};
|
|
15064
|
+
const account = loadConfig().account;
|
|
15065
|
+
return account ? { [ACCOUNT_HEADER]: account } : {};
|
|
15066
|
+
}
|
|
15067
|
+
function authHeaders(token) {
|
|
15068
|
+
return { authorization: `Bearer ${token}`, ...accountHeader(token) };
|
|
15069
|
+
}
|
|
15070
|
+
var lastApiUrl = null;
|
|
15071
|
+
function resolveApiUrl(flag) {
|
|
15072
|
+
lastApiUrl = flag ?? process.env.KRYD_API_URL ?? loadConfig().apiUrl ?? DEFAULT_API_URL;
|
|
15073
|
+
return lastApiUrl;
|
|
15074
|
+
}
|
|
15075
|
+
function currentApiUrl() {
|
|
15076
|
+
return lastApiUrl ?? resolveApiUrl();
|
|
15077
|
+
}
|
|
15078
|
+
var DEFAULT_DASHBOARD_URL = "https://app.kryd.eu";
|
|
15079
|
+
function resolveDashboardUrl(flag) {
|
|
15080
|
+
return flag ?? process.env.KRYD_DASHBOARD_URL ?? DEFAULT_DASHBOARD_URL;
|
|
15081
|
+
}
|
|
15082
|
+
var PROJECT_LINK_DIR = ".kryd";
|
|
15083
|
+
var PROJECT_LINK_FILE = "project.json";
|
|
15084
|
+
function findProjectLinkDir(cwd = process.cwd()) {
|
|
15085
|
+
let dir = cwd;
|
|
15086
|
+
for (; ; ) {
|
|
15087
|
+
if (existsSync(join(dir, PROJECT_LINK_DIR, PROJECT_LINK_FILE))) return dir;
|
|
15088
|
+
if (existsSync(join(dir, ".git"))) return null;
|
|
15089
|
+
const parent = dirname(dir);
|
|
15090
|
+
if (parent === dir) return null;
|
|
15091
|
+
dir = parent;
|
|
15092
|
+
}
|
|
15093
|
+
}
|
|
15094
|
+
function loadProjectLink(cwd = process.cwd()) {
|
|
15095
|
+
const root = findProjectLinkDir(cwd);
|
|
15096
|
+
if (!root) return null;
|
|
15097
|
+
const path = join(root, PROJECT_LINK_DIR, PROJECT_LINK_FILE);
|
|
15098
|
+
let raw;
|
|
15099
|
+
try {
|
|
15100
|
+
raw = readFileSync(path, "utf8");
|
|
15101
|
+
} catch {
|
|
15102
|
+
return null;
|
|
15103
|
+
}
|
|
15104
|
+
let parsed;
|
|
15105
|
+
try {
|
|
15106
|
+
parsed = JSON.parse(raw);
|
|
15107
|
+
} catch {
|
|
15108
|
+
process.stderr.write(`warning: ${path} is not valid JSON \u2014 ignoring it.
|
|
15109
|
+
`);
|
|
15110
|
+
return null;
|
|
15111
|
+
}
|
|
15112
|
+
if (!parsed || typeof parsed.projectId !== "string" || parsed.projectId.length === 0) {
|
|
15113
|
+
process.stderr.write(`warning: ${path} has no projectId \u2014 ignoring it.
|
|
15114
|
+
`);
|
|
15115
|
+
return null;
|
|
15116
|
+
}
|
|
15117
|
+
return parsed;
|
|
15118
|
+
}
|
|
15119
|
+
function saveProjectLink(cwd, link) {
|
|
15120
|
+
const dir = join(cwd, PROJECT_LINK_DIR);
|
|
15121
|
+
mkdirSync(dir, { recursive: true });
|
|
15122
|
+
const path = join(dir, PROJECT_LINK_FILE);
|
|
15123
|
+
writeFileSync(path, JSON.stringify(link, null, 2) + "\n");
|
|
15124
|
+
return path;
|
|
15125
|
+
}
|
|
15126
|
+
function ensureIgnored(cwd, entry, aliases = []) {
|
|
15127
|
+
const path = join(cwd, ".gitignore");
|
|
15128
|
+
let current = "";
|
|
15129
|
+
try {
|
|
15130
|
+
current = readFileSync(path, "utf8");
|
|
15131
|
+
} catch {
|
|
15132
|
+
}
|
|
15133
|
+
const present = current.split("\n").map((l) => l.trim()).some((l) => l === entry || aliases.includes(l));
|
|
15134
|
+
if (present) return false;
|
|
15135
|
+
const prefix = current.length > 0 && !current.endsWith("\n") ? "\n" : "";
|
|
15136
|
+
appendFileSync(path, `${prefix}${entry}
|
|
15137
|
+
`);
|
|
15138
|
+
return true;
|
|
15139
|
+
}
|
|
15140
|
+
function ensureKrydIgnored(cwd) {
|
|
15141
|
+
ensureIgnored(cwd, `${PROJECT_LINK_DIR}/`, [PROJECT_LINK_DIR]);
|
|
15142
|
+
}
|
|
15143
|
+
function directoryIsLinked(cwd) {
|
|
15144
|
+
const path = join(cwd, PROJECT_LINK_DIR, PROJECT_LINK_FILE);
|
|
15145
|
+
if (!existsSync(path)) return null;
|
|
15146
|
+
return loadProjectLink(cwd);
|
|
15147
|
+
}
|
|
15148
|
+
function writeSecretFile(path, contents) {
|
|
15149
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
15150
|
+
writeFileSync(path, contents, { mode: 384 });
|
|
15151
|
+
chmodSync(path, 384);
|
|
15152
|
+
}
|
|
15153
|
+
function clearProjectLinkIfMatches(projectId, cwd = process.cwd()) {
|
|
15154
|
+
const root = findProjectLinkDir(cwd);
|
|
15155
|
+
if (!root) return false;
|
|
15156
|
+
const link = loadProjectLink(cwd);
|
|
15157
|
+
if (link?.projectId !== projectId) return false;
|
|
15158
|
+
try {
|
|
15159
|
+
rmSync(join(root, PROJECT_LINK_DIR, PROJECT_LINK_FILE));
|
|
15160
|
+
return true;
|
|
15161
|
+
} catch {
|
|
15162
|
+
return false;
|
|
15163
|
+
}
|
|
15164
|
+
}
|
|
15165
|
+
function resolvedLink() {
|
|
15166
|
+
return lastLinkResolution;
|
|
15167
|
+
}
|
|
15168
|
+
var lastLinkResolution = null;
|
|
15169
|
+
function resolveProjectId(explicit, cwd = process.cwd()) {
|
|
15170
|
+
lastLinkResolution = null;
|
|
15171
|
+
if (explicit) return explicit;
|
|
15172
|
+
const root = findProjectLinkDir(cwd);
|
|
15173
|
+
if (!root) return null;
|
|
15174
|
+
const link = loadProjectLink(cwd);
|
|
15175
|
+
if (!link) return null;
|
|
15176
|
+
lastLinkResolution = { root, link };
|
|
15177
|
+
return link.projectId;
|
|
15178
|
+
}
|
|
15179
|
+
function rememberLinkAccount(root, account) {
|
|
15180
|
+
if (!account.accountId) return false;
|
|
15181
|
+
const link = directoryIsLinked(root);
|
|
15182
|
+
if (!link) return false;
|
|
15183
|
+
const slug = account.accountSlug ?? link.accountSlug;
|
|
15184
|
+
if (link.accountId === account.accountId && link.accountSlug === slug) return false;
|
|
15185
|
+
saveProjectLink(root, {
|
|
15186
|
+
...link,
|
|
15187
|
+
accountId: account.accountId,
|
|
15188
|
+
// `exactOptionalPropertyTypes`: an absent key and an explicit `undefined` are different types.
|
|
15189
|
+
...slug ? { accountSlug: slug } : {}
|
|
15190
|
+
});
|
|
15191
|
+
return true;
|
|
15192
|
+
}
|
|
15193
|
+
|
|
14939
15194
|
// src/client.ts
|
|
14940
15195
|
var ApiError = class extends Error {
|
|
14941
15196
|
envelope;
|
|
@@ -14965,7 +15220,7 @@ async function parseEnvelope(res) {
|
|
|
14965
15220
|
}
|
|
14966
15221
|
async function whoami(apiUrl, token) {
|
|
14967
15222
|
const res = await fetch(`${apiUrl}/me`, {
|
|
14968
|
-
headers: {
|
|
15223
|
+
headers: { ...authHeaders(token) }
|
|
14969
15224
|
});
|
|
14970
15225
|
if (!res.ok) {
|
|
14971
15226
|
throw new ApiError(
|
|
@@ -14981,7 +15236,7 @@ async function linkProject(apiUrl, token, input) {
|
|
|
14981
15236
|
method: "POST",
|
|
14982
15237
|
headers: {
|
|
14983
15238
|
"content-type": "application/json",
|
|
14984
|
-
|
|
15239
|
+
...authHeaders(token)
|
|
14985
15240
|
},
|
|
14986
15241
|
body: JSON.stringify(input)
|
|
14987
15242
|
});
|
|
@@ -15005,7 +15260,7 @@ async function attachDatabase(apiUrl, token, input) {
|
|
|
15005
15260
|
method: "POST",
|
|
15006
15261
|
headers: {
|
|
15007
15262
|
"content-type": "application/json",
|
|
15008
|
-
|
|
15263
|
+
...authHeaders(token)
|
|
15009
15264
|
},
|
|
15010
15265
|
body: JSON.stringify({
|
|
15011
15266
|
projectId: input.projectId,
|
|
@@ -15036,7 +15291,7 @@ async function createStorage(apiUrl, token, input) {
|
|
|
15036
15291
|
method: "POST",
|
|
15037
15292
|
headers: {
|
|
15038
15293
|
"content-type": "application/json",
|
|
15039
|
-
|
|
15294
|
+
...authHeaders(token)
|
|
15040
15295
|
},
|
|
15041
15296
|
body: JSON.stringify({ projectId: input.projectId })
|
|
15042
15297
|
});
|
|
@@ -15058,7 +15313,7 @@ async function enableAi(apiUrl, token, input) {
|
|
|
15058
15313
|
method: "POST",
|
|
15059
15314
|
headers: {
|
|
15060
15315
|
"content-type": "application/json",
|
|
15061
|
-
|
|
15316
|
+
...authHeaders(token)
|
|
15062
15317
|
},
|
|
15063
15318
|
body: JSON.stringify({ projectId: input.projectId })
|
|
15064
15319
|
});
|
|
@@ -15077,7 +15332,7 @@ async function enableAi(apiUrl, token, input) {
|
|
|
15077
15332
|
}
|
|
15078
15333
|
async function getAiStatus(apiUrl, token, projectId) {
|
|
15079
15334
|
const res = await fetch(`${apiUrl}/ai/${encodeURIComponent(projectId)}`, {
|
|
15080
|
-
headers: {
|
|
15335
|
+
headers: { ...authHeaders(token) }
|
|
15081
15336
|
});
|
|
15082
15337
|
if (!res.ok) {
|
|
15083
15338
|
throw new ApiError(
|
|
@@ -15095,7 +15350,7 @@ async function getAiStatus(apiUrl, token, projectId) {
|
|
|
15095
15350
|
async function removeAi(apiUrl, token, projectId) {
|
|
15096
15351
|
const res = await fetch(`${apiUrl}/ai/${encodeURIComponent(projectId)}`, {
|
|
15097
15352
|
method: "DELETE",
|
|
15098
|
-
headers: {
|
|
15353
|
+
headers: { ...authHeaders(token) }
|
|
15099
15354
|
});
|
|
15100
15355
|
if (!res.ok) {
|
|
15101
15356
|
throw new ApiError(
|
|
@@ -15117,7 +15372,7 @@ async function addWorkflows(apiUrl, token, input) {
|
|
|
15117
15372
|
method: "POST",
|
|
15118
15373
|
headers: {
|
|
15119
15374
|
"content-type": "application/json",
|
|
15120
|
-
|
|
15375
|
+
...authHeaders(token)
|
|
15121
15376
|
},
|
|
15122
15377
|
body: JSON.stringify({ projectId: input.projectId })
|
|
15123
15378
|
});
|
|
@@ -15132,7 +15387,7 @@ async function addWorkflows(apiUrl, token, input) {
|
|
|
15132
15387
|
}
|
|
15133
15388
|
async function getWorkflowStatus(apiUrl, token, projectId) {
|
|
15134
15389
|
const res = await fetch(`${apiUrl}/workflows/${encodeURIComponent(projectId)}`, {
|
|
15135
|
-
headers: {
|
|
15390
|
+
headers: { ...authHeaders(token) }
|
|
15136
15391
|
});
|
|
15137
15392
|
if (!res.ok) {
|
|
15138
15393
|
throw new ApiError(
|
|
@@ -15146,7 +15401,7 @@ async function getWorkflowStatus(apiUrl, token, projectId) {
|
|
|
15146
15401
|
async function removeWorkflows(apiUrl, token, projectId) {
|
|
15147
15402
|
const res = await fetch(`${apiUrl}/workflows/${encodeURIComponent(projectId)}`, {
|
|
15148
15403
|
method: "DELETE",
|
|
15149
|
-
headers: {
|
|
15404
|
+
headers: { ...authHeaders(token) }
|
|
15150
15405
|
});
|
|
15151
15406
|
if (!res.ok) {
|
|
15152
15407
|
throw new ApiError(
|
|
@@ -15197,7 +15452,7 @@ function domainsUrl(apiUrl, projectId) {
|
|
|
15197
15452
|
}
|
|
15198
15453
|
async function listDomains(apiUrl, token, projectId) {
|
|
15199
15454
|
const res = await fetch(domainsUrl(apiUrl, projectId), {
|
|
15200
|
-
headers: {
|
|
15455
|
+
headers: { ...authHeaders(token) }
|
|
15201
15456
|
});
|
|
15202
15457
|
if (!res.ok) {
|
|
15203
15458
|
throw new ApiError(`Domain listing failed (${res.status})`, await parseEnvelope(res), res.status);
|
|
@@ -15214,7 +15469,7 @@ async function listDomains(apiUrl, token, projectId) {
|
|
|
15214
15469
|
async function addDomain(apiUrl, token, projectId, host, app, redirectTo) {
|
|
15215
15470
|
const res = await fetch(domainsUrl(apiUrl, projectId), {
|
|
15216
15471
|
method: "POST",
|
|
15217
|
-
headers: { "content-type": "application/json",
|
|
15472
|
+
headers: { "content-type": "application/json", ...authHeaders(token) },
|
|
15218
15473
|
body: JSON.stringify({ host, ...app ? { app } : {}, ...redirectTo !== void 0 ? { redirectTo } : {} })
|
|
15219
15474
|
});
|
|
15220
15475
|
if (!res.ok) {
|
|
@@ -15225,7 +15480,7 @@ async function addDomain(apiUrl, token, projectId, host, app, redirectTo) {
|
|
|
15225
15480
|
async function removeDomain(apiUrl, token, projectId, host) {
|
|
15226
15481
|
const res = await fetch(`${domainsUrl(apiUrl, projectId)}/${encodeURIComponent(host)}`, {
|
|
15227
15482
|
method: "DELETE",
|
|
15228
|
-
headers: {
|
|
15483
|
+
headers: { ...authHeaders(token) }
|
|
15229
15484
|
});
|
|
15230
15485
|
if (!res.ok) {
|
|
15231
15486
|
throw new ApiError(`Domain removal failed (${res.status})`, await parseEnvelope(res), res.status);
|
|
@@ -15237,7 +15492,7 @@ function deployHookUrl(apiUrl, projectId) {
|
|
|
15237
15492
|
async function createDeployHook(apiUrl, token, projectId) {
|
|
15238
15493
|
const res = await fetch(deployHookUrl(apiUrl, projectId), {
|
|
15239
15494
|
method: "PUT",
|
|
15240
|
-
headers: {
|
|
15495
|
+
headers: { ...authHeaders(token) }
|
|
15241
15496
|
});
|
|
15242
15497
|
if (!res.ok) {
|
|
15243
15498
|
throw new ApiError(`Deploy hook creation failed (${res.status})`, await parseEnvelope(res), res.status);
|
|
@@ -15251,7 +15506,7 @@ async function createDeployHook(apiUrl, token, projectId) {
|
|
|
15251
15506
|
async function removeDeployHook(apiUrl, token, projectId) {
|
|
15252
15507
|
const res = await fetch(deployHookUrl(apiUrl, projectId), {
|
|
15253
15508
|
method: "DELETE",
|
|
15254
|
-
headers: {
|
|
15509
|
+
headers: { ...authHeaders(token) }
|
|
15255
15510
|
});
|
|
15256
15511
|
if (!res.ok) {
|
|
15257
15512
|
throw new ApiError(`Deploy hook removal failed (${res.status})`, await parseEnvelope(res), res.status);
|
|
@@ -15264,7 +15519,7 @@ async function removeDeployHook(apiUrl, token, projectId) {
|
|
|
15264
15519
|
}
|
|
15265
15520
|
async function fetchResourceStatus(apiUrl, token, path) {
|
|
15266
15521
|
const res = await fetch(`${apiUrl}${path}`, {
|
|
15267
|
-
headers: {
|
|
15522
|
+
headers: { ...authHeaders(token) }
|
|
15268
15523
|
});
|
|
15269
15524
|
if (!res.ok) {
|
|
15270
15525
|
throw new ApiError(
|
|
@@ -15347,7 +15602,7 @@ async function pollResourceDetached(fetchStatus, opts) {
|
|
|
15347
15602
|
async function detachDatabase(apiUrl, token, projectId) {
|
|
15348
15603
|
const res = await fetch(
|
|
15349
15604
|
`${apiUrl}/databases/${encodeURIComponent(projectId)}`,
|
|
15350
|
-
{ method: "DELETE", headers: {
|
|
15605
|
+
{ method: "DELETE", headers: { ...authHeaders(token) } }
|
|
15351
15606
|
);
|
|
15352
15607
|
if (!res.ok) {
|
|
15353
15608
|
throw new ApiError(
|
|
@@ -15359,7 +15614,7 @@ async function detachDatabase(apiUrl, token, projectId) {
|
|
|
15359
15614
|
}
|
|
15360
15615
|
async function getProject(apiUrl, token, projectId) {
|
|
15361
15616
|
const res = await fetch(`${apiUrl}/projects/${encodeURIComponent(projectId)}`, {
|
|
15362
|
-
headers: {
|
|
15617
|
+
headers: { ...authHeaders(token) }
|
|
15363
15618
|
});
|
|
15364
15619
|
if (res.status === 404) return null;
|
|
15365
15620
|
if (!res.ok) {
|
|
@@ -15369,7 +15624,7 @@ async function getProject(apiUrl, token, projectId) {
|
|
|
15369
15624
|
}
|
|
15370
15625
|
async function listProjects(apiUrl, token) {
|
|
15371
15626
|
const res = await fetch(`${apiUrl}/projects`, {
|
|
15372
|
-
headers: {
|
|
15627
|
+
headers: { ...authHeaders(token) }
|
|
15373
15628
|
});
|
|
15374
15629
|
if (!res.ok) {
|
|
15375
15630
|
throw new ApiError(`Listing projects failed (${res.status})`, await parseEnvelope(res), res.status);
|
|
@@ -15379,7 +15634,7 @@ async function listProjects(apiUrl, token) {
|
|
|
15379
15634
|
async function deleteProject(apiUrl, token, projectId) {
|
|
15380
15635
|
const res = await fetch(`${apiUrl}/projects/${encodeURIComponent(projectId)}`, {
|
|
15381
15636
|
method: "DELETE",
|
|
15382
|
-
headers: {
|
|
15637
|
+
headers: { ...authHeaders(token) }
|
|
15383
15638
|
});
|
|
15384
15639
|
if (!res.ok) {
|
|
15385
15640
|
throw new ApiError(`Project delete failed (${res.status})`, await parseEnvelope(res), res.status);
|
|
@@ -15412,7 +15667,7 @@ async function pollProjectDeleted(read, opts) {
|
|
|
15412
15667
|
async function detachStorage(apiUrl, token, projectId) {
|
|
15413
15668
|
const res = await fetch(`${apiUrl}/storage/${encodeURIComponent(projectId)}`, {
|
|
15414
15669
|
method: "DELETE",
|
|
15415
|
-
headers: {
|
|
15670
|
+
headers: { ...authHeaders(token) }
|
|
15416
15671
|
});
|
|
15417
15672
|
if (!res.ok) {
|
|
15418
15673
|
throw new ApiError(
|
|
@@ -15425,7 +15680,7 @@ async function detachStorage(apiUrl, token, projectId) {
|
|
|
15425
15680
|
async function listEnvVars(apiUrl, token, projectId, include = "all") {
|
|
15426
15681
|
const res = await fetch(
|
|
15427
15682
|
`${apiUrl}/projects/${encodeURIComponent(projectId)}/env?scope=${encodeURIComponent(include)}`,
|
|
15428
|
-
{ headers: {
|
|
15683
|
+
{ headers: { ...authHeaders(token) } }
|
|
15429
15684
|
);
|
|
15430
15685
|
if (!res.ok) {
|
|
15431
15686
|
throw new ApiError(
|
|
@@ -15478,7 +15733,7 @@ async function pullEnvValues(apiUrl, token, projectId, environment = "production
|
|
|
15478
15733
|
const query = environment === "production" ? "" : `?environment=${encodeURIComponent(environment)}`;
|
|
15479
15734
|
const res = await fetch(
|
|
15480
15735
|
`${apiUrl}/projects/${encodeURIComponent(projectId)}/env/values${query}`,
|
|
15481
|
-
{ headers: {
|
|
15736
|
+
{ headers: { ...authHeaders(token) } }
|
|
15482
15737
|
);
|
|
15483
15738
|
if (!res.ok) {
|
|
15484
15739
|
throw new ApiError(
|
|
@@ -15512,7 +15767,7 @@ async function setEnvVar(apiUrl, token, projectId, key, value, scope = "runtime"
|
|
|
15512
15767
|
method: "PUT",
|
|
15513
15768
|
headers: {
|
|
15514
15769
|
"content-type": "application/json",
|
|
15515
|
-
|
|
15770
|
+
...authHeaders(token)
|
|
15516
15771
|
},
|
|
15517
15772
|
// `scope`/`environment` are sent only when NOT their defaults. An api older than KRYD-156/159
|
|
15518
15773
|
// ignores unknown body fields, so this is belt-and-braces — but it keeps a plain runtime
|
|
@@ -15553,7 +15808,7 @@ async function removeEnvVar(apiUrl, token, projectId, key, scope = "runtime", en
|
|
|
15553
15808
|
const query = params.toString() ? `?${params.toString()}` : "";
|
|
15554
15809
|
const res = await fetch(
|
|
15555
15810
|
`${apiUrl}/projects/${encodeURIComponent(projectId)}/env/${encodeURIComponent(key)}${query}`,
|
|
15556
|
-
{ method: "DELETE", headers: {
|
|
15811
|
+
{ method: "DELETE", headers: { ...authHeaders(token) } }
|
|
15557
15812
|
);
|
|
15558
15813
|
if (!res.ok) {
|
|
15559
15814
|
throw new ApiError(
|
|
@@ -15579,7 +15834,7 @@ async function triggerDeploy(apiUrl, token, projectId) {
|
|
|
15579
15834
|
method: "POST",
|
|
15580
15835
|
headers: {
|
|
15581
15836
|
"content-type": "application/json",
|
|
15582
|
-
|
|
15837
|
+
...authHeaders(token)
|
|
15583
15838
|
},
|
|
15584
15839
|
body: JSON.stringify({ projectId })
|
|
15585
15840
|
});
|
|
@@ -15597,7 +15852,7 @@ async function rollbackDeploy(apiUrl, token, projectId, deploymentId, app) {
|
|
|
15597
15852
|
method: "POST",
|
|
15598
15853
|
headers: {
|
|
15599
15854
|
"content-type": "application/json",
|
|
15600
|
-
|
|
15855
|
+
...authHeaders(token)
|
|
15601
15856
|
},
|
|
15602
15857
|
body: JSON.stringify({
|
|
15603
15858
|
projectId,
|
|
@@ -15615,7 +15870,7 @@ async function redeployProject(apiUrl, token, projectId, app) {
|
|
|
15615
15870
|
method: "POST",
|
|
15616
15871
|
headers: {
|
|
15617
15872
|
"content-type": "application/json",
|
|
15618
|
-
|
|
15873
|
+
...authHeaders(token)
|
|
15619
15874
|
},
|
|
15620
15875
|
body: JSON.stringify({ projectId, ...app ? { app } : {} })
|
|
15621
15876
|
});
|
|
@@ -15627,7 +15882,7 @@ async function redeployProject(apiUrl, token, projectId, app) {
|
|
|
15627
15882
|
async function listDeployments(apiUrl, token, projectId) {
|
|
15628
15883
|
const url2 = new URL(`${apiUrl}/deployments`);
|
|
15629
15884
|
if (projectId) url2.searchParams.set("projectId", projectId);
|
|
15630
|
-
const res = await fetch(url2, { headers: {
|
|
15885
|
+
const res = await fetch(url2, { headers: { ...authHeaders(token) } });
|
|
15631
15886
|
if (!res.ok) {
|
|
15632
15887
|
throw new ApiError(`Listing deploys failed (${res.status})`, await parseEnvelope(res), res.status);
|
|
15633
15888
|
}
|
|
@@ -15731,7 +15986,7 @@ async function streamDeploy(apiUrl, token, deploymentId, handlers, opts = {}) {
|
|
|
15731
15986
|
let dropped = false;
|
|
15732
15987
|
try {
|
|
15733
15988
|
const res = await doFetch(`${apiUrl}/deployments/${deploymentId}/logs`, {
|
|
15734
|
-
headers: {
|
|
15989
|
+
headers: { ...authHeaders(token), accept: "text/event-stream" }
|
|
15735
15990
|
});
|
|
15736
15991
|
if (!res.ok) {
|
|
15737
15992
|
throw new ApiError(`Log stream failed (${res.status})`, await parseEnvelope(res), res.status);
|
|
@@ -15773,7 +16028,7 @@ async function streamRuntime(apiUrl, token, id, handlers, opts = {}) {
|
|
|
15773
16028
|
if (afterNs) url2.searchParams.set("after", afterNs);
|
|
15774
16029
|
else if (opts.since) url2.searchParams.set("since", opts.since);
|
|
15775
16030
|
const res = await doFetch(url2, {
|
|
15776
|
-
headers: {
|
|
16031
|
+
headers: { ...authHeaders(token), accept: "text/event-stream" },
|
|
15777
16032
|
...opts.signal ? { signal: opts.signal } : {}
|
|
15778
16033
|
});
|
|
15779
16034
|
if (!res.ok) {
|
|
@@ -15803,7 +16058,7 @@ async function streamRuntime(apiUrl, token, id, handlers, opts = {}) {
|
|
|
15803
16058
|
async function fetchDeployLog(apiUrl, token, deploymentId, opts = {}) {
|
|
15804
16059
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
15805
16060
|
const res = await doFetch(`${apiUrl}/deployments/${deploymentId}/log`, {
|
|
15806
|
-
headers: {
|
|
16061
|
+
headers: { ...authHeaders(token) }
|
|
15807
16062
|
});
|
|
15808
16063
|
if (res.status === 404) return null;
|
|
15809
16064
|
if (!res.ok) {
|
|
@@ -15818,7 +16073,7 @@ async function fetchDeployLog(apiUrl, token, deploymentId, opts = {}) {
|
|
|
15818
16073
|
async function fetchPushCredential(apiUrl, token, projectId) {
|
|
15819
16074
|
const res = await fetch(
|
|
15820
16075
|
`${apiUrl}/repos/${encodeURIComponent(projectId)}/push-credential`,
|
|
15821
|
-
{ headers: {
|
|
16076
|
+
{ headers: { ...authHeaders(token) } }
|
|
15822
16077
|
);
|
|
15823
16078
|
if (!res.ok) {
|
|
15824
16079
|
throw new ApiError(
|
|
@@ -15847,7 +16102,7 @@ async function supportFetch(apiUrl, token, path, what, init = {}) {
|
|
|
15847
16102
|
const res = await fetch(`${apiUrl}${path}`, {
|
|
15848
16103
|
method: init.method ?? "GET",
|
|
15849
16104
|
headers: {
|
|
15850
|
-
|
|
16105
|
+
...authHeaders(token),
|
|
15851
16106
|
...init.body !== void 0 ? { "content-type": "application/json" } : {}
|
|
15852
16107
|
},
|
|
15853
16108
|
...init.body !== void 0 ? { body: JSON.stringify(init.body) } : {}
|
|
@@ -15897,181 +16152,6 @@ async function replySupportTicket(apiUrl, token, ref, text) {
|
|
|
15897
16152
|
return body;
|
|
15898
16153
|
}
|
|
15899
16154
|
|
|
15900
|
-
// src/config.ts
|
|
15901
|
-
import {
|
|
15902
|
-
appendFileSync,
|
|
15903
|
-
chmodSync,
|
|
15904
|
-
existsSync,
|
|
15905
|
-
mkdirSync,
|
|
15906
|
-
readFileSync,
|
|
15907
|
-
rmSync,
|
|
15908
|
-
writeFileSync
|
|
15909
|
-
} from "node:fs";
|
|
15910
|
-
import { homedir } from "node:os";
|
|
15911
|
-
import { dirname, join } from "node:path";
|
|
15912
|
-
var DEFAULT_API_URL = "http://localhost:3000";
|
|
15913
|
-
function configPath() {
|
|
15914
|
-
const dir = process.env.KRYD_CONFIG_DIR ?? join(homedir(), ".kryd");
|
|
15915
|
-
return join(dir, "config.json");
|
|
15916
|
-
}
|
|
15917
|
-
function loadConfig() {
|
|
15918
|
-
let raw;
|
|
15919
|
-
try {
|
|
15920
|
-
raw = readFileSync(configPath(), "utf8");
|
|
15921
|
-
} catch {
|
|
15922
|
-
return {};
|
|
15923
|
-
}
|
|
15924
|
-
try {
|
|
15925
|
-
return JSON.parse(raw);
|
|
15926
|
-
} catch {
|
|
15927
|
-
process.stderr.write(
|
|
15928
|
-
`warning: ${configPath()} is not valid JSON \u2014 ignoring it.
|
|
15929
|
-
`
|
|
15930
|
-
);
|
|
15931
|
-
return {};
|
|
15932
|
-
}
|
|
15933
|
-
}
|
|
15934
|
-
function writeConfig(config2) {
|
|
15935
|
-
const path = configPath();
|
|
15936
|
-
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
15937
|
-
writeFileSync(path, JSON.stringify(config2, null, 2) + "\n", { mode: 384 });
|
|
15938
|
-
chmodSync(path, 384);
|
|
15939
|
-
}
|
|
15940
|
-
function saveToken(token, apiUrl) {
|
|
15941
|
-
const config2 = loadConfig();
|
|
15942
|
-
config2.token = token;
|
|
15943
|
-
if (apiUrl) config2.apiUrl = apiUrl;
|
|
15944
|
-
writeConfig(config2);
|
|
15945
|
-
}
|
|
15946
|
-
function clearToken() {
|
|
15947
|
-
const config2 = loadConfig();
|
|
15948
|
-
delete config2.token;
|
|
15949
|
-
writeConfig(config2);
|
|
15950
|
-
}
|
|
15951
|
-
var lastApiUrl = null;
|
|
15952
|
-
function resolveApiUrl(flag) {
|
|
15953
|
-
lastApiUrl = flag ?? process.env.KRYD_API_URL ?? loadConfig().apiUrl ?? DEFAULT_API_URL;
|
|
15954
|
-
return lastApiUrl;
|
|
15955
|
-
}
|
|
15956
|
-
function currentApiUrl() {
|
|
15957
|
-
return lastApiUrl ?? resolveApiUrl();
|
|
15958
|
-
}
|
|
15959
|
-
var DEFAULT_DASHBOARD_URL = "https://app.kryd.eu";
|
|
15960
|
-
function resolveDashboardUrl(flag) {
|
|
15961
|
-
return flag ?? process.env.KRYD_DASHBOARD_URL ?? DEFAULT_DASHBOARD_URL;
|
|
15962
|
-
}
|
|
15963
|
-
var PROJECT_LINK_DIR = ".kryd";
|
|
15964
|
-
var PROJECT_LINK_FILE = "project.json";
|
|
15965
|
-
function findProjectLinkDir(cwd = process.cwd()) {
|
|
15966
|
-
let dir = cwd;
|
|
15967
|
-
for (; ; ) {
|
|
15968
|
-
if (existsSync(join(dir, PROJECT_LINK_DIR, PROJECT_LINK_FILE))) return dir;
|
|
15969
|
-
if (existsSync(join(dir, ".git"))) return null;
|
|
15970
|
-
const parent = dirname(dir);
|
|
15971
|
-
if (parent === dir) return null;
|
|
15972
|
-
dir = parent;
|
|
15973
|
-
}
|
|
15974
|
-
}
|
|
15975
|
-
function loadProjectLink(cwd = process.cwd()) {
|
|
15976
|
-
const root = findProjectLinkDir(cwd);
|
|
15977
|
-
if (!root) return null;
|
|
15978
|
-
const path = join(root, PROJECT_LINK_DIR, PROJECT_LINK_FILE);
|
|
15979
|
-
let raw;
|
|
15980
|
-
try {
|
|
15981
|
-
raw = readFileSync(path, "utf8");
|
|
15982
|
-
} catch {
|
|
15983
|
-
return null;
|
|
15984
|
-
}
|
|
15985
|
-
let parsed;
|
|
15986
|
-
try {
|
|
15987
|
-
parsed = JSON.parse(raw);
|
|
15988
|
-
} catch {
|
|
15989
|
-
process.stderr.write(`warning: ${path} is not valid JSON \u2014 ignoring it.
|
|
15990
|
-
`);
|
|
15991
|
-
return null;
|
|
15992
|
-
}
|
|
15993
|
-
if (!parsed || typeof parsed.projectId !== "string" || parsed.projectId.length === 0) {
|
|
15994
|
-
process.stderr.write(`warning: ${path} has no projectId \u2014 ignoring it.
|
|
15995
|
-
`);
|
|
15996
|
-
return null;
|
|
15997
|
-
}
|
|
15998
|
-
return parsed;
|
|
15999
|
-
}
|
|
16000
|
-
function saveProjectLink(cwd, link) {
|
|
16001
|
-
const dir = join(cwd, PROJECT_LINK_DIR);
|
|
16002
|
-
mkdirSync(dir, { recursive: true });
|
|
16003
|
-
const path = join(dir, PROJECT_LINK_FILE);
|
|
16004
|
-
writeFileSync(path, JSON.stringify(link, null, 2) + "\n");
|
|
16005
|
-
return path;
|
|
16006
|
-
}
|
|
16007
|
-
function ensureIgnored(cwd, entry, aliases = []) {
|
|
16008
|
-
const path = join(cwd, ".gitignore");
|
|
16009
|
-
let current = "";
|
|
16010
|
-
try {
|
|
16011
|
-
current = readFileSync(path, "utf8");
|
|
16012
|
-
} catch {
|
|
16013
|
-
}
|
|
16014
|
-
const present = current.split("\n").map((l) => l.trim()).some((l) => l === entry || aliases.includes(l));
|
|
16015
|
-
if (present) return false;
|
|
16016
|
-
const prefix = current.length > 0 && !current.endsWith("\n") ? "\n" : "";
|
|
16017
|
-
appendFileSync(path, `${prefix}${entry}
|
|
16018
|
-
`);
|
|
16019
|
-
return true;
|
|
16020
|
-
}
|
|
16021
|
-
function ensureKrydIgnored(cwd) {
|
|
16022
|
-
ensureIgnored(cwd, `${PROJECT_LINK_DIR}/`, [PROJECT_LINK_DIR]);
|
|
16023
|
-
}
|
|
16024
|
-
function directoryIsLinked(cwd) {
|
|
16025
|
-
const path = join(cwd, PROJECT_LINK_DIR, PROJECT_LINK_FILE);
|
|
16026
|
-
if (!existsSync(path)) return null;
|
|
16027
|
-
return loadProjectLink(cwd);
|
|
16028
|
-
}
|
|
16029
|
-
function writeSecretFile(path, contents) {
|
|
16030
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
16031
|
-
writeFileSync(path, contents, { mode: 384 });
|
|
16032
|
-
chmodSync(path, 384);
|
|
16033
|
-
}
|
|
16034
|
-
function clearProjectLinkIfMatches(projectId, cwd = process.cwd()) {
|
|
16035
|
-
const root = findProjectLinkDir(cwd);
|
|
16036
|
-
if (!root) return false;
|
|
16037
|
-
const link = loadProjectLink(cwd);
|
|
16038
|
-
if (link?.projectId !== projectId) return false;
|
|
16039
|
-
try {
|
|
16040
|
-
rmSync(join(root, PROJECT_LINK_DIR, PROJECT_LINK_FILE));
|
|
16041
|
-
return true;
|
|
16042
|
-
} catch {
|
|
16043
|
-
return false;
|
|
16044
|
-
}
|
|
16045
|
-
}
|
|
16046
|
-
function resolvedLink() {
|
|
16047
|
-
return lastLinkResolution;
|
|
16048
|
-
}
|
|
16049
|
-
var lastLinkResolution = null;
|
|
16050
|
-
function resolveProjectId(explicit, cwd = process.cwd()) {
|
|
16051
|
-
lastLinkResolution = null;
|
|
16052
|
-
if (explicit) return explicit;
|
|
16053
|
-
const root = findProjectLinkDir(cwd);
|
|
16054
|
-
if (!root) return null;
|
|
16055
|
-
const link = loadProjectLink(cwd);
|
|
16056
|
-
if (!link) return null;
|
|
16057
|
-
lastLinkResolution = { root, link };
|
|
16058
|
-
return link.projectId;
|
|
16059
|
-
}
|
|
16060
|
-
function rememberLinkAccount(root, account) {
|
|
16061
|
-
if (!account.accountId) return false;
|
|
16062
|
-
const link = directoryIsLinked(root);
|
|
16063
|
-
if (!link) return false;
|
|
16064
|
-
const slug = account.accountSlug ?? link.accountSlug;
|
|
16065
|
-
if (link.accountId === account.accountId && link.accountSlug === slug) return false;
|
|
16066
|
-
saveProjectLink(root, {
|
|
16067
|
-
...link,
|
|
16068
|
-
accountId: account.accountId,
|
|
16069
|
-
// `exactOptionalPropertyTypes`: an absent key and an explicit `undefined` are different types.
|
|
16070
|
-
...slug ? { accountSlug: slug } : {}
|
|
16071
|
-
});
|
|
16072
|
-
return true;
|
|
16073
|
-
}
|
|
16074
|
-
|
|
16075
16155
|
// src/support.ts
|
|
16076
16156
|
var SUPPORT_TYPES = TICKET_TYPES;
|
|
16077
16157
|
var MESSAGE_MAX = 2e4;
|
|
@@ -16156,7 +16236,7 @@ function reportSupportError(err) {
|
|
|
16156
16236
|
process.exitCode = 1;
|
|
16157
16237
|
}
|
|
16158
16238
|
function requireToken() {
|
|
16159
|
-
const token =
|
|
16239
|
+
const token = resolveToken();
|
|
16160
16240
|
if (!token) usage("Not logged in \u2014 run `kryd login` first.");
|
|
16161
16241
|
return token ?? null;
|
|
16162
16242
|
}
|
|
@@ -16308,6 +16388,272 @@ async function runSupportReply(raw, opts) {
|
|
|
16308
16388
|
}
|
|
16309
16389
|
}
|
|
16310
16390
|
|
|
16391
|
+
// src/account.ts
|
|
16392
|
+
function fail(message) {
|
|
16393
|
+
process.stderr.write(`${message}
|
|
16394
|
+
`);
|
|
16395
|
+
process.exitCode = 1;
|
|
16396
|
+
}
|
|
16397
|
+
function membershipLabel(m) {
|
|
16398
|
+
return m.slug ?? m.name ?? m.accountId;
|
|
16399
|
+
}
|
|
16400
|
+
function accountHint(err) {
|
|
16401
|
+
if (!(err instanceof ApiError) || !err.envelope) return "";
|
|
16402
|
+
const { code, details } = err.envelope.error;
|
|
16403
|
+
if (code === ERROR_CODES.accountRequired) {
|
|
16404
|
+
return "You belong to several accounts. Choose one with `kryd account use <slug>` (`kryd account list` shows them).\n";
|
|
16405
|
+
}
|
|
16406
|
+
if (code === ERROR_CODES.permissionRequired) {
|
|
16407
|
+
const permission = typeof details?.permission === "string" ? ` \`${details.permission}\`` : " this permission";
|
|
16408
|
+
const slug = loadConfig().accountSlug;
|
|
16409
|
+
return `Your role in ${slug ? `account ${slug}` : "this account"} lacks${permission}. Ask the account's owner, or switch with \`kryd account use <slug>\`.
|
|
16410
|
+
`;
|
|
16411
|
+
}
|
|
16412
|
+
const stored = loadConfig();
|
|
16413
|
+
if (code === ERROR_CODES.forbidden && stored.account && /not a member/i.test(err.envelope.error.message)) {
|
|
16414
|
+
const name = stored.accountSlug ?? stored.account;
|
|
16415
|
+
return `You are no longer a member of account ${name}, which this machine has chosen. See your accounts with \`kryd account list\`, then pick one with \`kryd account use <slug>\`.
|
|
16416
|
+
`;
|
|
16417
|
+
}
|
|
16418
|
+
return "";
|
|
16419
|
+
}
|
|
16420
|
+
async function runAccountList(opts = {}) {
|
|
16421
|
+
const token = resolveToken();
|
|
16422
|
+
if (!token) return fail("Not logged in \u2014 run `kryd login` first.");
|
|
16423
|
+
let memberships;
|
|
16424
|
+
let current;
|
|
16425
|
+
try {
|
|
16426
|
+
const me = await whoami(resolveApiUrl(opts.apiUrl), token);
|
|
16427
|
+
memberships = me.memberships ?? [];
|
|
16428
|
+
current = me.accountId;
|
|
16429
|
+
} catch (err) {
|
|
16430
|
+
return fail(err instanceof ApiError && err.envelope ? err.envelope.error.message : String(err));
|
|
16431
|
+
}
|
|
16432
|
+
if (opts.json) {
|
|
16433
|
+
process.stdout.write(`${JSON.stringify(memberships.map((m) => ({ ...m, current: m.accountId === current })), null, 2)}
|
|
16434
|
+
`);
|
|
16435
|
+
return;
|
|
16436
|
+
}
|
|
16437
|
+
if (memberships.length === 0) {
|
|
16438
|
+
process.stdout.write("You do not belong to any account.\n");
|
|
16439
|
+
return;
|
|
16440
|
+
}
|
|
16441
|
+
const rows = [
|
|
16442
|
+
["", "SLUG", "NAME", "ROLE", "ID"],
|
|
16443
|
+
...memberships.map((m) => [m.accountId === current ? "*" : "", m.slug ?? "\u2014", m.name, m.role, m.accountId])
|
|
16444
|
+
];
|
|
16445
|
+
const widths = rows[0].map((_, i) => Math.max(...rows.map((r) => r[i].length)));
|
|
16446
|
+
for (const r of rows) {
|
|
16447
|
+
process.stdout.write(`${r.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd()}
|
|
16448
|
+
`);
|
|
16449
|
+
}
|
|
16450
|
+
}
|
|
16451
|
+
async function runAccountUse(target, opts = {}) {
|
|
16452
|
+
const token = resolveToken();
|
|
16453
|
+
if (!token) return fail("Not logged in \u2014 run `kryd login` first.");
|
|
16454
|
+
if (token.startsWith("kryd_")) {
|
|
16455
|
+
return fail("A deploy token is bound to one account and cannot switch. Unset KRYD_TOKEN to use your login.");
|
|
16456
|
+
}
|
|
16457
|
+
let memberships;
|
|
16458
|
+
try {
|
|
16459
|
+
memberships = (await whoami(resolveApiUrl(opts.apiUrl), token)).memberships ?? [];
|
|
16460
|
+
} catch (err) {
|
|
16461
|
+
return fail(err instanceof ApiError && err.envelope ? err.envelope.error.message : String(err));
|
|
16462
|
+
}
|
|
16463
|
+
const wanted = target.trim();
|
|
16464
|
+
const match = memberships.find((m) => m.slug === wanted || m.accountId === wanted);
|
|
16465
|
+
if (!match) {
|
|
16466
|
+
const known = memberships.map(membershipLabel).join(", ") || "none";
|
|
16467
|
+
return fail(`You are not a member of an account "${wanted}". Your accounts: ${known}.`);
|
|
16468
|
+
}
|
|
16469
|
+
saveAccount({ accountId: match.accountId, slug: match.slug });
|
|
16470
|
+
process.stdout.write(`Now using account ${membershipLabel(match)} (${match.role}).
|
|
16471
|
+
`);
|
|
16472
|
+
}
|
|
16473
|
+
async function settleAccountAfterLogin(apiUrl, token) {
|
|
16474
|
+
if (token.startsWith("kryd_")) return;
|
|
16475
|
+
let memberships;
|
|
16476
|
+
try {
|
|
16477
|
+
memberships = (await whoami(apiUrl, token)).memberships ?? [];
|
|
16478
|
+
} catch {
|
|
16479
|
+
return;
|
|
16480
|
+
}
|
|
16481
|
+
const stored = loadConfig().account;
|
|
16482
|
+
if (stored && memberships.some((m) => m.accountId === stored)) return;
|
|
16483
|
+
if (memberships.length <= 1) {
|
|
16484
|
+
if (stored) clearAccount();
|
|
16485
|
+
return;
|
|
16486
|
+
}
|
|
16487
|
+
const pick2 = memberships.find((m) => m.role === "owner") ?? memberships[0];
|
|
16488
|
+
saveAccount({ accountId: pick2.accountId, slug: pick2.slug });
|
|
16489
|
+
process.stdout.write(
|
|
16490
|
+
`You belong to ${memberships.length} accounts; using ${membershipLabel(pick2)}. Switch with \`kryd account use <slug>\`.
|
|
16491
|
+
`
|
|
16492
|
+
);
|
|
16493
|
+
}
|
|
16494
|
+
|
|
16495
|
+
// src/tokens.ts
|
|
16496
|
+
var TOKEN_EXPIRY_CHOICES = ["30", "90", "365", "never"];
|
|
16497
|
+
var DEFAULT_TOKEN_EXPIRY = "90";
|
|
16498
|
+
var MAX_TOKEN_NAME = 32;
|
|
16499
|
+
function expiresInSeconds(choice) {
|
|
16500
|
+
if (choice === "never") return null;
|
|
16501
|
+
if (!TOKEN_EXPIRY_CHOICES.includes(choice)) return void 0;
|
|
16502
|
+
return Number(choice) * 24 * 60 * 60;
|
|
16503
|
+
}
|
|
16504
|
+
async function tokenFetch(apiUrl, token, path, what, body) {
|
|
16505
|
+
const res = await fetch(`${apiUrl}/api/auth/api-key/${path}`, {
|
|
16506
|
+
method: body === void 0 ? "GET" : "POST",
|
|
16507
|
+
headers: {
|
|
16508
|
+
...authHeaders(token),
|
|
16509
|
+
...body !== void 0 ? { "content-type": "application/json" } : {}
|
|
16510
|
+
},
|
|
16511
|
+
...body !== void 0 ? { body: JSON.stringify(body) } : {}
|
|
16512
|
+
});
|
|
16513
|
+
if (!res.ok) {
|
|
16514
|
+
const parsed = await res.json().catch(() => void 0);
|
|
16515
|
+
const message = typeof parsed?.message === "string" ? parsed.message : `${what} failed (${res.status})`;
|
|
16516
|
+
const code = typeof parsed?.code === "string" ? parsed.code : `http.${res.status}`;
|
|
16517
|
+
throw new ApiError(`${what} failed (${res.status})`, { error: { code, message } }, res.status);
|
|
16518
|
+
}
|
|
16519
|
+
try {
|
|
16520
|
+
return await res.json();
|
|
16521
|
+
} catch {
|
|
16522
|
+
throw new ApiError(`The API returned an unexpected answer to "${what.toLowerCase()}".`);
|
|
16523
|
+
}
|
|
16524
|
+
}
|
|
16525
|
+
var isToken = (v) => {
|
|
16526
|
+
const t = v;
|
|
16527
|
+
return typeof t?.id === "string" && typeof t?.createdAt === "string";
|
|
16528
|
+
};
|
|
16529
|
+
async function createDeployToken(apiUrl, token, input) {
|
|
16530
|
+
const body = await tokenFetch(apiUrl, token, "create", "Creating the token", input);
|
|
16531
|
+
if (!isToken(body) || typeof body.key !== "string" || body.key.length === 0) {
|
|
16532
|
+
throw new ApiError("The API returned an unexpected answer to creating the token.");
|
|
16533
|
+
}
|
|
16534
|
+
return body;
|
|
16535
|
+
}
|
|
16536
|
+
async function listDeployTokens(apiUrl, token) {
|
|
16537
|
+
const body = await tokenFetch(apiUrl, token, "list", "Listing the tokens");
|
|
16538
|
+
if (!Array.isArray(body?.apiKeys) || !body.apiKeys.every(isToken)) {
|
|
16539
|
+
throw new ApiError("The API returned an unexpected answer to listing the tokens.");
|
|
16540
|
+
}
|
|
16541
|
+
return body.apiKeys;
|
|
16542
|
+
}
|
|
16543
|
+
async function revokeDeployToken(apiUrl, token, id) {
|
|
16544
|
+
await tokenFetch(apiUrl, token, "delete", "Revoking the token", { keyId: id });
|
|
16545
|
+
}
|
|
16546
|
+
function fail2(message) {
|
|
16547
|
+
process.stderr.write(`${message}
|
|
16548
|
+
`);
|
|
16549
|
+
process.exitCode = 1;
|
|
16550
|
+
}
|
|
16551
|
+
function reportTokenError(err) {
|
|
16552
|
+
if (err instanceof ApiError && err.status === 401) {
|
|
16553
|
+
fail2("Not logged in, or the session expired \u2014 run `kryd login`.");
|
|
16554
|
+
} else if (err instanceof ApiError && err.status === 403 && resolveToken()?.startsWith("kryd_")) {
|
|
16555
|
+
fail2(
|
|
16556
|
+
"A deploy token cannot manage tokens. Sign in with `kryd login` (and unset KRYD_TOKEN for this command)."
|
|
16557
|
+
);
|
|
16558
|
+
} else if (err instanceof ApiError && err.status === 404) {
|
|
16559
|
+
fail2("No such token on your account. `kryd token list` shows their ids.");
|
|
16560
|
+
} else if (err instanceof ApiError && err.envelope) {
|
|
16561
|
+
fail2(err.envelope.error.message);
|
|
16562
|
+
process.stderr.write(accountHint(err));
|
|
16563
|
+
} else {
|
|
16564
|
+
fail2(err instanceof Error ? err.message : String(err));
|
|
16565
|
+
}
|
|
16566
|
+
}
|
|
16567
|
+
function requireToken2() {
|
|
16568
|
+
const token = resolveToken();
|
|
16569
|
+
if (!token) fail2("Not logged in \u2014 run `kryd login` first.");
|
|
16570
|
+
return token ?? null;
|
|
16571
|
+
}
|
|
16572
|
+
var day = (iso) => iso ? iso.slice(0, 10) : "\u2014";
|
|
16573
|
+
async function runTokenCreate(name, opts) {
|
|
16574
|
+
const trimmed = name.trim();
|
|
16575
|
+
if (trimmed.length === 0 || trimmed.length > MAX_TOKEN_NAME) {
|
|
16576
|
+
return fail2(`A token name is 1 to ${MAX_TOKEN_NAME} characters.`);
|
|
16577
|
+
}
|
|
16578
|
+
const choice = opts.expires ?? DEFAULT_TOKEN_EXPIRY;
|
|
16579
|
+
const expiresIn = expiresInSeconds(choice);
|
|
16580
|
+
if (expiresIn === void 0) {
|
|
16581
|
+
return fail2(`--expires is one of ${TOKEN_EXPIRY_CHOICES.join(", ")} (days, or never).`);
|
|
16582
|
+
}
|
|
16583
|
+
const token = requireToken2();
|
|
16584
|
+
if (!token) return;
|
|
16585
|
+
try {
|
|
16586
|
+
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
16587
|
+
const created = await createDeployToken(apiUrl, token, { name: trimmed, expiresIn });
|
|
16588
|
+
process.stdout.write(`${created.key}
|
|
16589
|
+
`);
|
|
16590
|
+
const account = await boundAccountLabel(apiUrl, token, created);
|
|
16591
|
+
process.stderr.write(
|
|
16592
|
+
`Created deploy token "${trimmed}" (${created.id})${account ? ` for account ${account}` : ""}, ${created.expiresAt ? `valid until ${day(created.expiresAt)}` : "without expiry"}.
|
|
16593
|
+
This token will not be shown again. Store it now, e.g. as the CI secret KRYD_TOKEN.
|
|
16594
|
+
`
|
|
16595
|
+
);
|
|
16596
|
+
} catch (err) {
|
|
16597
|
+
reportTokenError(err);
|
|
16598
|
+
}
|
|
16599
|
+
}
|
|
16600
|
+
async function boundAccountLabel(apiUrl, token, created) {
|
|
16601
|
+
const id = typeof created.metadata?.accountId === "string" ? created.metadata.accountId : null;
|
|
16602
|
+
if (!id) return null;
|
|
16603
|
+
try {
|
|
16604
|
+
const me = await whoami(apiUrl, token);
|
|
16605
|
+
const m = me.memberships?.find((x) => x.accountId === id);
|
|
16606
|
+
return m?.slug ?? id;
|
|
16607
|
+
} catch {
|
|
16608
|
+
return id;
|
|
16609
|
+
}
|
|
16610
|
+
}
|
|
16611
|
+
async function runTokenList(opts) {
|
|
16612
|
+
const token = requireToken2();
|
|
16613
|
+
if (!token) return;
|
|
16614
|
+
try {
|
|
16615
|
+
const list = await listDeployTokens(resolveApiUrl(opts.apiUrl), token);
|
|
16616
|
+
if (opts.json) {
|
|
16617
|
+
process.stdout.write(`${JSON.stringify(list, null, 2)}
|
|
16618
|
+
`);
|
|
16619
|
+
return;
|
|
16620
|
+
}
|
|
16621
|
+
if (list.length === 0) {
|
|
16622
|
+
process.stdout.write("No deploy tokens. Create one with `kryd token create <name>`.\n");
|
|
16623
|
+
return;
|
|
16624
|
+
}
|
|
16625
|
+
const rows = [
|
|
16626
|
+
["ID", "NAME", "STARTS WITH", "CREATED", "LAST USED", "EXPIRES"],
|
|
16627
|
+
...list.map((t) => [
|
|
16628
|
+
t.id,
|
|
16629
|
+
t.name ?? "\u2014",
|
|
16630
|
+
t.start ? `${t.start}\u2026` : "\u2014",
|
|
16631
|
+
day(t.createdAt),
|
|
16632
|
+
day(t.lastRequest),
|
|
16633
|
+
t.expiresAt ? day(t.expiresAt) : "never"
|
|
16634
|
+
])
|
|
16635
|
+
];
|
|
16636
|
+
const widths = rows[0].map((_, i) => Math.max(...rows.map((r) => r[i].length)));
|
|
16637
|
+
for (const r of rows) {
|
|
16638
|
+
process.stdout.write(`${r.map((c, i) => c.padEnd(widths[i])).join(" ").trimEnd()}
|
|
16639
|
+
`);
|
|
16640
|
+
}
|
|
16641
|
+
} catch (err) {
|
|
16642
|
+
reportTokenError(err);
|
|
16643
|
+
}
|
|
16644
|
+
}
|
|
16645
|
+
async function runTokenRevoke(id, opts) {
|
|
16646
|
+
const token = requireToken2();
|
|
16647
|
+
if (!token) return;
|
|
16648
|
+
try {
|
|
16649
|
+
await revokeDeployToken(resolveApiUrl(opts.apiUrl), token, id);
|
|
16650
|
+
process.stdout.write(`Revoked ${id}. Anything still using it now gets 401.
|
|
16651
|
+
`);
|
|
16652
|
+
} catch (err) {
|
|
16653
|
+
reportTokenError(err);
|
|
16654
|
+
}
|
|
16655
|
+
}
|
|
16656
|
+
|
|
16311
16657
|
// src/browser-login.ts
|
|
16312
16658
|
import { spawn } from "node:child_process";
|
|
16313
16659
|
import { randomUUID } from "node:crypto";
|
|
@@ -16396,7 +16742,7 @@ async function runGitCredential(operation, opts = {}) {
|
|
|
16396
16742
|
const input = parseCredentialInput(opts.stdin ?? "");
|
|
16397
16743
|
if (input.protocol && input.protocol !== "https") return;
|
|
16398
16744
|
const cwd = opts.cwd ?? process.cwd();
|
|
16399
|
-
const token =
|
|
16745
|
+
const token = resolveToken();
|
|
16400
16746
|
if (!token) return;
|
|
16401
16747
|
const projectId = resolveProjectId(void 0, cwd);
|
|
16402
16748
|
if (!projectId) return;
|
|
@@ -17588,6 +17934,7 @@ async function reportError(err) {
|
|
|
17588
17934
|
process.stderr.write(`${err instanceof Error ? err.message : String(err)}
|
|
17589
17935
|
`);
|
|
17590
17936
|
}
|
|
17937
|
+
process.stderr.write(accountHint(err));
|
|
17591
17938
|
process.exitCode = 1;
|
|
17592
17939
|
if (err instanceof ApiError) {
|
|
17593
17940
|
process.stderr.write(await explainMissingProject(err));
|
|
@@ -17607,7 +17954,7 @@ async function explainMissingProject(err) {
|
|
|
17607
17954
|
if (err.status !== 404) return "";
|
|
17608
17955
|
const resolution = resolvedLink();
|
|
17609
17956
|
if (!resolution) return "";
|
|
17610
|
-
const token =
|
|
17957
|
+
const token = resolveToken();
|
|
17611
17958
|
if (!token) return "";
|
|
17612
17959
|
if (await projectIsVisible(token, resolution.link.projectId) !== false) return "";
|
|
17613
17960
|
let session;
|
|
@@ -17635,7 +17982,7 @@ async function backfillLinkAccount() {
|
|
|
17635
17982
|
const resolution = resolvedLink();
|
|
17636
17983
|
if (!resolution) return;
|
|
17637
17984
|
if (resolution.link.accountId && resolution.link.accountSlug) return;
|
|
17638
|
-
const token =
|
|
17985
|
+
const token = resolveToken();
|
|
17639
17986
|
if (!token) return;
|
|
17640
17987
|
if (await projectIsVisible(token, resolution.link.projectId) !== true) return;
|
|
17641
17988
|
try {
|
|
@@ -17660,33 +18007,51 @@ async function runLogin(opts) {
|
|
|
17660
18007
|
if (pastedToken) {
|
|
17661
18008
|
saveToken(pastedToken, apiUrl);
|
|
17662
18009
|
process.stdout.write("Token saved.\n");
|
|
18010
|
+
warnIfEnvTokenOverrides();
|
|
17663
18011
|
return;
|
|
17664
18012
|
}
|
|
17665
18013
|
const { token, apiUrl: learnedApiUrl } = await browserLogin(resolveDashboardUrl(opts.dashboardUrl));
|
|
17666
18014
|
saveToken(token, learnedApiUrl ?? apiUrl);
|
|
17667
18015
|
process.stdout.write("Logged in.\n");
|
|
18016
|
+
await settleAccountAfterLogin(learnedApiUrl ?? apiUrl, token);
|
|
18017
|
+
warnIfEnvTokenOverrides();
|
|
17668
18018
|
}
|
|
17669
18019
|
async function runWhoami(opts = {}) {
|
|
17670
18020
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
17671
|
-
const token =
|
|
18021
|
+
const token = resolveToken() ?? "";
|
|
17672
18022
|
try {
|
|
17673
18023
|
const me = await whoami(apiUrl, token);
|
|
17674
|
-
|
|
17675
|
-
|
|
18024
|
+
const current = me.memberships?.find((m) => m.accountId === me.accountId);
|
|
18025
|
+
const label = current?.slug ?? me.accountSlug ?? me.accountId ?? "?";
|
|
18026
|
+
process.stdout.write(`${me.email} (account ${label}, role ${current?.role ?? me.role ?? "?"})
|
|
18027
|
+
`);
|
|
18028
|
+
const others = (me.memberships ?? []).filter((m) => m.accountId !== me.accountId);
|
|
18029
|
+
if (others.length > 0) {
|
|
18030
|
+
process.stdout.write(
|
|
18031
|
+
`Also in: ${others.map((m) => `${m.slug ?? m.accountId} (${m.role})`).join(", ")} \u2014 switch with \`kryd account use <slug>\`.
|
|
17676
18032
|
`
|
|
17677
|
-
|
|
18033
|
+
);
|
|
18034
|
+
}
|
|
17678
18035
|
} catch (err) {
|
|
17679
18036
|
await reportError(err);
|
|
17680
18037
|
}
|
|
17681
18038
|
}
|
|
18039
|
+
function warnIfEnvTokenOverrides() {
|
|
18040
|
+
if (process.env.KRYD_TOKEN?.trim()) {
|
|
18041
|
+
process.stderr.write(
|
|
18042
|
+
"KRYD_TOKEN is set in this shell and overrides the stored login; commands keep using it until you unset it.\n"
|
|
18043
|
+
);
|
|
18044
|
+
}
|
|
18045
|
+
}
|
|
17682
18046
|
function runLogout() {
|
|
17683
18047
|
clearToken();
|
|
17684
18048
|
process.stdout.write("Logged out.\n");
|
|
18049
|
+
warnIfEnvTokenOverrides();
|
|
17685
18050
|
}
|
|
17686
18051
|
async function runInit(opts) {
|
|
17687
18052
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
17688
18053
|
const cwd = opts.cwd ?? process.cwd();
|
|
17689
|
-
const token =
|
|
18054
|
+
const token = resolveToken();
|
|
17690
18055
|
if (!token) {
|
|
17691
18056
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
17692
18057
|
process.exitCode = 1;
|
|
@@ -17909,7 +18274,7 @@ async function replayStoredLog(apiUrl, token, deploymentId) {
|
|
|
17909
18274
|
}
|
|
17910
18275
|
async function runLogs(opts) {
|
|
17911
18276
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
17912
|
-
const token =
|
|
18277
|
+
const token = resolveToken();
|
|
17913
18278
|
if (!token) {
|
|
17914
18279
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
17915
18280
|
process.exitCode = 1;
|
|
@@ -17961,7 +18326,7 @@ async function runLogs(opts) {
|
|
|
17961
18326
|
}
|
|
17962
18327
|
async function runRuntimeLogs(opts) {
|
|
17963
18328
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
17964
|
-
const token =
|
|
18329
|
+
const token = resolveToken();
|
|
17965
18330
|
if (!token) {
|
|
17966
18331
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
17967
18332
|
process.exitCode = 1;
|
|
@@ -18018,7 +18383,7 @@ async function runRuntimeLogs(opts) {
|
|
|
18018
18383
|
}
|
|
18019
18384
|
async function runDeploy(opts) {
|
|
18020
18385
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18021
|
-
const token =
|
|
18386
|
+
const token = resolveToken();
|
|
18022
18387
|
if (!token) {
|
|
18023
18388
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18024
18389
|
process.exitCode = 1;
|
|
@@ -18054,7 +18419,7 @@ function renderFor(logs) {
|
|
|
18054
18419
|
var LOGS_FLAG_HELP = "stream the full build log line by line instead of the progress display";
|
|
18055
18420
|
async function runPush(opts) {
|
|
18056
18421
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18057
|
-
const token =
|
|
18422
|
+
const token = resolveToken();
|
|
18058
18423
|
if (!token) {
|
|
18059
18424
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18060
18425
|
process.exitCode = 1;
|
|
@@ -18206,7 +18571,7 @@ async function followApps(apiUrl, token, deployments, render, estimateFrom) {
|
|
|
18206
18571
|
}
|
|
18207
18572
|
async function runRollback(opts) {
|
|
18208
18573
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18209
|
-
const token =
|
|
18574
|
+
const token = resolveToken();
|
|
18210
18575
|
if (!token) {
|
|
18211
18576
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18212
18577
|
process.exitCode = 1;
|
|
@@ -18237,7 +18602,7 @@ async function runRollback(opts) {
|
|
|
18237
18602
|
}
|
|
18238
18603
|
async function runRedeploy(opts) {
|
|
18239
18604
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18240
|
-
const token =
|
|
18605
|
+
const token = resolveToken();
|
|
18241
18606
|
if (!token) {
|
|
18242
18607
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18243
18608
|
process.exitCode = 1;
|
|
@@ -18267,7 +18632,7 @@ ${note}
|
|
|
18267
18632
|
}
|
|
18268
18633
|
async function runDbAdd(opts) {
|
|
18269
18634
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18270
|
-
const token =
|
|
18635
|
+
const token = resolveToken();
|
|
18271
18636
|
if (!token) {
|
|
18272
18637
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18273
18638
|
process.exitCode = 1;
|
|
@@ -18348,7 +18713,7 @@ function reportSettleResult(result, opts) {
|
|
|
18348
18713
|
}
|
|
18349
18714
|
async function runStorageAdd(opts) {
|
|
18350
18715
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18351
|
-
const token =
|
|
18716
|
+
const token = resolveToken();
|
|
18352
18717
|
if (!token) {
|
|
18353
18718
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18354
18719
|
process.exitCode = 1;
|
|
@@ -18442,7 +18807,7 @@ ${listed}Re-run \`kryd project rm <id>\` with the one you mean.
|
|
|
18442
18807
|
}
|
|
18443
18808
|
async function runProjectRemove(opts) {
|
|
18444
18809
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18445
|
-
const token =
|
|
18810
|
+
const token = resolveToken();
|
|
18446
18811
|
if (!token) {
|
|
18447
18812
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18448
18813
|
process.exitCode = 1;
|
|
@@ -18557,7 +18922,7 @@ async function describeProjectResources(apiUrl, token, projectId) {
|
|
|
18557
18922
|
}
|
|
18558
18923
|
async function runDbRemove(opts) {
|
|
18559
18924
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18560
|
-
const token =
|
|
18925
|
+
const token = resolveToken();
|
|
18561
18926
|
if (!token) {
|
|
18562
18927
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18563
18928
|
process.exitCode = 1;
|
|
@@ -18596,7 +18961,7 @@ async function runDbRemove(opts) {
|
|
|
18596
18961
|
}
|
|
18597
18962
|
async function runAiAdd(opts) {
|
|
18598
18963
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18599
|
-
const token =
|
|
18964
|
+
const token = resolveToken();
|
|
18600
18965
|
if (!token) {
|
|
18601
18966
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18602
18967
|
process.exitCode = 1;
|
|
@@ -18621,7 +18986,7 @@ async function runAiAdd(opts) {
|
|
|
18621
18986
|
}
|
|
18622
18987
|
async function runAiRemove(opts) {
|
|
18623
18988
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18624
|
-
const token =
|
|
18989
|
+
const token = resolveToken();
|
|
18625
18990
|
if (!token) {
|
|
18626
18991
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18627
18992
|
process.exitCode = 1;
|
|
@@ -18688,7 +19053,7 @@ Remove it before attaching another.
|
|
|
18688
19053
|
}
|
|
18689
19054
|
async function runDbStatus(opts) {
|
|
18690
19055
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18691
|
-
const token =
|
|
19056
|
+
const token = resolveToken();
|
|
18692
19057
|
if (!token) {
|
|
18693
19058
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18694
19059
|
process.exitCode = 1;
|
|
@@ -18708,7 +19073,7 @@ async function runDbStatus(opts) {
|
|
|
18708
19073
|
}
|
|
18709
19074
|
async function runStorageStatus(opts) {
|
|
18710
19075
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18711
|
-
const token =
|
|
19076
|
+
const token = resolveToken();
|
|
18712
19077
|
if (!token) {
|
|
18713
19078
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18714
19079
|
process.exitCode = 1;
|
|
@@ -18728,7 +19093,7 @@ async function runStorageStatus(opts) {
|
|
|
18728
19093
|
}
|
|
18729
19094
|
async function runAiStatus(opts) {
|
|
18730
19095
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18731
|
-
const token =
|
|
19096
|
+
const token = resolveToken();
|
|
18732
19097
|
if (!token) {
|
|
18733
19098
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18734
19099
|
process.exitCode = 1;
|
|
@@ -18764,7 +19129,7 @@ function daysUntil(iso) {
|
|
|
18764
19129
|
var TOKEN_EXPIRY_WARNING_DAYS = 30;
|
|
18765
19130
|
async function runWorkflowAdd(opts) {
|
|
18766
19131
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18767
|
-
const token =
|
|
19132
|
+
const token = resolveToken();
|
|
18768
19133
|
if (!token) {
|
|
18769
19134
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18770
19135
|
process.exitCode = 1;
|
|
@@ -18797,7 +19162,7 @@ HATCHET_CLIENT_TOKEN will be injected on your next production deploy (kryd deplo
|
|
|
18797
19162
|
}
|
|
18798
19163
|
async function runWorkflowRemove(opts) {
|
|
18799
19164
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18800
|
-
const token =
|
|
19165
|
+
const token = resolveToken();
|
|
18801
19166
|
if (!token) {
|
|
18802
19167
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18803
19168
|
process.exitCode = 1;
|
|
@@ -18842,7 +19207,7 @@ Workflow history, crons and schedules stay until the project is deleted.
|
|
|
18842
19207
|
}
|
|
18843
19208
|
async function runWorkflowStatus(opts) {
|
|
18844
19209
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18845
|
-
const token =
|
|
19210
|
+
const token = resolveToken();
|
|
18846
19211
|
if (!token) {
|
|
18847
19212
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18848
19213
|
process.exitCode = 1;
|
|
@@ -18901,7 +19266,7 @@ function formatDomainRecords(records) {
|
|
|
18901
19266
|
}
|
|
18902
19267
|
function domainContext(opts, usage2) {
|
|
18903
19268
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
18904
|
-
const token =
|
|
19269
|
+
const token = resolveToken();
|
|
18905
19270
|
if (!token) {
|
|
18906
19271
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
18907
19272
|
process.exitCode = 1;
|
|
@@ -19308,7 +19673,7 @@ HATCHET_CLIENT_TOKEN is injected into your app and your worker on production dep
|
|
|
19308
19673
|
}
|
|
19309
19674
|
async function runStorageRemove(opts) {
|
|
19310
19675
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19311
|
-
const token =
|
|
19676
|
+
const token = resolveToken();
|
|
19312
19677
|
if (!token) {
|
|
19313
19678
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19314
19679
|
process.exitCode = 1;
|
|
@@ -19371,7 +19736,7 @@ function envAnnotation(environments) {
|
|
|
19371
19736
|
}
|
|
19372
19737
|
async function runProjectList(opts) {
|
|
19373
19738
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19374
|
-
const token =
|
|
19739
|
+
const token = resolveToken();
|
|
19375
19740
|
if (!token) {
|
|
19376
19741
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19377
19742
|
process.exitCode = 1;
|
|
@@ -19422,7 +19787,7 @@ ${rows}`;
|
|
|
19422
19787
|
}
|
|
19423
19788
|
async function runEnvList(opts) {
|
|
19424
19789
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19425
|
-
const token =
|
|
19790
|
+
const token = resolveToken();
|
|
19426
19791
|
if (!token) {
|
|
19427
19792
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19428
19793
|
process.exitCode = 1;
|
|
@@ -19504,7 +19869,7 @@ function toDotenvLine(key, value) {
|
|
|
19504
19869
|
}
|
|
19505
19870
|
async function runEnvPull(opts) {
|
|
19506
19871
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19507
|
-
const token =
|
|
19872
|
+
const token = resolveToken();
|
|
19508
19873
|
if (!token) {
|
|
19509
19874
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19510
19875
|
process.exitCode = 1;
|
|
@@ -19605,7 +19970,7 @@ async function runEnvSet(opts) {
|
|
|
19605
19970
|
const scope = opts.build ? "build" : "runtime";
|
|
19606
19971
|
const environment = opts.preview ? "preview" : "production";
|
|
19607
19972
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19608
|
-
const token =
|
|
19973
|
+
const token = resolveToken();
|
|
19609
19974
|
if (!token) {
|
|
19610
19975
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19611
19976
|
process.exitCode = 1;
|
|
@@ -19700,7 +20065,7 @@ async function runEnvRemove(opts) {
|
|
|
19700
20065
|
const scope = opts.build ? "build" : "runtime";
|
|
19701
20066
|
const environment = opts.preview ? "preview" : "production";
|
|
19702
20067
|
const apiUrl = resolveApiUrl(opts.apiUrl);
|
|
19703
|
-
const token =
|
|
20068
|
+
const token = resolveToken();
|
|
19704
20069
|
if (!token) {
|
|
19705
20070
|
process.stderr.write("Not logged in \u2014 run `kryd login` first.\n");
|
|
19706
20071
|
process.exitCode = 1;
|
|
@@ -19794,7 +20159,7 @@ Run \`kryd env list\` to see what is set. (If you meant the runtime variable of
|
|
|
19794
20159
|
await reportError(err);
|
|
19795
20160
|
}
|
|
19796
20161
|
}
|
|
19797
|
-
var CLI_VERSION = true ? "0.
|
|
20162
|
+
var CLI_VERSION = true ? "0.16.0" : "0.0.0-dev";
|
|
19798
20163
|
var program = new Command();
|
|
19799
20164
|
program.name("kryd").description("Kryd CLI").version(CLI_VERSION);
|
|
19800
20165
|
program.hook("postAction", async () => {
|
|
@@ -19805,6 +20170,9 @@ program.command("login").description("Sign in via the browser (default) and stor
|
|
|
19805
20170
|
"supply a token instead of the browser flow (prefer $KRYD_TOKEN \u2014 flags are visible in `ps`)"
|
|
19806
20171
|
).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));
|
|
19807
20172
|
program.command("whoami").description("Show the current account").option("--api-url <url>", "control-plane API base URL").action((opts) => runWhoami(opts));
|
|
20173
|
+
var accountCommand = program.command("account").description("The accounts you belong to, and which one commands run in");
|
|
20174
|
+
accountCommand.command("list").description("List your accounts and your role in each; * marks the one commands run in").option("--json", "print the accounts as JSON on stdout").option("--api-url <url>", "control-plane API base URL").action((opts) => runAccountList(opts));
|
|
20175
|
+
accountCommand.command("use <slug>").description("Run every following command in this account").option("--api-url <url>", "control-plane API base URL").action((slug, opts) => runAccountUse(slug, opts));
|
|
19808
20176
|
program.command("logout").description("Clear the stored token").action(() => runLogout());
|
|
19809
20177
|
program.command("git-credential <operation>", { hidden: true }).description("git credential helper (invoked by git, not by you)").option("--api-url <url>", "control-plane API base URL").action(async (operation, opts) => {
|
|
19810
20178
|
const chunks = [];
|
|
@@ -19942,6 +20310,10 @@ support.command("new").description(
|
|
|
19942
20310
|
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));
|
|
19943
20311
|
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));
|
|
19944
20312
|
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));
|
|
20313
|
+
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");
|
|
20314
|
+
tokenCommand.command("create <name>").description("Create a deploy token and print it once \u2014 it acts as you, bound to the current 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));
|
|
20315
|
+
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));
|
|
20316
|
+
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));
|
|
19945
20317
|
function invokedDirectly() {
|
|
19946
20318
|
const entry = process.argv[1];
|
|
19947
20319
|
if (!entry) return false;
|