@mifune/agro 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agro.js +493 -88
- package/package.json +1 -1
package/dist/agro.js
CHANGED
|
@@ -1424,10 +1424,16 @@ async function askSecretPiped(question) {
|
|
|
1424
1424
|
process.on("SIGINT", onSigint);
|
|
1425
1425
|
try {
|
|
1426
1426
|
return await new Promise((resolve14) => {
|
|
1427
|
-
|
|
1427
|
+
let settled = false;
|
|
1428
|
+
const settle = (answer) => {
|
|
1429
|
+
if (settled) return;
|
|
1430
|
+
settled = true;
|
|
1428
1431
|
process.stdout.write("\n");
|
|
1429
1432
|
resolve14(answer.trim());
|
|
1430
|
-
}
|
|
1433
|
+
};
|
|
1434
|
+
rl.question(` ${question} `, settle);
|
|
1435
|
+
rl.once("line", settle);
|
|
1436
|
+
rl.once("close", () => settle(rl.line));
|
|
1431
1437
|
});
|
|
1432
1438
|
} finally {
|
|
1433
1439
|
process.removeListener("SIGINT", onSigint);
|
|
@@ -1471,7 +1477,7 @@ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readdirSync as read
|
|
|
1471
1477
|
import { dirname as dirname4, join as join4, resolve as resolve5, sep as sep2 } from "node:path";
|
|
1472
1478
|
|
|
1473
1479
|
// agro-asset:/home/runner/work/agro/agro/.devcontainer/docker-compose.yml
|
|
1474
|
-
var docker_compose_default = 'name: ${SANDBOX_NAME:-agro}\n\nservices:\n sandbox:\n image: ${AGRO_SANDBOX_IMAGE:-sandbox-${SANDBOX_NAME:-agro}}\n pull_policy: ${AGRO_PULL_POLICY:-missing}\n container_name: ${SANDBOX_NAME:-agro}\n build:\n context: ${AGRO_REPO_DIR:-..}\n dockerfile: .devcontainer/Dockerfile\n volumes:\n - ${AGRO_HOME_MOUNT:-workspace}:/home/sandbox\n - ${AGRO_REPO_DIR:-..}:/home/sandbox/harness\n extra_hosts:\n - "host.docker.internal:host-gateway"\n environment:\n - SANDBOX_NAME=${SANDBOX_NAME:-agro}\n - SANDBOX_PASSWORD=${SANDBOX_PASSWORD:-}\n - TZ=${TZ:-America/Los_Angeles}\n - CLAUDE_DANGEROUSLY_SKIP_PERMISSIONS=true\n - CC_SAFETY_NET_STRICT=1\n - CC_SAFETY_NET_WORKTREE=1\n - GIT_USER_NAME=${GIT_USER_NAME:-}\n - GIT_USER_EMAIL=${GIT_USER_EMAIL:-}\n - GH_TOKEN=${GH_TOKEN:-}\n - TYPESAFE_API_KEY=${TYPESAFE_API_KEY:-}\n stdin_open: true\n tty: true\n init: true\n entrypoint: ["/usr/local/bin/sandbox-main.sh"]\n stop_signal: SIGTERM\n healthcheck:\n test: ["CMD", "bash", "-c", "exec bash /home/sandbox/harness/.agro/scripts/sandbox-healthcheck.sh"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 600s\n restart: unless-stopped\n\nvolumes:\n workspace:\n';
|
|
1480
|
+
var docker_compose_default = 'name: ${SANDBOX_NAME:-agro}\n\nservices:\n sandbox:\n image: ${AGRO_SANDBOX_IMAGE:-sandbox-${SANDBOX_NAME:-agro}}\n pull_policy: ${AGRO_PULL_POLICY:-missing}\n container_name: ${SANDBOX_NAME:-agro}\n build:\n context: ${AGRO_REPO_DIR:-..}\n dockerfile: .devcontainer/Dockerfile\n volumes:\n - ${AGRO_HOME_MOUNT:-workspace}:/home/sandbox\n - ${AGRO_REPO_DIR:-..}:/home/sandbox/harness\n extra_hosts:\n - "host.docker.internal:host-gateway"\n environment:\n - SANDBOX_NAME=${SANDBOX_NAME:-agro}\n - SANDBOX_PASSWORD=${SANDBOX_PASSWORD:-}\n - TZ=${TZ:-America/Los_Angeles}\n - CLAUDE_DANGEROUSLY_SKIP_PERMISSIONS=true\n - CC_SAFETY_NET_STRICT=1\n - CC_SAFETY_NET_WORKTREE=1\n - GIT_USER_NAME=${GIT_USER_NAME:-}\n - GIT_USER_EMAIL=${GIT_USER_EMAIL:-}\n - GH_TOKEN=${GH_TOKEN:-}\n - TYPESAFE_API_KEY=${TYPESAFE_API_KEY:-}\n - XAI_API_KEY=${XAI_API_KEY:-}\n stdin_open: true\n tty: true\n init: true\n entrypoint: ["/usr/local/bin/sandbox-main.sh"]\n stop_signal: SIGTERM\n healthcheck:\n test: ["CMD", "bash", "-c", "exec bash /home/sandbox/harness/.agro/scripts/sandbox-healthcheck.sh"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 600s\n restart: unless-stopped\n\nvolumes:\n workspace:\n';
|
|
1475
1481
|
|
|
1476
1482
|
// agro-asset:/home/runner/work/agro/agro/.devcontainer/docker-compose.image-only.yml
|
|
1477
1483
|
var docker_compose_image_only_default = 'name: ${SANDBOX_NAME:-agro}\n\nservices:\n sandbox:\n image: ${AGRO_SANDBOX_IMAGE:-ghcr.io/mifunedev/agro:latest}\n pull_policy: ${AGRO_PULL_POLICY:-always}\n container_name: ${SANDBOX_NAME:-agro}\n volumes:\n - ${AGRO_HOME_MOUNT:-workspace}:/home/sandbox\n extra_hosts:\n - "host.docker.internal:host-gateway"\n environment:\n - SANDBOX_NAME=${SANDBOX_NAME:-agro}\n - SANDBOX_PASSWORD=${SANDBOX_PASSWORD:-}\n - TZ=${TZ:-America/Los_Angeles}\n - CLAUDE_DANGEROUSLY_SKIP_PERMISSIONS=true\n - CC_SAFETY_NET_STRICT=1\n - CC_SAFETY_NET_WORKTREE=1\n - GIT_USER_NAME=${GIT_USER_NAME:-}\n - GIT_USER_EMAIL=${GIT_USER_EMAIL:-}\n - GH_TOKEN=${GH_TOKEN:-}\n - TYPESAFE_API_KEY=${TYPESAFE_API_KEY:-}\n stdin_open: true\n tty: true\n init: true\n entrypoint: ["/usr/local/bin/sandbox-main.sh"]\n stop_signal: SIGTERM\n healthcheck:\n test: ["CMD", "bash", "-c", "exec bash /home/sandbox/harness/.agro/scripts/sandbox-healthcheck.sh"]\n interval: 30s\n timeout: 10s\n retries: 3\n start_period: 600s\n restart: unless-stopped\n\nvolumes:\n workspace:\n';
|
|
@@ -2034,7 +2040,15 @@ var SECRET_KEYS = [
|
|
|
2034
2040
|
"PI_SLACK_BOT_TOKEN",
|
|
2035
2041
|
"LANGFUSE_PUBLIC_KEY",
|
|
2036
2042
|
"LANGFUSE_SECRET_KEY",
|
|
2037
|
-
"TYPESAFE_API_KEY"
|
|
2043
|
+
"TYPESAFE_API_KEY",
|
|
2044
|
+
"AGRO_CONSOLE_URL",
|
|
2045
|
+
"AGRO_CONSOLE_TOKEN"
|
|
2046
|
+
];
|
|
2047
|
+
var COMPOSE_SECRET_KEYS = [
|
|
2048
|
+
"GH_TOKEN",
|
|
2049
|
+
"SANDBOX_PASSWORD",
|
|
2050
|
+
"TYPESAFE_API_KEY",
|
|
2051
|
+
"XAI_API_KEY"
|
|
2038
2052
|
];
|
|
2039
2053
|
var ALLOWED = new Set(SECRET_KEYS);
|
|
2040
2054
|
function secretsFilePath(root) {
|
|
@@ -2099,7 +2113,7 @@ import { basename as basename6, join as join6, sep as sep3 } from "node:path";
|
|
|
2099
2113
|
import { basename as basename4, resolve as resolve7 } from "node:path";
|
|
2100
2114
|
|
|
2101
2115
|
// src/lib/version.ts
|
|
2102
|
-
var AGRO_VERSION = true ? "0.
|
|
2116
|
+
var AGRO_VERSION = true ? "0.23.0" : "0.0.0-dev";
|
|
2103
2117
|
var OFFICIAL_IMAGE = "ghcr.io/mifunedev/agro";
|
|
2104
2118
|
var RELEASE_VERSION = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[a-z][a-z0-9]*\.[0-9]+)?$/;
|
|
2105
2119
|
function parseReleaseVersion(value) {
|
|
@@ -3423,6 +3437,14 @@ Set it with \`${opts.bin} config set ${key}\` instead.
|
|
|
3423
3437
|
|
|
3424
3438
|
Keys:
|
|
3425
3439
|
${secretKeyList()}
|
|
3440
|
+
`
|
|
3441
|
+
);
|
|
3442
|
+
return 1;
|
|
3443
|
+
}
|
|
3444
|
+
if (opts.sandbox !== void 0 && !COMPOSE_SECRET_KEYS.includes(key)) {
|
|
3445
|
+
io.stderr(
|
|
3446
|
+
`${opts.bin} secret set: ${key} does not reach the container from --sandbox ${opts.sandbox} \u2014 Compose passes only ${COMPOSE_SECRET_KEYS.join(", ")}.
|
|
3447
|
+
Run \`${opts.bin} secret set ${key}\` in the project checkout instead.
|
|
3426
3448
|
`
|
|
3427
3449
|
);
|
|
3428
3450
|
return 1;
|
|
@@ -3489,6 +3511,9 @@ import { homedir as homedir2, tmpdir as tmpdir2 } from "node:os";
|
|
|
3489
3511
|
import { dirname as dirname7, join as join11, relative } from "node:path";
|
|
3490
3512
|
|
|
3491
3513
|
// src/lib/tracing/harness-writers.ts
|
|
3514
|
+
import { join as join10 } from "node:path";
|
|
3515
|
+
|
|
3516
|
+
// src/lib/tracing/providers/langfuse.ts
|
|
3492
3517
|
import { join as join9 } from "node:path";
|
|
3493
3518
|
|
|
3494
3519
|
// src/lib/tracing/writer.ts
|
|
@@ -3531,16 +3556,66 @@ function isPlainObject(value) {
|
|
|
3531
3556
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3532
3557
|
}
|
|
3533
3558
|
|
|
3559
|
+
// src/lib/tracing/providers/langfuse.ts
|
|
3560
|
+
var LANGFUSE_PLUGIN_MAX_CHARS = 1e5;
|
|
3561
|
+
var LANGFUSE_FRAGMENT_KEYS = [
|
|
3562
|
+
"LANGFUSE_PUBLIC_KEY",
|
|
3563
|
+
"LANGFUSE_SECRET_KEY",
|
|
3564
|
+
"LANGFUSE_BASE_URL",
|
|
3565
|
+
"LANGFUSE_TRACING_ENVIRONMENT",
|
|
3566
|
+
"PI_LANGFUSE_MAX_CHARS"
|
|
3567
|
+
];
|
|
3568
|
+
function langfuseFragmentPath(home) {
|
|
3569
|
+
return join9(home, ".config", "agro", "langfuse.env");
|
|
3570
|
+
}
|
|
3571
|
+
function loadLangfuseCredentials(root) {
|
|
3572
|
+
const publicKey = readSecret(root, "LANGFUSE_PUBLIC_KEY");
|
|
3573
|
+
const secretKey = readSecret(root, "LANGFUSE_SECRET_KEY");
|
|
3574
|
+
if (publicKey !== void 0 && secretKey !== void 0) return { publicKey, secretKey };
|
|
3575
|
+
const missing = [
|
|
3576
|
+
publicKey === void 0 ? "LANGFUSE_PUBLIC_KEY" : void 0,
|
|
3577
|
+
secretKey === void 0 ? "LANGFUSE_SECRET_KEY" : void 0
|
|
3578
|
+
].filter((key) => key !== void 0);
|
|
3579
|
+
throw new Error(
|
|
3580
|
+
`${missing.join(" and ")} ${missing.length === 1 ? "is" : "are"} not set in .env \u2014 run \`${activeBin()} secret set ${missing[0]} <value>\` first`
|
|
3581
|
+
);
|
|
3582
|
+
}
|
|
3583
|
+
var BARE_SAFE_VALUE = /^[A-Za-z0-9_.:/@%+=,-]+$/;
|
|
3584
|
+
var UNCARRIABLE_IN_VALUE = /[\u0000-\u001f\u007f']/;
|
|
3585
|
+
function renderLangfuseFragment(input) {
|
|
3586
|
+
const values = {
|
|
3587
|
+
LANGFUSE_PUBLIC_KEY: input.publicKey,
|
|
3588
|
+
LANGFUSE_SECRET_KEY: input.secretKey,
|
|
3589
|
+
LANGFUSE_BASE_URL: input.baseUrl,
|
|
3590
|
+
LANGFUSE_TRACING_ENVIRONMENT: input.environment,
|
|
3591
|
+
PI_LANGFUSE_MAX_CHARS: String(LANGFUSE_PLUGIN_MAX_CHARS)
|
|
3592
|
+
};
|
|
3593
|
+
return LANGFUSE_FRAGMENT_KEYS.map((key) => `${key}=${quoteFragmentValue(key, values[key])}
|
|
3594
|
+
`).join("");
|
|
3595
|
+
}
|
|
3596
|
+
function writeLangfuseFragment(home, input) {
|
|
3597
|
+
return writeTextIfChanged(langfuseFragmentPath(home), renderLangfuseFragment(input), PRIVATE_FILE_MODE);
|
|
3598
|
+
}
|
|
3599
|
+
function quoteFragmentValue(key, value) {
|
|
3600
|
+
if (value === "") throw new Error(`${key} must not be empty`);
|
|
3601
|
+
if (UNCARRIABLE_IN_VALUE.test(value)) {
|
|
3602
|
+
throw new Error(
|
|
3603
|
+
`${key} must not contain a newline, a control character, or a single quote \u2014 zsh reads the fragment and does not carry that value safely`
|
|
3604
|
+
);
|
|
3605
|
+
}
|
|
3606
|
+
return BARE_SAFE_VALUE.test(value) ? value : `'${value}'`;
|
|
3607
|
+
}
|
|
3608
|
+
|
|
3534
3609
|
// src/lib/tracing/harness-writers.ts
|
|
3535
3610
|
var CODEX_TRACING_TAGS = ["codex"];
|
|
3536
3611
|
function claudeCodeSettingsPath(home) {
|
|
3537
|
-
return
|
|
3612
|
+
return join10(home, ".claude", "settings.json");
|
|
3538
3613
|
}
|
|
3539
3614
|
function piTracingConfigPath(home) {
|
|
3540
|
-
return
|
|
3615
|
+
return join10(home, ".pi", "agent", "langfuse.json");
|
|
3541
3616
|
}
|
|
3542
3617
|
function codexTracingConfigPath(home) {
|
|
3543
|
-
return
|
|
3618
|
+
return join10(home, ".codex", "langfuse.json");
|
|
3544
3619
|
}
|
|
3545
3620
|
var claudeCodeTracingWriter = (home, settings) => {
|
|
3546
3621
|
const path4 = claudeCodeSettingsPath(home);
|
|
@@ -3554,7 +3629,8 @@ var claudeCodeTracingWriter = (home, settings) => {
|
|
|
3554
3629
|
env: {
|
|
3555
3630
|
...env,
|
|
3556
3631
|
LANGFUSE_BASE_URL: settings.baseUrl,
|
|
3557
|
-
LANGFUSE_TRACING_ENVIRONMENT: settings.environment
|
|
3632
|
+
LANGFUSE_TRACING_ENVIRONMENT: settings.environment,
|
|
3633
|
+
CC_LANGFUSE_MAX_CHARS: String(LANGFUSE_PLUGIN_MAX_CHARS)
|
|
3558
3634
|
}
|
|
3559
3635
|
};
|
|
3560
3636
|
return writeTextIfChanged(path4, renderJson(next));
|
|
@@ -3783,54 +3859,6 @@ function harnessBinPath(prefix) {
|
|
|
3783
3859
|
return `${prefix}/bin`;
|
|
3784
3860
|
}
|
|
3785
3861
|
|
|
3786
|
-
// src/lib/tracing/providers/langfuse.ts
|
|
3787
|
-
import { join as join10 } from "node:path";
|
|
3788
|
-
var LANGFUSE_FRAGMENT_KEYS = [
|
|
3789
|
-
"LANGFUSE_PUBLIC_KEY",
|
|
3790
|
-
"LANGFUSE_SECRET_KEY",
|
|
3791
|
-
"LANGFUSE_BASE_URL",
|
|
3792
|
-
"LANGFUSE_TRACING_ENVIRONMENT"
|
|
3793
|
-
];
|
|
3794
|
-
function langfuseFragmentPath(home) {
|
|
3795
|
-
return join10(home, ".config", "agro", "langfuse.env");
|
|
3796
|
-
}
|
|
3797
|
-
function loadLangfuseCredentials(root) {
|
|
3798
|
-
const publicKey = readSecret(root, "LANGFUSE_PUBLIC_KEY");
|
|
3799
|
-
const secretKey = readSecret(root, "LANGFUSE_SECRET_KEY");
|
|
3800
|
-
if (publicKey !== void 0 && secretKey !== void 0) return { publicKey, secretKey };
|
|
3801
|
-
const missing = [
|
|
3802
|
-
publicKey === void 0 ? "LANGFUSE_PUBLIC_KEY" : void 0,
|
|
3803
|
-
secretKey === void 0 ? "LANGFUSE_SECRET_KEY" : void 0
|
|
3804
|
-
].filter((key) => key !== void 0);
|
|
3805
|
-
throw new Error(
|
|
3806
|
-
`${missing.join(" and ")} ${missing.length === 1 ? "is" : "are"} not set in .env \u2014 run \`${activeBin()} secret set ${missing[0]} <value>\` first`
|
|
3807
|
-
);
|
|
3808
|
-
}
|
|
3809
|
-
var BARE_SAFE_VALUE = /^[A-Za-z0-9_.:/@%+=,-]+$/;
|
|
3810
|
-
var UNCARRIABLE_IN_VALUE = /[\u0000-\u001f\u007f']/;
|
|
3811
|
-
function renderLangfuseFragment(input) {
|
|
3812
|
-
const values = {
|
|
3813
|
-
LANGFUSE_PUBLIC_KEY: input.publicKey,
|
|
3814
|
-
LANGFUSE_SECRET_KEY: input.secretKey,
|
|
3815
|
-
LANGFUSE_BASE_URL: input.baseUrl,
|
|
3816
|
-
LANGFUSE_TRACING_ENVIRONMENT: input.environment
|
|
3817
|
-
};
|
|
3818
|
-
return LANGFUSE_FRAGMENT_KEYS.map((key) => `${key}=${quoteFragmentValue(key, values[key])}
|
|
3819
|
-
`).join("");
|
|
3820
|
-
}
|
|
3821
|
-
function writeLangfuseFragment(home, input) {
|
|
3822
|
-
return writeTextIfChanged(langfuseFragmentPath(home), renderLangfuseFragment(input), PRIVATE_FILE_MODE);
|
|
3823
|
-
}
|
|
3824
|
-
function quoteFragmentValue(key, value) {
|
|
3825
|
-
if (value === "") throw new Error(`${key} must not be empty`);
|
|
3826
|
-
if (UNCARRIABLE_IN_VALUE.test(value)) {
|
|
3827
|
-
throw new Error(
|
|
3828
|
-
`${key} must not contain a newline, a control character, or a single quote \u2014 zsh reads the fragment and does not carry that value safely`
|
|
3829
|
-
);
|
|
3830
|
-
}
|
|
3831
|
-
return BARE_SAFE_VALUE.test(value) ? value : `'${value}'`;
|
|
3832
|
-
}
|
|
3833
|
-
|
|
3834
3862
|
// src/commands/langfuse.ts
|
|
3835
3863
|
var DEFAULT_LANGFUSE_BASE_URL = "https://cloud.langfuse.com";
|
|
3836
3864
|
var LANGFUSE_FRAGMENT_LABEL = "~/.config/agro/langfuse.env";
|
|
@@ -5095,6 +5123,357 @@ function runEgress(verb, opts, io) {
|
|
|
5095
5123
|
});
|
|
5096
5124
|
}
|
|
5097
5125
|
|
|
5126
|
+
// src/command-table.ts
|
|
5127
|
+
var CONFIG_VERBS = ["show", "set", "repo"];
|
|
5128
|
+
var CONFIG_INTEGRATIONS = ["langfuse"];
|
|
5129
|
+
var SECRET_VERBS = ["set", "get", "list"];
|
|
5130
|
+
var LANGFUSE_VERBS = ["apply", "status", "disable"];
|
|
5131
|
+
var SANDBOX_SUBCOMMANDS = ["install", "list", "upgrade"];
|
|
5132
|
+
var WORKSPACE_SUBCOMMANDS = ["create", "list"];
|
|
5133
|
+
var HARNESS_SUBCOMMANDS = ["list", "install", "status", "uninstall"];
|
|
5134
|
+
var TOOL_SUBCOMMANDS = ["list", "install", "status", "uninstall"];
|
|
5135
|
+
var EGRESS_VERBS = ["log", "reload"];
|
|
5136
|
+
var NODE_VERBS = ["create", "list", "delete"];
|
|
5137
|
+
function isOneOf(values, value) {
|
|
5138
|
+
return value !== void 0 && values.includes(value);
|
|
5139
|
+
}
|
|
5140
|
+
var AGRO_COMMANDS = Object.freeze({
|
|
5141
|
+
config: [...CONFIG_VERBS, ...CONFIG_INTEGRATIONS],
|
|
5142
|
+
secret: SECRET_VERBS,
|
|
5143
|
+
langfuse: LANGFUSE_VERBS,
|
|
5144
|
+
update: null,
|
|
5145
|
+
"self-upgrade": null,
|
|
5146
|
+
vendor: null,
|
|
5147
|
+
sandbox: SANDBOX_SUBCOMMANDS,
|
|
5148
|
+
shell: null,
|
|
5149
|
+
compose: null,
|
|
5150
|
+
workspace: WORKSPACE_SUBCOMMANDS,
|
|
5151
|
+
harness: HARNESS_SUBCOMMANDS,
|
|
5152
|
+
tool: TOOL_SUBCOMMANDS,
|
|
5153
|
+
gateway: null,
|
|
5154
|
+
egress: EGRESS_VERBS,
|
|
5155
|
+
node: NODE_VERBS,
|
|
5156
|
+
...Object.fromEntries(composeVerbs().map((verb) => [verb, null]))
|
|
5157
|
+
});
|
|
5158
|
+
|
|
5159
|
+
// src/commands/node.ts
|
|
5160
|
+
var DEFAULT_CONSOLE_URL = "https://console.mifune.dev";
|
|
5161
|
+
var CONSOLE_CONFIG_MISSING = "set AGRO_CONSOLE_TOKEN, or run agro secret set AGRO_CONSOLE_TOKEN";
|
|
5162
|
+
var NODE_READY_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
5163
|
+
var NODE_FAST_POLL_INTERVAL_MS = 1e3;
|
|
5164
|
+
var NODE_FAST_POLL_WINDOW_MS = 30 * 1e3;
|
|
5165
|
+
var NODE_POLL_INTERVAL_MS = 5e3;
|
|
5166
|
+
var FREE_TIER_SPEC = "n4";
|
|
5167
|
+
var FAILURE_STATUSES = /* @__PURE__ */ new Set(["failed", "destroy_requested", "destroying", "destroyed", "retired"]);
|
|
5168
|
+
function resolveConsoleConfig(env, readSecret2) {
|
|
5169
|
+
const lookup = (key) => {
|
|
5170
|
+
const fromEnv = env[key]?.trim();
|
|
5171
|
+
if (fromEnv !== void 0 && fromEnv !== "") return fromEnv;
|
|
5172
|
+
const fromSecret = readSecret2(key)?.trim();
|
|
5173
|
+
return fromSecret === void 0 || fromSecret === "" ? void 0 : fromSecret;
|
|
5174
|
+
};
|
|
5175
|
+
const token = lookup("AGRO_CONSOLE_TOKEN");
|
|
5176
|
+
if (token === void 0) return void 0;
|
|
5177
|
+
const url = lookup("AGRO_CONSOLE_URL") ?? DEFAULT_CONSOLE_URL;
|
|
5178
|
+
return { url: url.replace(/\/+$/, ""), token };
|
|
5179
|
+
}
|
|
5180
|
+
function readProjectSecret(key) {
|
|
5181
|
+
try {
|
|
5182
|
+
return readSecret(resolveProjectRoot(), key);
|
|
5183
|
+
} catch {
|
|
5184
|
+
return void 0;
|
|
5185
|
+
}
|
|
5186
|
+
}
|
|
5187
|
+
function nodePollDelayMs(elapsedMs) {
|
|
5188
|
+
return elapsedMs < NODE_FAST_POLL_WINDOW_MS ? NODE_FAST_POLL_INTERVAL_MS : NODE_POLL_INTERVAL_MS;
|
|
5189
|
+
}
|
|
5190
|
+
function defaultDeps2() {
|
|
5191
|
+
return {
|
|
5192
|
+
fetch: (input, init) => globalThis.fetch(input, init),
|
|
5193
|
+
env: process.env,
|
|
5194
|
+
readSecret: readProjectSecret,
|
|
5195
|
+
sleep: (ms) => new Promise((done) => setTimeout(done, ms)),
|
|
5196
|
+
now: () => Date.now(),
|
|
5197
|
+
pollDelayMs: nodePollDelayMs,
|
|
5198
|
+
timeoutMs: NODE_READY_TIMEOUT_MS,
|
|
5199
|
+
confirm: (question) => confirm(question, false)
|
|
5200
|
+
};
|
|
5201
|
+
}
|
|
5202
|
+
function nodeHelp(bin) {
|
|
5203
|
+
return `${bin} node \u2014 Create, list, and delete agro-console sandbox nodes (remote VMs)
|
|
5204
|
+
|
|
5205
|
+
Usage:
|
|
5206
|
+
${bin} node create --spec <spec> --ssh-key <uuid> [--ssh-key <uuid>] [--name <name>] [--free-tier] [--json]
|
|
5207
|
+
${bin} node list [--json]
|
|
5208
|
+
${bin} node delete <id> [--yes]
|
|
5209
|
+
|
|
5210
|
+
\`create\` waits up to 15 minutes for the node to reach \`running\`, then prints
|
|
5211
|
+
its SSH command. \`--free-tier\` requests the console free tier. The spec
|
|
5212
|
+
defaults to n4. The verbs read AGRO_CONSOLE_URL and AGRO_CONSOLE_TOKEN from the
|
|
5213
|
+
environment, then from the project .env (\`${bin} secret set <KEY>\`).
|
|
5214
|
+
AGRO_CONSOLE_URL defaults to ${DEFAULT_CONSOLE_URL}.
|
|
5215
|
+
`;
|
|
5216
|
+
}
|
|
5217
|
+
function asRecord(value) {
|
|
5218
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
5219
|
+
}
|
|
5220
|
+
function asText(value) {
|
|
5221
|
+
if (typeof value === "string" && value !== "") return value;
|
|
5222
|
+
if (typeof value === "number") return String(value);
|
|
5223
|
+
return null;
|
|
5224
|
+
}
|
|
5225
|
+
function toNode(value) {
|
|
5226
|
+
const outer = asRecord(value);
|
|
5227
|
+
const record = asRecord(outer?.node) ?? outer;
|
|
5228
|
+
const id = asText(record?.id);
|
|
5229
|
+
if (record === void 0 || id === null) return void 0;
|
|
5230
|
+
return {
|
|
5231
|
+
id,
|
|
5232
|
+
name: asText(record.name),
|
|
5233
|
+
profile: asText(record.profile),
|
|
5234
|
+
status: asText(record.status) ?? "unknown",
|
|
5235
|
+
ipv4: asText(record.ipv4),
|
|
5236
|
+
ssh: asText(record.ssh)
|
|
5237
|
+
};
|
|
5238
|
+
}
|
|
5239
|
+
function sshCommand(node) {
|
|
5240
|
+
if (node.ssh !== null) return node.ssh;
|
|
5241
|
+
return node.ipv4 === null ? null : `ssh sandbox@${node.ipv4}`;
|
|
5242
|
+
}
|
|
5243
|
+
function errorDetail(status, body) {
|
|
5244
|
+
const record = asRecord(body);
|
|
5245
|
+
const nested = asRecord(record?.error);
|
|
5246
|
+
const code = asText(record?.code) ?? asText(nested?.code) ?? "no code";
|
|
5247
|
+
const message = asText(record?.error) ?? asText(nested?.message) ?? asText(record?.message);
|
|
5248
|
+
return `HTTP ${status} ${code}${message === null ? "" : `: ${message}`}`;
|
|
5249
|
+
}
|
|
5250
|
+
async function callApi(deps, config, method, path4, payload) {
|
|
5251
|
+
let response;
|
|
5252
|
+
try {
|
|
5253
|
+
response = await deps.fetch(`${config.url}${path4}`, {
|
|
5254
|
+
method,
|
|
5255
|
+
headers: {
|
|
5256
|
+
Authorization: `Bearer ${config.token}`,
|
|
5257
|
+
Accept: "application/json",
|
|
5258
|
+
...payload === void 0 ? {} : { "Content-Type": "application/json" }
|
|
5259
|
+
},
|
|
5260
|
+
...payload === void 0 ? {} : { body: JSON.stringify(payload) }
|
|
5261
|
+
});
|
|
5262
|
+
} catch (error) {
|
|
5263
|
+
return { ok: false, message: `request failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
5264
|
+
}
|
|
5265
|
+
const text = await response.text();
|
|
5266
|
+
let body = null;
|
|
5267
|
+
try {
|
|
5268
|
+
body = text === "" ? null : JSON.parse(text);
|
|
5269
|
+
} catch {
|
|
5270
|
+
body = null;
|
|
5271
|
+
}
|
|
5272
|
+
if (response.status >= 400) {
|
|
5273
|
+
return { ok: false, status: response.status, message: errorDetail(response.status, body) };
|
|
5274
|
+
}
|
|
5275
|
+
return { ok: true, status: response.status, body };
|
|
5276
|
+
}
|
|
5277
|
+
function parseCreate(tokens) {
|
|
5278
|
+
const args = { sshKeyIds: [], freeTier: false, json: false };
|
|
5279
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
5280
|
+
const token = tokens[i];
|
|
5281
|
+
if (token === "--json") {
|
|
5282
|
+
args.json = true;
|
|
5283
|
+
continue;
|
|
5284
|
+
}
|
|
5285
|
+
if (token === "--free-tier") {
|
|
5286
|
+
args.freeTier = true;
|
|
5287
|
+
continue;
|
|
5288
|
+
}
|
|
5289
|
+
const [flag, inline] = token.startsWith("--") && token.includes("=") ? [token.slice(0, token.indexOf("=")), token.slice(token.indexOf("=") + 1)] : [token, void 0];
|
|
5290
|
+
if (flag !== "--spec" && flag !== "--ssh-key" && flag !== "--name") {
|
|
5291
|
+
return { ok: false, error: token.startsWith("-") ? `unknown flag "${token}"` : `unexpected argument "${token}"` };
|
|
5292
|
+
}
|
|
5293
|
+
const value = inline ?? tokens[++i];
|
|
5294
|
+
if (value === void 0 || value === "") return { ok: false, error: `${flag} requires a value` };
|
|
5295
|
+
if (flag === "--spec") args.spec = value;
|
|
5296
|
+
else if (flag === "--name") args.name = value;
|
|
5297
|
+
else args.sshKeyIds.push(value);
|
|
5298
|
+
}
|
|
5299
|
+
if (args.spec === void 0 && args.freeTier) args.spec = FREE_TIER_SPEC;
|
|
5300
|
+
if (args.spec === void 0) return { ok: false, error: "--spec is required" };
|
|
5301
|
+
if (args.sshKeyIds.length === 0) return { ok: false, error: "at least one --ssh-key is required" };
|
|
5302
|
+
return { ok: true, args };
|
|
5303
|
+
}
|
|
5304
|
+
function printCreateResult(io, node, json) {
|
|
5305
|
+
const ssh = node.status === "running" ? sshCommand(node) : null;
|
|
5306
|
+
if (json) {
|
|
5307
|
+
io.stdout(`${JSON.stringify({ id: node.id, status: node.status, ssh })}
|
|
5308
|
+
`);
|
|
5309
|
+
return;
|
|
5310
|
+
}
|
|
5311
|
+
io.stdout(`id: ${node.id}
|
|
5312
|
+
status: ${node.status}
|
|
5313
|
+
${ssh === null ? "" : `${ssh}
|
|
5314
|
+
`}`);
|
|
5315
|
+
}
|
|
5316
|
+
async function runCreate(tokens, io, deps, config, bin) {
|
|
5317
|
+
const prefix = `${bin} node create`;
|
|
5318
|
+
const parsed = parseCreate(tokens);
|
|
5319
|
+
if (!parsed.ok) {
|
|
5320
|
+
io.stderr(`${prefix}: ${parsed.error}
|
|
5321
|
+
`);
|
|
5322
|
+
return 1;
|
|
5323
|
+
}
|
|
5324
|
+
const { spec, sshKeyIds, name, freeTier, json } = parsed.args;
|
|
5325
|
+
const requestedAt = deps.now();
|
|
5326
|
+
const created = await callApi(deps, config, "POST", "/api/nodes", {
|
|
5327
|
+
profile: "sandbox",
|
|
5328
|
+
spec,
|
|
5329
|
+
sshKeyIds,
|
|
5330
|
+
...name === void 0 ? {} : { name },
|
|
5331
|
+
...freeTier ? { freeTier: true } : {}
|
|
5332
|
+
});
|
|
5333
|
+
if (!created.ok) {
|
|
5334
|
+
io.stderr(`${prefix}: ${created.message}
|
|
5335
|
+
`);
|
|
5336
|
+
return 1;
|
|
5337
|
+
}
|
|
5338
|
+
let node = toNode(created.body);
|
|
5339
|
+
if (node === void 0) {
|
|
5340
|
+
io.stderr(`${prefix}: the console response has no node id
|
|
5341
|
+
`);
|
|
5342
|
+
return 1;
|
|
5343
|
+
}
|
|
5344
|
+
io.stderr(`${prefix}: node ${node.id} ${node.status}
|
|
5345
|
+
`);
|
|
5346
|
+
const deadline = deps.now() + deps.timeoutMs;
|
|
5347
|
+
while (node.status !== "running" && !FAILURE_STATUSES.has(node.status)) {
|
|
5348
|
+
const remaining = deadline - deps.now();
|
|
5349
|
+
if (remaining <= 0) {
|
|
5350
|
+
printCreateResult(io, node, json);
|
|
5351
|
+
io.stderr(`${prefix}: node ${node.id} is not running after 15 minutes \u2014 last status ${node.status}
|
|
5352
|
+
`);
|
|
5353
|
+
return 1;
|
|
5354
|
+
}
|
|
5355
|
+
await deps.sleep(Math.min(deps.pollDelayMs(deps.now() - requestedAt), remaining));
|
|
5356
|
+
const polled = await callApi(deps, config, "GET", `/api/nodes/${encodeURIComponent(node.id)}`);
|
|
5357
|
+
if (!polled.ok) {
|
|
5358
|
+
io.stderr(`${prefix}: node ${node.id}: ${polled.message}
|
|
5359
|
+
`);
|
|
5360
|
+
return 1;
|
|
5361
|
+
}
|
|
5362
|
+
const previous = node.status;
|
|
5363
|
+
node = toNode(polled.body) ?? { ...node, status: "unknown" };
|
|
5364
|
+
if (node.status !== previous) io.stderr(`${prefix}: node ${node.id} ${node.status}
|
|
5365
|
+
`);
|
|
5366
|
+
}
|
|
5367
|
+
printCreateResult(io, node, json);
|
|
5368
|
+
if (node.status !== "running") {
|
|
5369
|
+
io.stderr(`${prefix}: node ${node.id} failed \u2014 last status ${node.status}
|
|
5370
|
+
`);
|
|
5371
|
+
return 1;
|
|
5372
|
+
}
|
|
5373
|
+
return 0;
|
|
5374
|
+
}
|
|
5375
|
+
function toNodeList(body) {
|
|
5376
|
+
const record = asRecord(body);
|
|
5377
|
+
const items = Array.isArray(body) ? body : record?.nodes ?? record?.data ?? record?.items;
|
|
5378
|
+
if (!Array.isArray(items)) return [];
|
|
5379
|
+
return items.flatMap((item) => toNode(item) ?? []);
|
|
5380
|
+
}
|
|
5381
|
+
async function runList(tokens, io, deps, config, bin) {
|
|
5382
|
+
const prefix = `${bin} node list`;
|
|
5383
|
+
const extra = tokens.find((token) => token !== "--json");
|
|
5384
|
+
if (extra !== void 0) {
|
|
5385
|
+
io.stderr(`${prefix}: ${extra.startsWith("-") ? "unknown flag" : "unexpected argument"} "${extra}"
|
|
5386
|
+
`);
|
|
5387
|
+
return 1;
|
|
5388
|
+
}
|
|
5389
|
+
const listed = await callApi(deps, config, "GET", "/api/nodes");
|
|
5390
|
+
if (!listed.ok) {
|
|
5391
|
+
io.stderr(`${prefix}: ${listed.message}
|
|
5392
|
+
`);
|
|
5393
|
+
return 1;
|
|
5394
|
+
}
|
|
5395
|
+
const rows = toNodeList(listed.body).map(({ id, name, profile, status, ipv4 }) => ({ id, name, profile, status, ipv4 }));
|
|
5396
|
+
if (tokens.includes("--json")) {
|
|
5397
|
+
io.stdout(`${JSON.stringify(rows)}
|
|
5398
|
+
`);
|
|
5399
|
+
return 0;
|
|
5400
|
+
}
|
|
5401
|
+
if (rows.length === 0) {
|
|
5402
|
+
io.stdout("no nodes\n");
|
|
5403
|
+
return 0;
|
|
5404
|
+
}
|
|
5405
|
+
const table = [
|
|
5406
|
+
["ID", "NAME", "PROFILE", "STATUS", "IPV4"],
|
|
5407
|
+
...rows.map((row) => [row.id, row.name ?? "-", row.profile ?? "-", row.status, row.ipv4 ?? "-"])
|
|
5408
|
+
];
|
|
5409
|
+
const widths = table[0].map((_, column) => Math.max(...table.map((cells) => cells[column].length)));
|
|
5410
|
+
for (const cells of table) {
|
|
5411
|
+
io.stdout(`${cells.map((cell3, column) => cell3.padEnd(widths[column])).join(" ").trimEnd()}
|
|
5412
|
+
`);
|
|
5413
|
+
}
|
|
5414
|
+
return 0;
|
|
5415
|
+
}
|
|
5416
|
+
async function runDelete(tokens, io, deps, config, bin) {
|
|
5417
|
+
const prefix = `${bin} node delete`;
|
|
5418
|
+
const flag = tokens.find((token) => token.startsWith("-") && token !== "--yes" && token !== "-y");
|
|
5419
|
+
if (flag !== void 0) {
|
|
5420
|
+
io.stderr(`${prefix}: unknown flag "${flag}"
|
|
5421
|
+
`);
|
|
5422
|
+
return 1;
|
|
5423
|
+
}
|
|
5424
|
+
const ids = tokens.filter((token) => !token.startsWith("-"));
|
|
5425
|
+
if (ids.length !== 1) {
|
|
5426
|
+
io.stderr(`${prefix}: takes exactly one node id
|
|
5427
|
+
`);
|
|
5428
|
+
return 1;
|
|
5429
|
+
}
|
|
5430
|
+
const [id] = ids;
|
|
5431
|
+
const yes = tokens.includes("--yes") || tokens.includes("-y");
|
|
5432
|
+
if (!yes && !await deps.confirm(`Destroy console node ${id}?`)) {
|
|
5433
|
+
io.stderr(`${prefix}: aborted \u2014 node ${id} unchanged
|
|
5434
|
+
`);
|
|
5435
|
+
return 1;
|
|
5436
|
+
}
|
|
5437
|
+
const deleted = await callApi(deps, config, "DELETE", `/api/nodes/${encodeURIComponent(id)}`);
|
|
5438
|
+
if (!deleted.ok) {
|
|
5439
|
+
io.stderr(
|
|
5440
|
+
deleted.status === 403 ? `${prefix}: destroy needs the admin role in agro-console
|
|
5441
|
+
` : `${prefix}: ${deleted.message}
|
|
5442
|
+
`
|
|
5443
|
+
);
|
|
5444
|
+
return 1;
|
|
5445
|
+
}
|
|
5446
|
+
io.stdout(`deleted ${id}
|
|
5447
|
+
`);
|
|
5448
|
+
return 0;
|
|
5449
|
+
}
|
|
5450
|
+
async function runNode(args, io, overrides = {}, bin = "agro") {
|
|
5451
|
+
const [verb, ...rest] = args;
|
|
5452
|
+
if (verb === void 0 || verb === "--help" || verb === "-h" || verb === "help") {
|
|
5453
|
+
io.stdout(nodeHelp(bin));
|
|
5454
|
+
return verb === void 0 ? 1 : 0;
|
|
5455
|
+
}
|
|
5456
|
+
if (!isOneOf(NODE_VERBS, verb)) {
|
|
5457
|
+
io.stderr(`${bin} node: unknown subcommand "${verb}" \u2014 expected ${NODE_VERBS.join(", ")}
|
|
5458
|
+
`);
|
|
5459
|
+
return 1;
|
|
5460
|
+
}
|
|
5461
|
+
if (rest.some((token) => token === "--help" || token === "-h")) {
|
|
5462
|
+
io.stdout(nodeHelp(bin));
|
|
5463
|
+
return 0;
|
|
5464
|
+
}
|
|
5465
|
+
const deps = { ...defaultDeps2(), ...overrides };
|
|
5466
|
+
const config = resolveConsoleConfig(deps.env, deps.readSecret);
|
|
5467
|
+
if (config === void 0) {
|
|
5468
|
+
io.stderr(`${bin} node ${verb}: ${CONSOLE_CONFIG_MISSING}
|
|
5469
|
+
`);
|
|
5470
|
+
return 1;
|
|
5471
|
+
}
|
|
5472
|
+
if (verb === "list") return runList(rest, io, deps, config, bin);
|
|
5473
|
+
if (verb === "delete") return runDelete(rest, io, deps, config, bin);
|
|
5474
|
+
return runCreate(rest, io, deps, config, bin);
|
|
5475
|
+
}
|
|
5476
|
+
|
|
5098
5477
|
// src/commands/harness.ts
|
|
5099
5478
|
import { existsSync as existsSync20 } from "node:fs";
|
|
5100
5479
|
import { homedir as homedir3 } from "node:os";
|
|
@@ -6453,6 +6832,54 @@ install -d -m 0700 "$HOME/.tailscale"`
|
|
|
6453
6832
|
]),
|
|
6454
6833
|
docsPath: TOOLS_DOC
|
|
6455
6834
|
}),
|
|
6835
|
+
Object.freeze({
|
|
6836
|
+
id: "ffmpeg",
|
|
6837
|
+
title: "FFmpeg + FFprobe",
|
|
6838
|
+
kind: "installable",
|
|
6839
|
+
binary: "ffmpeg",
|
|
6840
|
+
verifyArgv: Object.freeze(["bash", "-lc", "command -v ffmpeg >/dev/null && command -v ffprobe >/dev/null"]),
|
|
6841
|
+
versionArgv: Object.freeze(["ffmpeg", "-version"]),
|
|
6842
|
+
installArgv: Object.freeze([
|
|
6843
|
+
"bash",
|
|
6844
|
+
"-lc",
|
|
6845
|
+
[
|
|
6846
|
+
"set -e",
|
|
6847
|
+
"release=b6.1.1",
|
|
6848
|
+
'case "$(dpkg --print-architecture)" in',
|
|
6849
|
+
" amd64) arch=x64; ffmpeg_sha=bfe8a8fc511530457b528c48d77b5737527b504a3797a9bc4866aeca69c2dffa; ffprobe_sha=25d9b6ccb05e3d9de9e04e31e2506d8dd7f9f0418981965ac6df12e8d3afd067 ;;",
|
|
6850
|
+
" arm64) arch=arm64; ffmpeg_sha=754a678672298bc68156adff58aa7385a592c2b30b1d0ae8750c45c915c4bac0; ffprobe_sha=2ab6aba60ee84412dff9188720703376cb4e7aaf7e0b5e43aa8249f2acae5bf8 ;;",
|
|
6851
|
+
' *) echo "no pinned FFmpeg build for $(dpkg --print-architecture)" >&2; exit 1 ;;',
|
|
6852
|
+
"esac",
|
|
6853
|
+
'prefix="${NPM_USER_PREFIX:-$HOME/.local}"',
|
|
6854
|
+
'tmp="$(mktemp -d)"',
|
|
6855
|
+
`trap 'rm -rf "$tmp"' EXIT`,
|
|
6856
|
+
"for name in ffmpeg ffprobe; do",
|
|
6857
|
+
' curl -fsSL "https://github.com/eugeneware/ffmpeg-static/releases/download/$release/$name-linux-$arch.gz" -o "$tmp/$name.gz"',
|
|
6858
|
+
"done",
|
|
6859
|
+
'echo "$ffmpeg_sha $tmp/ffmpeg.gz" | sha256sum -c -',
|
|
6860
|
+
'echo "$ffprobe_sha $tmp/ffprobe.gz" | sha256sum -c -',
|
|
6861
|
+
'install -d "$prefix/bin"',
|
|
6862
|
+
"for name in ffmpeg ffprobe; do",
|
|
6863
|
+
' gzip -dc "$tmp/$name.gz" > "$tmp/$name"',
|
|
6864
|
+
' install -m 0755 "$tmp/$name" "$prefix/bin/$name"',
|
|
6865
|
+
"done",
|
|
6866
|
+
'"$prefix/bin/ffprobe" -version >/dev/null',
|
|
6867
|
+
'if ! "$prefix/bin/ffmpeg" -hide_banner -encoders | grep -qw libx264; then',
|
|
6868
|
+
' echo "$prefix/bin/ffmpeg lists no libx264 encoder" >&2',
|
|
6869
|
+
" exit 1",
|
|
6870
|
+
"fi"
|
|
6871
|
+
].join("\n")
|
|
6872
|
+
]),
|
|
6873
|
+
installUser: "sandbox",
|
|
6874
|
+
hostCapable: true,
|
|
6875
|
+
uninstallArgv: Object.freeze([
|
|
6876
|
+
"rm",
|
|
6877
|
+
"-rf",
|
|
6878
|
+
`${HARNESS_PREFIX_TOKEN}/bin/ffmpeg`,
|
|
6879
|
+
`${HARNESS_PREFIX_TOKEN}/bin/ffprobe`
|
|
6880
|
+
]),
|
|
6881
|
+
docsPath: TOOLS_DOC
|
|
6882
|
+
}),
|
|
6456
6883
|
Object.freeze({
|
|
6457
6884
|
id: "docker-engine",
|
|
6458
6885
|
title: "Docker Engine + Compose",
|
|
@@ -7246,37 +7673,6 @@ function fetchRemoteSource(opts) {
|
|
|
7246
7673
|
return opts.destDir;
|
|
7247
7674
|
}
|
|
7248
7675
|
|
|
7249
|
-
// src/command-table.ts
|
|
7250
|
-
var CONFIG_VERBS = ["show", "set", "repo"];
|
|
7251
|
-
var CONFIG_INTEGRATIONS = ["langfuse"];
|
|
7252
|
-
var SECRET_VERBS = ["set", "get", "list"];
|
|
7253
|
-
var LANGFUSE_VERBS = ["apply", "status", "disable"];
|
|
7254
|
-
var SANDBOX_SUBCOMMANDS = ["install", "list", "upgrade"];
|
|
7255
|
-
var WORKSPACE_SUBCOMMANDS = ["create", "list"];
|
|
7256
|
-
var HARNESS_SUBCOMMANDS = ["list", "install", "status", "uninstall"];
|
|
7257
|
-
var TOOL_SUBCOMMANDS = ["list", "install", "status", "uninstall"];
|
|
7258
|
-
var EGRESS_VERBS = ["log", "reload"];
|
|
7259
|
-
function isOneOf(values, value) {
|
|
7260
|
-
return value !== void 0 && values.includes(value);
|
|
7261
|
-
}
|
|
7262
|
-
var AGRO_COMMANDS = Object.freeze({
|
|
7263
|
-
config: [...CONFIG_VERBS, ...CONFIG_INTEGRATIONS],
|
|
7264
|
-
secret: SECRET_VERBS,
|
|
7265
|
-
langfuse: LANGFUSE_VERBS,
|
|
7266
|
-
update: null,
|
|
7267
|
-
"self-upgrade": null,
|
|
7268
|
-
vendor: null,
|
|
7269
|
-
sandbox: SANDBOX_SUBCOMMANDS,
|
|
7270
|
-
shell: null,
|
|
7271
|
-
compose: null,
|
|
7272
|
-
workspace: WORKSPACE_SUBCOMMANDS,
|
|
7273
|
-
harness: HARNESS_SUBCOMMANDS,
|
|
7274
|
-
tool: TOOL_SUBCOMMANDS,
|
|
7275
|
-
gateway: null,
|
|
7276
|
-
egress: EGRESS_VERBS,
|
|
7277
|
-
...Object.fromEntries(composeVerbs().map((verb) => [verb, null]))
|
|
7278
|
-
});
|
|
7279
|
-
|
|
7280
7676
|
// src/cli.ts
|
|
7281
7677
|
var DEFAULT_SOURCE_CONTROL_DIR = resolve13(
|
|
7282
7678
|
dirname10(fileURLToPath(import.meta.url)),
|
|
@@ -7323,6 +7719,7 @@ Usage:
|
|
|
7323
7719
|
${bin} compose config Print the resolved docker compose configuration
|
|
7324
7720
|
${bin} egress <args...> Read the egress proxy log or reload its allowlist (log|reload)
|
|
7325
7721
|
${bin} workspace <args...> Create and list host AGRO workspaces (create|list)
|
|
7722
|
+
${bin} node <args...> Manage remote agro-console sandbox nodes (create|list|delete)
|
|
7326
7723
|
${bin} harness <args...> Install and inspect agent CLI harnesses
|
|
7327
7724
|
${bin} tool <args...> Install and inspect sandbox tooling
|
|
7328
7725
|
${bin} gateway <args...> Manage a messaging client session (pi|hermes|openclaw)
|
|
@@ -8420,6 +8817,14 @@ async function main(argv) {
|
|
|
8420
8817
|
}
|
|
8421
8818
|
return runEgress(a.verb, a.name === void 0 ? { bin } : { bin, name: a.name }, lifecycleIo());
|
|
8422
8819
|
}
|
|
8820
|
+
if (first === "node") {
|
|
8821
|
+
return await runNode(
|
|
8822
|
+
argv.slice(1),
|
|
8823
|
+
{ stdout: (s) => process.stdout.write(s), stderr: (s) => process.stderr.write(s) },
|
|
8824
|
+
{},
|
|
8825
|
+
bin
|
|
8826
|
+
);
|
|
8827
|
+
}
|
|
8423
8828
|
if (first === "workspace") {
|
|
8424
8829
|
const parsed = parseWorkspaceArgs(argv.slice(1), bin);
|
|
8425
8830
|
if (!parsed.ok) {
|