@cimplify/sdk 0.44.32 → 0.44.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/deploy.mjs +160 -12
- package/dist/cli/commands/dev.mjs +49 -4
- package/dist/cli/commands/domains.mjs +128 -12
- package/dist/cli/commands/env.mjs +102 -7
- package/dist/cli/commands/link.mjs +58 -5
- package/dist/cli/commands/login.mjs +76 -7
- package/dist/cli/commands/logout.mjs +22 -1
- package/dist/cli/commands/logs.mjs +86 -12
- package/dist/cli/commands/projects.mjs +157 -11
- package/dist/cli/commands/repo.mjs +584 -0
- package/dist/cli/commands/rollback.mjs +83 -7
- package/dist/cli/commands/status.mjs +75 -5
- package/dist/cli/commands/unlink.mjs +22 -1
- package/dist/cli/commands/whoami.mjs +56 -5
- package/dist/dispatcher.mjs +83 -9
- package/package.json +1 -1
- package/templates/storefront-bakery/package.json +2 -3
- package/templates/storefront-fashion/package.json +2 -3
- package/templates/storefront-grocery/package.json +2 -3
- package/templates/storefront-restaurant/package.json +2 -3
- package/templates/storefront-retail/package.json +2 -3
- package/templates/storefront-services/package.json +2 -3
|
@@ -15,18 +15,50 @@ var CLI_ERROR_CODE = {
|
|
|
15
15
|
INVALID_INPUT: "INVALID_INPUT",
|
|
16
16
|
ALREADY_LINKED: "ALREADY_LINKED",
|
|
17
17
|
SERVER_ERROR: "SERVER_ERROR",
|
|
18
|
-
ABORTED: "ABORTED"
|
|
18
|
+
ABORTED: "ABORTED",
|
|
19
|
+
/** Operation needs interactive confirmation and the shell is non-interactive. */
|
|
20
|
+
INTERACTIVE_REQUIRED: "INTERACTIVE_REQUIRED"
|
|
21
|
+
};
|
|
19
22
|
var EXIT_CODE = {
|
|
20
23
|
OK: 0,
|
|
21
24
|
ERROR: 1,
|
|
22
|
-
SUPERSEDED: 2
|
|
25
|
+
SUPERSEDED: 2,
|
|
26
|
+
ABORTED: 3,
|
|
27
|
+
NOT_LINKED: 4,
|
|
28
|
+
ALREADY_LINKED: 5,
|
|
29
|
+
GIT_ERROR: 6,
|
|
30
|
+
INTERACTIVE_REQUIRED: 7,
|
|
31
|
+
PROJECT_NOT_FOUND: 8,
|
|
32
|
+
NETWORK_ERROR: 10,
|
|
33
|
+
SERVER_ERROR: 11,
|
|
34
|
+
TIMEOUT: 12,
|
|
35
|
+
NOT_LOGGED_IN: 20,
|
|
36
|
+
AUTH_FAILED: 21,
|
|
37
|
+
UNAUTHORIZED: 22,
|
|
38
|
+
INVALID_INPUT: 30
|
|
39
|
+
};
|
|
40
|
+
var EXIT_CODE_FOR = {
|
|
41
|
+
NOT_LOGGED_IN: EXIT_CODE.NOT_LOGGED_IN,
|
|
42
|
+
NOT_LINKED: EXIT_CODE.NOT_LINKED,
|
|
43
|
+
NETWORK_ERROR: EXIT_CODE.NETWORK_ERROR,
|
|
44
|
+
AUTH_FAILED: EXIT_CODE.AUTH_FAILED,
|
|
45
|
+
PROJECT_NOT_FOUND: EXIT_CODE.PROJECT_NOT_FOUND,
|
|
46
|
+
GIT_ERROR: EXIT_CODE.GIT_ERROR,
|
|
47
|
+
INVALID_INPUT: EXIT_CODE.INVALID_INPUT,
|
|
48
|
+
ALREADY_LINKED: EXIT_CODE.ALREADY_LINKED,
|
|
49
|
+
SERVER_ERROR: EXIT_CODE.SERVER_ERROR,
|
|
50
|
+
ABORTED: EXIT_CODE.ABORTED,
|
|
51
|
+
UNAUTHORIZED: EXIT_CODE.UNAUTHORIZED,
|
|
52
|
+
TIMEOUT: EXIT_CODE.TIMEOUT,
|
|
53
|
+
INTERACTIVE_REQUIRED: EXIT_CODE.INTERACTIVE_REQUIRED
|
|
23
54
|
};
|
|
24
55
|
var CliError = class extends Error {
|
|
25
|
-
constructor(code, message,
|
|
56
|
+
constructor(code, message, options = {}) {
|
|
26
57
|
super(message);
|
|
27
58
|
this.name = "CliError";
|
|
28
59
|
this.code = code;
|
|
29
|
-
this.exitCode = exitCode;
|
|
60
|
+
this.exitCode = options.exitCode ?? EXIT_CODE_FOR[code];
|
|
61
|
+
this.remediation = options.remediation;
|
|
30
62
|
}
|
|
31
63
|
};
|
|
32
64
|
|
|
@@ -281,6 +313,7 @@ var ARGS_STATUS_PORCELAIN = ["status", "--porcelain=v1"];
|
|
|
281
313
|
var ARGS_REV_PARSE_HEAD = ["rev-parse", "HEAD"];
|
|
282
314
|
var ARGS_REV_PARSE_ABBREV_REF = ["rev-parse", "--abbrev-ref", "HEAD"];
|
|
283
315
|
var ARGS_REV_PARSE_SHOW_TOPLEVEL = ["rev-parse", "--show-toplevel"];
|
|
316
|
+
var ARGS_REMOTE_GET_URL = ["remote", "get-url"];
|
|
284
317
|
var ARGS_PUSH = ["push"];
|
|
285
318
|
async function runGit(args, cwd) {
|
|
286
319
|
return new Promise((resolve, reject) => {
|
|
@@ -325,10 +358,27 @@ async function gitDetectRoot(cwd) {
|
|
|
325
358
|
if (code !== 0) return null;
|
|
326
359
|
return stdout.trimEnd() || null;
|
|
327
360
|
}
|
|
361
|
+
async function gitGetRemoteUrl(cwd, name = REMOTE_DEFAULT) {
|
|
362
|
+
const { code, stdout } = await runGit([...ARGS_REMOTE_GET_URL, name], cwd);
|
|
363
|
+
if (code !== 0) return null;
|
|
364
|
+
return stdout.trimEnd() || null;
|
|
365
|
+
}
|
|
328
366
|
async function gitPush(cwd, branch) {
|
|
329
367
|
const args = branch ? [...ARGS_PUSH, REMOTE_DEFAULT, branch] : [...ARGS_PUSH];
|
|
330
368
|
await runGitOk(args, cwd);
|
|
331
369
|
}
|
|
370
|
+
async function gitPushToUrl(cwd, url, branch) {
|
|
371
|
+
await runGitOk([...ARGS_PUSH, url, branch], cwd);
|
|
372
|
+
}
|
|
373
|
+
var FREESTYLE_GIT_HOST = "git.freestyle.sh";
|
|
374
|
+
function isFreestyleRemote(url) {
|
|
375
|
+
if (!url) return false;
|
|
376
|
+
try {
|
|
377
|
+
return new URL(url).hostname === FREESTYLE_GIT_HOST;
|
|
378
|
+
} catch {
|
|
379
|
+
return url.includes(FREESTYLE_GIT_HOST);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
332
382
|
|
|
333
383
|
// src/cli/output.ts
|
|
334
384
|
var RESET = "\x1B[0m";
|
|
@@ -339,34 +389,68 @@ var CYAN_OPEN = "\x1B[36m";
|
|
|
339
389
|
var PREFIX_STEP = "\u25B8";
|
|
340
390
|
var PREFIX_SUCCESS = "\u2713";
|
|
341
391
|
var PREFIX_FAILURE = "\u2717";
|
|
392
|
+
var ENV_JSON = "CIMPLIFY_JSON";
|
|
393
|
+
var ENV_YES = "CIMPLIFY_YES";
|
|
394
|
+
function envFlag(name) {
|
|
395
|
+
const v = process.env[name];
|
|
396
|
+
return v === "1" || v === "true";
|
|
397
|
+
}
|
|
398
|
+
function isJsonMode() {
|
|
399
|
+
return envFlag(ENV_JSON);
|
|
400
|
+
}
|
|
401
|
+
function isAutoYes() {
|
|
402
|
+
return envFlag(ENV_YES);
|
|
403
|
+
}
|
|
404
|
+
function isInteractive() {
|
|
405
|
+
return Boolean(process.stdout.isTTY) && Boolean(process.stdin.isTTY);
|
|
406
|
+
}
|
|
342
407
|
function wrap(open, value) {
|
|
343
408
|
return `${open}${value}${RESET}`;
|
|
344
409
|
}
|
|
345
410
|
function dim(value) {
|
|
346
|
-
return wrap(DIM_OPEN, value);
|
|
411
|
+
return isJsonMode() ? value : wrap(DIM_OPEN, value);
|
|
347
412
|
}
|
|
348
413
|
function red(value) {
|
|
349
|
-
return wrap(RED_OPEN, value);
|
|
414
|
+
return isJsonMode() ? value : wrap(RED_OPEN, value);
|
|
350
415
|
}
|
|
351
416
|
function green(value) {
|
|
352
|
-
return wrap(GREEN_OPEN, value);
|
|
417
|
+
return isJsonMode() ? value : wrap(GREEN_OPEN, value);
|
|
353
418
|
}
|
|
354
419
|
function cyan(value) {
|
|
355
|
-
return wrap(CYAN_OPEN, value);
|
|
420
|
+
return isJsonMode() ? value : wrap(CYAN_OPEN, value);
|
|
356
421
|
}
|
|
357
422
|
function step(message) {
|
|
423
|
+
if (isJsonMode()) return;
|
|
358
424
|
console.log(`${cyan(PREFIX_STEP)} ${message}`);
|
|
359
425
|
}
|
|
360
426
|
function success(message) {
|
|
427
|
+
if (isJsonMode()) return;
|
|
361
428
|
console.log(`${green(PREFIX_SUCCESS)} ${message}`);
|
|
362
429
|
}
|
|
363
430
|
function failure(message) {
|
|
431
|
+
if (isJsonMode()) return;
|
|
364
432
|
console.error(`${red(PREFIX_FAILURE)} ${message}`);
|
|
365
433
|
}
|
|
366
434
|
function info(message) {
|
|
435
|
+
if (isJsonMode()) return;
|
|
367
436
|
console.log(message);
|
|
368
437
|
}
|
|
438
|
+
var ENV_EMITTED = "__CIMPLIFY_RESULT_EMITTED";
|
|
439
|
+
function result(data) {
|
|
440
|
+
if (!isJsonMode()) return;
|
|
441
|
+
if (process.env[ENV_EMITTED] === "1") return;
|
|
442
|
+
process.env[ENV_EMITTED] = "1";
|
|
443
|
+
process.stdout.write(`${JSON.stringify({ ok: true, data })}
|
|
444
|
+
`);
|
|
445
|
+
}
|
|
369
446
|
async function promptLine(question) {
|
|
447
|
+
if (!isInteractive()) {
|
|
448
|
+
throw new CliError(
|
|
449
|
+
CLI_ERROR_CODE.INTERACTIVE_REQUIRED,
|
|
450
|
+
"this operation needs interactive input but stdin is not a TTY",
|
|
451
|
+
{ remediation: "run interactively, or supply the value via a flag" }
|
|
452
|
+
);
|
|
453
|
+
}
|
|
370
454
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
371
455
|
try {
|
|
372
456
|
return await new Promise((resolve) => {
|
|
@@ -377,6 +461,14 @@ async function promptLine(question) {
|
|
|
377
461
|
}
|
|
378
462
|
}
|
|
379
463
|
async function promptYesNo(question, defaultNo = true) {
|
|
464
|
+
if (isAutoYes()) return true;
|
|
465
|
+
if (!isInteractive()) {
|
|
466
|
+
throw new CliError(
|
|
467
|
+
CLI_ERROR_CODE.INTERACTIVE_REQUIRED,
|
|
468
|
+
`this operation needs confirmation: ${question}`,
|
|
469
|
+
{ remediation: "re-run with --yes to accept, or run interactively" }
|
|
470
|
+
);
|
|
471
|
+
}
|
|
380
472
|
const suffix = defaultNo ? " [y/N] " : " [Y/n] ";
|
|
381
473
|
const answer = (await promptLine(`${question}${suffix}`)).trim().toLowerCase();
|
|
382
474
|
if (answer === "") return !defaultNo;
|
|
@@ -402,6 +494,16 @@ var TERMINAL_DEPLOYMENT_STATUSES = /* @__PURE__ */ new Set([
|
|
|
402
494
|
DEPLOYMENT_STATUS.CANCELLED,
|
|
403
495
|
DEPLOYMENT_STATUS.SUPERSEDED
|
|
404
496
|
]);
|
|
497
|
+
var REPO_PROVIDER = {
|
|
498
|
+
FREESTYLE: "freestyle",
|
|
499
|
+
GITHUB: "github",
|
|
500
|
+
GITEA: "gitea",
|
|
501
|
+
EXTERNAL: "external"
|
|
502
|
+
};
|
|
503
|
+
new Set(Object.values(REPO_PROVIDER));
|
|
504
|
+
var TOKEN_PURPOSE = {
|
|
505
|
+
EDITOR: "editor"
|
|
506
|
+
};
|
|
405
507
|
|
|
406
508
|
// src/cli/progress.ts
|
|
407
509
|
var POLL_INTERVAL_MS = 1e3;
|
|
@@ -479,6 +581,19 @@ function reportTerminal(progress) {
|
|
|
479
581
|
}
|
|
480
582
|
}
|
|
481
583
|
|
|
584
|
+
// src/cli/commands/repo.ts
|
|
585
|
+
function repoEndpoint(businessId, projectId) {
|
|
586
|
+
return `/v1/businesses/${encodeURIComponent(businessId)}/projects/${encodeURIComponent(projectId)}/repo`;
|
|
587
|
+
}
|
|
588
|
+
function cloneTokenEndpoint(businessId, projectId) {
|
|
589
|
+
return `${repoEndpoint(businessId, projectId)}/clone-token`;
|
|
590
|
+
}
|
|
591
|
+
async function fetchCloneToken(client, businessId, projectId, purpose = TOKEN_PURPOSE.EDITOR) {
|
|
592
|
+
return client.post(cloneTokenEndpoint(businessId, projectId), {
|
|
593
|
+
purpose
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
482
597
|
// src/cli/commands/deploy.ts
|
|
483
598
|
var FLAG_PROD = "prod";
|
|
484
599
|
var FLAG_REF = "ref";
|
|
@@ -492,6 +607,7 @@ async function run(argv) {
|
|
|
492
607
|
const args = parseArgs(argv);
|
|
493
608
|
const auth = await readAuth();
|
|
494
609
|
const link = await readProjectLink();
|
|
610
|
+
const client = ApiClient.fromAuth(auth);
|
|
495
611
|
const cwd = process.cwd();
|
|
496
612
|
const root = await gitDetectRoot(cwd);
|
|
497
613
|
if (!root) {
|
|
@@ -516,8 +632,20 @@ async function run(argv) {
|
|
|
516
632
|
const localSha = await gitCurrentSha(root);
|
|
517
633
|
const gitRef = explicitRef ?? localSha;
|
|
518
634
|
if (!flagBool(args, FLAG_NO_PUSH)) {
|
|
519
|
-
|
|
520
|
-
|
|
635
|
+
const originUrl = await gitGetRemoteUrl(root);
|
|
636
|
+
if (isFreestyleRemote(originUrl)) {
|
|
637
|
+
step(`Pushing ${branch} to Freestyle (with minted clone token)`);
|
|
638
|
+
const token = await fetchCloneToken(
|
|
639
|
+
client,
|
|
640
|
+
link.businessId,
|
|
641
|
+
link.projectId,
|
|
642
|
+
TOKEN_PURPOSE.EDITOR
|
|
643
|
+
);
|
|
644
|
+
await gitPushToUrl(root, token.clone_url, branch);
|
|
645
|
+
} else {
|
|
646
|
+
step(`Pushing ${branch} to origin`);
|
|
647
|
+
await gitPush(root, branch);
|
|
648
|
+
}
|
|
521
649
|
} else {
|
|
522
650
|
info(dim("Skipping git push (--no-push)"));
|
|
523
651
|
}
|
|
@@ -528,7 +656,6 @@ async function run(argv) {
|
|
|
528
656
|
trigger: DEPLOY_TRIGGER.CLI
|
|
529
657
|
};
|
|
530
658
|
step(`Triggering ${envScope} deploy for ${gitRef.slice(0, 12)}`);
|
|
531
|
-
const client = ApiClient.fromAuth(auth);
|
|
532
659
|
const enqueued = await client.post(
|
|
533
660
|
deployEndpoint(link.businessId, link.projectId),
|
|
534
661
|
body
|
|
@@ -548,13 +675,34 @@ async function run(argv) {
|
|
|
548
675
|
} else {
|
|
549
676
|
success(`Enqueued deployment ${enqueued.deployment_id} (sha ${enqueued.git_sha.slice(0, 12)})`);
|
|
550
677
|
}
|
|
551
|
-
if (flagBool(args, FLAG_NO_POLL))
|
|
678
|
+
if (flagBool(args, FLAG_NO_POLL)) {
|
|
679
|
+
result({
|
|
680
|
+
deployment: {
|
|
681
|
+
id: enqueued.deployment_id,
|
|
682
|
+
git_sha: enqueued.git_sha,
|
|
683
|
+
existing: enqueued.existing,
|
|
684
|
+
environment: envScope,
|
|
685
|
+
status: enqueued.existing ? "in_progress" : "queued"
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
552
690
|
const final = await pollDeployment(
|
|
553
691
|
client,
|
|
554
692
|
link.businessId,
|
|
555
693
|
link.projectId,
|
|
556
694
|
enqueued.deployment_id
|
|
557
695
|
);
|
|
696
|
+
result({
|
|
697
|
+
deployment: {
|
|
698
|
+
id: enqueued.deployment_id,
|
|
699
|
+
git_sha: enqueued.git_sha,
|
|
700
|
+
environment: envScope,
|
|
701
|
+
status: final.status,
|
|
702
|
+
url: final.url ?? null,
|
|
703
|
+
error_message: final.error_message ?? null
|
|
704
|
+
}
|
|
705
|
+
});
|
|
558
706
|
if (final.status === DEPLOYMENT_STATUS.ACTIVE) {
|
|
559
707
|
process.exitCode = EXIT_CODE.OK;
|
|
560
708
|
return;
|
|
@@ -16,13 +16,42 @@ var CLI_ERROR_CODE = {
|
|
|
16
16
|
ALREADY_LINKED: "ALREADY_LINKED",
|
|
17
17
|
SERVER_ERROR: "SERVER_ERROR"};
|
|
18
18
|
var EXIT_CODE = {
|
|
19
|
-
|
|
19
|
+
ABORTED: 3,
|
|
20
|
+
NOT_LINKED: 4,
|
|
21
|
+
ALREADY_LINKED: 5,
|
|
22
|
+
GIT_ERROR: 6,
|
|
23
|
+
INTERACTIVE_REQUIRED: 7,
|
|
24
|
+
PROJECT_NOT_FOUND: 8,
|
|
25
|
+
NETWORK_ERROR: 10,
|
|
26
|
+
SERVER_ERROR: 11,
|
|
27
|
+
TIMEOUT: 12,
|
|
28
|
+
NOT_LOGGED_IN: 20,
|
|
29
|
+
AUTH_FAILED: 21,
|
|
30
|
+
UNAUTHORIZED: 22,
|
|
31
|
+
INVALID_INPUT: 30
|
|
32
|
+
};
|
|
33
|
+
var EXIT_CODE_FOR = {
|
|
34
|
+
NOT_LOGGED_IN: EXIT_CODE.NOT_LOGGED_IN,
|
|
35
|
+
NOT_LINKED: EXIT_CODE.NOT_LINKED,
|
|
36
|
+
NETWORK_ERROR: EXIT_CODE.NETWORK_ERROR,
|
|
37
|
+
AUTH_FAILED: EXIT_CODE.AUTH_FAILED,
|
|
38
|
+
PROJECT_NOT_FOUND: EXIT_CODE.PROJECT_NOT_FOUND,
|
|
39
|
+
GIT_ERROR: EXIT_CODE.GIT_ERROR,
|
|
40
|
+
INVALID_INPUT: EXIT_CODE.INVALID_INPUT,
|
|
41
|
+
ALREADY_LINKED: EXIT_CODE.ALREADY_LINKED,
|
|
42
|
+
SERVER_ERROR: EXIT_CODE.SERVER_ERROR,
|
|
43
|
+
ABORTED: EXIT_CODE.ABORTED,
|
|
44
|
+
UNAUTHORIZED: EXIT_CODE.UNAUTHORIZED,
|
|
45
|
+
TIMEOUT: EXIT_CODE.TIMEOUT,
|
|
46
|
+
INTERACTIVE_REQUIRED: EXIT_CODE.INTERACTIVE_REQUIRED
|
|
47
|
+
};
|
|
20
48
|
var CliError = class extends Error {
|
|
21
|
-
constructor(code, message,
|
|
49
|
+
constructor(code, message, options = {}) {
|
|
22
50
|
super(message);
|
|
23
51
|
this.name = "CliError";
|
|
24
52
|
this.code = code;
|
|
25
|
-
this.exitCode = exitCode;
|
|
53
|
+
this.exitCode = options.exitCode ?? EXIT_CODE_FOR[code];
|
|
54
|
+
this.remediation = options.remediation;
|
|
26
55
|
}
|
|
27
56
|
};
|
|
28
57
|
|
|
@@ -296,13 +325,22 @@ function formatEnvFile(entries) {
|
|
|
296
325
|
// src/cli/output.ts
|
|
297
326
|
var RESET = "\x1B[0m";
|
|
298
327
|
var DIM_OPEN = "\x1B[2m";
|
|
328
|
+
var ENV_JSON = "CIMPLIFY_JSON";
|
|
329
|
+
function envFlag(name) {
|
|
330
|
+
const v = process.env[name];
|
|
331
|
+
return v === "1" || v === "true";
|
|
332
|
+
}
|
|
333
|
+
function isJsonMode() {
|
|
334
|
+
return envFlag(ENV_JSON);
|
|
335
|
+
}
|
|
299
336
|
function wrap(open, value) {
|
|
300
337
|
return `${open}${value}${RESET}`;
|
|
301
338
|
}
|
|
302
339
|
function dim(value) {
|
|
303
|
-
return wrap(DIM_OPEN, value);
|
|
340
|
+
return isJsonMode() ? value : wrap(DIM_OPEN, value);
|
|
304
341
|
}
|
|
305
342
|
function info(message) {
|
|
343
|
+
if (isJsonMode()) return;
|
|
306
344
|
console.log(message);
|
|
307
345
|
}
|
|
308
346
|
|
|
@@ -311,6 +349,13 @@ var ENV_SCOPE = {
|
|
|
311
349
|
PRODUCTION: "production"
|
|
312
350
|
};
|
|
313
351
|
var PUBLIC_ENV_PREFIX = "NEXT_PUBLIC_";
|
|
352
|
+
var REPO_PROVIDER = {
|
|
353
|
+
FREESTYLE: "freestyle",
|
|
354
|
+
GITHUB: "github",
|
|
355
|
+
GITEA: "gitea",
|
|
356
|
+
EXTERNAL: "external"
|
|
357
|
+
};
|
|
358
|
+
new Set(Object.values(REPO_PROVIDER));
|
|
314
359
|
|
|
315
360
|
// src/cli/commands/dev.ts
|
|
316
361
|
var FLAG_REMOTE = "remote";
|
|
@@ -13,15 +13,47 @@ var CLI_ERROR_CODE = {
|
|
|
13
13
|
INVALID_INPUT: "INVALID_INPUT",
|
|
14
14
|
ALREADY_LINKED: "ALREADY_LINKED",
|
|
15
15
|
SERVER_ERROR: "SERVER_ERROR",
|
|
16
|
-
ABORTED: "ABORTED"
|
|
16
|
+
ABORTED: "ABORTED",
|
|
17
|
+
/** Operation needs interactive confirmation and the shell is non-interactive. */
|
|
18
|
+
INTERACTIVE_REQUIRED: "INTERACTIVE_REQUIRED"
|
|
19
|
+
};
|
|
17
20
|
var EXIT_CODE = {
|
|
18
|
-
|
|
21
|
+
ABORTED: 3,
|
|
22
|
+
NOT_LINKED: 4,
|
|
23
|
+
ALREADY_LINKED: 5,
|
|
24
|
+
GIT_ERROR: 6,
|
|
25
|
+
INTERACTIVE_REQUIRED: 7,
|
|
26
|
+
PROJECT_NOT_FOUND: 8,
|
|
27
|
+
NETWORK_ERROR: 10,
|
|
28
|
+
SERVER_ERROR: 11,
|
|
29
|
+
TIMEOUT: 12,
|
|
30
|
+
NOT_LOGGED_IN: 20,
|
|
31
|
+
AUTH_FAILED: 21,
|
|
32
|
+
UNAUTHORIZED: 22,
|
|
33
|
+
INVALID_INPUT: 30
|
|
34
|
+
};
|
|
35
|
+
var EXIT_CODE_FOR = {
|
|
36
|
+
NOT_LOGGED_IN: EXIT_CODE.NOT_LOGGED_IN,
|
|
37
|
+
NOT_LINKED: EXIT_CODE.NOT_LINKED,
|
|
38
|
+
NETWORK_ERROR: EXIT_CODE.NETWORK_ERROR,
|
|
39
|
+
AUTH_FAILED: EXIT_CODE.AUTH_FAILED,
|
|
40
|
+
PROJECT_NOT_FOUND: EXIT_CODE.PROJECT_NOT_FOUND,
|
|
41
|
+
GIT_ERROR: EXIT_CODE.GIT_ERROR,
|
|
42
|
+
INVALID_INPUT: EXIT_CODE.INVALID_INPUT,
|
|
43
|
+
ALREADY_LINKED: EXIT_CODE.ALREADY_LINKED,
|
|
44
|
+
SERVER_ERROR: EXIT_CODE.SERVER_ERROR,
|
|
45
|
+
ABORTED: EXIT_CODE.ABORTED,
|
|
46
|
+
UNAUTHORIZED: EXIT_CODE.UNAUTHORIZED,
|
|
47
|
+
TIMEOUT: EXIT_CODE.TIMEOUT,
|
|
48
|
+
INTERACTIVE_REQUIRED: EXIT_CODE.INTERACTIVE_REQUIRED
|
|
49
|
+
};
|
|
19
50
|
var CliError = class extends Error {
|
|
20
|
-
constructor(code, message,
|
|
51
|
+
constructor(code, message, options = {}) {
|
|
21
52
|
super(message);
|
|
22
53
|
this.name = "CliError";
|
|
23
54
|
this.code = code;
|
|
24
|
-
this.exitCode = exitCode;
|
|
55
|
+
this.exitCode = options.exitCode ?? EXIT_CODE_FOR[code];
|
|
56
|
+
this.remediation = options.remediation;
|
|
25
57
|
}
|
|
26
58
|
};
|
|
27
59
|
|
|
@@ -262,25 +294,57 @@ var BOLD_OPEN = "\x1B[1m";
|
|
|
262
294
|
var DIM_OPEN = "\x1B[2m";
|
|
263
295
|
var GREEN_OPEN = "\x1B[32m";
|
|
264
296
|
var PREFIX_SUCCESS = "\u2713";
|
|
297
|
+
var ENV_JSON = "CIMPLIFY_JSON";
|
|
298
|
+
var ENV_YES = "CIMPLIFY_YES";
|
|
299
|
+
function envFlag(name) {
|
|
300
|
+
const v = process.env[name];
|
|
301
|
+
return v === "1" || v === "true";
|
|
302
|
+
}
|
|
303
|
+
function isJsonMode() {
|
|
304
|
+
return envFlag(ENV_JSON);
|
|
305
|
+
}
|
|
306
|
+
function isAutoYes() {
|
|
307
|
+
return envFlag(ENV_YES);
|
|
308
|
+
}
|
|
309
|
+
function isInteractive() {
|
|
310
|
+
return Boolean(process.stdout.isTTY) && Boolean(process.stdin.isTTY);
|
|
311
|
+
}
|
|
265
312
|
function wrap(open, value) {
|
|
266
313
|
return `${open}${value}${RESET}`;
|
|
267
314
|
}
|
|
268
315
|
function bold(value) {
|
|
269
|
-
return wrap(BOLD_OPEN, value);
|
|
316
|
+
return isJsonMode() ? value : wrap(BOLD_OPEN, value);
|
|
270
317
|
}
|
|
271
318
|
function dim(value) {
|
|
272
|
-
return wrap(DIM_OPEN, value);
|
|
319
|
+
return isJsonMode() ? value : wrap(DIM_OPEN, value);
|
|
273
320
|
}
|
|
274
321
|
function green(value) {
|
|
275
|
-
return wrap(GREEN_OPEN, value);
|
|
322
|
+
return isJsonMode() ? value : wrap(GREEN_OPEN, value);
|
|
276
323
|
}
|
|
277
324
|
function success(message) {
|
|
325
|
+
if (isJsonMode()) return;
|
|
278
326
|
console.log(`${green(PREFIX_SUCCESS)} ${message}`);
|
|
279
327
|
}
|
|
280
328
|
function info(message) {
|
|
329
|
+
if (isJsonMode()) return;
|
|
281
330
|
console.log(message);
|
|
282
331
|
}
|
|
332
|
+
var ENV_EMITTED = "__CIMPLIFY_RESULT_EMITTED";
|
|
333
|
+
function result(data) {
|
|
334
|
+
if (!isJsonMode()) return;
|
|
335
|
+
if (process.env[ENV_EMITTED] === "1") return;
|
|
336
|
+
process.env[ENV_EMITTED] = "1";
|
|
337
|
+
process.stdout.write(`${JSON.stringify({ ok: true, data })}
|
|
338
|
+
`);
|
|
339
|
+
}
|
|
283
340
|
async function promptLine(question) {
|
|
341
|
+
if (!isInteractive()) {
|
|
342
|
+
throw new CliError(
|
|
343
|
+
CLI_ERROR_CODE.INTERACTIVE_REQUIRED,
|
|
344
|
+
"this operation needs interactive input but stdin is not a TTY",
|
|
345
|
+
{ remediation: "run interactively, or supply the value via a flag" }
|
|
346
|
+
);
|
|
347
|
+
}
|
|
284
348
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
285
349
|
try {
|
|
286
350
|
return await new Promise((resolve) => {
|
|
@@ -291,6 +355,14 @@ async function promptLine(question) {
|
|
|
291
355
|
}
|
|
292
356
|
}
|
|
293
357
|
async function promptYesNo(question, defaultNo = true) {
|
|
358
|
+
if (isAutoYes()) return true;
|
|
359
|
+
if (!isInteractive()) {
|
|
360
|
+
throw new CliError(
|
|
361
|
+
CLI_ERROR_CODE.INTERACTIVE_REQUIRED,
|
|
362
|
+
`this operation needs confirmation: ${question}`,
|
|
363
|
+
{ remediation: "re-run with --yes to accept, or run interactively" }
|
|
364
|
+
);
|
|
365
|
+
}
|
|
294
366
|
const suffix = defaultNo ? " [y/N] " : " [Y/n] ";
|
|
295
367
|
const answer = (await promptLine(`${question}${suffix}`)).trim().toLowerCase();
|
|
296
368
|
if (answer === "") return !defaultNo;
|
|
@@ -308,6 +380,13 @@ var DOMAIN_TYPE = {
|
|
|
308
380
|
CUSTOM: "custom",
|
|
309
381
|
CIMPLIFY: "cimplify"
|
|
310
382
|
};
|
|
383
|
+
var REPO_PROVIDER = {
|
|
384
|
+
FREESTYLE: "freestyle",
|
|
385
|
+
GITHUB: "github",
|
|
386
|
+
GITEA: "gitea",
|
|
387
|
+
EXTERNAL: "external"
|
|
388
|
+
};
|
|
389
|
+
new Set(Object.values(REPO_PROVIDER));
|
|
311
390
|
|
|
312
391
|
// src/cli/commands/domains.ts
|
|
313
392
|
var SUB_LS = "ls";
|
|
@@ -425,6 +504,7 @@ async function listDomains(client, businessId) {
|
|
|
425
504
|
const domains = await client.get(domainsEndpoint(businessId));
|
|
426
505
|
if (domains.length === 0) {
|
|
427
506
|
info(dim("No domains. Add one: cimplify domains add <domain>"));
|
|
507
|
+
result({ domains: [] });
|
|
428
508
|
return;
|
|
429
509
|
}
|
|
430
510
|
const domainWidth = Math.max(COL_DOMAIN.length, ...domains.map((d) => d.domain.length));
|
|
@@ -453,6 +533,16 @@ async function listDomains(client, businessId) {
|
|
|
453
533
|
].join(COL_GAP)
|
|
454
534
|
);
|
|
455
535
|
}
|
|
536
|
+
result({
|
|
537
|
+
domains: domains.map((d) => ({
|
|
538
|
+
id: d.id,
|
|
539
|
+
domain: d.domain,
|
|
540
|
+
domain_type: d.domain_type,
|
|
541
|
+
is_verified: d.is_verified,
|
|
542
|
+
is_primary: d.is_primary,
|
|
543
|
+
created_at: d.created_at ?? null
|
|
544
|
+
}))
|
|
545
|
+
});
|
|
456
546
|
}
|
|
457
547
|
async function addDomain(client, businessId, args) {
|
|
458
548
|
const domain = args.positional[1];
|
|
@@ -466,15 +556,25 @@ async function addDomain(client, businessId, args) {
|
|
|
466
556
|
const body = { domain, domain_type: domainType };
|
|
467
557
|
const created = await client.post(domainsEndpoint(businessId), body);
|
|
468
558
|
success(`Added ${created.domain} (${created.domain_type})`);
|
|
559
|
+
let dnsRecords = null;
|
|
469
560
|
if (domainType === DOMAIN_TYPE.CUSTOM) {
|
|
470
561
|
try {
|
|
471
562
|
const dns = await client.get(dnsRecordsEndpoint(domain));
|
|
472
|
-
|
|
473
|
-
printDnsRecords(domain,
|
|
563
|
+
dnsRecords = Array.isArray(dns) ? dns : dns.records;
|
|
564
|
+
printDnsRecords(domain, dnsRecords);
|
|
474
565
|
} catch (err) {
|
|
475
566
|
info(dim(`(Could not fetch DNS records: ${err instanceof Error ? err.message : String(err)})`));
|
|
476
567
|
}
|
|
477
568
|
}
|
|
569
|
+
result({
|
|
570
|
+
domain: {
|
|
571
|
+
id: created.id,
|
|
572
|
+
domain: created.domain,
|
|
573
|
+
domain_type: created.domain_type,
|
|
574
|
+
is_verified: created.is_verified
|
|
575
|
+
},
|
|
576
|
+
dns_records: dnsRecords
|
|
577
|
+
});
|
|
478
578
|
}
|
|
479
579
|
function printDnsRecords(domain, records) {
|
|
480
580
|
if (!records || records.length === 0) {
|
|
@@ -508,12 +608,13 @@ async function verifyDomain(client, businessId, args) {
|
|
|
508
608
|
);
|
|
509
609
|
}
|
|
510
610
|
const target = await findDomain(client, businessId, domain);
|
|
511
|
-
const
|
|
512
|
-
if (
|
|
513
|
-
success(`Verified ${
|
|
611
|
+
const verified = await client.post(domainVerifyEndpoint(businessId, target.id));
|
|
612
|
+
if (verified.is_verified) {
|
|
613
|
+
success(`Verified ${verified.domain}`);
|
|
514
614
|
} else {
|
|
515
615
|
info(dim(`Not yet verified \u2014 DNS may still be propagating. Try again in a few minutes.`));
|
|
516
616
|
}
|
|
617
|
+
result({ domain: verified.domain, verified: verified.is_verified });
|
|
517
618
|
}
|
|
518
619
|
async function removeDomain(client, businessId, args) {
|
|
519
620
|
const domain = args.positional[1];
|
|
@@ -532,6 +633,7 @@ async function removeDomain(client, businessId, args) {
|
|
|
532
633
|
}
|
|
533
634
|
await client.delete(domainItemEndpoint(businessId, target.id));
|
|
534
635
|
success(`Removed ${domain}`);
|
|
636
|
+
result({ removed: true, domain });
|
|
535
637
|
}
|
|
536
638
|
async function findDomain(client, businessId, domain) {
|
|
537
639
|
const domains = await client.get(domainsEndpoint(businessId));
|
|
@@ -573,6 +675,7 @@ async function attachDomain(client, businessId, args) {
|
|
|
573
675
|
);
|
|
574
676
|
success(`Attached ${domain} to project for ${envScope}${isPrimary ? " (primary)" : ""}`);
|
|
575
677
|
info(dim("Next deploy will bind this domain. Run: cimplify deploy"));
|
|
678
|
+
result({ attached: true, domain, env: envScope, primary: isPrimary });
|
|
576
679
|
}
|
|
577
680
|
async function detachDomain(client, businessId, args) {
|
|
578
681
|
const domain = args.positional[1];
|
|
@@ -594,6 +697,7 @@ async function detachDomain(client, businessId, args) {
|
|
|
594
697
|
await client.delete(projectDomainItemEndpoint(businessId, link.projectId, attachment.id));
|
|
595
698
|
success(`Detached ${domain} (${envScope})`);
|
|
596
699
|
info(dim("Next deploy will not bind this domain. Existing deployments continue serving until redeploy."));
|
|
700
|
+
result({ detached: true, domain, env: envScope });
|
|
597
701
|
}
|
|
598
702
|
async function setPrimaryAttachment(client, businessId, args) {
|
|
599
703
|
const domain = args.positional[1];
|
|
@@ -610,6 +714,7 @@ async function setPrimaryAttachment(client, businessId, args) {
|
|
|
610
714
|
projectDomainPrimaryEndpoint(businessId, link.projectId, attachment.id)
|
|
611
715
|
);
|
|
612
716
|
success(`Set ${domain} as primary for ${envScope}`);
|
|
717
|
+
result({ primary: true, domain, env: envScope });
|
|
613
718
|
}
|
|
614
719
|
async function listAttachedForCurrentProject(client, businessId) {
|
|
615
720
|
const link = await readProjectLink();
|
|
@@ -618,6 +723,7 @@ async function listAttachedForCurrentProject(client, businessId) {
|
|
|
618
723
|
);
|
|
619
724
|
if (attachments.length === 0) {
|
|
620
725
|
info(dim("No domains attached to this project. Attach one: cimplify domains attach <domain>"));
|
|
726
|
+
result({ attached: [] });
|
|
621
727
|
return;
|
|
622
728
|
}
|
|
623
729
|
const domains = await client.get(domainsEndpoint(businessId));
|
|
@@ -634,6 +740,16 @@ async function listAttachedForCurrentProject(client, businessId) {
|
|
|
634
740
|
].join(COL_GAP)
|
|
635
741
|
);
|
|
636
742
|
}
|
|
743
|
+
result({
|
|
744
|
+
attached: attachments.map((a) => ({
|
|
745
|
+
attachment_id: a.id,
|
|
746
|
+
business_domain_id: a.business_domain_id,
|
|
747
|
+
domain: domainById.get(a.business_domain_id)?.domain ?? null,
|
|
748
|
+
env: a.env_scope,
|
|
749
|
+
primary: a.is_primary,
|
|
750
|
+
attached_at: a.attached_at
|
|
751
|
+
}))
|
|
752
|
+
});
|
|
637
753
|
}
|
|
638
754
|
async function findAttachmentForCurrentProject(client, businessId, projectId, domain, envScope) {
|
|
639
755
|
const target = await findDomain(client, businessId, domain);
|