@insforge/cli 0.1.41 → 0.1.43
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 +185 -170
- package/dist/index.js +507 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { readFileSync as
|
|
5
|
-
import { join as
|
|
4
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
5
|
+
import { join as join8, dirname } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import * as clack14 from "@clack/prompts";
|
|
@@ -797,6 +797,47 @@ async function reportCliUsage(toolName, success, maxRetries = 1) {
|
|
|
797
797
|
}
|
|
798
798
|
}
|
|
799
799
|
|
|
800
|
+
// src/lib/analytics.ts
|
|
801
|
+
import { PostHog } from "posthog-node";
|
|
802
|
+
var POSTHOG_API_KEY = "phc_ueV1ii62wdBTkH7E70ugyeqHIHu8dFDdjs0qq3TZhJz";
|
|
803
|
+
var POSTHOG_HOST = process.env.POSTHOG_HOST || "https://us.i.posthog.com";
|
|
804
|
+
var client = null;
|
|
805
|
+
function getClient() {
|
|
806
|
+
if (!POSTHOG_API_KEY) return null;
|
|
807
|
+
if (!client) {
|
|
808
|
+
client = new PostHog(POSTHOG_API_KEY, { host: POSTHOG_HOST });
|
|
809
|
+
}
|
|
810
|
+
return client;
|
|
811
|
+
}
|
|
812
|
+
function captureEvent(distinctId, event, properties) {
|
|
813
|
+
try {
|
|
814
|
+
getClient()?.capture({ distinctId, event, properties });
|
|
815
|
+
} catch {
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function trackCommand(command, distinctId, properties) {
|
|
819
|
+
captureEvent(distinctId, "cli_command_invoked", {
|
|
820
|
+
command,
|
|
821
|
+
...properties
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
function trackDiagnose(subcommand, config) {
|
|
825
|
+
captureEvent(config.project_id, "cli_diagnose_invoked", {
|
|
826
|
+
subcommand,
|
|
827
|
+
project_id: config.project_id,
|
|
828
|
+
project_name: config.project_name,
|
|
829
|
+
org_id: config.org_id,
|
|
830
|
+
region: config.region,
|
|
831
|
+
oss_mode: config.project_id === "oss-project"
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
async function shutdownAnalytics() {
|
|
835
|
+
try {
|
|
836
|
+
if (client) await client.shutdown();
|
|
837
|
+
} catch {
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
800
841
|
// src/commands/projects/link.ts
|
|
801
842
|
function buildOssHost(appkey, region) {
|
|
802
843
|
return `https://${appkey}.${region}.insforge.app`;
|
|
@@ -831,6 +872,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
831
872
|
} else {
|
|
832
873
|
outputSuccess(`Linked to direct project at ${projectConfig2.oss_host}`);
|
|
833
874
|
}
|
|
875
|
+
trackCommand("link", "oss-org", { direct: true });
|
|
834
876
|
await installSkills(json);
|
|
835
877
|
await reportCliUsage("cli.link_direct", true, 6);
|
|
836
878
|
try {
|
|
@@ -916,6 +958,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
916
958
|
oss_host: buildOssHost(project.appkey, project.region)
|
|
917
959
|
};
|
|
918
960
|
saveProjectConfig(projectConfig);
|
|
961
|
+
trackCommand("link", project.organization_id);
|
|
919
962
|
if (json) {
|
|
920
963
|
outputJson({ success: true, project: { id: project.id, name: project.name, region: project.region } });
|
|
921
964
|
} else {
|
|
@@ -930,6 +973,8 @@ function registerProjectLinkCommand(program2) {
|
|
|
930
973
|
} catch (err) {
|
|
931
974
|
await reportCliUsage("cli.link", false);
|
|
932
975
|
handleError(err, json);
|
|
976
|
+
} finally {
|
|
977
|
+
await shutdownAnalytics();
|
|
933
978
|
}
|
|
934
979
|
});
|
|
935
980
|
}
|
|
@@ -967,7 +1012,15 @@ async function ossFetch(path4, options = {}) {
|
|
|
967
1012
|
const res = await fetch(`${config.oss_host}${path4}`, { ...options, headers });
|
|
968
1013
|
if (!res.ok) {
|
|
969
1014
|
const err = await res.json().catch(() => ({}));
|
|
970
|
-
|
|
1015
|
+
let message = err.message ?? err.error ?? `OSS request failed: ${res.status}`;
|
|
1016
|
+
if (err.nextActions) {
|
|
1017
|
+
message += `
|
|
1018
|
+
${err.nextActions}`;
|
|
1019
|
+
}
|
|
1020
|
+
if (res.status === 404 && path4.startsWith("/api/compute")) {
|
|
1021
|
+
message = "Compute services are not available on this backend.\nSelf-hosted: upgrade your InsForge instance. Cloud: contact your InsForge admin to enable compute.";
|
|
1022
|
+
}
|
|
1023
|
+
throw new CLIError(message);
|
|
971
1024
|
}
|
|
972
1025
|
return res;
|
|
973
1026
|
}
|
|
@@ -1896,41 +1949,6 @@ async function readEnvFile(cwd) {
|
|
|
1896
1949
|
return [];
|
|
1897
1950
|
}
|
|
1898
1951
|
|
|
1899
|
-
// src/lib/analytics.ts
|
|
1900
|
-
import { PostHog } from "posthog-node";
|
|
1901
|
-
var POSTHOG_API_KEY = "phc_ueV1ii62wdBTkH7E70ugyeqHIHu8dFDdjs0qq3TZhJz";
|
|
1902
|
-
var POSTHOG_HOST = process.env.POSTHOG_HOST || "https://us.i.posthog.com";
|
|
1903
|
-
var client = null;
|
|
1904
|
-
function getClient() {
|
|
1905
|
-
if (!POSTHOG_API_KEY) return null;
|
|
1906
|
-
if (!client) {
|
|
1907
|
-
client = new PostHog(POSTHOG_API_KEY, { host: POSTHOG_HOST });
|
|
1908
|
-
}
|
|
1909
|
-
return client;
|
|
1910
|
-
}
|
|
1911
|
-
function captureEvent(distinctId, event, properties) {
|
|
1912
|
-
try {
|
|
1913
|
-
getClient()?.capture({ distinctId, event, properties });
|
|
1914
|
-
} catch {
|
|
1915
|
-
}
|
|
1916
|
-
}
|
|
1917
|
-
function trackDiagnose(subcommand, config) {
|
|
1918
|
-
captureEvent(config.project_id, "cli_diagnose_invoked", {
|
|
1919
|
-
subcommand,
|
|
1920
|
-
project_id: config.project_id,
|
|
1921
|
-
project_name: config.project_name,
|
|
1922
|
-
org_id: config.org_id,
|
|
1923
|
-
region: config.region,
|
|
1924
|
-
oss_mode: config.project_id === "oss-project"
|
|
1925
|
-
});
|
|
1926
|
-
}
|
|
1927
|
-
async function shutdownAnalytics() {
|
|
1928
|
-
try {
|
|
1929
|
-
if (client) await client.shutdown();
|
|
1930
|
-
} catch {
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
1952
|
// src/commands/deployments/deploy.ts
|
|
1935
1953
|
import * as path2 from "path";
|
|
1936
1954
|
import * as fs2 from "fs/promises";
|
|
@@ -2363,6 +2381,7 @@ function registerCreateCommand(program2) {
|
|
|
2363
2381
|
}
|
|
2364
2382
|
}
|
|
2365
2383
|
await installSkills(json);
|
|
2384
|
+
trackCommand("create", orgId);
|
|
2366
2385
|
await reportCliUsage("cli.create", true, 6);
|
|
2367
2386
|
if (hasTemplate) {
|
|
2368
2387
|
const installSpinner = !json ? clack10.spinner() : null;
|
|
@@ -3271,6 +3290,445 @@ function registerSchedulesLogsCommand(schedulesCmd2) {
|
|
|
3271
3290
|
});
|
|
3272
3291
|
}
|
|
3273
3292
|
|
|
3293
|
+
// src/commands/compute/list.ts
|
|
3294
|
+
function registerComputeListCommand(computeCmd2) {
|
|
3295
|
+
computeCmd2.command("list").description("List all compute services").action(async (_opts, cmd) => {
|
|
3296
|
+
const { json } = getRootOpts(cmd);
|
|
3297
|
+
try {
|
|
3298
|
+
await requireAuth();
|
|
3299
|
+
const res = await ossFetch("/api/compute/services");
|
|
3300
|
+
const raw = await res.json();
|
|
3301
|
+
const services = Array.isArray(raw) ? raw : [];
|
|
3302
|
+
if (json) {
|
|
3303
|
+
outputJson(services);
|
|
3304
|
+
} else {
|
|
3305
|
+
if (services.length === 0) {
|
|
3306
|
+
console.log("No compute services found.");
|
|
3307
|
+
await reportCliUsage("cli.compute.list", true);
|
|
3308
|
+
return;
|
|
3309
|
+
}
|
|
3310
|
+
outputTable(
|
|
3311
|
+
["Name", "Status", "Image", "CPU", "Memory", "Endpoint"],
|
|
3312
|
+
services.map((s) => [
|
|
3313
|
+
String(s.name ?? "-"),
|
|
3314
|
+
String(s.status ?? "-"),
|
|
3315
|
+
String(s.imageUrl ?? "-"),
|
|
3316
|
+
String(s.cpu ?? "-"),
|
|
3317
|
+
s.memory ? `${s.memory}MB` : "-",
|
|
3318
|
+
String(s.endpointUrl ?? "-")
|
|
3319
|
+
])
|
|
3320
|
+
);
|
|
3321
|
+
}
|
|
3322
|
+
await reportCliUsage("cli.compute.list", true);
|
|
3323
|
+
} catch (err) {
|
|
3324
|
+
await reportCliUsage("cli.compute.list", false);
|
|
3325
|
+
handleError(err, json);
|
|
3326
|
+
}
|
|
3327
|
+
});
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3330
|
+
// src/commands/compute/get.ts
|
|
3331
|
+
function registerComputeGetCommand(computeCmd2) {
|
|
3332
|
+
computeCmd2.command("get <id>").description("Get details of a compute service").action(async (id, _opts, cmd) => {
|
|
3333
|
+
const { json } = getRootOpts(cmd);
|
|
3334
|
+
try {
|
|
3335
|
+
await requireAuth();
|
|
3336
|
+
const res = await ossFetch(`/api/compute/services/${encodeURIComponent(id)}`);
|
|
3337
|
+
const service = await res.json();
|
|
3338
|
+
if (json) {
|
|
3339
|
+
outputJson(service);
|
|
3340
|
+
} else {
|
|
3341
|
+
outputInfo(`Name: ${service.name}`);
|
|
3342
|
+
outputInfo(`ID: ${service.id}`);
|
|
3343
|
+
outputInfo(`Status: ${service.status}`);
|
|
3344
|
+
outputInfo(`Image: ${service.imageUrl}`);
|
|
3345
|
+
outputInfo(`CPU: ${service.cpu}`);
|
|
3346
|
+
outputInfo(`Memory: ${service.memory}MB`);
|
|
3347
|
+
outputInfo(`Region: ${service.region}`);
|
|
3348
|
+
outputInfo(`Endpoint: ${service.endpointUrl ?? "n/a"}`);
|
|
3349
|
+
outputInfo(`Created: ${service.createdAt}`);
|
|
3350
|
+
}
|
|
3351
|
+
await reportCliUsage("cli.compute.get", true);
|
|
3352
|
+
} catch (err) {
|
|
3353
|
+
await reportCliUsage("cli.compute.get", false);
|
|
3354
|
+
handleError(err, json);
|
|
3355
|
+
}
|
|
3356
|
+
});
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3359
|
+
// src/commands/compute/create.ts
|
|
3360
|
+
function registerComputeCreateCommand(computeCmd2) {
|
|
3361
|
+
computeCmd2.command("create").description("Create and deploy a compute service").requiredOption("--name <name>", "Service name (DNS-safe, e.g. my-api)").requiredOption("--image <image>", "Docker image URL (e.g. nginx:alpine)").option("--port <port>", "Container port", "8080").option("--cpu <tier>", "CPU tier (shared-1x, shared-2x, performance-1x, performance-2x, performance-4x)", "shared-1x").option("--memory <mb>", "Memory in MB (256, 512, 1024, 2048, 4096, 8192)", "512").option("--region <region>", "Fly.io region", "iad").option("--env <json>", "Environment variables as JSON object").action(async (opts, cmd) => {
|
|
3362
|
+
const { json } = getRootOpts(cmd);
|
|
3363
|
+
try {
|
|
3364
|
+
await requireAuth();
|
|
3365
|
+
const body = {
|
|
3366
|
+
name: opts.name,
|
|
3367
|
+
imageUrl: opts.image,
|
|
3368
|
+
port: Number(opts.port),
|
|
3369
|
+
cpu: opts.cpu,
|
|
3370
|
+
memory: Number(opts.memory),
|
|
3371
|
+
region: opts.region
|
|
3372
|
+
};
|
|
3373
|
+
if (opts.env) {
|
|
3374
|
+
try {
|
|
3375
|
+
body.envVars = JSON.parse(opts.env);
|
|
3376
|
+
} catch {
|
|
3377
|
+
throw new CLIError("Invalid JSON for --env");
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
const res = await ossFetch("/api/compute/services", {
|
|
3381
|
+
method: "POST",
|
|
3382
|
+
body: JSON.stringify(body)
|
|
3383
|
+
});
|
|
3384
|
+
const service = await res.json();
|
|
3385
|
+
if (json) {
|
|
3386
|
+
outputJson(service);
|
|
3387
|
+
} else {
|
|
3388
|
+
outputSuccess(`Service "${service.name}" created [${service.status}]`);
|
|
3389
|
+
if (service.endpointUrl) {
|
|
3390
|
+
console.log(` Endpoint: ${service.endpointUrl}`);
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
await reportCliUsage("cli.compute.create", true);
|
|
3394
|
+
} catch (err) {
|
|
3395
|
+
await reportCliUsage("cli.compute.create", false);
|
|
3396
|
+
handleError(err, json);
|
|
3397
|
+
}
|
|
3398
|
+
});
|
|
3399
|
+
}
|
|
3400
|
+
|
|
3401
|
+
// src/commands/compute/update.ts
|
|
3402
|
+
function registerComputeUpdateCommand(computeCmd2) {
|
|
3403
|
+
computeCmd2.command("update <id>").description("Update a compute service").option("--image <image>", "Docker image URL").option("--port <port>", "Container port").option("--cpu <tier>", "CPU tier").option("--memory <mb>", "Memory in MB").option("--region <region>", "Fly.io region").option("--env <json>", "Environment variables as JSON object").action(async (id, opts, cmd) => {
|
|
3404
|
+
const { json } = getRootOpts(cmd);
|
|
3405
|
+
try {
|
|
3406
|
+
await requireAuth();
|
|
3407
|
+
const body = {};
|
|
3408
|
+
if (opts.image) body.imageUrl = opts.image;
|
|
3409
|
+
if (opts.port) {
|
|
3410
|
+
if (!Number.isFinite(Number(opts.port))) {
|
|
3411
|
+
throw new CLIError("Invalid value for --port: must be a number");
|
|
3412
|
+
}
|
|
3413
|
+
body.port = Number(opts.port);
|
|
3414
|
+
}
|
|
3415
|
+
if (opts.cpu) body.cpu = opts.cpu;
|
|
3416
|
+
if (opts.memory) {
|
|
3417
|
+
if (!Number.isFinite(Number(opts.memory))) {
|
|
3418
|
+
throw new CLIError("Invalid value for --memory: must be a number");
|
|
3419
|
+
}
|
|
3420
|
+
body.memory = Number(opts.memory);
|
|
3421
|
+
}
|
|
3422
|
+
if (opts.region) body.region = opts.region;
|
|
3423
|
+
if (opts.env) {
|
|
3424
|
+
try {
|
|
3425
|
+
body.envVars = JSON.parse(opts.env);
|
|
3426
|
+
} catch {
|
|
3427
|
+
throw new CLIError("Invalid JSON for --env");
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
if (Object.keys(body).length === 0) {
|
|
3431
|
+
throw new CLIError("No update fields provided. Use --image, --port, --cpu, --memory, --region, or --env.");
|
|
3432
|
+
}
|
|
3433
|
+
const res = await ossFetch(`/api/compute/services/${encodeURIComponent(id)}`, {
|
|
3434
|
+
method: "PATCH",
|
|
3435
|
+
body: JSON.stringify(body)
|
|
3436
|
+
});
|
|
3437
|
+
const service = await res.json();
|
|
3438
|
+
if (json) {
|
|
3439
|
+
outputJson(service);
|
|
3440
|
+
} else {
|
|
3441
|
+
outputSuccess(`Service "${service.name}" updated [${service.status}]`);
|
|
3442
|
+
}
|
|
3443
|
+
await reportCliUsage("cli.compute.update", true);
|
|
3444
|
+
} catch (err) {
|
|
3445
|
+
await reportCliUsage("cli.compute.update", false);
|
|
3446
|
+
handleError(err, json);
|
|
3447
|
+
}
|
|
3448
|
+
});
|
|
3449
|
+
}
|
|
3450
|
+
|
|
3451
|
+
// src/commands/compute/delete.ts
|
|
3452
|
+
function registerComputeDeleteCommand(computeCmd2) {
|
|
3453
|
+
computeCmd2.command("delete <id>").description("Delete a compute service and its Fly.io resources").action(async (id, _opts, cmd) => {
|
|
3454
|
+
const { json } = getRootOpts(cmd);
|
|
3455
|
+
try {
|
|
3456
|
+
await requireAuth();
|
|
3457
|
+
const res = await ossFetch(`/api/compute/services/${encodeURIComponent(id)}`, {
|
|
3458
|
+
method: "DELETE"
|
|
3459
|
+
});
|
|
3460
|
+
const data = await res.json();
|
|
3461
|
+
if (json) {
|
|
3462
|
+
outputJson(data);
|
|
3463
|
+
} else {
|
|
3464
|
+
outputSuccess("Service deleted.");
|
|
3465
|
+
}
|
|
3466
|
+
await reportCliUsage("cli.compute.delete", true);
|
|
3467
|
+
} catch (err) {
|
|
3468
|
+
await reportCliUsage("cli.compute.delete", false);
|
|
3469
|
+
handleError(err, json);
|
|
3470
|
+
}
|
|
3471
|
+
});
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3474
|
+
// src/commands/compute/start.ts
|
|
3475
|
+
function registerComputeStartCommand(computeCmd2) {
|
|
3476
|
+
computeCmd2.command("start <id>").description("Start a stopped compute service").action(async (id, _opts, cmd) => {
|
|
3477
|
+
const { json } = getRootOpts(cmd);
|
|
3478
|
+
try {
|
|
3479
|
+
await requireAuth();
|
|
3480
|
+
const res = await ossFetch(`/api/compute/services/${encodeURIComponent(id)}/start`, {
|
|
3481
|
+
method: "POST"
|
|
3482
|
+
});
|
|
3483
|
+
const service = await res.json();
|
|
3484
|
+
if (json) {
|
|
3485
|
+
outputJson(service);
|
|
3486
|
+
} else {
|
|
3487
|
+
outputSuccess(`Service "${service.name}" started [${service.status}]`);
|
|
3488
|
+
if (service.endpointUrl) {
|
|
3489
|
+
console.log(` Endpoint: ${service.endpointUrl}`);
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
await reportCliUsage("cli.compute.start", true);
|
|
3493
|
+
} catch (err) {
|
|
3494
|
+
await reportCliUsage("cli.compute.start", false);
|
|
3495
|
+
handleError(err, json);
|
|
3496
|
+
}
|
|
3497
|
+
});
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3500
|
+
// src/commands/compute/stop.ts
|
|
3501
|
+
function registerComputeStopCommand(computeCmd2) {
|
|
3502
|
+
computeCmd2.command("stop <id>").description("Stop a running compute service").action(async (id, _opts, cmd) => {
|
|
3503
|
+
const { json } = getRootOpts(cmd);
|
|
3504
|
+
try {
|
|
3505
|
+
await requireAuth();
|
|
3506
|
+
const res = await ossFetch(`/api/compute/services/${encodeURIComponent(id)}/stop`, {
|
|
3507
|
+
method: "POST"
|
|
3508
|
+
});
|
|
3509
|
+
const service = await res.json();
|
|
3510
|
+
if (json) {
|
|
3511
|
+
outputJson(service);
|
|
3512
|
+
} else {
|
|
3513
|
+
outputSuccess(`Service "${service.name}" stopped.`);
|
|
3514
|
+
}
|
|
3515
|
+
await reportCliUsage("cli.compute.stop", true);
|
|
3516
|
+
} catch (err) {
|
|
3517
|
+
await reportCliUsage("cli.compute.stop", false);
|
|
3518
|
+
handleError(err, json);
|
|
3519
|
+
}
|
|
3520
|
+
});
|
|
3521
|
+
}
|
|
3522
|
+
|
|
3523
|
+
// src/commands/compute/logs.ts
|
|
3524
|
+
function registerComputeLogsCommand(computeCmd2) {
|
|
3525
|
+
computeCmd2.command("logs <id>").description("Get compute service logs (machine events)").option("--limit <n>", "Max number of log entries", "50").action(async (id, opts, cmd) => {
|
|
3526
|
+
const { json } = getRootOpts(cmd);
|
|
3527
|
+
try {
|
|
3528
|
+
await requireAuth();
|
|
3529
|
+
const limit = Math.max(1, Math.min(Number(opts.limit) || 50, 1e3));
|
|
3530
|
+
const res = await ossFetch(
|
|
3531
|
+
`/api/compute/services/${encodeURIComponent(id)}/logs?limit=${limit}`
|
|
3532
|
+
);
|
|
3533
|
+
const logs = await res.json();
|
|
3534
|
+
if (json) {
|
|
3535
|
+
outputJson(logs);
|
|
3536
|
+
} else {
|
|
3537
|
+
if (!Array.isArray(logs) || logs.length === 0) {
|
|
3538
|
+
console.log("No logs found.");
|
|
3539
|
+
await reportCliUsage("cli.compute.logs", true);
|
|
3540
|
+
return;
|
|
3541
|
+
}
|
|
3542
|
+
for (const entry of logs) {
|
|
3543
|
+
const ts = new Date(entry.timestamp).toISOString();
|
|
3544
|
+
console.log(`${ts} ${entry.message}`);
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
await reportCliUsage("cli.compute.logs", true);
|
|
3548
|
+
} catch (err) {
|
|
3549
|
+
await reportCliUsage("cli.compute.logs", false);
|
|
3550
|
+
handleError(err, json);
|
|
3551
|
+
}
|
|
3552
|
+
});
|
|
3553
|
+
}
|
|
3554
|
+
|
|
3555
|
+
// src/commands/compute/deploy.ts
|
|
3556
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, renameSync } from "fs";
|
|
3557
|
+
import { join as join7 } from "path";
|
|
3558
|
+
import { execSync, spawn } from "child_process";
|
|
3559
|
+
function parseFlyToml(dir) {
|
|
3560
|
+
const tomlPath = join7(dir, "fly.toml");
|
|
3561
|
+
if (!existsSync5(tomlPath)) return {};
|
|
3562
|
+
const content = readFileSync6(tomlPath, "utf-8");
|
|
3563
|
+
const config = {};
|
|
3564
|
+
const portMatch = content.match(/internal_port\s*=\s*(\d+)/);
|
|
3565
|
+
if (portMatch) config.internalPort = Number(portMatch[1]);
|
|
3566
|
+
const regionMatch = content.match(/primary_region\s*=\s*'([^']+)'/);
|
|
3567
|
+
if (regionMatch) config.region = regionMatch[1];
|
|
3568
|
+
const memoryMatch = content.match(/memory\s*=\s*'(\d+)/);
|
|
3569
|
+
if (memoryMatch) config.memory = memoryMatch[1];
|
|
3570
|
+
const cpuKindMatch = content.match(/cpu_kind\s*=\s*'([^']+)'/);
|
|
3571
|
+
if (cpuKindMatch) config.cpuKind = cpuKindMatch[1];
|
|
3572
|
+
const cpusMatch = content.match(/cpus\s*=\s*(\d+)/);
|
|
3573
|
+
if (cpusMatch) config.cpus = Number(cpusMatch[1]);
|
|
3574
|
+
return config;
|
|
3575
|
+
}
|
|
3576
|
+
function memoryToCpuTier(cpuKind, cpus) {
|
|
3577
|
+
if (!cpuKind) return void 0;
|
|
3578
|
+
const kind = cpuKind === "performance" ? "performance" : "shared";
|
|
3579
|
+
const count = cpus ?? 1;
|
|
3580
|
+
return `${kind}-${count}x`;
|
|
3581
|
+
}
|
|
3582
|
+
function generateFlyToml(appName, opts) {
|
|
3583
|
+
const cpuParts = opts.cpu.split("-");
|
|
3584
|
+
const cpuKind = cpuParts[0] ?? "shared";
|
|
3585
|
+
const cpuCount = parseInt(cpuParts[1] ?? "1", 10);
|
|
3586
|
+
return `# Auto-generated by InsForge CLI
|
|
3587
|
+
app = '${appName}'
|
|
3588
|
+
primary_region = '${opts.region}'
|
|
3589
|
+
|
|
3590
|
+
[build]
|
|
3591
|
+
dockerfile = 'Dockerfile'
|
|
3592
|
+
|
|
3593
|
+
[http_service]
|
|
3594
|
+
internal_port = ${opts.port}
|
|
3595
|
+
force_https = true
|
|
3596
|
+
auto_stop_machines = 'stop'
|
|
3597
|
+
auto_start_machines = true
|
|
3598
|
+
min_machines_running = 0
|
|
3599
|
+
|
|
3600
|
+
[[vm]]
|
|
3601
|
+
memory = '${opts.memory}mb'
|
|
3602
|
+
cpu_kind = '${cpuKind}'
|
|
3603
|
+
cpus = ${cpuCount}
|
|
3604
|
+
`;
|
|
3605
|
+
}
|
|
3606
|
+
function checkFlyctl() {
|
|
3607
|
+
try {
|
|
3608
|
+
execSync("flyctl version", { stdio: "pipe" });
|
|
3609
|
+
} catch {
|
|
3610
|
+
throw new CLIError(
|
|
3611
|
+
"flyctl is not installed.\nInstall it with: curl -L https://fly.io/install.sh | sh\nOr: brew install flyctl"
|
|
3612
|
+
);
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
function getFlyToken() {
|
|
3616
|
+
const token = process.env.FLY_API_TOKEN;
|
|
3617
|
+
if (!token) {
|
|
3618
|
+
throw new CLIError(
|
|
3619
|
+
"FLY_API_TOKEN environment variable is required for compute deploy.\nSet it with: export FLY_API_TOKEN=<your-fly-token>"
|
|
3620
|
+
);
|
|
3621
|
+
}
|
|
3622
|
+
return token;
|
|
3623
|
+
}
|
|
3624
|
+
function registerComputeDeployCommand(computeCmd2) {
|
|
3625
|
+
computeCmd2.command("deploy [directory]").description("Build and deploy a Dockerfile as a compute service").requiredOption("--name <name>", "Service name").option("--port <port>", "Container port").option("--cpu <tier>", "CPU tier (shared-1x, shared-2x, performance-1x, etc.)").option("--memory <mb>", "Memory in MB (256, 512, 1024, 2048, 4096, 8192)").option("--region <region>", "Fly.io region").option("--env <json>", "Environment variables as JSON object").action(async (directory, opts, cmd) => {
|
|
3626
|
+
const { json } = getRootOpts(cmd);
|
|
3627
|
+
try {
|
|
3628
|
+
await requireAuth();
|
|
3629
|
+
checkFlyctl();
|
|
3630
|
+
const flyToken = getFlyToken();
|
|
3631
|
+
const dir = directory ?? process.cwd();
|
|
3632
|
+
const dockerfilePath = join7(dir, "Dockerfile");
|
|
3633
|
+
if (!existsSync5(dockerfilePath)) {
|
|
3634
|
+
throw new CLIError(`No Dockerfile found in ${dir}`);
|
|
3635
|
+
}
|
|
3636
|
+
const flyTomlDefaults = parseFlyToml(dir);
|
|
3637
|
+
const port = Number(opts.port) || flyTomlDefaults.internalPort || 8080;
|
|
3638
|
+
const cpu = opts.cpu || memoryToCpuTier(flyTomlDefaults.cpuKind, flyTomlDefaults.cpus) || "shared-1x";
|
|
3639
|
+
const memory = Number(opts.memory) || Number(flyTomlDefaults.memory) || 512;
|
|
3640
|
+
const region = opts.region || flyTomlDefaults.region || "iad";
|
|
3641
|
+
let envVars;
|
|
3642
|
+
if (opts.env) {
|
|
3643
|
+
try {
|
|
3644
|
+
envVars = JSON.parse(opts.env);
|
|
3645
|
+
} catch {
|
|
3646
|
+
throw new CLIError("Invalid JSON for --env");
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
if (!json) outputInfo(`Checking for existing service "${opts.name}"...`);
|
|
3650
|
+
const listRes = await ossFetch("/api/compute/services");
|
|
3651
|
+
const services = await listRes.json();
|
|
3652
|
+
const existing = services.find((s) => s.name === opts.name);
|
|
3653
|
+
let serviceId;
|
|
3654
|
+
let flyAppId;
|
|
3655
|
+
if (existing) {
|
|
3656
|
+
serviceId = existing.id;
|
|
3657
|
+
flyAppId = existing.flyAppId;
|
|
3658
|
+
if (!json) outputInfo(`Found existing service, redeploying...`);
|
|
3659
|
+
} else {
|
|
3660
|
+
if (!json) outputInfo(`Creating service "${opts.name}"...`);
|
|
3661
|
+
const body = {
|
|
3662
|
+
name: opts.name,
|
|
3663
|
+
imageUrl: "dockerfile",
|
|
3664
|
+
port,
|
|
3665
|
+
cpu,
|
|
3666
|
+
memory,
|
|
3667
|
+
region
|
|
3668
|
+
};
|
|
3669
|
+
if (envVars) body.envVars = envVars;
|
|
3670
|
+
const deployRes = await ossFetch("/api/compute/services/deploy", {
|
|
3671
|
+
method: "POST",
|
|
3672
|
+
body: JSON.stringify(body)
|
|
3673
|
+
});
|
|
3674
|
+
const service = await deployRes.json();
|
|
3675
|
+
serviceId = service.id;
|
|
3676
|
+
flyAppId = service.flyAppId;
|
|
3677
|
+
}
|
|
3678
|
+
const existingTomlPath = join7(dir, "fly.toml");
|
|
3679
|
+
const backupTomlPath = join7(dir, "fly.toml.insforge-backup");
|
|
3680
|
+
let hadExistingToml = false;
|
|
3681
|
+
if (existsSync5(existingTomlPath)) {
|
|
3682
|
+
hadExistingToml = true;
|
|
3683
|
+
renameSync(existingTomlPath, backupTomlPath);
|
|
3684
|
+
}
|
|
3685
|
+
const tomlContent = generateFlyToml(flyAppId, { port, memory, cpu, region });
|
|
3686
|
+
writeFileSync4(existingTomlPath, tomlContent);
|
|
3687
|
+
try {
|
|
3688
|
+
if (!json) outputInfo("Building and deploying (this may take a few minutes)...");
|
|
3689
|
+
await new Promise((resolve2, reject) => {
|
|
3690
|
+
const child = spawn(
|
|
3691
|
+
"flyctl",
|
|
3692
|
+
["deploy", "--remote-only", "--app", flyAppId, "--access-token", flyToken, "--yes"],
|
|
3693
|
+
{ cwd: dir, stdio: json ? "pipe" : "inherit" }
|
|
3694
|
+
);
|
|
3695
|
+
child.on("close", (code) => {
|
|
3696
|
+
if (code === 0) resolve2();
|
|
3697
|
+
else reject(new CLIError(`flyctl deploy failed with exit code ${code}`));
|
|
3698
|
+
});
|
|
3699
|
+
child.on("error", (err) => reject(new CLIError(`flyctl deploy error: ${err.message}`)));
|
|
3700
|
+
});
|
|
3701
|
+
if (!json) outputInfo("Syncing deployment info...");
|
|
3702
|
+
const syncRes = await ossFetch(
|
|
3703
|
+
`/api/compute/services/${encodeURIComponent(serviceId)}/sync`,
|
|
3704
|
+
{ method: "PATCH" }
|
|
3705
|
+
);
|
|
3706
|
+
const synced = await syncRes.json();
|
|
3707
|
+
if (json) {
|
|
3708
|
+
outputJson(synced);
|
|
3709
|
+
} else {
|
|
3710
|
+
outputSuccess(`Service "${synced.name}" deployed [${synced.status}]`);
|
|
3711
|
+
if (synced.endpointUrl) {
|
|
3712
|
+
console.log(` Endpoint: ${synced.endpointUrl}`);
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
await reportCliUsage("cli.compute.deploy", true);
|
|
3716
|
+
} finally {
|
|
3717
|
+
try {
|
|
3718
|
+
unlinkSync2(existingTomlPath);
|
|
3719
|
+
if (hadExistingToml) {
|
|
3720
|
+
renameSync(backupTomlPath, existingTomlPath);
|
|
3721
|
+
}
|
|
3722
|
+
} catch {
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
} catch (err) {
|
|
3726
|
+
await reportCliUsage("cli.compute.deploy", false);
|
|
3727
|
+
handleError(err, json);
|
|
3728
|
+
}
|
|
3729
|
+
});
|
|
3730
|
+
}
|
|
3731
|
+
|
|
3274
3732
|
// src/commands/logs.ts
|
|
3275
3733
|
var VALID_SOURCES = ["insforge.logs", "postgREST.logs", "postgres.logs", "function.logs", "function-deploy.logs"];
|
|
3276
3734
|
var SOURCE_LOOKUP = new Map(VALID_SOURCES.map((s) => [s.toLowerCase(), s]));
|
|
@@ -4019,7 +4477,7 @@ function formatBytesCompact(bytes) {
|
|
|
4019
4477
|
|
|
4020
4478
|
// src/index.ts
|
|
4021
4479
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
4022
|
-
var pkg = JSON.parse(
|
|
4480
|
+
var pkg = JSON.parse(readFileSync7(join8(__dirname, "../package.json"), "utf-8"));
|
|
4023
4481
|
var INSFORGE_LOGO = `
|
|
4024
4482
|
\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
4025
4483
|
\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
|
|
@@ -4087,6 +4545,16 @@ registerLogsCommand(program);
|
|
|
4087
4545
|
registerMetadataCommand(program);
|
|
4088
4546
|
var diagnoseCmd = program.command("diagnose");
|
|
4089
4547
|
registerDiagnoseCommands(diagnoseCmd);
|
|
4548
|
+
var computeCmd = program.command("compute").description("Manage compute services (Docker containers on Fly.io)");
|
|
4549
|
+
registerComputeListCommand(computeCmd);
|
|
4550
|
+
registerComputeGetCommand(computeCmd);
|
|
4551
|
+
registerComputeCreateCommand(computeCmd);
|
|
4552
|
+
registerComputeDeployCommand(computeCmd);
|
|
4553
|
+
registerComputeUpdateCommand(computeCmd);
|
|
4554
|
+
registerComputeDeleteCommand(computeCmd);
|
|
4555
|
+
registerComputeStartCommand(computeCmd);
|
|
4556
|
+
registerComputeStopCommand(computeCmd);
|
|
4557
|
+
registerComputeLogsCommand(computeCmd);
|
|
4090
4558
|
var schedulesCmd = program.command("schedules").description("Manage scheduled tasks (cron jobs)");
|
|
4091
4559
|
registerSchedulesListCommand(schedulesCmd);
|
|
4092
4560
|
registerSchedulesGetCommand(schedulesCmd);
|