@insforge/cli 0.1.5 → 0.1.7
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 +2 -0
- package/dist/index.js +389 -354
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from "url";
|
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
|
|
9
9
|
// src/commands/login.ts
|
|
10
|
-
import * as
|
|
10
|
+
import * as clack3 from "@clack/prompts";
|
|
11
11
|
|
|
12
12
|
// src/lib/config.ts
|
|
13
13
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
@@ -17,6 +17,7 @@ var GLOBAL_DIR = join(homedir(), ".insforge");
|
|
|
17
17
|
var CREDENTIALS_FILE = join(GLOBAL_DIR, "credentials.json");
|
|
18
18
|
var CONFIG_FILE = join(GLOBAL_DIR, "config.json");
|
|
19
19
|
var DEFAULT_PLATFORM_URL = "https://api.insforge.dev";
|
|
20
|
+
var DEFAULT_FRONTEND_URL = "https://insforge.dev";
|
|
20
21
|
function ensureGlobalDir() {
|
|
21
22
|
if (!existsSync(GLOBAL_DIR)) {
|
|
22
23
|
mkdirSync(GLOBAL_DIR, { recursive: true });
|
|
@@ -78,6 +79,9 @@ function saveProjectConfig(config) {
|
|
|
78
79
|
function getPlatformApiUrl(override) {
|
|
79
80
|
return process.env.INSFORGE_API_URL ?? override ?? getGlobalConfig().platform_api_url ?? DEFAULT_PLATFORM_URL;
|
|
80
81
|
}
|
|
82
|
+
function getFrontendUrl() {
|
|
83
|
+
return process.env.INSFORGE_FRONTEND_URL ?? DEFAULT_FRONTEND_URL;
|
|
84
|
+
}
|
|
81
85
|
function getAccessToken() {
|
|
82
86
|
return process.env.INSFORGE_ACCESS_TOKEN ?? getCredentials()?.access_token ?? null;
|
|
83
87
|
}
|
|
@@ -135,6 +139,7 @@ function getRootOpts(cmd) {
|
|
|
135
139
|
import { createServer } from "http";
|
|
136
140
|
import { randomBytes, createHash } from "crypto";
|
|
137
141
|
import { URL } from "url";
|
|
142
|
+
import * as clack from "@clack/prompts";
|
|
138
143
|
var DEFAULT_CLIENT_ID = "clf_NK8cMUs41gm8ZcfdtSguVw";
|
|
139
144
|
var OAUTH_SCOPES = "user:read organizations:read projects:read projects:write";
|
|
140
145
|
function generatePKCE() {
|
|
@@ -242,14 +247,77 @@ function startCallbackServer() {
|
|
|
242
247
|
}, 5 * 60 * 1e3).unref();
|
|
243
248
|
});
|
|
244
249
|
}
|
|
250
|
+
async function performOAuthLogin(apiUrl) {
|
|
251
|
+
const platformUrl = getPlatformApiUrl(apiUrl);
|
|
252
|
+
const config = getGlobalConfig();
|
|
253
|
+
const clientId = config.oauth_client_id ?? DEFAULT_CLIENT_ID;
|
|
254
|
+
const pkce = generatePKCE();
|
|
255
|
+
const state = generateState();
|
|
256
|
+
const { port, result, close } = await startCallbackServer();
|
|
257
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
258
|
+
const authUrl = buildAuthorizeUrl({
|
|
259
|
+
platformUrl,
|
|
260
|
+
clientId,
|
|
261
|
+
redirectUri,
|
|
262
|
+
codeChallenge: pkce.code_challenge,
|
|
263
|
+
state,
|
|
264
|
+
scopes: OAUTH_SCOPES
|
|
265
|
+
});
|
|
266
|
+
clack.log.info("Opening browser for authentication...");
|
|
267
|
+
clack.log.info(`If browser doesn't open, visit:
|
|
268
|
+
${authUrl}`);
|
|
269
|
+
try {
|
|
270
|
+
const open = (await import("open")).default;
|
|
271
|
+
await open(authUrl);
|
|
272
|
+
} catch {
|
|
273
|
+
clack.log.warn("Could not open browser. Please visit the URL above.");
|
|
274
|
+
}
|
|
275
|
+
const s = clack.spinner();
|
|
276
|
+
s.start("Waiting for authentication...");
|
|
277
|
+
try {
|
|
278
|
+
const callbackResult = await result;
|
|
279
|
+
close();
|
|
280
|
+
if (callbackResult.state !== state) {
|
|
281
|
+
s.stop("Authentication failed");
|
|
282
|
+
throw new Error("State mismatch. Possible CSRF attack.");
|
|
283
|
+
}
|
|
284
|
+
s.message("Exchanging authorization code...");
|
|
285
|
+
const tokens = await exchangeCodeForTokens({
|
|
286
|
+
platformUrl,
|
|
287
|
+
code: callbackResult.code,
|
|
288
|
+
redirectUri,
|
|
289
|
+
clientId,
|
|
290
|
+
codeVerifier: pkce.code_verifier
|
|
291
|
+
});
|
|
292
|
+
const creds = {
|
|
293
|
+
access_token: tokens.access_token,
|
|
294
|
+
refresh_token: tokens.refresh_token,
|
|
295
|
+
user: { id: "", name: "", email: "", avatar_url: null, email_verified: true }
|
|
296
|
+
};
|
|
297
|
+
saveCredentials(creds);
|
|
298
|
+
try {
|
|
299
|
+
const profile = await getProfile(apiUrl);
|
|
300
|
+
creds.user = profile;
|
|
301
|
+
saveCredentials(creds);
|
|
302
|
+
s.stop(`Authenticated as ${profile.email}`);
|
|
303
|
+
} catch {
|
|
304
|
+
s.stop("Authenticated successfully");
|
|
305
|
+
}
|
|
306
|
+
return creds;
|
|
307
|
+
} catch (err) {
|
|
308
|
+
close();
|
|
309
|
+
s.stop("Authentication failed");
|
|
310
|
+
throw err;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
245
313
|
|
|
246
314
|
// src/lib/credentials.ts
|
|
247
|
-
|
|
315
|
+
import * as clack2 from "@clack/prompts";
|
|
316
|
+
async function requireAuth(apiUrl) {
|
|
248
317
|
const creds = getCredentials();
|
|
249
|
-
if (
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return creds;
|
|
318
|
+
if (creds && creds.access_token) return creds;
|
|
319
|
+
clack2.log.info("You need to log in to continue.");
|
|
320
|
+
return await performOAuthLogin(apiUrl);
|
|
253
321
|
}
|
|
254
322
|
async function refreshAccessToken(apiUrl) {
|
|
255
323
|
const creds = getCredentials();
|
|
@@ -371,7 +439,7 @@ function registerLoginCommand(program2) {
|
|
|
371
439
|
if (opts.email) {
|
|
372
440
|
await loginWithEmail(json, apiUrl);
|
|
373
441
|
} else {
|
|
374
|
-
await loginWithOAuth(json, apiUrl
|
|
442
|
+
await loginWithOAuth(json, apiUrl);
|
|
375
443
|
}
|
|
376
444
|
} catch (err) {
|
|
377
445
|
if (err instanceof Error && err.message.includes("cancelled")) {
|
|
@@ -383,28 +451,28 @@ function registerLoginCommand(program2) {
|
|
|
383
451
|
}
|
|
384
452
|
async function loginWithEmail(json, apiUrl) {
|
|
385
453
|
if (!json) {
|
|
386
|
-
|
|
454
|
+
clack3.intro("InsForge CLI");
|
|
387
455
|
}
|
|
388
|
-
const email = json ? process.env.INSFORGE_EMAIL : await
|
|
456
|
+
const email = json ? process.env.INSFORGE_EMAIL : await clack3.text({
|
|
389
457
|
message: "Email:",
|
|
390
458
|
validate: (v) => v.includes("@") ? void 0 : "Please enter a valid email"
|
|
391
459
|
});
|
|
392
|
-
if (
|
|
393
|
-
|
|
460
|
+
if (clack3.isCancel(email)) {
|
|
461
|
+
clack3.cancel("Login cancelled.");
|
|
394
462
|
throw new Error("cancelled");
|
|
395
463
|
}
|
|
396
|
-
const password2 = json ? process.env.INSFORGE_PASSWORD : await
|
|
464
|
+
const password2 = json ? process.env.INSFORGE_PASSWORD : await clack3.password({
|
|
397
465
|
message: "Password:"
|
|
398
466
|
});
|
|
399
|
-
if (
|
|
400
|
-
|
|
467
|
+
if (clack3.isCancel(password2)) {
|
|
468
|
+
clack3.cancel("Login cancelled.");
|
|
401
469
|
throw new Error("cancelled");
|
|
402
470
|
}
|
|
403
471
|
if (!email || !password2) {
|
|
404
472
|
throw new Error("Email and password are required. Set INSFORGE_EMAIL and INSFORGE_PASSWORD environment variables for non-interactive mode.");
|
|
405
473
|
}
|
|
406
474
|
if (!json) {
|
|
407
|
-
const s =
|
|
475
|
+
const s = clack3.spinner();
|
|
408
476
|
s.start("Authenticating...");
|
|
409
477
|
const result = await login(email, password2, apiUrl);
|
|
410
478
|
const creds = {
|
|
@@ -414,7 +482,7 @@ async function loginWithEmail(json, apiUrl) {
|
|
|
414
482
|
};
|
|
415
483
|
saveCredentials(creds);
|
|
416
484
|
s.stop(`Authenticated as ${result.user.email}`);
|
|
417
|
-
|
|
485
|
+
clack3.outro("Done");
|
|
418
486
|
} else {
|
|
419
487
|
const result = await login(email, password2, apiUrl);
|
|
420
488
|
const creds = {
|
|
@@ -426,105 +494,15 @@ async function loginWithEmail(json, apiUrl) {
|
|
|
426
494
|
console.log(JSON.stringify({ success: true, user: result.user }));
|
|
427
495
|
}
|
|
428
496
|
}
|
|
429
|
-
async function loginWithOAuth(json, apiUrl
|
|
430
|
-
const platformUrl = getPlatformApiUrl(apiUrl);
|
|
431
|
-
const config = getGlobalConfig();
|
|
432
|
-
const clientId = clientIdOverride ?? config.oauth_client_id ?? DEFAULT_CLIENT_ID;
|
|
433
|
-
const pkce = generatePKCE();
|
|
434
|
-
const state = generateState();
|
|
435
|
-
const { port, result, close } = await startCallbackServer();
|
|
436
|
-
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
437
|
-
const authUrl = buildAuthorizeUrl({
|
|
438
|
-
platformUrl,
|
|
439
|
-
clientId,
|
|
440
|
-
redirectUri,
|
|
441
|
-
codeChallenge: pkce.code_challenge,
|
|
442
|
-
state,
|
|
443
|
-
scopes: OAUTH_SCOPES
|
|
444
|
-
});
|
|
497
|
+
async function loginWithOAuth(json, apiUrl) {
|
|
445
498
|
if (!json) {
|
|
446
|
-
|
|
447
|
-
clack.log.info("Opening browser for authentication...");
|
|
448
|
-
clack.log.info(`If browser doesn't open, visit:
|
|
449
|
-
${authUrl}`);
|
|
450
|
-
}
|
|
451
|
-
try {
|
|
452
|
-
const open = (await import("open")).default;
|
|
453
|
-
await open(authUrl);
|
|
454
|
-
} catch {
|
|
455
|
-
if (!json) {
|
|
456
|
-
clack.log.warn(`Could not open browser. Please visit the URL above.`);
|
|
457
|
-
}
|
|
499
|
+
clack3.intro("InsForge CLI");
|
|
458
500
|
}
|
|
501
|
+
const creds = await performOAuthLogin(apiUrl);
|
|
459
502
|
if (!json) {
|
|
460
|
-
|
|
461
|
-
s.start("Waiting for authentication...");
|
|
462
|
-
try {
|
|
463
|
-
const callbackResult = await result;
|
|
464
|
-
close();
|
|
465
|
-
if (callbackResult.state !== state) {
|
|
466
|
-
s.stop("Authentication failed");
|
|
467
|
-
throw new Error("State mismatch. Possible CSRF attack.");
|
|
468
|
-
}
|
|
469
|
-
s.message("Exchanging authorization code...");
|
|
470
|
-
const tokens = await exchangeCodeForTokens({
|
|
471
|
-
platformUrl,
|
|
472
|
-
code: callbackResult.code,
|
|
473
|
-
redirectUri,
|
|
474
|
-
clientId,
|
|
475
|
-
codeVerifier: pkce.code_verifier
|
|
476
|
-
});
|
|
477
|
-
const creds = {
|
|
478
|
-
access_token: tokens.access_token,
|
|
479
|
-
refresh_token: tokens.refresh_token,
|
|
480
|
-
user: { id: "", name: "", email: "", avatar_url: null, email_verified: true }
|
|
481
|
-
};
|
|
482
|
-
saveCredentials(creds);
|
|
483
|
-
try {
|
|
484
|
-
const profile = await getProfile(apiUrl);
|
|
485
|
-
creds.user = profile;
|
|
486
|
-
saveCredentials(creds);
|
|
487
|
-
s.stop(`Authenticated as ${profile.email}`);
|
|
488
|
-
} catch {
|
|
489
|
-
s.stop("Authenticated successfully");
|
|
490
|
-
}
|
|
491
|
-
clack.outro("Done");
|
|
492
|
-
} catch (err) {
|
|
493
|
-
close();
|
|
494
|
-
s.stop("Authentication failed");
|
|
495
|
-
throw err;
|
|
496
|
-
}
|
|
503
|
+
clack3.outro("Done");
|
|
497
504
|
} else {
|
|
498
|
-
|
|
499
|
-
const callbackResult = await result;
|
|
500
|
-
close();
|
|
501
|
-
if (callbackResult.state !== state) {
|
|
502
|
-
throw new Error("State mismatch.");
|
|
503
|
-
}
|
|
504
|
-
const tokens = await exchangeCodeForTokens({
|
|
505
|
-
platformUrl,
|
|
506
|
-
code: callbackResult.code,
|
|
507
|
-
redirectUri,
|
|
508
|
-
clientId,
|
|
509
|
-
codeVerifier: pkce.code_verifier
|
|
510
|
-
});
|
|
511
|
-
const creds = {
|
|
512
|
-
access_token: tokens.access_token,
|
|
513
|
-
refresh_token: tokens.refresh_token,
|
|
514
|
-
user: { id: "", name: "", email: "", avatar_url: null, email_verified: true }
|
|
515
|
-
};
|
|
516
|
-
saveCredentials(creds);
|
|
517
|
-
try {
|
|
518
|
-
const profile = await getProfile(apiUrl);
|
|
519
|
-
creds.user = profile;
|
|
520
|
-
saveCredentials(creds);
|
|
521
|
-
} catch {
|
|
522
|
-
}
|
|
523
|
-
console.log(JSON.stringify({ success: true, user: creds.user }));
|
|
524
|
-
} catch (err) {
|
|
525
|
-
close();
|
|
526
|
-
throw err;
|
|
527
|
-
}
|
|
505
|
+
console.log(JSON.stringify({ success: true, user: creds.user }));
|
|
528
506
|
}
|
|
529
507
|
}
|
|
530
508
|
|
|
@@ -572,7 +550,7 @@ function registerWhoamiCommand(program2) {
|
|
|
572
550
|
program2.command("whoami").description("Show current authenticated user").action(async (_opts, cmd) => {
|
|
573
551
|
const { json, apiUrl } = getRootOpts(cmd);
|
|
574
552
|
try {
|
|
575
|
-
requireAuth();
|
|
553
|
+
await requireAuth(apiUrl);
|
|
576
554
|
const profile = await getProfile(apiUrl);
|
|
577
555
|
if (json) {
|
|
578
556
|
outputJson(profile);
|
|
@@ -592,7 +570,7 @@ function registerOrgsCommands(orgsCmd2) {
|
|
|
592
570
|
orgsCmd2.command("list").description("List all organizations").action(async (_opts, cmd) => {
|
|
593
571
|
const { json, apiUrl } = getRootOpts(cmd);
|
|
594
572
|
try {
|
|
595
|
-
requireAuth();
|
|
573
|
+
await requireAuth(apiUrl);
|
|
596
574
|
const orgs = await listOrganizations(apiUrl);
|
|
597
575
|
if (json) {
|
|
598
576
|
outputJson(orgs);
|
|
@@ -617,12 +595,12 @@ function registerOrgsCommands(orgsCmd2) {
|
|
|
617
595
|
}
|
|
618
596
|
|
|
619
597
|
// src/commands/projects/list.ts
|
|
620
|
-
import * as
|
|
598
|
+
import * as clack4 from "@clack/prompts";
|
|
621
599
|
function registerProjectsCommands(projectsCmd2) {
|
|
622
600
|
projectsCmd2.command("list").description("List all projects in an organization").option("--org-id <id>", "Organization ID (uses default if not specified)").action(async (opts, cmd) => {
|
|
623
601
|
const { json, apiUrl } = getRootOpts(cmd);
|
|
624
602
|
try {
|
|
625
|
-
requireAuth();
|
|
603
|
+
await requireAuth(apiUrl);
|
|
626
604
|
let orgId = opts.orgId ?? getGlobalConfig().default_org_id;
|
|
627
605
|
if (!orgId) {
|
|
628
606
|
const orgs = await listOrganizations(apiUrl);
|
|
@@ -632,14 +610,14 @@ function registerProjectsCommands(projectsCmd2) {
|
|
|
632
610
|
if (orgs.length === 1) {
|
|
633
611
|
orgId = orgs[0].id;
|
|
634
612
|
} else if (!json) {
|
|
635
|
-
const selected = await
|
|
613
|
+
const selected = await clack4.select({
|
|
636
614
|
message: "Select an organization:",
|
|
637
615
|
options: orgs.map((o) => ({
|
|
638
616
|
value: o.id,
|
|
639
617
|
label: o.name
|
|
640
618
|
}))
|
|
641
619
|
});
|
|
642
|
-
if (
|
|
620
|
+
if (clack4.isCancel(selected)) {
|
|
643
621
|
process.exit(0);
|
|
644
622
|
}
|
|
645
623
|
orgId = selected;
|
|
@@ -667,23 +645,23 @@ function registerProjectsCommands(projectsCmd2) {
|
|
|
667
645
|
}
|
|
668
646
|
|
|
669
647
|
// src/commands/projects/link.ts
|
|
670
|
-
import * as
|
|
648
|
+
import * as clack6 from "@clack/prompts";
|
|
671
649
|
|
|
672
650
|
// src/lib/skills.ts
|
|
673
651
|
import { exec } from "child_process";
|
|
674
652
|
import { promisify } from "util";
|
|
675
|
-
import * as
|
|
653
|
+
import * as clack5 from "@clack/prompts";
|
|
676
654
|
var execAsync = promisify(exec);
|
|
677
655
|
async function installSkills(json) {
|
|
678
656
|
try {
|
|
679
|
-
if (!json)
|
|
657
|
+
if (!json) clack5.log.info("Installing InsForge agent skills...");
|
|
680
658
|
await execAsync("npx skills add insforge/agent-skills -y -a antigravity -a augment -a claude-code -a cline -a codex -a cursor -a gemini-cli -a github-copilot -a kilo -a qoder -a qwen-code -a roo -a trae -a windsurf", {
|
|
681
659
|
cwd: process.cwd(),
|
|
682
660
|
timeout: 6e4
|
|
683
661
|
});
|
|
684
|
-
if (!json)
|
|
662
|
+
if (!json) clack5.log.success("InsForge agent skills installed.");
|
|
685
663
|
} catch {
|
|
686
|
-
if (!json)
|
|
664
|
+
if (!json) clack5.log.warn("Failed to install agent skills. You can run manually: npx skills add insforge/agent-skills");
|
|
687
665
|
}
|
|
688
666
|
}
|
|
689
667
|
|
|
@@ -695,7 +673,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
695
673
|
program2.command("link").description("Link current directory to an InsForge project").option("--project-id <id>", "Project ID to link").option("--org-id <id>", "Organization ID").action(async (opts, cmd) => {
|
|
696
674
|
const { json, apiUrl } = getRootOpts(cmd);
|
|
697
675
|
try {
|
|
698
|
-
requireAuth();
|
|
676
|
+
await requireAuth(apiUrl);
|
|
699
677
|
let orgId = opts.orgId;
|
|
700
678
|
let projectId = opts.projectId;
|
|
701
679
|
if (!orgId) {
|
|
@@ -706,14 +684,14 @@ function registerProjectLinkCommand(program2) {
|
|
|
706
684
|
if (json) {
|
|
707
685
|
throw new CLIError("Specify --org-id in JSON mode.");
|
|
708
686
|
}
|
|
709
|
-
const selected = await
|
|
687
|
+
const selected = await clack6.select({
|
|
710
688
|
message: "Select an organization:",
|
|
711
689
|
options: orgs.map((o) => ({
|
|
712
690
|
value: o.id,
|
|
713
691
|
label: o.name
|
|
714
692
|
}))
|
|
715
693
|
});
|
|
716
|
-
if (
|
|
694
|
+
if (clack6.isCancel(selected)) process.exit(0);
|
|
717
695
|
orgId = selected;
|
|
718
696
|
}
|
|
719
697
|
const config = getGlobalConfig();
|
|
@@ -727,14 +705,14 @@ function registerProjectLinkCommand(program2) {
|
|
|
727
705
|
if (json) {
|
|
728
706
|
throw new CLIError("Specify --project-id in JSON mode.");
|
|
729
707
|
}
|
|
730
|
-
const selected = await
|
|
708
|
+
const selected = await clack6.select({
|
|
731
709
|
message: "Select a project to link:",
|
|
732
710
|
options: projects.map((p) => ({
|
|
733
711
|
value: p.id,
|
|
734
712
|
label: `${p.name} (${p.region}, ${p.status})`
|
|
735
713
|
}))
|
|
736
714
|
});
|
|
737
|
-
if (
|
|
715
|
+
if (clack6.isCancel(selected)) process.exit(0);
|
|
738
716
|
projectId = selected;
|
|
739
717
|
}
|
|
740
718
|
const [project, apiKey] = await Promise.all([
|
|
@@ -796,7 +774,7 @@ function registerDbCommands(dbCmd2) {
|
|
|
796
774
|
dbCmd2.command("query <sql>").description("Execute a SQL query against the database").option("--unrestricted", "Use unrestricted mode (allows system table access)").action(async (sql, opts, cmd) => {
|
|
797
775
|
const { json } = getRootOpts(cmd);
|
|
798
776
|
try {
|
|
799
|
-
requireAuth();
|
|
777
|
+
await requireAuth();
|
|
800
778
|
const endpoint = opts.unrestricted ? "/api/database/advance/rawsql/unrestricted" : "/api/database/advance/rawsql";
|
|
801
779
|
const res = await ossFetch(endpoint, {
|
|
802
780
|
method: "POST",
|
|
@@ -832,7 +810,7 @@ function registerDbTablesCommand(dbCmd2) {
|
|
|
832
810
|
dbCmd2.command("tables").description("List all database tables").action(async (_opts, cmd) => {
|
|
833
811
|
const { json } = getRootOpts(cmd);
|
|
834
812
|
try {
|
|
835
|
-
requireAuth();
|
|
813
|
+
await requireAuth();
|
|
836
814
|
const res = await ossFetch("/api/database/tables");
|
|
837
815
|
const tables = await res.json();
|
|
838
816
|
if (json) {
|
|
@@ -871,7 +849,7 @@ function registerDbFunctionsCommand(dbCmd2) {
|
|
|
871
849
|
dbCmd2.command("functions").description("List all database functions").action(async (_opts, cmd) => {
|
|
872
850
|
const { json } = getRootOpts(cmd);
|
|
873
851
|
try {
|
|
874
|
-
requireAuth();
|
|
852
|
+
await requireAuth();
|
|
875
853
|
const res = await ossFetch("/api/database/functions");
|
|
876
854
|
const raw = await res.json();
|
|
877
855
|
const functions = extractArray(raw);
|
|
@@ -911,7 +889,7 @@ function registerDbIndexesCommand(dbCmd2) {
|
|
|
911
889
|
dbCmd2.command("indexes").description("List all database indexes").action(async (_opts, cmd) => {
|
|
912
890
|
const { json } = getRootOpts(cmd);
|
|
913
891
|
try {
|
|
914
|
-
requireAuth();
|
|
892
|
+
await requireAuth();
|
|
915
893
|
const res = await ossFetch("/api/database/indexes");
|
|
916
894
|
const raw = await res.json();
|
|
917
895
|
const indexes = extractArray2(raw);
|
|
@@ -957,7 +935,7 @@ function registerDbPoliciesCommand(dbCmd2) {
|
|
|
957
935
|
dbCmd2.command("policies").description("List all RLS policies").action(async (_opts, cmd) => {
|
|
958
936
|
const { json } = getRootOpts(cmd);
|
|
959
937
|
try {
|
|
960
|
-
requireAuth();
|
|
938
|
+
await requireAuth();
|
|
961
939
|
const res = await ossFetch("/api/database/policies");
|
|
962
940
|
const raw = await res.json();
|
|
963
941
|
const policies = extractArray3(raw);
|
|
@@ -1004,7 +982,7 @@ function registerDbTriggersCommand(dbCmd2) {
|
|
|
1004
982
|
dbCmd2.command("triggers").description("List all database triggers").action(async (_opts, cmd) => {
|
|
1005
983
|
const { json } = getRootOpts(cmd);
|
|
1006
984
|
try {
|
|
1007
|
-
requireAuth();
|
|
985
|
+
await requireAuth();
|
|
1008
986
|
const res = await ossFetch("/api/database/triggers");
|
|
1009
987
|
const raw = await res.json();
|
|
1010
988
|
const triggers = extractArray4(raw);
|
|
@@ -1038,7 +1016,7 @@ function registerDbRpcCommand(dbCmd2) {
|
|
|
1038
1016
|
dbCmd2.command("rpc <functionName>").description("Call a database function via RPC").option("--data <json>", "JSON body to pass as function parameters").action(async (functionName, opts, cmd) => {
|
|
1039
1017
|
const { json } = getRootOpts(cmd);
|
|
1040
1018
|
try {
|
|
1041
|
-
requireAuth();
|
|
1019
|
+
await requireAuth();
|
|
1042
1020
|
const body = opts.data ? JSON.stringify(JSON.parse(opts.data)) : void 0;
|
|
1043
1021
|
const res = await ossFetch(`/api/database/rpc/${encodeURIComponent(functionName)}`, {
|
|
1044
1022
|
method: body ? "POST" : "GET",
|
|
@@ -1062,7 +1040,7 @@ function registerDbExportCommand(dbCmd2) {
|
|
|
1062
1040
|
dbCmd2.command("export").description("Export database schema and/or data").option("--format <format>", "Export format: sql or json", "sql").option("--tables <tables>", "Comma-separated list of tables to export (default: all)").option("--no-data", "Exclude table data (schema only)").option("--include-functions", "Include database functions").option("--include-sequences", "Include sequences").option("--include-views", "Include views").option("--row-limit <n>", "Maximum rows per table").option("-o, --output <file>", "Output file path (default: stdout)").action(async (opts, cmd) => {
|
|
1063
1041
|
const { json } = getRootOpts(cmd);
|
|
1064
1042
|
try {
|
|
1065
|
-
requireAuth();
|
|
1043
|
+
await requireAuth();
|
|
1066
1044
|
const body = {
|
|
1067
1045
|
format: opts.format,
|
|
1068
1046
|
includeData: opts.data !== false
|
|
@@ -1117,7 +1095,7 @@ function registerDbImportCommand(dbCmd2) {
|
|
|
1117
1095
|
dbCmd2.command("import <file>").description("Import database from a local SQL file").option("--truncate", "Truncate existing tables before import").action(async (file, opts, cmd) => {
|
|
1118
1096
|
const { json } = getRootOpts(cmd);
|
|
1119
1097
|
try {
|
|
1120
|
-
requireAuth();
|
|
1098
|
+
await requireAuth();
|
|
1121
1099
|
const config = getProjectConfig();
|
|
1122
1100
|
if (!config) throw new ProjectNotLinkedError();
|
|
1123
1101
|
const fileContent = readFileSync2(file);
|
|
@@ -1155,7 +1133,7 @@ function registerRecordsCommands(recordsCmd2) {
|
|
|
1155
1133
|
recordsCmd2.command("list <table>").description("List records from a table").option("--select <columns>", "Columns to select (comma-separated)").option("--filter <filter>", 'Filter expression (e.g. "name=eq.John")').option("--order <order>", 'Order by (e.g. "created_at.desc")').option("--limit <n>", "Limit number of records", parseInt).option("--offset <n>", "Offset for pagination", parseInt).action(async (table, opts, cmd) => {
|
|
1156
1134
|
const { json } = getRootOpts(cmd);
|
|
1157
1135
|
try {
|
|
1158
|
-
requireAuth();
|
|
1136
|
+
await requireAuth();
|
|
1159
1137
|
const params = new URLSearchParams();
|
|
1160
1138
|
if (opts.select) params.set("select", opts.select);
|
|
1161
1139
|
if (opts.filter) params.set(opts.filter.split("=")[0], opts.filter.split("=").slice(1).join("="));
|
|
@@ -1197,7 +1175,7 @@ function registerRecordsCreateCommand(recordsCmd2) {
|
|
|
1197
1175
|
recordsCmd2.command("create <table>").description("Create record(s) in a table").option("--data <json>", "JSON data to insert (object or array of objects)").action(async (table, opts, cmd) => {
|
|
1198
1176
|
const { json } = getRootOpts(cmd);
|
|
1199
1177
|
try {
|
|
1200
|
-
requireAuth();
|
|
1178
|
+
await requireAuth();
|
|
1201
1179
|
if (!opts.data) {
|
|
1202
1180
|
throw new CLIError(`--data is required. Example: --data '{"name":"John"}'`);
|
|
1203
1181
|
}
|
|
@@ -1233,7 +1211,7 @@ function registerRecordsUpdateCommand(recordsCmd2) {
|
|
|
1233
1211
|
recordsCmd2.command("update <table>").description("Update records in a table matching a filter").option("--filter <filter>", 'Filter expression (e.g. "id=eq.123")').option("--data <json>", "JSON data to update").action(async (table, opts, cmd) => {
|
|
1234
1212
|
const { json } = getRootOpts(cmd);
|
|
1235
1213
|
try {
|
|
1236
|
-
requireAuth();
|
|
1214
|
+
await requireAuth();
|
|
1237
1215
|
if (!opts.filter) {
|
|
1238
1216
|
throw new CLIError("--filter is required to prevent accidental updates to all rows.");
|
|
1239
1217
|
}
|
|
@@ -1274,7 +1252,7 @@ function registerRecordsDeleteCommand(recordsCmd2) {
|
|
|
1274
1252
|
recordsCmd2.command("delete <table>").description("Delete records from a table matching a filter").option("--filter <filter>", 'Filter expression (e.g. "id=eq.123")').action(async (table, opts, cmd) => {
|
|
1275
1253
|
const { json } = getRootOpts(cmd);
|
|
1276
1254
|
try {
|
|
1277
|
-
requireAuth();
|
|
1255
|
+
await requireAuth();
|
|
1278
1256
|
if (!opts.filter) {
|
|
1279
1257
|
throw new CLIError("--filter is required to prevent accidental deletion of all rows.");
|
|
1280
1258
|
}
|
|
@@ -1303,7 +1281,7 @@ function registerFunctionsCommands(functionsCmd2) {
|
|
|
1303
1281
|
functionsCmd2.command("list").description("List all edge functions").action(async (_opts, cmd) => {
|
|
1304
1282
|
const { json } = getRootOpts(cmd);
|
|
1305
1283
|
try {
|
|
1306
|
-
requireAuth();
|
|
1284
|
+
await requireAuth();
|
|
1307
1285
|
const res = await ossFetch("/api/functions");
|
|
1308
1286
|
const data = await res.json();
|
|
1309
1287
|
const functions = data.functions ?? [];
|
|
@@ -1337,7 +1315,7 @@ function registerFunctionsDeployCommand(functionsCmd2) {
|
|
|
1337
1315
|
functionsCmd2.command("deploy <slug>").description("Deploy an edge function (create or update)").option("--file <path>", "Path to the function source file").option("--name <name>", "Function display name").option("--description <desc>", "Function description").action(async (slug, opts, cmd) => {
|
|
1338
1316
|
const { json } = getRootOpts(cmd);
|
|
1339
1317
|
try {
|
|
1340
|
-
requireAuth();
|
|
1318
|
+
await requireAuth();
|
|
1341
1319
|
const filePath = opts.file ?? join2(process.cwd(), "insforge", "functions", slug, "index.ts");
|
|
1342
1320
|
if (!existsSync2(filePath)) {
|
|
1343
1321
|
throw new CLIError(
|
|
@@ -1382,7 +1360,7 @@ function registerFunctionsInvokeCommand(functionsCmd2) {
|
|
|
1382
1360
|
functionsCmd2.command("invoke <slug>").description("Invoke an edge function").option("--data <json>", "JSON body to send to the function").option("--method <method>", "HTTP method (GET, POST, PUT, PATCH, DELETE)", "POST").action(async (slug, opts, cmd) => {
|
|
1383
1361
|
const { json } = getRootOpts(cmd);
|
|
1384
1362
|
try {
|
|
1385
|
-
requireAuth();
|
|
1363
|
+
await requireAuth();
|
|
1386
1364
|
const config = getProjectConfig();
|
|
1387
1365
|
if (!config) throw new ProjectNotLinkedError();
|
|
1388
1366
|
const method = opts.method.toUpperCase();
|
|
@@ -1431,7 +1409,7 @@ function registerFunctionsCodeCommand(functionsCmd2) {
|
|
|
1431
1409
|
functionsCmd2.command("code <slug>").description("Fetch and display the source code of an edge function").action(async (slug, _opts, cmd) => {
|
|
1432
1410
|
const { json } = getRootOpts(cmd);
|
|
1433
1411
|
try {
|
|
1434
|
-
requireAuth();
|
|
1412
|
+
await requireAuth();
|
|
1435
1413
|
const res = await ossFetch(`/api/functions/${encodeURIComponent(slug)}`);
|
|
1436
1414
|
const fn = await res.json();
|
|
1437
1415
|
if (json) {
|
|
@@ -1455,7 +1433,7 @@ function registerStorageBucketsCommand(storageCmd2) {
|
|
|
1455
1433
|
storageCmd2.command("buckets").description("List all storage buckets").action(async (_opts, cmd) => {
|
|
1456
1434
|
const { json } = getRootOpts(cmd);
|
|
1457
1435
|
try {
|
|
1458
|
-
requireAuth();
|
|
1436
|
+
await requireAuth();
|
|
1459
1437
|
const res = await ossFetch("/api/storage/buckets");
|
|
1460
1438
|
const raw = await res.json();
|
|
1461
1439
|
let buckets;
|
|
@@ -1492,7 +1470,7 @@ function registerStorageUploadCommand(storageCmd2) {
|
|
|
1492
1470
|
storageCmd2.command("upload <file>").description("Upload a file to a storage bucket").requiredOption("--bucket <name>", "Target bucket name").option("--key <objectKey>", "Object key (defaults to filename)").action(async (file, opts, cmd) => {
|
|
1493
1471
|
const { json } = getRootOpts(cmd);
|
|
1494
1472
|
try {
|
|
1495
|
-
requireAuth();
|
|
1473
|
+
await requireAuth();
|
|
1496
1474
|
const config = getProjectConfig();
|
|
1497
1475
|
if (!config) throw new ProjectNotLinkedError();
|
|
1498
1476
|
if (!existsSync3(file)) {
|
|
@@ -1535,7 +1513,7 @@ function registerStorageDownloadCommand(storageCmd2) {
|
|
|
1535
1513
|
storageCmd2.command("download <objectKey>").description("Download a file from a storage bucket").requiredOption("--bucket <name>", "Source bucket name").option("--output <path>", "Output file path (defaults to current directory)").action(async (objectKey, opts, cmd) => {
|
|
1536
1514
|
const { json } = getRootOpts(cmd);
|
|
1537
1515
|
try {
|
|
1538
|
-
requireAuth();
|
|
1516
|
+
await requireAuth();
|
|
1539
1517
|
const config = getProjectConfig();
|
|
1540
1518
|
if (!config) throw new ProjectNotLinkedError();
|
|
1541
1519
|
const bucketName = opts.bucket;
|
|
@@ -1568,7 +1546,7 @@ function registerStorageCreateBucketCommand(storageCmd2) {
|
|
|
1568
1546
|
storageCmd2.command("create-bucket <name>").description("Create a new storage bucket").option("--public", "Make the bucket publicly accessible (default)").option("--private", "Make the bucket private").action(async (name, opts, cmd) => {
|
|
1569
1547
|
const { json } = getRootOpts(cmd);
|
|
1570
1548
|
try {
|
|
1571
|
-
requireAuth();
|
|
1549
|
+
await requireAuth();
|
|
1572
1550
|
const isPublic = !opts.private;
|
|
1573
1551
|
const res = await ossFetch("/api/storage/buckets", {
|
|
1574
1552
|
method: "POST",
|
|
@@ -1587,17 +1565,17 @@ function registerStorageCreateBucketCommand(storageCmd2) {
|
|
|
1587
1565
|
}
|
|
1588
1566
|
|
|
1589
1567
|
// src/commands/storage/delete-bucket.ts
|
|
1590
|
-
import * as
|
|
1568
|
+
import * as clack7 from "@clack/prompts";
|
|
1591
1569
|
function registerStorageDeleteBucketCommand(storageCmd2) {
|
|
1592
1570
|
storageCmd2.command("delete-bucket <name>").description("Delete a storage bucket and all its objects").action(async (name, _opts, cmd) => {
|
|
1593
1571
|
const { json, yes } = getRootOpts(cmd);
|
|
1594
1572
|
try {
|
|
1595
|
-
requireAuth();
|
|
1573
|
+
await requireAuth();
|
|
1596
1574
|
if (!yes && !json) {
|
|
1597
|
-
const
|
|
1575
|
+
const confirm6 = await clack7.confirm({
|
|
1598
1576
|
message: `Delete bucket "${name}" and all its objects? This cannot be undone.`
|
|
1599
1577
|
});
|
|
1600
|
-
if (!
|
|
1578
|
+
if (!confirm6 || clack7.isCancel(confirm6)) {
|
|
1601
1579
|
process.exit(0);
|
|
1602
1580
|
}
|
|
1603
1581
|
}
|
|
@@ -1626,7 +1604,7 @@ function registerStorageListObjectsCommand(storageCmd2) {
|
|
|
1626
1604
|
storageCmd2.command("list-objects <bucket>").description("List objects in a storage bucket").option("--limit <n>", "Maximum number of objects to return", "100").option("--offset <n>", "Number of objects to skip", "0").option("--prefix <prefix>", "Filter objects by key prefix").option("--search <term>", "Search objects by key (partial match)").option("--sort <field>", "Sort by field: key, size, uploadedAt (default: key)").action(async (bucket, opts, cmd) => {
|
|
1627
1605
|
const { json } = getRootOpts(cmd);
|
|
1628
1606
|
try {
|
|
1629
|
-
requireAuth();
|
|
1607
|
+
await requireAuth();
|
|
1630
1608
|
const params = new URLSearchParams();
|
|
1631
1609
|
params.set("limit", opts.limit);
|
|
1632
1610
|
params.set("offset", opts.offset);
|
|
@@ -1675,9 +1653,166 @@ function registerStorageListObjectsCommand(storageCmd2) {
|
|
|
1675
1653
|
import { exec as exec2 } from "child_process";
|
|
1676
1654
|
import { tmpdir } from "os";
|
|
1677
1655
|
import { promisify as promisify2 } from "util";
|
|
1678
|
-
import * as
|
|
1656
|
+
import * as fs2 from "fs/promises";
|
|
1657
|
+
import * as path2 from "path";
|
|
1658
|
+
import * as clack9 from "@clack/prompts";
|
|
1659
|
+
|
|
1660
|
+
// src/commands/deployments/deploy.ts
|
|
1679
1661
|
import * as path from "path";
|
|
1680
|
-
import * as
|
|
1662
|
+
import * as fs from "fs/promises";
|
|
1663
|
+
import * as clack8 from "@clack/prompts";
|
|
1664
|
+
import archiver from "archiver";
|
|
1665
|
+
var POLL_INTERVAL_MS = 5e3;
|
|
1666
|
+
var POLL_TIMEOUT_MS = 12e4;
|
|
1667
|
+
var EXCLUDE_PATTERNS = [
|
|
1668
|
+
"node_modules",
|
|
1669
|
+
".git",
|
|
1670
|
+
".next",
|
|
1671
|
+
".env",
|
|
1672
|
+
".env.local",
|
|
1673
|
+
"dist",
|
|
1674
|
+
"build",
|
|
1675
|
+
".DS_Store",
|
|
1676
|
+
".insforge"
|
|
1677
|
+
];
|
|
1678
|
+
function shouldExclude(name) {
|
|
1679
|
+
const normalized = name.replace(/\\/g, "/");
|
|
1680
|
+
for (const pattern of EXCLUDE_PATTERNS) {
|
|
1681
|
+
if (normalized === pattern || normalized.startsWith(pattern + "/") || normalized.endsWith("/" + pattern) || normalized.includes("/" + pattern + "/")) {
|
|
1682
|
+
return true;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
if (normalized.endsWith(".log")) return true;
|
|
1686
|
+
return false;
|
|
1687
|
+
}
|
|
1688
|
+
async function createZipBuffer(sourceDir) {
|
|
1689
|
+
return new Promise((resolve2, reject) => {
|
|
1690
|
+
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
1691
|
+
const chunks = [];
|
|
1692
|
+
archive.on("data", (chunk) => chunks.push(chunk));
|
|
1693
|
+
archive.on("end", () => resolve2(Buffer.concat(chunks)));
|
|
1694
|
+
archive.on("error", (err) => reject(err));
|
|
1695
|
+
archive.directory(sourceDir, false, (entry) => {
|
|
1696
|
+
if (shouldExclude(entry.name)) return false;
|
|
1697
|
+
return entry;
|
|
1698
|
+
});
|
|
1699
|
+
void archive.finalize();
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
async function deployProject(opts) {
|
|
1703
|
+
const { sourceDir, startBody = {}, spinner: s } = opts;
|
|
1704
|
+
s?.start("Creating deployment...");
|
|
1705
|
+
const createRes = await ossFetch("/api/deployments", { method: "POST" });
|
|
1706
|
+
const { id: deploymentId, uploadUrl, uploadFields } = await createRes.json();
|
|
1707
|
+
s?.message("Compressing source files...");
|
|
1708
|
+
const zipBuffer = await createZipBuffer(sourceDir);
|
|
1709
|
+
s?.message("Uploading...");
|
|
1710
|
+
const formData = new FormData();
|
|
1711
|
+
for (const [key, value] of Object.entries(uploadFields)) {
|
|
1712
|
+
formData.append(key, value);
|
|
1713
|
+
}
|
|
1714
|
+
formData.append(
|
|
1715
|
+
"file",
|
|
1716
|
+
new Blob([zipBuffer], { type: "application/zip" }),
|
|
1717
|
+
"deployment.zip"
|
|
1718
|
+
);
|
|
1719
|
+
const uploadRes = await fetch(uploadUrl, { method: "POST", body: formData });
|
|
1720
|
+
if (!uploadRes.ok) {
|
|
1721
|
+
const uploadErr = await uploadRes.text();
|
|
1722
|
+
throw new CLIError(`Failed to upload: ${uploadErr}`);
|
|
1723
|
+
}
|
|
1724
|
+
s?.message("Starting deployment...");
|
|
1725
|
+
const startRes = await ossFetch(`/api/deployments/${deploymentId}/start`, {
|
|
1726
|
+
method: "POST",
|
|
1727
|
+
body: JSON.stringify(startBody)
|
|
1728
|
+
});
|
|
1729
|
+
await startRes.json();
|
|
1730
|
+
s?.message("Building and deploying...");
|
|
1731
|
+
const startTime = Date.now();
|
|
1732
|
+
let deployment = null;
|
|
1733
|
+
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
1734
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
1735
|
+
try {
|
|
1736
|
+
const statusRes = await ossFetch(`/api/deployments/${deploymentId}`);
|
|
1737
|
+
deployment = await statusRes.json();
|
|
1738
|
+
if (deployment.status === "ready" || deployment.status === "READY") {
|
|
1739
|
+
break;
|
|
1740
|
+
}
|
|
1741
|
+
if (deployment.status === "error" || deployment.status === "ERROR" || deployment.status === "canceled") {
|
|
1742
|
+
s?.stop("Deployment failed");
|
|
1743
|
+
throw new CLIError(deployment.error ?? `Deployment failed with status: ${deployment.status}`);
|
|
1744
|
+
}
|
|
1745
|
+
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
1746
|
+
s?.message(`Building and deploying... (${elapsed}s, status: ${deployment.status})`);
|
|
1747
|
+
} catch (err) {
|
|
1748
|
+
if (err instanceof CLIError) throw err;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
const isReady = deployment?.status === "ready" || deployment?.status === "READY";
|
|
1752
|
+
const liveUrl = isReady ? deployment?.deploymentUrl ?? deployment?.url ?? null : null;
|
|
1753
|
+
return { deploymentId, deployment, isReady, liveUrl };
|
|
1754
|
+
}
|
|
1755
|
+
function registerDeploymentsDeployCommand(deploymentsCmd2) {
|
|
1756
|
+
deploymentsCmd2.command("deploy [directory]").description("Deploy a frontend project to Vercel").option("--env <vars>", `Environment variables as JSON (e.g. '{"KEY":"value"}')`).option("--meta <meta>", "Deployment metadata as JSON").action(async (directory, opts, cmd) => {
|
|
1757
|
+
const { json } = getRootOpts(cmd);
|
|
1758
|
+
try {
|
|
1759
|
+
await requireAuth();
|
|
1760
|
+
const config = getProjectConfig();
|
|
1761
|
+
if (!config) throw new ProjectNotLinkedError();
|
|
1762
|
+
const sourceDir = path.resolve(directory ?? ".");
|
|
1763
|
+
const stats = await fs.stat(sourceDir).catch(() => null);
|
|
1764
|
+
if (!stats?.isDirectory()) {
|
|
1765
|
+
throw new CLIError(`"${sourceDir}" is not a valid directory.`);
|
|
1766
|
+
}
|
|
1767
|
+
const s = !json ? clack8.spinner() : null;
|
|
1768
|
+
const startBody = {};
|
|
1769
|
+
if (opts.env) {
|
|
1770
|
+
try {
|
|
1771
|
+
const parsed = JSON.parse(opts.env);
|
|
1772
|
+
if (Array.isArray(parsed)) {
|
|
1773
|
+
startBody.envVars = parsed;
|
|
1774
|
+
} else {
|
|
1775
|
+
startBody.envVars = Object.entries(parsed).map(([key, value]) => ({ key, value }));
|
|
1776
|
+
}
|
|
1777
|
+
} catch {
|
|
1778
|
+
throw new CLIError("Invalid --env JSON.");
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
if (opts.meta) {
|
|
1782
|
+
try {
|
|
1783
|
+
startBody.meta = JSON.parse(opts.meta);
|
|
1784
|
+
} catch {
|
|
1785
|
+
throw new CLIError("Invalid --meta JSON.");
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
const result = await deployProject({ sourceDir, startBody, spinner: s });
|
|
1789
|
+
if (result.isReady) {
|
|
1790
|
+
s?.stop("Deployment complete");
|
|
1791
|
+
if (json) {
|
|
1792
|
+
outputJson(result.deployment);
|
|
1793
|
+
} else {
|
|
1794
|
+
if (result.liveUrl) {
|
|
1795
|
+
clack8.log.success(`Live at: ${result.liveUrl}`);
|
|
1796
|
+
}
|
|
1797
|
+
clack8.log.info(`Deployment ID: ${result.deploymentId}`);
|
|
1798
|
+
}
|
|
1799
|
+
} else {
|
|
1800
|
+
s?.stop("Deployment is still building");
|
|
1801
|
+
if (json) {
|
|
1802
|
+
outputJson({ id: result.deploymentId, status: result.deployment?.status ?? "building", timedOut: true });
|
|
1803
|
+
} else {
|
|
1804
|
+
clack8.log.info(`Deployment ID: ${result.deploymentId}`);
|
|
1805
|
+
clack8.log.warn("Deployment did not finish within 2 minutes.");
|
|
1806
|
+
clack8.log.info(`Check status with: insforge deployments status ${result.deploymentId}`);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
} catch (err) {
|
|
1810
|
+
handleError(err, json);
|
|
1811
|
+
}
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
// src/commands/create.ts
|
|
1681
1816
|
var execAsync2 = promisify2(exec2);
|
|
1682
1817
|
function buildOssHost2(appkey, region) {
|
|
1683
1818
|
return `https://${appkey}.${region}.insforge.app`;
|
|
@@ -1692,15 +1827,15 @@ async function waitForProjectActive(projectId, apiUrl, timeoutMs = 12e4) {
|
|
|
1692
1827
|
throw new CLIError("Project creation timed out. Check the dashboard for status.");
|
|
1693
1828
|
}
|
|
1694
1829
|
async function copyDir(src, dest) {
|
|
1695
|
-
const entries = await
|
|
1830
|
+
const entries = await fs2.readdir(src, { withFileTypes: true });
|
|
1696
1831
|
for (const entry of entries) {
|
|
1697
|
-
const srcPath =
|
|
1698
|
-
const destPath =
|
|
1832
|
+
const srcPath = path2.join(src, entry.name);
|
|
1833
|
+
const destPath = path2.join(dest, entry.name);
|
|
1699
1834
|
if (entry.isDirectory()) {
|
|
1700
|
-
await
|
|
1835
|
+
await fs2.mkdir(destPath, { recursive: true });
|
|
1701
1836
|
await copyDir(srcPath, destPath);
|
|
1702
1837
|
} else {
|
|
1703
|
-
await
|
|
1838
|
+
await fs2.copyFile(srcPath, destPath);
|
|
1704
1839
|
}
|
|
1705
1840
|
}
|
|
1706
1841
|
}
|
|
@@ -1708,9 +1843,9 @@ function registerCreateCommand(program2) {
|
|
|
1708
1843
|
program2.command("create").description("Create a new InsForge project").option("--name <name>", "Project name").option("--org-id <id>", "Organization ID").option("--region <region>", "Deployment region (us-east, us-west, eu-central, ap-southeast)").option("--template <template>", "Template to use: react, nextjs, or empty").action(async (opts, cmd) => {
|
|
1709
1844
|
const { json, apiUrl } = getRootOpts(cmd);
|
|
1710
1845
|
try {
|
|
1711
|
-
requireAuth();
|
|
1846
|
+
await requireAuth(apiUrl);
|
|
1712
1847
|
if (!json) {
|
|
1713
|
-
|
|
1848
|
+
clack9.intro("Create a new InsForge project");
|
|
1714
1849
|
}
|
|
1715
1850
|
let orgId = opts.orgId;
|
|
1716
1851
|
if (!orgId) {
|
|
@@ -1721,14 +1856,14 @@ function registerCreateCommand(program2) {
|
|
|
1721
1856
|
if (json) {
|
|
1722
1857
|
throw new CLIError("Specify --org-id in JSON mode.");
|
|
1723
1858
|
}
|
|
1724
|
-
const selected = await
|
|
1859
|
+
const selected = await clack9.select({
|
|
1725
1860
|
message: "Select an organization:",
|
|
1726
1861
|
options: orgs.map((o) => ({
|
|
1727
1862
|
value: o.id,
|
|
1728
1863
|
label: o.name
|
|
1729
1864
|
}))
|
|
1730
1865
|
});
|
|
1731
|
-
if (
|
|
1866
|
+
if (clack9.isCancel(selected)) process.exit(0);
|
|
1732
1867
|
orgId = selected;
|
|
1733
1868
|
}
|
|
1734
1869
|
const globalConfig = getGlobalConfig();
|
|
@@ -1737,11 +1872,11 @@ function registerCreateCommand(program2) {
|
|
|
1737
1872
|
let projectName = opts.name;
|
|
1738
1873
|
if (!projectName) {
|
|
1739
1874
|
if (json) throw new CLIError("--name is required in JSON mode.");
|
|
1740
|
-
const name = await
|
|
1875
|
+
const name = await clack9.text({
|
|
1741
1876
|
message: "Project name:",
|
|
1742
1877
|
validate: (v) => v.length >= 2 ? void 0 : "Name must be at least 2 characters"
|
|
1743
1878
|
});
|
|
1744
|
-
if (
|
|
1879
|
+
if (clack9.isCancel(name)) process.exit(0);
|
|
1745
1880
|
projectName = name;
|
|
1746
1881
|
}
|
|
1747
1882
|
let template = opts.template;
|
|
@@ -1749,7 +1884,7 @@ function registerCreateCommand(program2) {
|
|
|
1749
1884
|
if (json) {
|
|
1750
1885
|
template = "empty";
|
|
1751
1886
|
} else {
|
|
1752
|
-
const selected = await
|
|
1887
|
+
const selected = await clack9.select({
|
|
1753
1888
|
message: "Choose a starter template:",
|
|
1754
1889
|
options: [
|
|
1755
1890
|
{ value: "react", label: "Web app template with React" },
|
|
@@ -1757,11 +1892,11 @@ function registerCreateCommand(program2) {
|
|
|
1757
1892
|
{ value: "empty", label: "Empty project" }
|
|
1758
1893
|
]
|
|
1759
1894
|
});
|
|
1760
|
-
if (
|
|
1895
|
+
if (clack9.isCancel(selected)) process.exit(0);
|
|
1761
1896
|
template = selected;
|
|
1762
1897
|
}
|
|
1763
1898
|
}
|
|
1764
|
-
const s = !json ?
|
|
1899
|
+
const s = !json ? clack9.spinner() : null;
|
|
1765
1900
|
s?.start("Creating project...");
|
|
1766
1901
|
const project = await createProject(orgId, projectName, opts.region, apiUrl);
|
|
1767
1902
|
s?.message("Waiting for project to become active...");
|
|
@@ -1778,18 +1913,68 @@ function registerCreateCommand(program2) {
|
|
|
1778
1913
|
};
|
|
1779
1914
|
saveProjectConfig(projectConfig);
|
|
1780
1915
|
s?.stop(`Project "${project.name}" created and linked`);
|
|
1781
|
-
|
|
1916
|
+
const hasTemplate = template !== "empty";
|
|
1917
|
+
if (hasTemplate) {
|
|
1782
1918
|
await downloadTemplate(template, projectConfig, projectName, json, apiUrl);
|
|
1783
1919
|
}
|
|
1784
1920
|
await installSkills(json);
|
|
1921
|
+
if (hasTemplate) {
|
|
1922
|
+
const installSpinner = !json ? clack9.spinner() : null;
|
|
1923
|
+
installSpinner?.start("Installing dependencies...");
|
|
1924
|
+
try {
|
|
1925
|
+
await execAsync2("npm install", { cwd: process.cwd(), maxBuffer: 10 * 1024 * 1024 });
|
|
1926
|
+
installSpinner?.stop("Dependencies installed");
|
|
1927
|
+
} catch (err) {
|
|
1928
|
+
installSpinner?.stop("Failed to install dependencies");
|
|
1929
|
+
if (!json) {
|
|
1930
|
+
clack9.log.warn(`npm install failed: ${err.message}`);
|
|
1931
|
+
clack9.log.info("Run `npm install` manually to install dependencies.");
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
let liveUrl = null;
|
|
1936
|
+
if (hasTemplate && !json) {
|
|
1937
|
+
const shouldDeploy = await clack9.confirm({
|
|
1938
|
+
message: "Would you like to deploy now?"
|
|
1939
|
+
});
|
|
1940
|
+
if (!clack9.isCancel(shouldDeploy) && shouldDeploy) {
|
|
1941
|
+
try {
|
|
1942
|
+
const deploySpinner = clack9.spinner();
|
|
1943
|
+
const result = await deployProject({
|
|
1944
|
+
sourceDir: process.cwd(),
|
|
1945
|
+
spinner: deploySpinner
|
|
1946
|
+
});
|
|
1947
|
+
if (result.isReady) {
|
|
1948
|
+
deploySpinner.stop("Deployment complete");
|
|
1949
|
+
liveUrl = result.liveUrl;
|
|
1950
|
+
} else {
|
|
1951
|
+
deploySpinner.stop("Deployment is still building");
|
|
1952
|
+
clack9.log.info(`Deployment ID: ${result.deploymentId}`);
|
|
1953
|
+
clack9.log.warn("Deployment did not finish within 2 minutes.");
|
|
1954
|
+
clack9.log.info(`Check status with: insforge deployments status ${result.deploymentId}`);
|
|
1955
|
+
}
|
|
1956
|
+
} catch (err) {
|
|
1957
|
+
clack9.log.warn(`Deploy failed: ${err.message}`);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
|
|
1785
1962
|
if (json) {
|
|
1786
1963
|
outputJson({
|
|
1787
1964
|
success: true,
|
|
1788
1965
|
project: { id: project.id, name: project.name, appkey: project.appkey, region: project.region },
|
|
1789
|
-
template
|
|
1966
|
+
template,
|
|
1967
|
+
urls: {
|
|
1968
|
+
dashboard: dashboardUrl,
|
|
1969
|
+
...liveUrl ? { liveSite: liveUrl } : {}
|
|
1970
|
+
}
|
|
1790
1971
|
});
|
|
1791
1972
|
} else {
|
|
1792
|
-
|
|
1973
|
+
clack9.log.step(`Dashboard: ${dashboardUrl}`);
|
|
1974
|
+
if (liveUrl) {
|
|
1975
|
+
clack9.log.success(`Live site: ${liveUrl}`);
|
|
1976
|
+
}
|
|
1977
|
+
clack9.outro("Done!");
|
|
1793
1978
|
}
|
|
1794
1979
|
} catch (err) {
|
|
1795
1980
|
handleError(err, json);
|
|
@@ -1797,7 +1982,7 @@ function registerCreateCommand(program2) {
|
|
|
1797
1982
|
});
|
|
1798
1983
|
}
|
|
1799
1984
|
async function downloadTemplate(framework, projectConfig, projectName, json, _apiUrl) {
|
|
1800
|
-
const s = !json ?
|
|
1985
|
+
const s = !json ? clack9.spinner() : null;
|
|
1801
1986
|
s?.start("Downloading template...");
|
|
1802
1987
|
try {
|
|
1803
1988
|
const anonKey = await getAnonKey();
|
|
@@ -1806,9 +1991,9 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
|
|
|
1806
1991
|
}
|
|
1807
1992
|
const tempDir = tmpdir();
|
|
1808
1993
|
const targetDir = projectName;
|
|
1809
|
-
const templatePath =
|
|
1994
|
+
const templatePath = path2.join(tempDir, targetDir);
|
|
1810
1995
|
try {
|
|
1811
|
-
await
|
|
1996
|
+
await fs2.rm(templatePath, { recursive: true, force: true });
|
|
1812
1997
|
} catch {
|
|
1813
1998
|
}
|
|
1814
1999
|
const frame = framework === "nextjs" ? "nextjs" : "react";
|
|
@@ -1821,14 +2006,14 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
|
|
|
1821
2006
|
s?.message("Copying template files...");
|
|
1822
2007
|
const cwd = process.cwd();
|
|
1823
2008
|
await copyDir(templatePath, cwd);
|
|
1824
|
-
await
|
|
2009
|
+
await fs2.rm(templatePath, { recursive: true, force: true }).catch(() => {
|
|
1825
2010
|
});
|
|
1826
2011
|
s?.stop("Template files downloaded");
|
|
1827
2012
|
} catch (err) {
|
|
1828
2013
|
s?.stop("Template download failed");
|
|
1829
2014
|
if (!json) {
|
|
1830
|
-
|
|
1831
|
-
|
|
2015
|
+
clack9.log.warn(`Failed to download template: ${err.message}`);
|
|
2016
|
+
clack9.log.info("You can manually set up the template later.");
|
|
1832
2017
|
}
|
|
1833
2018
|
}
|
|
1834
2019
|
}
|
|
@@ -1882,7 +2067,7 @@ function registerListCommand(program2) {
|
|
|
1882
2067
|
program2.command("list").description("List all organizations and their projects").action(async (_opts, cmd) => {
|
|
1883
2068
|
const { json, apiUrl } = getRootOpts(cmd);
|
|
1884
2069
|
try {
|
|
1885
|
-
requireAuth();
|
|
2070
|
+
await requireAuth(apiUrl);
|
|
1886
2071
|
const orgs = await listOrganizations(apiUrl);
|
|
1887
2072
|
if (orgs.length === 0) {
|
|
1888
2073
|
if (json) {
|
|
@@ -1939,162 +2124,12 @@ function registerListCommand(program2) {
|
|
|
1939
2124
|
});
|
|
1940
2125
|
}
|
|
1941
2126
|
|
|
1942
|
-
// src/commands/deployments/deploy.ts
|
|
1943
|
-
import * as path2 from "path";
|
|
1944
|
-
import * as fs2 from "fs/promises";
|
|
1945
|
-
import * as clack7 from "@clack/prompts";
|
|
1946
|
-
import archiver from "archiver";
|
|
1947
|
-
var POLL_INTERVAL_MS = 5e3;
|
|
1948
|
-
var POLL_TIMEOUT_MS = 12e4;
|
|
1949
|
-
var EXCLUDE_PATTERNS = [
|
|
1950
|
-
"node_modules",
|
|
1951
|
-
".git",
|
|
1952
|
-
".next",
|
|
1953
|
-
".env",
|
|
1954
|
-
".env.local",
|
|
1955
|
-
"dist",
|
|
1956
|
-
"build",
|
|
1957
|
-
".DS_Store",
|
|
1958
|
-
".insforge"
|
|
1959
|
-
];
|
|
1960
|
-
function shouldExclude(name) {
|
|
1961
|
-
const normalized = name.replace(/\\/g, "/");
|
|
1962
|
-
for (const pattern of EXCLUDE_PATTERNS) {
|
|
1963
|
-
if (normalized === pattern || normalized.startsWith(pattern + "/") || normalized.endsWith("/" + pattern) || normalized.includes("/" + pattern + "/")) {
|
|
1964
|
-
return true;
|
|
1965
|
-
}
|
|
1966
|
-
}
|
|
1967
|
-
if (normalized.endsWith(".log")) return true;
|
|
1968
|
-
return false;
|
|
1969
|
-
}
|
|
1970
|
-
async function createZipBuffer(sourceDir) {
|
|
1971
|
-
return new Promise((resolve2, reject) => {
|
|
1972
|
-
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
1973
|
-
const chunks = [];
|
|
1974
|
-
archive.on("data", (chunk) => chunks.push(chunk));
|
|
1975
|
-
archive.on("end", () => resolve2(Buffer.concat(chunks)));
|
|
1976
|
-
archive.on("error", (err) => reject(err));
|
|
1977
|
-
archive.directory(sourceDir, false, (entry) => {
|
|
1978
|
-
if (shouldExclude(entry.name)) return false;
|
|
1979
|
-
return entry;
|
|
1980
|
-
});
|
|
1981
|
-
void archive.finalize();
|
|
1982
|
-
});
|
|
1983
|
-
}
|
|
1984
|
-
function registerDeploymentsDeployCommand(deploymentsCmd2) {
|
|
1985
|
-
deploymentsCmd2.command("deploy [directory]").description("Deploy a frontend project to Vercel").option("--env <vars>", `Environment variables as JSON (e.g. '{"KEY":"value"}')`).option("--meta <meta>", "Deployment metadata as JSON").action(async (directory, opts, cmd) => {
|
|
1986
|
-
const { json } = getRootOpts(cmd);
|
|
1987
|
-
try {
|
|
1988
|
-
requireAuth();
|
|
1989
|
-
const config = getProjectConfig();
|
|
1990
|
-
if (!config) throw new ProjectNotLinkedError();
|
|
1991
|
-
const sourceDir = path2.resolve(directory ?? ".");
|
|
1992
|
-
const stats = await fs2.stat(sourceDir).catch(() => null);
|
|
1993
|
-
if (!stats?.isDirectory()) {
|
|
1994
|
-
throw new CLIError(`"${sourceDir}" is not a valid directory.`);
|
|
1995
|
-
}
|
|
1996
|
-
const s = !json ? clack7.spinner() : null;
|
|
1997
|
-
s?.start("Creating deployment...");
|
|
1998
|
-
const createRes = await ossFetch("/api/deployments", { method: "POST" });
|
|
1999
|
-
const { id: deploymentId, uploadUrl, uploadFields } = await createRes.json();
|
|
2000
|
-
s?.message("Compressing source files...");
|
|
2001
|
-
const zipBuffer = await createZipBuffer(sourceDir);
|
|
2002
|
-
s?.message("Uploading...");
|
|
2003
|
-
const formData = new FormData();
|
|
2004
|
-
for (const [key, value] of Object.entries(uploadFields)) {
|
|
2005
|
-
formData.append(key, value);
|
|
2006
|
-
}
|
|
2007
|
-
formData.append(
|
|
2008
|
-
"file",
|
|
2009
|
-
new Blob([zipBuffer], { type: "application/zip" }),
|
|
2010
|
-
"deployment.zip"
|
|
2011
|
-
);
|
|
2012
|
-
const uploadRes = await fetch(uploadUrl, { method: "POST", body: formData });
|
|
2013
|
-
if (!uploadRes.ok) {
|
|
2014
|
-
const uploadErr = await uploadRes.text();
|
|
2015
|
-
throw new CLIError(`Failed to upload: ${uploadErr}`);
|
|
2016
|
-
}
|
|
2017
|
-
s?.message("Starting deployment...");
|
|
2018
|
-
const startBody = {};
|
|
2019
|
-
if (opts.env) {
|
|
2020
|
-
try {
|
|
2021
|
-
const parsed = JSON.parse(opts.env);
|
|
2022
|
-
if (Array.isArray(parsed)) {
|
|
2023
|
-
startBody.envVars = parsed;
|
|
2024
|
-
} else {
|
|
2025
|
-
startBody.envVars = Object.entries(parsed).map(([key, value]) => ({ key, value }));
|
|
2026
|
-
}
|
|
2027
|
-
} catch {
|
|
2028
|
-
throw new CLIError("Invalid --env JSON.");
|
|
2029
|
-
}
|
|
2030
|
-
}
|
|
2031
|
-
if (opts.meta) {
|
|
2032
|
-
try {
|
|
2033
|
-
startBody.meta = JSON.parse(opts.meta);
|
|
2034
|
-
} catch {
|
|
2035
|
-
throw new CLIError("Invalid --meta JSON.");
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
const startRes = await ossFetch(`/api/deployments/${deploymentId}/start`, {
|
|
2039
|
-
method: "POST",
|
|
2040
|
-
body: JSON.stringify(startBody)
|
|
2041
|
-
});
|
|
2042
|
-
await startRes.json();
|
|
2043
|
-
s?.message("Building and deploying...");
|
|
2044
|
-
const startTime = Date.now();
|
|
2045
|
-
let deployment = null;
|
|
2046
|
-
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
2047
|
-
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
2048
|
-
try {
|
|
2049
|
-
const statusRes = await ossFetch(`/api/deployments/${deploymentId}`);
|
|
2050
|
-
deployment = await statusRes.json();
|
|
2051
|
-
if (deployment.status === "ready" || deployment.status === "READY") {
|
|
2052
|
-
break;
|
|
2053
|
-
}
|
|
2054
|
-
if (deployment.status === "error" || deployment.status === "ERROR" || deployment.status === "canceled") {
|
|
2055
|
-
s?.stop("Deployment failed");
|
|
2056
|
-
throw new CLIError(deployment.error ?? `Deployment failed with status: ${deployment.status}`);
|
|
2057
|
-
}
|
|
2058
|
-
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
2059
|
-
s?.message(`Building and deploying... (${elapsed}s, status: ${deployment.status})`);
|
|
2060
|
-
} catch (err) {
|
|
2061
|
-
if (err instanceof CLIError) throw err;
|
|
2062
|
-
}
|
|
2063
|
-
}
|
|
2064
|
-
const isReady = deployment?.status === "ready" || deployment?.status === "READY";
|
|
2065
|
-
if (isReady) {
|
|
2066
|
-
s?.stop("Deployment complete");
|
|
2067
|
-
if (json) {
|
|
2068
|
-
outputJson(deployment);
|
|
2069
|
-
} else {
|
|
2070
|
-
const liveUrl = deployment?.deploymentUrl ?? deployment?.url;
|
|
2071
|
-
if (liveUrl) {
|
|
2072
|
-
clack7.log.success(`Live at: ${liveUrl}`);
|
|
2073
|
-
}
|
|
2074
|
-
clack7.log.info(`Deployment ID: ${deploymentId}`);
|
|
2075
|
-
}
|
|
2076
|
-
} else {
|
|
2077
|
-
s?.stop("Deployment is still building");
|
|
2078
|
-
if (json) {
|
|
2079
|
-
outputJson({ id: deploymentId, status: deployment?.status ?? "building", timedOut: true });
|
|
2080
|
-
} else {
|
|
2081
|
-
clack7.log.info(`Deployment ID: ${deploymentId}`);
|
|
2082
|
-
clack7.log.warn("Deployment did not finish within 2 minutes.");
|
|
2083
|
-
clack7.log.info(`Check status with: insforge deployments status ${deploymentId}`);
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
} catch (err) {
|
|
2087
|
-
handleError(err, json);
|
|
2088
|
-
}
|
|
2089
|
-
});
|
|
2090
|
-
}
|
|
2091
|
-
|
|
2092
2127
|
// src/commands/deployments/list.ts
|
|
2093
2128
|
function registerDeploymentsListCommand(deploymentsCmd2) {
|
|
2094
2129
|
deploymentsCmd2.command("list").description("List all deployments").option("--limit <n>", "Limit number of results", "20").option("--offset <n>", "Offset for pagination", "0").action(async (opts, cmd) => {
|
|
2095
2130
|
const { json } = getRootOpts(cmd);
|
|
2096
2131
|
try {
|
|
2097
|
-
requireAuth();
|
|
2132
|
+
await requireAuth();
|
|
2098
2133
|
if (!getProjectConfig()) throw new ProjectNotLinkedError();
|
|
2099
2134
|
const res = await ossFetch(`/api/deployments?limit=${opts.limit}&offset=${opts.offset}`);
|
|
2100
2135
|
const raw = await res.json();
|
|
@@ -2135,7 +2170,7 @@ function registerDeploymentsStatusCommand(deploymentsCmd2) {
|
|
|
2135
2170
|
deploymentsCmd2.command("status <id>").description("Get deployment details and sync status from Vercel").option("--sync", "Sync status from Vercel before showing").action(async (id, opts, cmd) => {
|
|
2136
2171
|
const { json } = getRootOpts(cmd);
|
|
2137
2172
|
try {
|
|
2138
|
-
requireAuth();
|
|
2173
|
+
await requireAuth();
|
|
2139
2174
|
if (!getProjectConfig()) throw new ProjectNotLinkedError();
|
|
2140
2175
|
if (opts.sync) {
|
|
2141
2176
|
await ossFetch(`/api/deployments/${id}/sync`, { method: "POST" });
|
|
@@ -2166,18 +2201,18 @@ function registerDeploymentsStatusCommand(deploymentsCmd2) {
|
|
|
2166
2201
|
}
|
|
2167
2202
|
|
|
2168
2203
|
// src/commands/deployments/cancel.ts
|
|
2169
|
-
import * as
|
|
2204
|
+
import * as clack10 from "@clack/prompts";
|
|
2170
2205
|
function registerDeploymentsCancelCommand(deploymentsCmd2) {
|
|
2171
2206
|
deploymentsCmd2.command("cancel <id>").description("Cancel a deployment").action(async (id, _opts, cmd) => {
|
|
2172
2207
|
const { json, yes } = getRootOpts(cmd);
|
|
2173
2208
|
try {
|
|
2174
|
-
requireAuth();
|
|
2209
|
+
await requireAuth();
|
|
2175
2210
|
if (!getProjectConfig()) throw new ProjectNotLinkedError();
|
|
2176
2211
|
if (!yes && !json) {
|
|
2177
|
-
const confirmed = await
|
|
2212
|
+
const confirmed = await clack10.confirm({
|
|
2178
2213
|
message: `Cancel deployment ${id}?`
|
|
2179
2214
|
});
|
|
2180
|
-
if (
|
|
2215
|
+
if (clack10.isCancel(confirmed) || !confirmed) process.exit(0);
|
|
2181
2216
|
}
|
|
2182
2217
|
const res = await ossFetch(`/api/deployments/${id}/cancel`, { method: "POST" });
|
|
2183
2218
|
const result = await res.json();
|
|
@@ -2208,7 +2243,7 @@ Examples:
|
|
|
2208
2243
|
insforge docs storage rest-api Show REST API storage docs`).action(async (feature, language, _opts, cmd) => {
|
|
2209
2244
|
const { json } = getRootOpts(cmd);
|
|
2210
2245
|
try {
|
|
2211
|
-
requireAuth();
|
|
2246
|
+
await requireAuth();
|
|
2212
2247
|
if (!feature) {
|
|
2213
2248
|
await listDocs(json);
|
|
2214
2249
|
return;
|
|
@@ -2264,7 +2299,7 @@ function registerSecretsListCommand(secretsCmd2) {
|
|
|
2264
2299
|
secretsCmd2.command("list").description("List secrets (metadata only, values are hidden)").option("--all", "Include inactive (deleted) secrets").action(async (opts, cmd) => {
|
|
2265
2300
|
const { json } = getRootOpts(cmd);
|
|
2266
2301
|
try {
|
|
2267
|
-
requireAuth();
|
|
2302
|
+
await requireAuth();
|
|
2268
2303
|
const res = await ossFetch("/api/secrets");
|
|
2269
2304
|
const data = await res.json();
|
|
2270
2305
|
let secrets = data.secrets ?? [];
|
|
@@ -2304,7 +2339,7 @@ function registerSecretsGetCommand(secretsCmd2) {
|
|
|
2304
2339
|
secretsCmd2.command("get <key>").description("Get the decrypted value of a secret").action(async (key, _opts, cmd) => {
|
|
2305
2340
|
const { json } = getRootOpts(cmd);
|
|
2306
2341
|
try {
|
|
2307
|
-
requireAuth();
|
|
2342
|
+
await requireAuth();
|
|
2308
2343
|
const res = await ossFetch(`/api/secrets/${encodeURIComponent(key)}`);
|
|
2309
2344
|
const data = await res.json();
|
|
2310
2345
|
if (json) {
|
|
@@ -2323,7 +2358,7 @@ function registerSecretsAddCommand(secretsCmd2) {
|
|
|
2323
2358
|
secretsCmd2.command("add <key> <value>").description("Create a new secret").option("--reserved", "Mark secret as protected from deletion").option("--expires <date>", "Expiration date (ISO 8601 format)").action(async (key, value, opts, cmd) => {
|
|
2324
2359
|
const { json } = getRootOpts(cmd);
|
|
2325
2360
|
try {
|
|
2326
|
-
requireAuth();
|
|
2361
|
+
await requireAuth();
|
|
2327
2362
|
const body = { key, value };
|
|
2328
2363
|
if (opts.reserved) body.isReserved = true;
|
|
2329
2364
|
if (opts.expires) body.expiresAt = opts.expires;
|
|
@@ -2348,7 +2383,7 @@ function registerSecretsUpdateCommand(secretsCmd2) {
|
|
|
2348
2383
|
secretsCmd2.command("update <key>").description("Update an existing secret").option("--value <value>", "New secret value").option("--active <bool>", "Set active status (true/false)").option("--reserved <bool>", "Set reserved status (true/false)").option("--expires <date>", 'Expiration date (ISO 8601, or "null" to remove)').action(async (key, opts, cmd) => {
|
|
2349
2384
|
const { json } = getRootOpts(cmd);
|
|
2350
2385
|
try {
|
|
2351
|
-
requireAuth();
|
|
2386
|
+
await requireAuth();
|
|
2352
2387
|
const body = {};
|
|
2353
2388
|
if (opts.value !== void 0) body.value = opts.value;
|
|
2354
2389
|
if (opts.active !== void 0) body.isActive = opts.active === "true";
|
|
@@ -2374,17 +2409,17 @@ function registerSecretsUpdateCommand(secretsCmd2) {
|
|
|
2374
2409
|
}
|
|
2375
2410
|
|
|
2376
2411
|
// src/commands/secrets/delete.ts
|
|
2377
|
-
import * as
|
|
2412
|
+
import * as clack11 from "@clack/prompts";
|
|
2378
2413
|
function registerSecretsDeleteCommand(secretsCmd2) {
|
|
2379
2414
|
secretsCmd2.command("delete <key>").description("Delete a secret").action(async (key, _opts, cmd) => {
|
|
2380
2415
|
const { json, yes } = getRootOpts(cmd);
|
|
2381
2416
|
try {
|
|
2382
|
-
requireAuth();
|
|
2417
|
+
await requireAuth();
|
|
2383
2418
|
if (!yes && !json) {
|
|
2384
|
-
const
|
|
2419
|
+
const confirm6 = await clack11.confirm({
|
|
2385
2420
|
message: `Delete secret "${key}"? This cannot be undone.`
|
|
2386
2421
|
});
|
|
2387
|
-
if (!
|
|
2422
|
+
if (!confirm6 || clack11.isCancel(confirm6)) {
|
|
2388
2423
|
process.exit(0);
|
|
2389
2424
|
}
|
|
2390
2425
|
}
|
|
@@ -2408,7 +2443,7 @@ function registerSchedulesListCommand(schedulesCmd2) {
|
|
|
2408
2443
|
schedulesCmd2.command("list").description("List all schedules").action(async (_opts, cmd) => {
|
|
2409
2444
|
const { json } = getRootOpts(cmd);
|
|
2410
2445
|
try {
|
|
2411
|
-
requireAuth();
|
|
2446
|
+
await requireAuth();
|
|
2412
2447
|
const res = await ossFetch("/api/schedules");
|
|
2413
2448
|
const data = await res.json();
|
|
2414
2449
|
const schedules = Array.isArray(data) ? data : data.schedules ?? [];
|
|
@@ -2443,7 +2478,7 @@ function registerSchedulesGetCommand(schedulesCmd2) {
|
|
|
2443
2478
|
schedulesCmd2.command("get <id>").description("Get schedule details").action(async (id, _opts, cmd) => {
|
|
2444
2479
|
const { json } = getRootOpts(cmd);
|
|
2445
2480
|
try {
|
|
2446
|
-
requireAuth();
|
|
2481
|
+
await requireAuth();
|
|
2447
2482
|
const res = await ossFetch(`/api/schedules/${encodeURIComponent(id)}`);
|
|
2448
2483
|
const data = await res.json();
|
|
2449
2484
|
if (json) {
|
|
@@ -2473,7 +2508,7 @@ function registerSchedulesCreateCommand(schedulesCmd2) {
|
|
|
2473
2508
|
schedulesCmd2.command("create").description("Create a new schedule").requiredOption("--name <name>", "Schedule name").requiredOption("--cron <expression>", "Cron expression (5-field format)").requiredOption("--url <url>", "URL to invoke").requiredOption("--method <method>", "HTTP method (GET, POST, PUT, PATCH, DELETE)").option("--headers <json>", "HTTP headers as JSON").option("--body <json>", "Request body as JSON").action(async (opts, cmd) => {
|
|
2474
2509
|
const { json } = getRootOpts(cmd);
|
|
2475
2510
|
try {
|
|
2476
|
-
requireAuth();
|
|
2511
|
+
await requireAuth();
|
|
2477
2512
|
const body = {
|
|
2478
2513
|
name: opts.name,
|
|
2479
2514
|
cronSchedule: opts.cron,
|
|
@@ -2515,7 +2550,7 @@ function registerSchedulesUpdateCommand(schedulesCmd2) {
|
|
|
2515
2550
|
schedulesCmd2.command("update <id>").description("Update a schedule").option("--name <name>", "New schedule name").option("--cron <expression>", "New cron expression").option("--url <url>", "New URL to invoke").option("--method <method>", "New HTTP method").option("--headers <json>", "New HTTP headers as JSON").option("--body <json>", "New request body as JSON").option("--active <bool>", "Enable/disable schedule (true/false)").action(async (id, opts, cmd) => {
|
|
2516
2551
|
const { json } = getRootOpts(cmd);
|
|
2517
2552
|
try {
|
|
2518
|
-
requireAuth();
|
|
2553
|
+
await requireAuth();
|
|
2519
2554
|
const body = {};
|
|
2520
2555
|
if (opts.name !== void 0) body.name = opts.name;
|
|
2521
2556
|
if (opts.cron !== void 0) body.cronSchedule = opts.cron;
|
|
@@ -2556,17 +2591,17 @@ function registerSchedulesUpdateCommand(schedulesCmd2) {
|
|
|
2556
2591
|
}
|
|
2557
2592
|
|
|
2558
2593
|
// src/commands/schedules/delete.ts
|
|
2559
|
-
import * as
|
|
2594
|
+
import * as clack12 from "@clack/prompts";
|
|
2560
2595
|
function registerSchedulesDeleteCommand(schedulesCmd2) {
|
|
2561
2596
|
schedulesCmd2.command("delete <id>").description("Delete a schedule").action(async (id, _opts, cmd) => {
|
|
2562
2597
|
const { json, yes } = getRootOpts(cmd);
|
|
2563
2598
|
try {
|
|
2564
|
-
requireAuth();
|
|
2599
|
+
await requireAuth();
|
|
2565
2600
|
if (!yes && !json) {
|
|
2566
|
-
const
|
|
2601
|
+
const confirm6 = await clack12.confirm({
|
|
2567
2602
|
message: `Delete schedule "${id}"? This cannot be undone.`
|
|
2568
2603
|
});
|
|
2569
|
-
if (!
|
|
2604
|
+
if (!confirm6 || clack12.isCancel(confirm6)) {
|
|
2570
2605
|
process.exit(0);
|
|
2571
2606
|
}
|
|
2572
2607
|
}
|
|
@@ -2590,7 +2625,7 @@ function registerSchedulesLogsCommand(schedulesCmd2) {
|
|
|
2590
2625
|
schedulesCmd2.command("logs <id>").description("Get execution logs for a schedule").option("--limit <n>", "Max logs to return (default: 50, max: 100)", "50").option("--offset <n>", "Pagination offset", "0").action(async (id, opts, cmd) => {
|
|
2591
2626
|
const { json } = getRootOpts(cmd);
|
|
2592
2627
|
try {
|
|
2593
|
-
requireAuth();
|
|
2628
|
+
await requireAuth();
|
|
2594
2629
|
const limit = parseInt(opts.limit, 10) || 50;
|
|
2595
2630
|
const offset = parseInt(opts.offset, 10) || 0;
|
|
2596
2631
|
const res = await ossFetch(`/api/schedules/${encodeURIComponent(id)}/logs?limit=${limit}&offset=${offset}`);
|
|
@@ -2630,7 +2665,7 @@ function registerLogsCommand(program2) {
|
|
|
2630
2665
|
program2.command("logs <source>").description("Fetch backend container logs (insforge.logs | postgREST.logs | postgres.logs | function.logs)").option("--limit <n>", "Number of log entries to return", "20").action(async (source, opts, cmd) => {
|
|
2631
2666
|
const { json } = getRootOpts(cmd);
|
|
2632
2667
|
try {
|
|
2633
|
-
requireAuth();
|
|
2668
|
+
await requireAuth();
|
|
2634
2669
|
const resolved = SOURCE_LOOKUP.get(source.toLowerCase());
|
|
2635
2670
|
if (!resolved) {
|
|
2636
2671
|
console.error(`Invalid log source "${source}". Valid sources: ${VALID_SOURCES.join(", ")}`);
|