@openape/apes 1.5.0 → 1.6.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/cli.js
CHANGED
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
} from "./chunk-OBF7IMQ2.js";
|
|
64
64
|
|
|
65
65
|
// src/cli.ts
|
|
66
|
-
import
|
|
66
|
+
import consola44 from "consola";
|
|
67
67
|
|
|
68
68
|
// src/ape-shell.ts
|
|
69
69
|
import path from "path";
|
|
@@ -93,7 +93,7 @@ function rewriteApeShellArgs(argv, argv0) {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// src/cli.ts
|
|
96
|
-
import { defineCommand as
|
|
96
|
+
import { defineCommand as defineCommand54, runMain } from "citty";
|
|
97
97
|
|
|
98
98
|
// src/commands/auth/login.ts
|
|
99
99
|
import { Buffer as Buffer2 } from "buffer";
|
|
@@ -379,7 +379,7 @@ async function loginWithPKCE(idp) {
|
|
|
379
379
|
consola2.success(`Logged in as ${payload.email || payload.sub}`);
|
|
380
380
|
}
|
|
381
381
|
async function loginWithKey(idp, keyPath, agentEmail) {
|
|
382
|
-
const { readFileSync:
|
|
382
|
+
const { readFileSync: readFileSync15 } = await import("fs");
|
|
383
383
|
const { sign: sign3 } = await import("crypto");
|
|
384
384
|
const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-YBNNG5K5.js");
|
|
385
385
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
@@ -392,7 +392,7 @@ async function loginWithKey(idp, keyPath, agentEmail) {
|
|
|
392
392
|
throw new CliError(`Challenge failed: ${await challengeResp.text()}`);
|
|
393
393
|
}
|
|
394
394
|
const { challenge } = await challengeResp.json();
|
|
395
|
-
const keyContent =
|
|
395
|
+
const keyContent = readFileSync15(keyPath, "utf-8");
|
|
396
396
|
const privateKey = loadEd25519PrivateKey2(keyContent);
|
|
397
397
|
const signature = sign3(null, Buffer2.from(challenge), privateKey).toString("base64");
|
|
398
398
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -3918,71 +3918,186 @@ var agentsCommand = defineCommand28({
|
|
|
3918
3918
|
});
|
|
3919
3919
|
|
|
3920
3920
|
// src/commands/nest/index.ts
|
|
3921
|
-
import { defineCommand as
|
|
3921
|
+
import { defineCommand as defineCommand34 } from "citty";
|
|
3922
3922
|
|
|
3923
3923
|
// src/commands/nest/authorize.ts
|
|
3924
|
-
import {
|
|
3924
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
3925
|
+
import { join as join9 } from "path";
|
|
3926
|
+
import { defineCommand as defineCommand30 } from "citty";
|
|
3927
|
+
import consola26 from "consola";
|
|
3928
|
+
|
|
3929
|
+
// src/commands/nest/enroll.ts
|
|
3930
|
+
import { hostname as hostname4, homedir as homedir10 } from "os";
|
|
3931
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6, chmodSync } from "fs";
|
|
3932
|
+
import { join as join8 } from "path";
|
|
3925
3933
|
import { defineCommand as defineCommand29 } from "citty";
|
|
3926
3934
|
import consola25 from "consola";
|
|
3927
|
-
var
|
|
3935
|
+
var NEST_DATA_DIR = join8(homedir10(), ".openape", "nest");
|
|
3936
|
+
function nestAgentName() {
|
|
3937
|
+
const raw = hostname4().toLowerCase();
|
|
3938
|
+
const head = raw.split(".")[0] ?? raw;
|
|
3939
|
+
const safe = head.replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
3940
|
+
const trimmed = safe.slice(0, 16);
|
|
3941
|
+
return `nest-${trimmed || "host"}`;
|
|
3942
|
+
}
|
|
3943
|
+
var enrollNestCommand = defineCommand29({
|
|
3928
3944
|
meta: {
|
|
3929
|
-
name: "
|
|
3930
|
-
description: "
|
|
3945
|
+
name: "enroll",
|
|
3946
|
+
description: "Register the local nest as a DDISA agent at the IdP. One-time per machine. Required before `apes nest authorize` so YOLO-policies have a target identity."
|
|
3931
3947
|
},
|
|
3932
3948
|
args: {
|
|
3933
|
-
|
|
3949
|
+
name: {
|
|
3934
3950
|
type: "string",
|
|
3935
|
-
description: "
|
|
3951
|
+
description: "Override the nest agent name (default: nest-<short-hostname>)"
|
|
3936
3952
|
},
|
|
3937
|
-
|
|
3953
|
+
force: {
|
|
3938
3954
|
type: "boolean",
|
|
3939
|
-
description: "
|
|
3955
|
+
description: "Re-enroll even if ~/.openape/nest/.config/apes/auth.json already exists"
|
|
3940
3956
|
}
|
|
3941
3957
|
},
|
|
3942
3958
|
async run({ args }) {
|
|
3943
|
-
const
|
|
3944
|
-
|
|
3959
|
+
const idp = getIdpUrl();
|
|
3960
|
+
if (!idp) {
|
|
3961
|
+
throw new CliError("No IdP configured. Run `apes login <email>` first.");
|
|
3962
|
+
}
|
|
3963
|
+
const ownerAuth = loadAuth();
|
|
3964
|
+
if (!ownerAuth?.email) {
|
|
3965
|
+
throw new CliError("Run `apes login <email>` first \u2014 nest enroll attaches the new identity to your owner account.");
|
|
3966
|
+
}
|
|
3967
|
+
const name = args.name || nestAgentName();
|
|
3968
|
+
const authPath = join8(NEST_DATA_DIR, ".config", "apes", "auth.json");
|
|
3969
|
+
if (existsSync10(authPath) && !args.force) {
|
|
3970
|
+
throw new CliError(`Nest already enrolled at ${authPath}. Pass --force to re-enroll.`);
|
|
3971
|
+
}
|
|
3972
|
+
const sshDir = join8(NEST_DATA_DIR, ".ssh");
|
|
3973
|
+
const configDir = join8(NEST_DATA_DIR, ".config", "apes");
|
|
3974
|
+
mkdirSync4(sshDir, { recursive: true });
|
|
3975
|
+
mkdirSync4(configDir, { recursive: true });
|
|
3976
|
+
consola25.start(`Generating keypair for ${name}\u2026`);
|
|
3977
|
+
const { privatePem, publicSshLine } = generateKeyPairInMemory();
|
|
3978
|
+
writeFileSync6(join8(sshDir, "id_ed25519"), `${privatePem.trimEnd()}
|
|
3979
|
+
`, { mode: 384 });
|
|
3980
|
+
writeFileSync6(join8(sshDir, "id_ed25519.pub"), `${publicSshLine}
|
|
3981
|
+
`, { mode: 420 });
|
|
3982
|
+
chmodSync(sshDir, 448);
|
|
3983
|
+
consola25.start(`Registering nest at ${idp}\u2026`);
|
|
3984
|
+
const registration = await registerAgentAtIdp({ name, publicKey: publicSshLine, idp });
|
|
3985
|
+
consola25.success(`Registered as ${registration.email}`);
|
|
3986
|
+
consola25.start("Issuing nest access token\u2026");
|
|
3987
|
+
const { token, expiresIn } = await issueAgentToken({
|
|
3988
|
+
idp,
|
|
3989
|
+
agentEmail: registration.email,
|
|
3990
|
+
privateKeyPem: privatePem
|
|
3991
|
+
});
|
|
3992
|
+
const authJson = buildAgentAuthJson({
|
|
3993
|
+
idp,
|
|
3994
|
+
accessToken: token,
|
|
3995
|
+
email: registration.email,
|
|
3996
|
+
expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
|
|
3997
|
+
keyPath: join8(sshDir, "id_ed25519"),
|
|
3998
|
+
ownerEmail: ownerAuth.email
|
|
3999
|
+
});
|
|
4000
|
+
writeFileSync6(authPath, authJson, { mode: 384 });
|
|
4001
|
+
chmodSync(configDir, 448);
|
|
4002
|
+
consola25.success(`Nest enrolled \u2014 auth.json at ${authPath}`);
|
|
3945
4003
|
consola25.info("");
|
|
3946
|
-
consola25.info("
|
|
4004
|
+
consola25.info("Next: configure the YOLO-policy so the nest can spawn/destroy without prompts:");
|
|
3947
4005
|
consola25.info("");
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
}
|
|
3974
|
-
|
|
4006
|
+
consola25.info(" apes nest authorize");
|
|
4007
|
+
}
|
|
4008
|
+
});
|
|
4009
|
+
|
|
4010
|
+
// src/commands/nest/authorize.ts
|
|
4011
|
+
var DEFAULT_ALLOW_PATTERNS = [
|
|
4012
|
+
// Agent lifecycle ops the nest issues against `apes run --as root`
|
|
4013
|
+
"apes agents spawn *",
|
|
4014
|
+
"apes agents destroy *",
|
|
4015
|
+
"apes agents sync",
|
|
4016
|
+
// Bridge invocation the supervisor uses to keep agent processes
|
|
4017
|
+
// running. Pattern is intentionally precise — not a generic
|
|
4018
|
+
// `apes run --as *` wildcard — so a compromised nest can't pivot
|
|
4019
|
+
// to running arbitrary commands as arbitrary users.
|
|
4020
|
+
"apes run --as * -- openape-chat-bridge"
|
|
4021
|
+
];
|
|
4022
|
+
var authorizeNestCommand = defineCommand30({
|
|
4023
|
+
meta: {
|
|
4024
|
+
name: "authorize",
|
|
4025
|
+
description: "Set the YOLO-policy that lets the local nest spawn/destroy without per-call DDISA prompts"
|
|
4026
|
+
},
|
|
4027
|
+
args: {
|
|
4028
|
+
"allow": {
|
|
4029
|
+
type: "string",
|
|
4030
|
+
description: "Override allow_patterns (comma-separated globs). Default: nest-managed agent lifecycle."
|
|
4031
|
+
},
|
|
4032
|
+
"mode": {
|
|
4033
|
+
type: "string",
|
|
4034
|
+
description: "Policy mode (allow-list | deny-list). Default: allow-list \u2014 auto-approve only matched patterns."
|
|
4035
|
+
},
|
|
4036
|
+
"expires-in": {
|
|
4037
|
+
type: "string",
|
|
4038
|
+
description: "Optional duration like 30d, 6h. Omit for no expiry."
|
|
4039
|
+
}
|
|
4040
|
+
},
|
|
4041
|
+
async run({ args }) {
|
|
4042
|
+
const ownerAuth = loadAuth();
|
|
4043
|
+
if (!ownerAuth?.email || !ownerAuth.access_token) {
|
|
4044
|
+
throw new CliError("Run `apes login <email>` first \u2014 only the nest's owner can set its YOLO-policy.");
|
|
4045
|
+
}
|
|
4046
|
+
const nestAuthPath = join9(NEST_DATA_DIR, ".config", "apes", "auth.json");
|
|
4047
|
+
if (!existsSync11(nestAuthPath)) {
|
|
4048
|
+
throw new CliError("Nest not enrolled. Run `apes nest enroll` first.");
|
|
4049
|
+
}
|
|
4050
|
+
const nestAuth = JSON.parse(readFileSync10(nestAuthPath, "utf8"));
|
|
4051
|
+
if (!nestAuth.email) throw new CliError(`${nestAuthPath} has no email`);
|
|
4052
|
+
const idp = getIdpUrl();
|
|
4053
|
+
if (!idp) throw new CliError("No IdP configured.");
|
|
4054
|
+
const allowPatterns = typeof args.allow === "string" && args.allow ? args.allow.split(",").map((s) => s.trim()).filter(Boolean) : DEFAULT_ALLOW_PATTERNS;
|
|
4055
|
+
const mode = args.mode ?? "allow-list";
|
|
4056
|
+
const expiresAt = parseExpiresIn(args["expires-in"]);
|
|
4057
|
+
consola26.info(`Setting YOLO-policy on ${nestAuth.email}`);
|
|
4058
|
+
consola26.info(` mode: ${mode}`);
|
|
4059
|
+
consola26.info(` allow_patterns:`);
|
|
4060
|
+
for (const p of allowPatterns) consola26.info(` - ${p}`);
|
|
4061
|
+
if (expiresAt) consola26.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
|
|
4062
|
+
const url = `${idp}/api/users/${encodeURIComponent(nestAuth.email)}/yolo-policy`;
|
|
4063
|
+
const res = await fetch(url, {
|
|
4064
|
+
method: "PUT",
|
|
4065
|
+
headers: {
|
|
4066
|
+
"Authorization": `Bearer ${ownerAuth.access_token}`,
|
|
4067
|
+
"Content-Type": "application/json"
|
|
4068
|
+
},
|
|
4069
|
+
body: JSON.stringify({
|
|
4070
|
+
mode,
|
|
4071
|
+
allowPatterns,
|
|
4072
|
+
denyPatterns: [],
|
|
4073
|
+
expiresAt: expiresAt ?? null
|
|
4074
|
+
})
|
|
4075
|
+
});
|
|
4076
|
+
if (!res.ok) {
|
|
4077
|
+
const text = await res.text().catch(() => "");
|
|
4078
|
+
throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
|
|
3975
4079
|
}
|
|
4080
|
+
consola26.success("YOLO-policy applied. Nest-driven agent lifecycle is now zero-prompt.");
|
|
4081
|
+
consola26.info("Test: apes agents spawn <name> via the nest API \u2192 no DDISA prompt.");
|
|
3976
4082
|
}
|
|
3977
4083
|
});
|
|
4084
|
+
function parseExpiresIn(s) {
|
|
4085
|
+
if (!s) return null;
|
|
4086
|
+
const m = s.match(/^(\d+)([hdw])$/);
|
|
4087
|
+
if (!m) throw new CliError(`Invalid --expires-in "${s}" \u2014 expected forms like 30d, 6h, 2w`);
|
|
4088
|
+
const n = Number(m[1]);
|
|
4089
|
+
const unit = m[2];
|
|
4090
|
+
const seconds = unit === "h" ? 3600 : unit === "d" ? 86400 : 7 * 86400;
|
|
4091
|
+
return Math.floor(Date.now() / 1e3) + n * seconds;
|
|
4092
|
+
}
|
|
3978
4093
|
|
|
3979
4094
|
// src/commands/nest/install.ts
|
|
3980
|
-
import { execFileSync as
|
|
3981
|
-
import { existsSync as
|
|
3982
|
-
import { homedir as
|
|
3983
|
-
import { dirname as dirname3, join as
|
|
3984
|
-
import { defineCommand as
|
|
3985
|
-
import
|
|
4095
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
4096
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
|
|
4097
|
+
import { homedir as homedir11, userInfo as userInfo2 } from "os";
|
|
4098
|
+
import { dirname as dirname3, join as join10 } from "path";
|
|
4099
|
+
import { defineCommand as defineCommand31 } from "citty";
|
|
4100
|
+
import consola27 from "consola";
|
|
3986
4101
|
|
|
3987
4102
|
// src/commands/nest/apes-agents-adapter.ts
|
|
3988
4103
|
var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
|
|
@@ -4049,13 +4164,13 @@ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
|
|
|
4049
4164
|
// src/commands/nest/install.ts
|
|
4050
4165
|
var PLIST_LABEL = "ai.openape.nest";
|
|
4051
4166
|
function plistPath() {
|
|
4052
|
-
return
|
|
4167
|
+
return join10(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
4053
4168
|
}
|
|
4054
4169
|
function escape2(s) {
|
|
4055
4170
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
4056
4171
|
}
|
|
4057
4172
|
function buildPlist(args) {
|
|
4058
|
-
const logsDir =
|
|
4173
|
+
const logsDir = join10(args.userHome, "Library", "Logs");
|
|
4059
4174
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
4060
4175
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4061
4176
|
<plist version="1.0">
|
|
@@ -4067,7 +4182,7 @@ function buildPlist(args) {
|
|
|
4067
4182
|
<string>${escape2(args.nestBin)}</string>
|
|
4068
4183
|
</array>
|
|
4069
4184
|
<key>WorkingDirectory</key>
|
|
4070
|
-
<string>${escape2(args.
|
|
4185
|
+
<string>${escape2(args.nestHome)}</string>
|
|
4071
4186
|
<key>RunAtLoad</key>
|
|
4072
4187
|
<true/>
|
|
4073
4188
|
<key>KeepAlive</key>
|
|
@@ -4076,8 +4191,8 @@ function buildPlist(args) {
|
|
|
4076
4191
|
<integer>10</integer>
|
|
4077
4192
|
<key>EnvironmentVariables</key>
|
|
4078
4193
|
<dict>
|
|
4079
|
-
<key>HOME</key><string>${escape2(args.
|
|
4080
|
-
<key>PATH</key><string>${escape2(args.
|
|
4194
|
+
<key>HOME</key><string>${escape2(args.nestHome)}</string>
|
|
4195
|
+
<key>PATH</key><string>${escape2(args.userHome)}/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
4081
4196
|
<key>OPENAPE_NEST_PORT</key><string>${args.port}</string>
|
|
4082
4197
|
<key>OPENAPE_APES_BIN</key><string>${escape2(args.apesBin)}</string>
|
|
4083
4198
|
</dict>
|
|
@@ -4090,31 +4205,31 @@ function buildPlist(args) {
|
|
|
4090
4205
|
`;
|
|
4091
4206
|
}
|
|
4092
4207
|
function installAdapter2() {
|
|
4093
|
-
const target =
|
|
4094
|
-
|
|
4208
|
+
const target = join10(homedir11(), ".openape", "shapes", "adapters", "apes-agents.toml");
|
|
4209
|
+
mkdirSync5(dirname3(target), { recursive: true });
|
|
4095
4210
|
let existing = "";
|
|
4096
4211
|
try {
|
|
4097
|
-
existing =
|
|
4212
|
+
existing = readFileSync11(target, "utf8");
|
|
4098
4213
|
} catch {
|
|
4099
4214
|
}
|
|
4100
4215
|
if (existing === APES_AGENTS_ADAPTER_TOML) return false;
|
|
4101
|
-
|
|
4102
|
-
|
|
4216
|
+
writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
|
|
4217
|
+
consola27.success(`Wrote shapes adapter ${target}`);
|
|
4103
4218
|
return true;
|
|
4104
4219
|
}
|
|
4105
4220
|
function findBinary(name) {
|
|
4106
4221
|
for (const dir of [
|
|
4107
|
-
|
|
4222
|
+
join10(homedir11(), ".bun", "bin"),
|
|
4108
4223
|
"/opt/homebrew/bin",
|
|
4109
4224
|
"/usr/local/bin",
|
|
4110
4225
|
"/usr/bin"
|
|
4111
4226
|
]) {
|
|
4112
|
-
const p =
|
|
4113
|
-
if (
|
|
4227
|
+
const p = join10(dir, name);
|
|
4228
|
+
if (existsSync12(p)) return p;
|
|
4114
4229
|
}
|
|
4115
4230
|
throw new Error(`could not locate ${name} on PATH; install it first`);
|
|
4116
4231
|
}
|
|
4117
|
-
var installNestCommand =
|
|
4232
|
+
var installNestCommand = defineCommand31({
|
|
4118
4233
|
meta: {
|
|
4119
4234
|
name: "install",
|
|
4120
4235
|
description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
|
|
@@ -4126,54 +4241,55 @@ var installNestCommand = defineCommand30({
|
|
|
4126
4241
|
}
|
|
4127
4242
|
},
|
|
4128
4243
|
async run({ args }) {
|
|
4129
|
-
const homeDir =
|
|
4244
|
+
const homeDir = homedir11();
|
|
4130
4245
|
const port = Number(args.port ?? 9091);
|
|
4131
4246
|
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
4132
4247
|
throw new Error(`invalid port ${port}`);
|
|
4133
4248
|
}
|
|
4134
4249
|
const nestBin = findBinary("openape-nest");
|
|
4135
4250
|
const apesBin = findBinary("apes");
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4251
|
+
consola27.info(`Installing nest at ${plistPath()}`);
|
|
4252
|
+
consola27.info(` nest binary: ${nestBin}`);
|
|
4253
|
+
consola27.info(` apes binary: ${apesBin}`);
|
|
4254
|
+
consola27.info(` HTTP port: ${port}`);
|
|
4140
4255
|
installAdapter2();
|
|
4141
|
-
|
|
4142
|
-
|
|
4256
|
+
mkdirSync5(join10(homeDir, "Library", "LaunchAgents"), { recursive: true });
|
|
4257
|
+
mkdirSync5(NEST_DATA_DIR, { recursive: true });
|
|
4258
|
+
const desired = buildPlist({ nestBin, apesBin, userHome: homeDir, nestHome: NEST_DATA_DIR, port });
|
|
4143
4259
|
let existing = "";
|
|
4144
4260
|
try {
|
|
4145
|
-
existing =
|
|
4261
|
+
existing = readFileSync11(plistPath(), "utf8");
|
|
4146
4262
|
} catch {
|
|
4147
4263
|
}
|
|
4148
4264
|
if (existing !== desired) {
|
|
4149
|
-
|
|
4150
|
-
|
|
4265
|
+
writeFileSync7(plistPath(), desired, { mode: 420 });
|
|
4266
|
+
consola27.success("Wrote launchd plist");
|
|
4151
4267
|
} else {
|
|
4152
|
-
|
|
4268
|
+
consola27.info("plist already up to date");
|
|
4153
4269
|
}
|
|
4154
4270
|
const uid = userInfo2().uid;
|
|
4155
4271
|
try {
|
|
4156
|
-
|
|
4272
|
+
execFileSync9("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
|
|
4157
4273
|
} catch {
|
|
4158
4274
|
}
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4275
|
+
execFileSync9("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
|
|
4276
|
+
consola27.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
|
|
4277
|
+
consola27.info("");
|
|
4278
|
+
consola27.info("Next steps for zero-prompt spawn \u2014 both one-time:");
|
|
4279
|
+
consola27.info("");
|
|
4280
|
+
consola27.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
|
|
4281
|
+
consola27.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
|
|
4282
|
+
consola27.info("");
|
|
4283
|
+
consola27.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
|
|
4168
4284
|
}
|
|
4169
4285
|
});
|
|
4170
4286
|
|
|
4171
4287
|
// src/commands/nest/status.ts
|
|
4172
4288
|
import process2 from "process";
|
|
4173
|
-
import { defineCommand as
|
|
4174
|
-
import
|
|
4289
|
+
import { defineCommand as defineCommand32 } from "citty";
|
|
4290
|
+
import consola28 from "consola";
|
|
4175
4291
|
var DEFAULT_PORT = 9091;
|
|
4176
|
-
var statusNestCommand =
|
|
4292
|
+
var statusNestCommand = defineCommand32({
|
|
4177
4293
|
meta: {
|
|
4178
4294
|
name: "status",
|
|
4179
4295
|
description: "Print state of the local nest-daemon (agents registered, processes supervised)"
|
|
@@ -4193,8 +4309,8 @@ var statusNestCommand = defineCommand31({
|
|
|
4193
4309
|
} catch (err) {
|
|
4194
4310
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4195
4311
|
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
4196
|
-
|
|
4197
|
-
|
|
4312
|
+
consola28.error(`Nest daemon is not running at http://127.0.0.1:${port}`);
|
|
4313
|
+
consola28.info(" Run: apes nest install");
|
|
4198
4314
|
process2.exit(2);
|
|
4199
4315
|
}
|
|
4200
4316
|
throw err;
|
|
@@ -4203,15 +4319,15 @@ var statusNestCommand = defineCommand31({
|
|
|
4203
4319
|
console.log(JSON.stringify(status, null, 2));
|
|
4204
4320
|
return;
|
|
4205
4321
|
}
|
|
4206
|
-
|
|
4322
|
+
consola28.info(`Nest at http://127.0.0.1:${port} \u2014 ${status.agents} agent(s) registered, ${status.processes.length} supervised`);
|
|
4207
4323
|
if (status.processes.length === 0) {
|
|
4208
|
-
|
|
4324
|
+
consola28.info(" (no processes running)");
|
|
4209
4325
|
return;
|
|
4210
4326
|
}
|
|
4211
4327
|
for (const p of status.processes) {
|
|
4212
4328
|
const uptime = humanDuration(p.uptimeSec);
|
|
4213
4329
|
const crashTag = p.consecutiveCrashes > 0 ? ` \u26A0 ${p.consecutiveCrashes} crash(es)` : "";
|
|
4214
|
-
|
|
4330
|
+
consola28.info(` ${p.name.padEnd(16)} pid=${String(p.pid).padEnd(6)} up=${uptime}${crashTag}`);
|
|
4215
4331
|
}
|
|
4216
4332
|
}
|
|
4217
4333
|
});
|
|
@@ -4223,43 +4339,44 @@ function humanDuration(sec) {
|
|
|
4223
4339
|
}
|
|
4224
4340
|
|
|
4225
4341
|
// src/commands/nest/uninstall.ts
|
|
4226
|
-
import { execFileSync as
|
|
4227
|
-
import { existsSync as
|
|
4228
|
-
import { homedir as
|
|
4229
|
-
import { join as
|
|
4230
|
-
import { defineCommand as
|
|
4231
|
-
import
|
|
4342
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
4343
|
+
import { existsSync as existsSync13, unlinkSync } from "fs";
|
|
4344
|
+
import { homedir as homedir12, userInfo as userInfo3 } from "os";
|
|
4345
|
+
import { join as join11 } from "path";
|
|
4346
|
+
import { defineCommand as defineCommand33 } from "citty";
|
|
4347
|
+
import consola29 from "consola";
|
|
4232
4348
|
var PLIST_LABEL2 = "ai.openape.nest";
|
|
4233
|
-
var uninstallNestCommand =
|
|
4349
|
+
var uninstallNestCommand = defineCommand33({
|
|
4234
4350
|
meta: {
|
|
4235
4351
|
name: "uninstall",
|
|
4236
4352
|
description: "Stop + remove the local nest-daemon (registry + agents preserved)"
|
|
4237
4353
|
},
|
|
4238
4354
|
async run() {
|
|
4239
4355
|
const uid = userInfo3().uid;
|
|
4240
|
-
const path2 =
|
|
4356
|
+
const path2 = join11(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
|
|
4241
4357
|
try {
|
|
4242
|
-
|
|
4243
|
-
|
|
4358
|
+
execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
|
|
4359
|
+
consola29.success("Nest daemon stopped");
|
|
4244
4360
|
} catch {
|
|
4245
|
-
|
|
4361
|
+
consola29.info("Nest daemon was not loaded");
|
|
4246
4362
|
}
|
|
4247
|
-
if (
|
|
4363
|
+
if (existsSync13(path2)) {
|
|
4248
4364
|
unlinkSync(path2);
|
|
4249
|
-
|
|
4365
|
+
consola29.success(`Removed ${path2}`);
|
|
4250
4366
|
}
|
|
4251
|
-
|
|
4367
|
+
consola29.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
|
|
4252
4368
|
}
|
|
4253
4369
|
});
|
|
4254
4370
|
|
|
4255
4371
|
// src/commands/nest/index.ts
|
|
4256
|
-
var nestCommand =
|
|
4372
|
+
var nestCommand = defineCommand34({
|
|
4257
4373
|
meta: {
|
|
4258
4374
|
name: "nest",
|
|
4259
|
-
description: "Manage the local Nest control-plane daemon (install
|
|
4375
|
+
description: "Manage the local Nest control-plane daemon (install / enroll / authorize / status / uninstall). One-time setup: `install` (launchd) \u2192 `enroll` (own DDISA identity) \u2192 `authorize` (YOLO-policy). After that, `apes agents spawn` runs without per-call DDISA approvals."
|
|
4260
4376
|
},
|
|
4261
4377
|
subCommands: {
|
|
4262
4378
|
install: installNestCommand,
|
|
4379
|
+
enroll: enrollNestCommand,
|
|
4263
4380
|
authorize: authorizeNestCommand,
|
|
4264
4381
|
status: statusNestCommand,
|
|
4265
4382
|
uninstall: uninstallNestCommand
|
|
@@ -4267,15 +4384,15 @@ var nestCommand = defineCommand33({
|
|
|
4267
4384
|
});
|
|
4268
4385
|
|
|
4269
4386
|
// src/commands/adapter/index.ts
|
|
4270
|
-
import { defineCommand as
|
|
4271
|
-
import
|
|
4272
|
-
var adapterCommand =
|
|
4387
|
+
import { defineCommand as defineCommand35 } from "citty";
|
|
4388
|
+
import consola30 from "consola";
|
|
4389
|
+
var adapterCommand = defineCommand35({
|
|
4273
4390
|
meta: {
|
|
4274
4391
|
name: "adapter",
|
|
4275
4392
|
description: "Manage CLI adapters"
|
|
4276
4393
|
},
|
|
4277
4394
|
subCommands: {
|
|
4278
|
-
list:
|
|
4395
|
+
list: defineCommand35({
|
|
4279
4396
|
meta: {
|
|
4280
4397
|
name: "list",
|
|
4281
4398
|
description: "List available adapters"
|
|
@@ -4306,7 +4423,7 @@ var adapterCommand = defineCommand34({
|
|
|
4306
4423
|
`);
|
|
4307
4424
|
return;
|
|
4308
4425
|
}
|
|
4309
|
-
|
|
4426
|
+
consola30.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
4310
4427
|
for (const a of index2.adapters) {
|
|
4311
4428
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
4312
4429
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -4328,7 +4445,7 @@ var adapterCommand = defineCommand34({
|
|
|
4328
4445
|
return;
|
|
4329
4446
|
}
|
|
4330
4447
|
if (local.length === 0) {
|
|
4331
|
-
|
|
4448
|
+
consola30.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
4332
4449
|
return;
|
|
4333
4450
|
}
|
|
4334
4451
|
for (const a of local) {
|
|
@@ -4336,7 +4453,7 @@ var adapterCommand = defineCommand34({
|
|
|
4336
4453
|
}
|
|
4337
4454
|
}
|
|
4338
4455
|
}),
|
|
4339
|
-
install:
|
|
4456
|
+
install: defineCommand35({
|
|
4340
4457
|
meta: {
|
|
4341
4458
|
name: "install",
|
|
4342
4459
|
description: "Install an adapter from the registry"
|
|
@@ -4365,24 +4482,24 @@ var adapterCommand = defineCommand34({
|
|
|
4365
4482
|
for (const id of ids) {
|
|
4366
4483
|
const entry = findAdapter(index, id);
|
|
4367
4484
|
if (!entry) {
|
|
4368
|
-
|
|
4485
|
+
consola30.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
4369
4486
|
continue;
|
|
4370
4487
|
}
|
|
4371
4488
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
4372
4489
|
if (conflicts.length > 0) {
|
|
4373
4490
|
for (const c of conflicts) {
|
|
4374
|
-
|
|
4375
|
-
|
|
4491
|
+
consola30.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
|
|
4492
|
+
consola30.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
|
|
4376
4493
|
}
|
|
4377
4494
|
}
|
|
4378
4495
|
const result = await installAdapter(entry, { local });
|
|
4379
4496
|
const verb = result.updated ? "Updated" : "Installed";
|
|
4380
|
-
|
|
4381
|
-
|
|
4497
|
+
consola30.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
4498
|
+
consola30.info(`Digest: ${result.digest}`);
|
|
4382
4499
|
}
|
|
4383
4500
|
}
|
|
4384
4501
|
}),
|
|
4385
|
-
remove:
|
|
4502
|
+
remove: defineCommand35({
|
|
4386
4503
|
meta: {
|
|
4387
4504
|
name: "remove",
|
|
4388
4505
|
description: "Remove an installed adapter"
|
|
@@ -4405,9 +4522,9 @@ var adapterCommand = defineCommand34({
|
|
|
4405
4522
|
let failed = false;
|
|
4406
4523
|
for (const id of ids) {
|
|
4407
4524
|
if (removeAdapter(id, local)) {
|
|
4408
|
-
|
|
4525
|
+
consola30.success(`Removed adapter: ${id}`);
|
|
4409
4526
|
} else {
|
|
4410
|
-
|
|
4527
|
+
consola30.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4411
4528
|
failed = true;
|
|
4412
4529
|
}
|
|
4413
4530
|
}
|
|
@@ -4415,7 +4532,7 @@ var adapterCommand = defineCommand34({
|
|
|
4415
4532
|
throw new CliError("Some adapters could not be removed");
|
|
4416
4533
|
}
|
|
4417
4534
|
}),
|
|
4418
|
-
info:
|
|
4535
|
+
info: defineCommand35({
|
|
4419
4536
|
meta: {
|
|
4420
4537
|
name: "info",
|
|
4421
4538
|
description: "Show detailed adapter information"
|
|
@@ -4457,7 +4574,7 @@ var adapterCommand = defineCommand34({
|
|
|
4457
4574
|
}
|
|
4458
4575
|
}
|
|
4459
4576
|
}),
|
|
4460
|
-
search:
|
|
4577
|
+
search: defineCommand35({
|
|
4461
4578
|
meta: {
|
|
4462
4579
|
name: "search",
|
|
4463
4580
|
description: "Search adapters in the registry"
|
|
@@ -4489,7 +4606,7 @@ var adapterCommand = defineCommand34({
|
|
|
4489
4606
|
return;
|
|
4490
4607
|
}
|
|
4491
4608
|
if (results.length === 0) {
|
|
4492
|
-
|
|
4609
|
+
consola30.info(`No adapters matching "${query}"`);
|
|
4493
4610
|
return;
|
|
4494
4611
|
}
|
|
4495
4612
|
for (const a of results) {
|
|
@@ -4498,7 +4615,7 @@ var adapterCommand = defineCommand34({
|
|
|
4498
4615
|
}
|
|
4499
4616
|
}
|
|
4500
4617
|
}),
|
|
4501
|
-
update:
|
|
4618
|
+
update: defineCommand35({
|
|
4502
4619
|
meta: {
|
|
4503
4620
|
name: "update",
|
|
4504
4621
|
description: "Update installed adapters"
|
|
@@ -4524,33 +4641,33 @@ var adapterCommand = defineCommand34({
|
|
|
4524
4641
|
const targetId = args.id ? String(args.id) : void 0;
|
|
4525
4642
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
4526
4643
|
if (targets.length === 0) {
|
|
4527
|
-
|
|
4644
|
+
consola30.info("No adapters installed to update.");
|
|
4528
4645
|
return;
|
|
4529
4646
|
}
|
|
4530
4647
|
for (const id of targets) {
|
|
4531
4648
|
const entry = findAdapter(index, id);
|
|
4532
4649
|
if (!entry) {
|
|
4533
|
-
|
|
4650
|
+
consola30.warn(`${id}: not found in registry, skipping`);
|
|
4534
4651
|
continue;
|
|
4535
4652
|
}
|
|
4536
4653
|
const localDigest = getInstalledDigest(id, false);
|
|
4537
4654
|
if (localDigest === entry.digest) {
|
|
4538
|
-
|
|
4655
|
+
consola30.info(`${id}: already up to date`);
|
|
4539
4656
|
continue;
|
|
4540
4657
|
}
|
|
4541
4658
|
if (localDigest && !args.yes) {
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4659
|
+
consola30.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
|
|
4660
|
+
consola30.info(` Old: ${localDigest}`);
|
|
4661
|
+
consola30.info(` New: ${entry.digest}`);
|
|
4662
|
+
consola30.info(" Use --yes to confirm");
|
|
4546
4663
|
continue;
|
|
4547
4664
|
}
|
|
4548
4665
|
const result = await installAdapter(entry);
|
|
4549
|
-
|
|
4666
|
+
consola30.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
4550
4667
|
}
|
|
4551
4668
|
}
|
|
4552
4669
|
}),
|
|
4553
|
-
verify:
|
|
4670
|
+
verify: defineCommand35({
|
|
4554
4671
|
meta: {
|
|
4555
4672
|
name: "verify",
|
|
4556
4673
|
description: "Verify installed adapter against registry digest"
|
|
@@ -4583,7 +4700,7 @@ var adapterCommand = defineCommand34({
|
|
|
4583
4700
|
if (!localDigest)
|
|
4584
4701
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4585
4702
|
if (localDigest === entry.digest) {
|
|
4586
|
-
|
|
4703
|
+
consola30.success(`${id}: digest matches registry`);
|
|
4587
4704
|
} else {
|
|
4588
4705
|
console.log(` Local: ${localDigest}`);
|
|
4589
4706
|
console.log(` Registry: ${entry.digest}`);
|
|
@@ -4595,11 +4712,11 @@ var adapterCommand = defineCommand34({
|
|
|
4595
4712
|
});
|
|
4596
4713
|
|
|
4597
4714
|
// src/commands/run.ts
|
|
4598
|
-
import { execFileSync as
|
|
4599
|
-
import { hostname as
|
|
4715
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
4716
|
+
import { hostname as hostname5 } from "os";
|
|
4600
4717
|
import { basename } from "path";
|
|
4601
|
-
import { defineCommand as
|
|
4602
|
-
import
|
|
4718
|
+
import { defineCommand as defineCommand36 } from "citty";
|
|
4719
|
+
import consola31 from "consola";
|
|
4603
4720
|
function shouldWaitForGrant(args) {
|
|
4604
4721
|
return args.wait === true || process.env.APE_WAIT === "1";
|
|
4605
4722
|
}
|
|
@@ -4636,7 +4753,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4636
4753
|
const statusCmd = `apes grants status ${grant.id}`;
|
|
4637
4754
|
const executeCmd = `apes grants run ${grant.id}`;
|
|
4638
4755
|
if (mode === "human") {
|
|
4639
|
-
|
|
4756
|
+
consola31.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
|
|
4640
4757
|
console.log(` Approve in browser: ${approveUrl}`);
|
|
4641
4758
|
console.log(` Check status: ${statusCmd}`);
|
|
4642
4759
|
console.log(` Run after approval: ${executeCmd}`);
|
|
@@ -4646,7 +4763,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4646
4763
|
return;
|
|
4647
4764
|
}
|
|
4648
4765
|
const maxMin = getPollMaxMinutes();
|
|
4649
|
-
|
|
4766
|
+
consola31.success(`Grant ${grant.id} created (pending approval)`);
|
|
4650
4767
|
console.log(` Approve: ${approveUrl}`);
|
|
4651
4768
|
console.log(` Status: ${statusCmd} [--json]`);
|
|
4652
4769
|
console.log(` Execute: ${executeCmd} --wait`);
|
|
@@ -4668,7 +4785,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4668
4785
|
console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
|
|
4669
4786
|
console.log(" grant be reused on subsequent invocations without re-approval.");
|
|
4670
4787
|
}
|
|
4671
|
-
var runCommand =
|
|
4788
|
+
var runCommand = defineCommand36({
|
|
4672
4789
|
meta: {
|
|
4673
4790
|
name: "run",
|
|
4674
4791
|
description: "Execute a grant-secured command"
|
|
@@ -4757,7 +4874,7 @@ async function runShellMode(command, args) {
|
|
|
4757
4874
|
const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
|
|
4758
4875
|
if (adapterHandled) return;
|
|
4759
4876
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
4760
|
-
const targetHost = args.host ||
|
|
4877
|
+
const targetHost = args.host || hostname5();
|
|
4761
4878
|
try {
|
|
4762
4879
|
const grants = await apiFetch(
|
|
4763
4880
|
`${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
|
|
@@ -4771,7 +4888,7 @@ async function runShellMode(command, args) {
|
|
|
4771
4888
|
}
|
|
4772
4889
|
} catch {
|
|
4773
4890
|
}
|
|
4774
|
-
|
|
4891
|
+
consola31.info(`Requesting ape-shell session grant on ${targetHost}`);
|
|
4775
4892
|
const grant = await apiFetch(grantsUrl, {
|
|
4776
4893
|
method: "POST",
|
|
4777
4894
|
body: {
|
|
@@ -4791,8 +4908,8 @@ async function runShellMode(command, args) {
|
|
|
4791
4908
|
host: targetHost
|
|
4792
4909
|
});
|
|
4793
4910
|
if (shouldWaitForGrant(args)) {
|
|
4794
|
-
|
|
4795
|
-
|
|
4911
|
+
consola31.info(`Grant requested: ${grant.id}`);
|
|
4912
|
+
consola31.info("Waiting for approval...");
|
|
4796
4913
|
const maxWait = 3e5;
|
|
4797
4914
|
const interval = 3e3;
|
|
4798
4915
|
const start = Date.now();
|
|
@@ -4823,13 +4940,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4823
4940
|
try {
|
|
4824
4941
|
resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
|
|
4825
4942
|
} catch (err) {
|
|
4826
|
-
|
|
4943
|
+
consola31.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
|
|
4827
4944
|
return false;
|
|
4828
4945
|
}
|
|
4829
4946
|
try {
|
|
4830
4947
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
4831
4948
|
if (existingGrantId) {
|
|
4832
|
-
|
|
4949
|
+
consola31.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
|
|
4833
4950
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
4834
4951
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
4835
4952
|
return true;
|
|
@@ -4837,7 +4954,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4837
4954
|
} catch {
|
|
4838
4955
|
}
|
|
4839
4956
|
const approval = args.approval ?? "once";
|
|
4840
|
-
|
|
4957
|
+
consola31.info(`Requesting grant for: ${resolved.detail.display}`);
|
|
4841
4958
|
const grant = await createShapesGrant(resolved, {
|
|
4842
4959
|
idp,
|
|
4843
4960
|
approval,
|
|
@@ -4845,19 +4962,19 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4845
4962
|
});
|
|
4846
4963
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
4847
4964
|
const n = grant.similar_grants.similar_grants.length;
|
|
4848
|
-
|
|
4849
|
-
|
|
4965
|
+
consola31.info("");
|
|
4966
|
+
consola31.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
4850
4967
|
}
|
|
4851
4968
|
notifyGrantPending({
|
|
4852
4969
|
grantId: grant.id,
|
|
4853
4970
|
approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
|
|
4854
4971
|
command: resolved.detail?.display || parsed?.raw || "unknown",
|
|
4855
4972
|
audience: resolved.adapter?.cli?.audience ?? "shapes",
|
|
4856
|
-
host: args.host ||
|
|
4973
|
+
host: args.host || hostname5()
|
|
4857
4974
|
});
|
|
4858
4975
|
if (shouldWaitForGrant(args)) {
|
|
4859
|
-
|
|
4860
|
-
|
|
4976
|
+
consola31.info(`Grant requested: ${grant.id}`);
|
|
4977
|
+
consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4861
4978
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
4862
4979
|
if (status !== "approved")
|
|
4863
4980
|
throw new CliError(`Grant ${status}`);
|
|
@@ -4873,7 +4990,7 @@ function execShellCommand(command) {
|
|
|
4873
4990
|
throw new CliError("No command to execute");
|
|
4874
4991
|
try {
|
|
4875
4992
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
4876
|
-
|
|
4993
|
+
execFileSync11(command[0], command.slice(1), {
|
|
4877
4994
|
stdio: "inherit",
|
|
4878
4995
|
env: inheritedEnv
|
|
4879
4996
|
});
|
|
@@ -4931,7 +5048,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
4931
5048
|
try {
|
|
4932
5049
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
4933
5050
|
if (existingGrantId) {
|
|
4934
|
-
|
|
5051
|
+
consola31.info(`Reusing existing grant: ${existingGrantId}`);
|
|
4935
5052
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
4936
5053
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
4937
5054
|
return;
|
|
@@ -4945,17 +5062,17 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
4945
5062
|
});
|
|
4946
5063
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
4947
5064
|
const n = grant.similar_grants.similar_grants.length;
|
|
4948
|
-
|
|
4949
|
-
|
|
5065
|
+
consola31.info("");
|
|
5066
|
+
consola31.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
4950
5067
|
if (grant.similar_grants.widened_details?.length) {
|
|
4951
5068
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
4952
|
-
|
|
5069
|
+
consola31.info(` Broader scope: ${wider}`);
|
|
4953
5070
|
}
|
|
4954
|
-
|
|
5071
|
+
consola31.info("");
|
|
4955
5072
|
}
|
|
4956
5073
|
if (shouldWaitForGrant(args)) {
|
|
4957
|
-
|
|
4958
|
-
|
|
5074
|
+
consola31.info(`Grant requested: ${grant.id}`);
|
|
5075
|
+
consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4959
5076
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
4960
5077
|
if (status !== "approved")
|
|
4961
5078
|
throw new Error(`Grant ${status}`);
|
|
@@ -4974,8 +5091,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4974
5091
|
const idp = getIdpUrl(args.idp);
|
|
4975
5092
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
4976
5093
|
const command = action.split(" ");
|
|
4977
|
-
const targetHost = args.host ||
|
|
4978
|
-
|
|
5094
|
+
const targetHost = args.host || hostname5();
|
|
5095
|
+
consola31.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
4979
5096
|
const grant = await apiFetch(grantsUrl, {
|
|
4980
5097
|
method: "POST",
|
|
4981
5098
|
body: {
|
|
@@ -4992,9 +5109,9 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4992
5109
|
printPendingGrantInfo(grant, idp);
|
|
4993
5110
|
throw new CliExit(getAsyncExitCode());
|
|
4994
5111
|
}
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
5112
|
+
consola31.success(`Grant requested: ${grant.id}`);
|
|
5113
|
+
consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5114
|
+
consola31.info("Waiting for approval...");
|
|
4998
5115
|
const maxWait = 15 * 60 * 1e3;
|
|
4999
5116
|
const interval = 3e3;
|
|
5000
5117
|
const start = Date.now();
|
|
@@ -5002,7 +5119,7 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5002
5119
|
while (Date.now() - start < maxWait) {
|
|
5003
5120
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
5004
5121
|
if (status.status === "approved") {
|
|
5005
|
-
|
|
5122
|
+
consola31.success("Grant approved!");
|
|
5006
5123
|
approved = true;
|
|
5007
5124
|
break;
|
|
5008
5125
|
}
|
|
@@ -5017,15 +5134,15 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5017
5134
|
`Grant approval timed out after ${minutes} min (still pending). Check your DDISA inbox at ${idp}/grant-approval?grant_id=${grant.id} \u2014 if approved later, re-run the same \`apes run\` command and it will reuse the grant.`
|
|
5018
5135
|
);
|
|
5019
5136
|
}
|
|
5020
|
-
|
|
5137
|
+
consola31.info("Fetching grant token...");
|
|
5021
5138
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
5022
5139
|
method: "POST"
|
|
5023
5140
|
});
|
|
5024
5141
|
if (audience === "escapes") {
|
|
5025
|
-
|
|
5142
|
+
consola31.info(`Executing: ${command.join(" ")}`);
|
|
5026
5143
|
try {
|
|
5027
5144
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
5028
|
-
|
|
5145
|
+
execFileSync11(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
|
|
5029
5146
|
stdio: "inherit",
|
|
5030
5147
|
env: inheritedEnv
|
|
5031
5148
|
});
|
|
@@ -5040,8 +5157,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5040
5157
|
|
|
5041
5158
|
// src/commands/proxy.ts
|
|
5042
5159
|
import { spawn as spawn2 } from "child_process";
|
|
5043
|
-
import { defineCommand as
|
|
5044
|
-
import
|
|
5160
|
+
import { defineCommand as defineCommand37 } from "citty";
|
|
5161
|
+
import consola32 from "consola";
|
|
5045
5162
|
|
|
5046
5163
|
// src/proxy/config.ts
|
|
5047
5164
|
function buildDefaultProxyConfigToml(opts) {
|
|
@@ -5075,10 +5192,10 @@ note = "VPC-internal hostname suffix"
|
|
|
5075
5192
|
|
|
5076
5193
|
// src/proxy/local-proxy.ts
|
|
5077
5194
|
import { spawn } from "child_process";
|
|
5078
|
-
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as
|
|
5195
|
+
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
5079
5196
|
import { createRequire } from "module";
|
|
5080
5197
|
import { tmpdir as tmpdir3 } from "os";
|
|
5081
|
-
import { dirname as dirname4, join as
|
|
5198
|
+
import { dirname as dirname4, join as join12, resolve as resolve4 } from "path";
|
|
5082
5199
|
var require2 = createRequire(import.meta.url);
|
|
5083
5200
|
function findProxyBin() {
|
|
5084
5201
|
const pkgPath = require2.resolve("@openape/proxy/package.json");
|
|
@@ -5090,9 +5207,9 @@ function findProxyBin() {
|
|
|
5090
5207
|
return resolve4(dirname4(pkgPath), binRel);
|
|
5091
5208
|
}
|
|
5092
5209
|
async function startEphemeralProxy(configToml) {
|
|
5093
|
-
const tmpDir = mkdtempSync3(
|
|
5094
|
-
const configPath =
|
|
5095
|
-
|
|
5210
|
+
const tmpDir = mkdtempSync3(join12(tmpdir3(), "openape-proxy-"));
|
|
5211
|
+
const configPath = join12(tmpDir, "config.toml");
|
|
5212
|
+
writeFileSync8(configPath, configToml, { mode: 384 });
|
|
5096
5213
|
const binPath = findProxyBin();
|
|
5097
5214
|
const child = spawn(process.execPath, [binPath, "-c", configPath], {
|
|
5098
5215
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -5184,10 +5301,10 @@ function resolveProxyConfigOptions() {
|
|
|
5184
5301
|
77
|
|
5185
5302
|
);
|
|
5186
5303
|
}
|
|
5187
|
-
|
|
5304
|
+
consola32.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
|
|
5188
5305
|
return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
|
|
5189
5306
|
}
|
|
5190
|
-
var proxyCommand =
|
|
5307
|
+
var proxyCommand = defineCommand37({
|
|
5191
5308
|
meta: {
|
|
5192
5309
|
name: "proxy",
|
|
5193
5310
|
description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
|
|
@@ -5209,12 +5326,12 @@ var proxyCommand = defineCommand36({
|
|
|
5209
5326
|
let close = null;
|
|
5210
5327
|
if (reuseUrl) {
|
|
5211
5328
|
proxyUrl = reuseUrl;
|
|
5212
|
-
|
|
5329
|
+
consola32.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
|
|
5213
5330
|
} else {
|
|
5214
5331
|
const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
|
|
5215
5332
|
proxyUrl = ephemeral.url;
|
|
5216
5333
|
close = ephemeral.close;
|
|
5217
|
-
|
|
5334
|
+
consola32.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
|
|
5218
5335
|
}
|
|
5219
5336
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
|
|
5220
5337
|
const childEnv = {
|
|
@@ -5246,7 +5363,7 @@ var proxyCommand = defineCommand36({
|
|
|
5246
5363
|
else resolveExit(code ?? 0);
|
|
5247
5364
|
});
|
|
5248
5365
|
child.once("error", (err) => {
|
|
5249
|
-
|
|
5366
|
+
consola32.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
|
|
5250
5367
|
resolveExit(127);
|
|
5251
5368
|
});
|
|
5252
5369
|
});
|
|
@@ -5260,8 +5377,8 @@ function signalNumber(signal) {
|
|
|
5260
5377
|
}
|
|
5261
5378
|
|
|
5262
5379
|
// src/commands/explain.ts
|
|
5263
|
-
import { defineCommand as
|
|
5264
|
-
var explainCommand =
|
|
5380
|
+
import { defineCommand as defineCommand38 } from "citty";
|
|
5381
|
+
var explainCommand = defineCommand38({
|
|
5265
5382
|
meta: {
|
|
5266
5383
|
name: "explain",
|
|
5267
5384
|
description: "Show what permission a command would need"
|
|
@@ -5299,9 +5416,9 @@ var explainCommand = defineCommand37({
|
|
|
5299
5416
|
});
|
|
5300
5417
|
|
|
5301
5418
|
// src/commands/config/get.ts
|
|
5302
|
-
import { defineCommand as
|
|
5303
|
-
import
|
|
5304
|
-
var configGetCommand =
|
|
5419
|
+
import { defineCommand as defineCommand39 } from "citty";
|
|
5420
|
+
import consola33 from "consola";
|
|
5421
|
+
var configGetCommand = defineCommand39({
|
|
5305
5422
|
meta: {
|
|
5306
5423
|
name: "get",
|
|
5307
5424
|
description: "Get a configuration value"
|
|
@@ -5321,7 +5438,7 @@ var configGetCommand = defineCommand38({
|
|
|
5321
5438
|
if (idp)
|
|
5322
5439
|
console.log(idp);
|
|
5323
5440
|
else
|
|
5324
|
-
|
|
5441
|
+
consola33.info("No IdP configured.");
|
|
5325
5442
|
break;
|
|
5326
5443
|
}
|
|
5327
5444
|
case "email": {
|
|
@@ -5329,7 +5446,7 @@ var configGetCommand = defineCommand38({
|
|
|
5329
5446
|
if (auth?.email)
|
|
5330
5447
|
console.log(auth.email);
|
|
5331
5448
|
else
|
|
5332
|
-
|
|
5449
|
+
consola33.info("Not logged in.");
|
|
5333
5450
|
break;
|
|
5334
5451
|
}
|
|
5335
5452
|
default: {
|
|
@@ -5342,7 +5459,7 @@ var configGetCommand = defineCommand38({
|
|
|
5342
5459
|
if (sectionObj && field in sectionObj) {
|
|
5343
5460
|
console.log(sectionObj[field]);
|
|
5344
5461
|
} else {
|
|
5345
|
-
|
|
5462
|
+
consola33.info(`Key "${key}" not set.`);
|
|
5346
5463
|
}
|
|
5347
5464
|
} else {
|
|
5348
5465
|
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
@@ -5353,9 +5470,9 @@ var configGetCommand = defineCommand38({
|
|
|
5353
5470
|
});
|
|
5354
5471
|
|
|
5355
5472
|
// src/commands/config/set.ts
|
|
5356
|
-
import { defineCommand as
|
|
5357
|
-
import
|
|
5358
|
-
var configSetCommand =
|
|
5473
|
+
import { defineCommand as defineCommand40 } from "citty";
|
|
5474
|
+
import consola34 from "consola";
|
|
5475
|
+
var configSetCommand = defineCommand40({
|
|
5359
5476
|
meta: {
|
|
5360
5477
|
name: "set",
|
|
5361
5478
|
description: "Set a configuration value"
|
|
@@ -5391,12 +5508,12 @@ var configSetCommand = defineCommand39({
|
|
|
5391
5508
|
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
5392
5509
|
}
|
|
5393
5510
|
saveConfig(config);
|
|
5394
|
-
|
|
5511
|
+
consola34.success(`Set ${key} = ${value}`);
|
|
5395
5512
|
}
|
|
5396
5513
|
});
|
|
5397
5514
|
|
|
5398
5515
|
// src/commands/fetch/index.ts
|
|
5399
|
-
import { defineCommand as
|
|
5516
|
+
import { defineCommand as defineCommand41 } from "citty";
|
|
5400
5517
|
async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
5401
5518
|
const token = getAuthToken();
|
|
5402
5519
|
if (!token) {
|
|
@@ -5432,13 +5549,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
|
5432
5549
|
throw new CliError(`HTTP ${response.status} ${response.statusText}`);
|
|
5433
5550
|
}
|
|
5434
5551
|
}
|
|
5435
|
-
var fetchCommand =
|
|
5552
|
+
var fetchCommand = defineCommand41({
|
|
5436
5553
|
meta: {
|
|
5437
5554
|
name: "fetch",
|
|
5438
5555
|
description: "Make authenticated HTTP requests"
|
|
5439
5556
|
},
|
|
5440
5557
|
subCommands: {
|
|
5441
|
-
get:
|
|
5558
|
+
get: defineCommand41({
|
|
5442
5559
|
meta: {
|
|
5443
5560
|
name: "get",
|
|
5444
5561
|
description: "GET request with auth token"
|
|
@@ -5464,7 +5581,7 @@ var fetchCommand = defineCommand40({
|
|
|
5464
5581
|
await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
|
|
5465
5582
|
}
|
|
5466
5583
|
}),
|
|
5467
|
-
post:
|
|
5584
|
+
post: defineCommand41({
|
|
5468
5585
|
meta: {
|
|
5469
5586
|
name: "post",
|
|
5470
5587
|
description: "POST request with auth token"
|
|
@@ -5503,8 +5620,8 @@ var fetchCommand = defineCommand40({
|
|
|
5503
5620
|
});
|
|
5504
5621
|
|
|
5505
5622
|
// src/commands/mcp/index.ts
|
|
5506
|
-
import { defineCommand as
|
|
5507
|
-
var mcpCommand =
|
|
5623
|
+
import { defineCommand as defineCommand42 } from "citty";
|
|
5624
|
+
var mcpCommand = defineCommand42({
|
|
5508
5625
|
meta: {
|
|
5509
5626
|
name: "mcp",
|
|
5510
5627
|
description: "Start MCP server for AI agents"
|
|
@@ -5527,48 +5644,48 @@ var mcpCommand = defineCommand41({
|
|
|
5527
5644
|
if (transport !== "stdio" && transport !== "sse") {
|
|
5528
5645
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
5529
5646
|
}
|
|
5530
|
-
const { startMcpServer } = await import("./server-
|
|
5647
|
+
const { startMcpServer } = await import("./server-PHANS7PS.js");
|
|
5531
5648
|
await startMcpServer(transport, port);
|
|
5532
5649
|
}
|
|
5533
5650
|
});
|
|
5534
5651
|
|
|
5535
5652
|
// src/commands/init/index.ts
|
|
5536
|
-
import { existsSync as
|
|
5653
|
+
import { existsSync as existsSync14, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
|
|
5537
5654
|
import { randomBytes } from "crypto";
|
|
5538
|
-
import { execFileSync as
|
|
5539
|
-
import { join as
|
|
5540
|
-
import { defineCommand as
|
|
5541
|
-
import
|
|
5655
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
5656
|
+
import { join as join13 } from "path";
|
|
5657
|
+
import { defineCommand as defineCommand43 } from "citty";
|
|
5658
|
+
import consola35 from "consola";
|
|
5542
5659
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
5543
5660
|
async function downloadTemplate(repo, targetDir) {
|
|
5544
5661
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
5545
5662
|
await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
|
|
5546
5663
|
}
|
|
5547
5664
|
function installDeps(dir) {
|
|
5548
|
-
const hasLockFile = (name) =>
|
|
5665
|
+
const hasLockFile = (name) => existsSync14(join13(dir, name));
|
|
5549
5666
|
if (hasLockFile("pnpm-lock.yaml")) {
|
|
5550
|
-
|
|
5667
|
+
execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5551
5668
|
} else if (hasLockFile("bun.lockb")) {
|
|
5552
|
-
|
|
5669
|
+
execFileSync12("bun", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5553
5670
|
} else {
|
|
5554
|
-
|
|
5671
|
+
execFileSync12("npm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5555
5672
|
}
|
|
5556
5673
|
}
|
|
5557
5674
|
async function promptChoice(message, choices) {
|
|
5558
|
-
const result = await
|
|
5675
|
+
const result = await consola35.prompt(message, { type: "select", options: choices });
|
|
5559
5676
|
if (typeof result === "symbol") {
|
|
5560
5677
|
throw new CliExit(0);
|
|
5561
5678
|
}
|
|
5562
5679
|
return result;
|
|
5563
5680
|
}
|
|
5564
5681
|
async function promptText(message, defaultValue) {
|
|
5565
|
-
const result = await
|
|
5682
|
+
const result = await consola35.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
5566
5683
|
if (typeof result === "symbol") {
|
|
5567
5684
|
throw new CliExit(0);
|
|
5568
5685
|
}
|
|
5569
5686
|
return result || defaultValue || "";
|
|
5570
5687
|
}
|
|
5571
|
-
var initCommand =
|
|
5688
|
+
var initCommand = defineCommand43({
|
|
5572
5689
|
meta: {
|
|
5573
5690
|
name: "init",
|
|
5574
5691
|
description: "Scaffold a new OpenApe project"
|
|
@@ -5610,23 +5727,23 @@ var initCommand = defineCommand42({
|
|
|
5610
5727
|
});
|
|
5611
5728
|
async function initSP(targetDir) {
|
|
5612
5729
|
const dir = targetDir || "my-app";
|
|
5613
|
-
if (
|
|
5730
|
+
if (existsSync14(join13(dir, "package.json"))) {
|
|
5614
5731
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5615
5732
|
}
|
|
5616
|
-
|
|
5733
|
+
consola35.start("Scaffolding SP starter...");
|
|
5617
5734
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
5618
|
-
|
|
5619
|
-
|
|
5735
|
+
consola35.success("Scaffolded from openape-sp-starter");
|
|
5736
|
+
consola35.start("Installing dependencies...");
|
|
5620
5737
|
installDeps(dir);
|
|
5621
|
-
|
|
5622
|
-
const envExample =
|
|
5623
|
-
const envFile =
|
|
5624
|
-
if (
|
|
5738
|
+
consola35.success("Dependencies installed");
|
|
5739
|
+
const envExample = join13(dir, ".env.example");
|
|
5740
|
+
const envFile = join13(dir, ".env");
|
|
5741
|
+
if (existsSync14(envExample) && !existsSync14(envFile)) {
|
|
5625
5742
|
copyFileSync(envExample, envFile);
|
|
5626
|
-
|
|
5743
|
+
consola35.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
5627
5744
|
}
|
|
5628
5745
|
console.log("");
|
|
5629
|
-
|
|
5746
|
+
consola35.box([
|
|
5630
5747
|
`cd ${dir}`,
|
|
5631
5748
|
"npm run dev",
|
|
5632
5749
|
"",
|
|
@@ -5635,7 +5752,7 @@ async function initSP(targetDir) {
|
|
|
5635
5752
|
}
|
|
5636
5753
|
async function initIdP(targetDir) {
|
|
5637
5754
|
const dir = targetDir || "my-idp";
|
|
5638
|
-
if (
|
|
5755
|
+
if (existsSync14(join13(dir, "package.json"))) {
|
|
5639
5756
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5640
5757
|
}
|
|
5641
5758
|
const domain = await promptText("Domain for the IdP", "localhost");
|
|
@@ -5645,15 +5762,15 @@ async function initIdP(targetDir) {
|
|
|
5645
5762
|
"s3 (S3-compatible)"
|
|
5646
5763
|
]);
|
|
5647
5764
|
const adminEmail = await promptText("Admin email");
|
|
5648
|
-
|
|
5765
|
+
consola35.start("Scaffolding IdP starter...");
|
|
5649
5766
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
5650
|
-
|
|
5651
|
-
|
|
5767
|
+
consola35.success("Scaffolded from openape-idp-starter");
|
|
5768
|
+
consola35.start("Installing dependencies...");
|
|
5652
5769
|
installDeps(dir);
|
|
5653
|
-
|
|
5770
|
+
consola35.success("Dependencies installed");
|
|
5654
5771
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
5655
5772
|
const managementToken = randomBytes(32).toString("hex");
|
|
5656
|
-
|
|
5773
|
+
consola35.success("Secrets generated");
|
|
5657
5774
|
const isLocalhost = domain === "localhost";
|
|
5658
5775
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
5659
5776
|
const envContent = [
|
|
@@ -5667,11 +5784,11 @@ async function initIdP(targetDir) {
|
|
|
5667
5784
|
`NUXT_OPENAPE_RP_ID=${domain}`,
|
|
5668
5785
|
`NUXT_OPENAPE_RP_ORIGIN=${origin}`
|
|
5669
5786
|
].join("\n");
|
|
5670
|
-
|
|
5787
|
+
writeFileSync9(join13(dir, ".env"), `${envContent}
|
|
5671
5788
|
`, { mode: 384 });
|
|
5672
|
-
|
|
5789
|
+
consola35.success(".env created");
|
|
5673
5790
|
console.log("");
|
|
5674
|
-
|
|
5791
|
+
consola35.box([
|
|
5675
5792
|
`cd ${dir}`,
|
|
5676
5793
|
"npm run dev",
|
|
5677
5794
|
"",
|
|
@@ -5688,11 +5805,11 @@ async function initIdP(targetDir) {
|
|
|
5688
5805
|
|
|
5689
5806
|
// src/commands/enroll.ts
|
|
5690
5807
|
import { Buffer as Buffer5 } from "buffer";
|
|
5691
|
-
import { existsSync as
|
|
5808
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
5692
5809
|
import { execFile as execFile2 } from "child_process";
|
|
5693
5810
|
import { sign as sign2 } from "crypto";
|
|
5694
|
-
import { defineCommand as
|
|
5695
|
-
import
|
|
5811
|
+
import { defineCommand as defineCommand44 } from "citty";
|
|
5812
|
+
import consola36 from "consola";
|
|
5696
5813
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
5697
5814
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
5698
5815
|
var POLL_INTERVAL = 3e3;
|
|
@@ -5704,7 +5821,7 @@ function openBrowser2(url) {
|
|
|
5704
5821
|
}
|
|
5705
5822
|
async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
5706
5823
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
5707
|
-
const keyContent =
|
|
5824
|
+
const keyContent = readFileSync12(resolvedKey, "utf-8");
|
|
5708
5825
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
5709
5826
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
5710
5827
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -5735,7 +5852,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
5735
5852
|
}
|
|
5736
5853
|
throw new Error("Enrollment timed out. Please check the browser and try again.");
|
|
5737
5854
|
}
|
|
5738
|
-
var enrollCommand =
|
|
5855
|
+
var enrollCommand = defineCommand44({
|
|
5739
5856
|
meta: {
|
|
5740
5857
|
name: "enroll",
|
|
5741
5858
|
description: "Enroll an agent with an Identity Provider"
|
|
@@ -5755,38 +5872,38 @@ var enrollCommand = defineCommand43({
|
|
|
5755
5872
|
}
|
|
5756
5873
|
},
|
|
5757
5874
|
async run({ args }) {
|
|
5758
|
-
const idp = args.idp || await
|
|
5875
|
+
const idp = args.idp || await consola36.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
|
|
5759
5876
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
5760
5877
|
return r;
|
|
5761
5878
|
}) || DEFAULT_IDP_URL2;
|
|
5762
|
-
const agentName = args.name || await
|
|
5879
|
+
const agentName = args.name || await consola36.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
|
|
5763
5880
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
5764
5881
|
return r;
|
|
5765
5882
|
});
|
|
5766
5883
|
if (!agentName) {
|
|
5767
5884
|
throw new CliError("Agent name is required.");
|
|
5768
5885
|
}
|
|
5769
|
-
const keyPath = args.key || await
|
|
5886
|
+
const keyPath = args.key || await consola36.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
|
|
5770
5887
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
5771
5888
|
return r;
|
|
5772
5889
|
}) || DEFAULT_KEY_PATH;
|
|
5773
5890
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
5774
5891
|
let publicKey;
|
|
5775
|
-
if (
|
|
5892
|
+
if (existsSync15(resolvedKey)) {
|
|
5776
5893
|
publicKey = readPublicKey(resolvedKey);
|
|
5777
|
-
|
|
5894
|
+
consola36.success(`Using existing key ${keyPath}`);
|
|
5778
5895
|
} else {
|
|
5779
|
-
|
|
5896
|
+
consola36.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
5780
5897
|
publicKey = generateAndSaveKey(keyPath);
|
|
5781
|
-
|
|
5898
|
+
consola36.success(`Key pair generated at ${keyPath}`);
|
|
5782
5899
|
}
|
|
5783
5900
|
const encodedKey = encodeURIComponent(publicKey);
|
|
5784
5901
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
5785
|
-
|
|
5786
|
-
|
|
5902
|
+
consola36.info("Opening browser for enrollment...");
|
|
5903
|
+
consola36.info(`\u2192 ${idp}/enroll`);
|
|
5787
5904
|
openBrowser2(enrollUrl);
|
|
5788
5905
|
console.log("");
|
|
5789
|
-
const agentEmail = await
|
|
5906
|
+
const agentEmail = await consola36.prompt(
|
|
5790
5907
|
"Agent email (shown in browser after enrollment)",
|
|
5791
5908
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
5792
5909
|
).then((r) => {
|
|
@@ -5796,7 +5913,7 @@ var enrollCommand = defineCommand43({
|
|
|
5796
5913
|
if (!agentEmail) {
|
|
5797
5914
|
throw new CliError("Agent email is required to verify enrollment.");
|
|
5798
5915
|
}
|
|
5799
|
-
|
|
5916
|
+
consola36.start("Verifying enrollment...");
|
|
5800
5917
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
5801
5918
|
saveAuth({
|
|
5802
5919
|
idp,
|
|
@@ -5808,18 +5925,18 @@ var enrollCommand = defineCommand43({
|
|
|
5808
5925
|
config.defaults = { ...config.defaults, idp };
|
|
5809
5926
|
config.agent = { key: keyPath, email: agentEmail };
|
|
5810
5927
|
saveConfig(config);
|
|
5811
|
-
|
|
5812
|
-
|
|
5928
|
+
consola36.success(`Agent enrolled as ${agentEmail}`);
|
|
5929
|
+
consola36.success("Config saved to ~/.config/apes/");
|
|
5813
5930
|
console.log("");
|
|
5814
|
-
|
|
5931
|
+
consola36.info("Verify with: apes whoami");
|
|
5815
5932
|
}
|
|
5816
5933
|
});
|
|
5817
5934
|
|
|
5818
5935
|
// src/commands/register-user.ts
|
|
5819
|
-
import { existsSync as
|
|
5820
|
-
import { defineCommand as
|
|
5821
|
-
import
|
|
5822
|
-
var registerUserCommand =
|
|
5936
|
+
import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
|
|
5937
|
+
import { defineCommand as defineCommand45 } from "citty";
|
|
5938
|
+
import consola37 from "consola";
|
|
5939
|
+
var registerUserCommand = defineCommand45({
|
|
5823
5940
|
meta: {
|
|
5824
5941
|
name: "register-user",
|
|
5825
5942
|
description: "Register a sub-user with SSH key"
|
|
@@ -5855,8 +5972,8 @@ var registerUserCommand = defineCommand44({
|
|
|
5855
5972
|
throw new CliError("No IdP URL configured. Run `apes login` first.");
|
|
5856
5973
|
}
|
|
5857
5974
|
let publicKey = args.key;
|
|
5858
|
-
if (
|
|
5859
|
-
publicKey =
|
|
5975
|
+
if (existsSync16(args.key)) {
|
|
5976
|
+
publicKey = readFileSync13(args.key, "utf-8").trim();
|
|
5860
5977
|
}
|
|
5861
5978
|
if (!publicKey.startsWith("ssh-ed25519 ")) {
|
|
5862
5979
|
throw new CliError("Public key must be in ssh-ed25519 format.");
|
|
@@ -5874,18 +5991,18 @@ var registerUserCommand = defineCommand44({
|
|
|
5874
5991
|
...userType ? { type: userType } : {}
|
|
5875
5992
|
}
|
|
5876
5993
|
});
|
|
5877
|
-
|
|
5994
|
+
consola37.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
|
|
5878
5995
|
}
|
|
5879
5996
|
});
|
|
5880
5997
|
|
|
5881
5998
|
// src/commands/utils/index.ts
|
|
5882
|
-
import { defineCommand as
|
|
5999
|
+
import { defineCommand as defineCommand47 } from "citty";
|
|
5883
6000
|
|
|
5884
6001
|
// src/commands/utils/dig.ts
|
|
5885
|
-
import { defineCommand as
|
|
5886
|
-
import
|
|
6002
|
+
import { defineCommand as defineCommand46 } from "citty";
|
|
6003
|
+
import consola38 from "consola";
|
|
5887
6004
|
import { resolveDDISA as resolveDDISA2 } from "@openape/core";
|
|
5888
|
-
var digCommand =
|
|
6005
|
+
var digCommand = defineCommand46({
|
|
5889
6006
|
meta: {
|
|
5890
6007
|
name: "dig",
|
|
5891
6008
|
description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
|
|
@@ -5958,12 +6075,12 @@ var digCommand = defineCommand45({
|
|
|
5958
6075
|
console.log(` domain: ${domain}`);
|
|
5959
6076
|
console.log("");
|
|
5960
6077
|
if (!result.ddisa.found) {
|
|
5961
|
-
|
|
6078
|
+
consola38.warn(`No DDISA record at _ddisa.${domain}`);
|
|
5962
6079
|
if (result.hint) console.log(`
|
|
5963
6080
|
${result.hint}`);
|
|
5964
6081
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
5965
6082
|
}
|
|
5966
|
-
|
|
6083
|
+
consola38.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
|
|
5967
6084
|
console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
|
|
5968
6085
|
console.log(` IdP URL: ${result.ddisa.idp}`);
|
|
5969
6086
|
if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
|
|
@@ -5973,13 +6090,13 @@ ${result.hint}`);
|
|
|
5973
6090
|
return;
|
|
5974
6091
|
}
|
|
5975
6092
|
if (result.idpDiscovery.ok) {
|
|
5976
|
-
|
|
6093
|
+
consola38.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
|
|
5977
6094
|
if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
|
|
5978
6095
|
if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
|
|
5979
6096
|
if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
|
|
5980
6097
|
if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
|
|
5981
6098
|
} else {
|
|
5982
|
-
|
|
6099
|
+
consola38.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
|
|
5983
6100
|
if (result.hint) console.log(`
|
|
5984
6101
|
${result.hint}`);
|
|
5985
6102
|
throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
|
|
@@ -5988,7 +6105,7 @@ ${result.hint}`);
|
|
|
5988
6105
|
});
|
|
5989
6106
|
|
|
5990
6107
|
// src/commands/utils/index.ts
|
|
5991
|
-
var utilsCommand =
|
|
6108
|
+
var utilsCommand = defineCommand47({
|
|
5992
6109
|
meta: {
|
|
5993
6110
|
name: "utils",
|
|
5994
6111
|
description: "Admin/diagnostic utilities (dig, \u2026)"
|
|
@@ -5999,12 +6116,12 @@ var utilsCommand = defineCommand46({
|
|
|
5999
6116
|
});
|
|
6000
6117
|
|
|
6001
6118
|
// src/commands/sessions/index.ts
|
|
6002
|
-
import { defineCommand as
|
|
6119
|
+
import { defineCommand as defineCommand50 } from "citty";
|
|
6003
6120
|
|
|
6004
6121
|
// src/commands/sessions/list.ts
|
|
6005
|
-
import { defineCommand as
|
|
6006
|
-
import
|
|
6007
|
-
var sessionsListCommand =
|
|
6122
|
+
import { defineCommand as defineCommand48 } from "citty";
|
|
6123
|
+
import consola39 from "consola";
|
|
6124
|
+
var sessionsListCommand = defineCommand48({
|
|
6008
6125
|
meta: {
|
|
6009
6126
|
name: "list",
|
|
6010
6127
|
description: "List your active refresh-token families (one per logged-in device)."
|
|
@@ -6022,7 +6139,7 @@ var sessionsListCommand = defineCommand47({
|
|
|
6022
6139
|
return;
|
|
6023
6140
|
}
|
|
6024
6141
|
if (result.data.length === 0) {
|
|
6025
|
-
|
|
6142
|
+
consola39.info("No active sessions.");
|
|
6026
6143
|
return;
|
|
6027
6144
|
}
|
|
6028
6145
|
for (const f of result.data) {
|
|
@@ -6034,9 +6151,9 @@ var sessionsListCommand = defineCommand47({
|
|
|
6034
6151
|
});
|
|
6035
6152
|
|
|
6036
6153
|
// src/commands/sessions/remove.ts
|
|
6037
|
-
import { defineCommand as
|
|
6038
|
-
import
|
|
6039
|
-
var sessionsRemoveCommand =
|
|
6154
|
+
import { defineCommand as defineCommand49 } from "citty";
|
|
6155
|
+
import consola40 from "consola";
|
|
6156
|
+
var sessionsRemoveCommand = defineCommand49({
|
|
6040
6157
|
meta: {
|
|
6041
6158
|
name: "remove",
|
|
6042
6159
|
description: "Revoke one of your active refresh-token families by id."
|
|
@@ -6052,12 +6169,12 @@ var sessionsRemoveCommand = defineCommand48({
|
|
|
6052
6169
|
const id = String(args.familyId).trim();
|
|
6053
6170
|
if (!id) throw new CliError("familyId required");
|
|
6054
6171
|
await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
6055
|
-
|
|
6172
|
+
consola40.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
|
|
6056
6173
|
}
|
|
6057
6174
|
});
|
|
6058
6175
|
|
|
6059
6176
|
// src/commands/sessions/index.ts
|
|
6060
|
-
var sessionsCommand =
|
|
6177
|
+
var sessionsCommand = defineCommand50({
|
|
6061
6178
|
meta: {
|
|
6062
6179
|
name: "sessions",
|
|
6063
6180
|
description: "Manage your active refresh-token sessions across devices"
|
|
@@ -6069,10 +6186,10 @@ var sessionsCommand = defineCommand49({
|
|
|
6069
6186
|
});
|
|
6070
6187
|
|
|
6071
6188
|
// src/commands/dns-check.ts
|
|
6072
|
-
import { defineCommand as
|
|
6073
|
-
import
|
|
6189
|
+
import { defineCommand as defineCommand51 } from "citty";
|
|
6190
|
+
import consola41 from "consola";
|
|
6074
6191
|
import { resolveDDISA as resolveDDISA3 } from "@openape/core";
|
|
6075
|
-
var dnsCheckCommand =
|
|
6192
|
+
var dnsCheckCommand = defineCommand51({
|
|
6076
6193
|
meta: {
|
|
6077
6194
|
name: "dns-check",
|
|
6078
6195
|
description: "Validate DDISA DNS TXT records for a domain"
|
|
@@ -6086,7 +6203,7 @@ var dnsCheckCommand = defineCommand50({
|
|
|
6086
6203
|
},
|
|
6087
6204
|
async run({ args }) {
|
|
6088
6205
|
const domain = args.domain;
|
|
6089
|
-
|
|
6206
|
+
consola41.start(`Checking _ddisa.${domain}...`);
|
|
6090
6207
|
try {
|
|
6091
6208
|
const result = await resolveDDISA3(domain);
|
|
6092
6209
|
if (!result) {
|
|
@@ -6095,7 +6212,7 @@ var dnsCheckCommand = defineCommand50({
|
|
|
6095
6212
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
6096
6213
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6097
6214
|
}
|
|
6098
|
-
|
|
6215
|
+
consola41.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
6099
6216
|
console.log("");
|
|
6100
6217
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
6101
6218
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -6104,14 +6221,14 @@ var dnsCheckCommand = defineCommand50({
|
|
|
6104
6221
|
if (result.priority !== void 0)
|
|
6105
6222
|
console.log(` Priority: ${result.priority}`);
|
|
6106
6223
|
console.log("");
|
|
6107
|
-
|
|
6224
|
+
consola41.start(`Verifying IdP at ${result.idp}...`);
|
|
6108
6225
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
6109
6226
|
if (!discoResp.ok) {
|
|
6110
|
-
|
|
6227
|
+
consola41.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
6111
6228
|
return;
|
|
6112
6229
|
}
|
|
6113
6230
|
const disco = await discoResp.json();
|
|
6114
|
-
|
|
6231
|
+
consola41.success(`IdP is reachable`);
|
|
6115
6232
|
console.log(` Issuer: ${disco.issuer}`);
|
|
6116
6233
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
6117
6234
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -6129,7 +6246,7 @@ var dnsCheckCommand = defineCommand50({
|
|
|
6129
6246
|
// src/commands/health.ts
|
|
6130
6247
|
import { exec } from "child_process";
|
|
6131
6248
|
import { promisify } from "util";
|
|
6132
|
-
import { defineCommand as
|
|
6249
|
+
import { defineCommand as defineCommand52 } from "citty";
|
|
6133
6250
|
var execAsync = promisify(exec);
|
|
6134
6251
|
async function resolveApeShellPath() {
|
|
6135
6252
|
try {
|
|
@@ -6165,7 +6282,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
6165
6282
|
}
|
|
6166
6283
|
}
|
|
6167
6284
|
async function runHealth(args) {
|
|
6168
|
-
const version = true ? "1.
|
|
6285
|
+
const version = true ? "1.6.0" : "0.0.0";
|
|
6169
6286
|
const auth = loadAuth();
|
|
6170
6287
|
if (!auth) {
|
|
6171
6288
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -6228,7 +6345,7 @@ async function runHealth(args) {
|
|
|
6228
6345
|
throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
|
|
6229
6346
|
}
|
|
6230
6347
|
}
|
|
6231
|
-
var healthCommand =
|
|
6348
|
+
var healthCommand = defineCommand52({
|
|
6232
6349
|
meta: {
|
|
6233
6350
|
name: "health",
|
|
6234
6351
|
description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
|
|
@@ -6246,8 +6363,8 @@ var healthCommand = defineCommand51({
|
|
|
6246
6363
|
});
|
|
6247
6364
|
|
|
6248
6365
|
// src/commands/workflows.ts
|
|
6249
|
-
import { defineCommand as
|
|
6250
|
-
import
|
|
6366
|
+
import { defineCommand as defineCommand53 } from "citty";
|
|
6367
|
+
import consola42 from "consola";
|
|
6251
6368
|
|
|
6252
6369
|
// src/guides/index.ts
|
|
6253
6370
|
var guides = [
|
|
@@ -6297,7 +6414,7 @@ var guides = [
|
|
|
6297
6414
|
];
|
|
6298
6415
|
|
|
6299
6416
|
// src/commands/workflows.ts
|
|
6300
|
-
var workflowsCommand =
|
|
6417
|
+
var workflowsCommand = defineCommand53({
|
|
6301
6418
|
meta: {
|
|
6302
6419
|
name: "workflows",
|
|
6303
6420
|
description: "Discover workflow guides"
|
|
@@ -6318,7 +6435,7 @@ var workflowsCommand = defineCommand52({
|
|
|
6318
6435
|
if (args.id) {
|
|
6319
6436
|
const guide = guides.find((g) => g.id === String(args.id));
|
|
6320
6437
|
if (!guide) {
|
|
6321
|
-
|
|
6438
|
+
consola42.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
6322
6439
|
throw new CliError(`Guide not found: ${args.id}`);
|
|
6323
6440
|
}
|
|
6324
6441
|
if (args.json) {
|
|
@@ -6358,26 +6475,26 @@ var workflowsCommand = defineCommand52({
|
|
|
6358
6475
|
});
|
|
6359
6476
|
|
|
6360
6477
|
// src/version-check.ts
|
|
6361
|
-
import { existsSync as
|
|
6362
|
-
import { homedir as
|
|
6363
|
-
import { join as
|
|
6364
|
-
import
|
|
6478
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
|
|
6479
|
+
import { homedir as homedir13 } from "os";
|
|
6480
|
+
import { join as join14 } from "path";
|
|
6481
|
+
import consola43 from "consola";
|
|
6365
6482
|
var PACKAGE_NAME = "@openape/apes";
|
|
6366
6483
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6367
|
-
var CACHE_FILE =
|
|
6484
|
+
var CACHE_FILE = join14(homedir13(), ".config", "apes", ".version-check.json");
|
|
6368
6485
|
function readCache() {
|
|
6369
|
-
if (!
|
|
6486
|
+
if (!existsSync17(CACHE_FILE)) return null;
|
|
6370
6487
|
try {
|
|
6371
|
-
return JSON.parse(
|
|
6488
|
+
return JSON.parse(readFileSync14(CACHE_FILE, "utf-8"));
|
|
6372
6489
|
} catch {
|
|
6373
6490
|
return null;
|
|
6374
6491
|
}
|
|
6375
6492
|
}
|
|
6376
6493
|
function writeCache(entry) {
|
|
6377
6494
|
try {
|
|
6378
|
-
const dir =
|
|
6379
|
-
if (!
|
|
6380
|
-
|
|
6495
|
+
const dir = join14(homedir13(), ".config", "apes");
|
|
6496
|
+
if (!existsSync17(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
6497
|
+
writeFileSync10(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
|
|
6381
6498
|
} catch {
|
|
6382
6499
|
}
|
|
6383
6500
|
}
|
|
@@ -6406,7 +6523,7 @@ async function fetchLatestVersion() {
|
|
|
6406
6523
|
}
|
|
6407
6524
|
function warnIfBehind(currentVersion, latest) {
|
|
6408
6525
|
if (compareSemver(currentVersion, latest) < 0) {
|
|
6409
|
-
|
|
6526
|
+
consola43.warn(
|
|
6410
6527
|
`apes ${currentVersion} is behind latest @openape/apes@${latest}. Run \`npm i -g @openape/apes@latest\` to update. (Suppress with APES_NO_UPDATE_CHECK=1.)`
|
|
6411
6528
|
);
|
|
6412
6529
|
}
|
|
@@ -6438,10 +6555,10 @@ if (shellRewrite) {
|
|
|
6438
6555
|
if (shellRewrite.action === "rewrite") {
|
|
6439
6556
|
process.argv = shellRewrite.argv;
|
|
6440
6557
|
} else if (shellRewrite.action === "version") {
|
|
6441
|
-
console.log(`ape-shell ${"1.
|
|
6558
|
+
console.log(`ape-shell ${"1.6.0"} (OpenApe DDISA shell wrapper)`);
|
|
6442
6559
|
process.exit(0);
|
|
6443
6560
|
} else if (shellRewrite.action === "help") {
|
|
6444
|
-
console.log(`ape-shell ${"1.
|
|
6561
|
+
console.log(`ape-shell ${"1.6.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
6445
6562
|
console.log("");
|
|
6446
6563
|
console.log("Usage:");
|
|
6447
6564
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -6465,7 +6582,7 @@ if (shellRewrite) {
|
|
|
6465
6582
|
}
|
|
6466
6583
|
}
|
|
6467
6584
|
var debug = process.argv.includes("--debug");
|
|
6468
|
-
var grantsCommand =
|
|
6585
|
+
var grantsCommand = defineCommand54({
|
|
6469
6586
|
meta: {
|
|
6470
6587
|
name: "grants",
|
|
6471
6588
|
description: "Grant management"
|
|
@@ -6486,7 +6603,7 @@ var grantsCommand = defineCommand53({
|
|
|
6486
6603
|
"delegation-revoke": delegationRevokeCommand
|
|
6487
6604
|
}
|
|
6488
6605
|
});
|
|
6489
|
-
var configCommand =
|
|
6606
|
+
var configCommand = defineCommand54({
|
|
6490
6607
|
meta: {
|
|
6491
6608
|
name: "config",
|
|
6492
6609
|
description: "Configuration management"
|
|
@@ -6496,10 +6613,10 @@ var configCommand = defineCommand53({
|
|
|
6496
6613
|
set: configSetCommand
|
|
6497
6614
|
}
|
|
6498
6615
|
});
|
|
6499
|
-
var main =
|
|
6616
|
+
var main = defineCommand54({
|
|
6500
6617
|
meta: {
|
|
6501
6618
|
name: "apes",
|
|
6502
|
-
version: "1.
|
|
6619
|
+
version: "1.6.0",
|
|
6503
6620
|
description: "Unified CLI for OpenApe"
|
|
6504
6621
|
},
|
|
6505
6622
|
subCommands: {
|
|
@@ -6555,20 +6672,20 @@ async function maybeRefreshAuth() {
|
|
|
6555
6672
|
}
|
|
6556
6673
|
}
|
|
6557
6674
|
await maybeRefreshAuth();
|
|
6558
|
-
await maybeWarnStaleVersion("1.
|
|
6675
|
+
await maybeWarnStaleVersion("1.6.0").catch(() => {
|
|
6559
6676
|
});
|
|
6560
6677
|
runMain(main).catch((err) => {
|
|
6561
6678
|
if (err instanceof CliExit) {
|
|
6562
6679
|
process.exit(err.exitCode);
|
|
6563
6680
|
}
|
|
6564
6681
|
if (err instanceof CliError) {
|
|
6565
|
-
|
|
6682
|
+
consola44.error(err.message);
|
|
6566
6683
|
process.exit(err.exitCode);
|
|
6567
6684
|
}
|
|
6568
6685
|
if (debug) {
|
|
6569
|
-
|
|
6686
|
+
consola44.error(err);
|
|
6570
6687
|
} else {
|
|
6571
|
-
|
|
6688
|
+
consola44.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
6572
6689
|
}
|
|
6573
6690
|
process.exit(1);
|
|
6574
6691
|
});
|