@openape/apes 1.5.0 → 1.6.1
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,193 @@ 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
|
+
// Outer spawn-grant — what the nest's HTTP handler invokes.
|
|
4013
|
+
"apes agents spawn *",
|
|
4014
|
+
"apes agents destroy *",
|
|
4015
|
+
"apes agents sync",
|
|
4016
|
+
// Inner setup.sh-grant — `apes agents spawn` itself shells out to
|
|
4017
|
+
// `apes run --as root --wait -- bash <tempdir>/setup.sh` to do the
|
|
4018
|
+
// dscl/launchctl/heredoc-write work. Path looks like
|
|
4019
|
+
// `bash /var/folders/.../apes-spawn-<name>-XXXX/setup.sh`. The narrow
|
|
4020
|
+
// glob below limits the auto-approval to that exact lifecycle path
|
|
4021
|
+
// — `bash *` would be unsafe.
|
|
4022
|
+
"bash *apes-spawn-*setup.sh",
|
|
4023
|
+
// Bridge invocation the nest's process supervisor uses (Stage 1
|
|
4024
|
+
// supervisor work). Intentionally precise — not a generic
|
|
4025
|
+
// `apes run --as *` wildcard — so a compromised nest can't pivot
|
|
4026
|
+
// to running arbitrary commands as arbitrary users.
|
|
4027
|
+
"apes run --as * -- openape-chat-bridge"
|
|
4028
|
+
];
|
|
4029
|
+
var authorizeNestCommand = defineCommand30({
|
|
4030
|
+
meta: {
|
|
4031
|
+
name: "authorize",
|
|
4032
|
+
description: "Set the YOLO-policy that lets the local nest spawn/destroy without per-call DDISA prompts"
|
|
4033
|
+
},
|
|
4034
|
+
args: {
|
|
4035
|
+
"allow": {
|
|
4036
|
+
type: "string",
|
|
4037
|
+
description: "Override allow_patterns (comma-separated globs). Default: nest-managed agent lifecycle."
|
|
4038
|
+
},
|
|
4039
|
+
"mode": {
|
|
4040
|
+
type: "string",
|
|
4041
|
+
description: "Policy mode (allow-list | deny-list). Default: allow-list \u2014 auto-approve only matched patterns."
|
|
4042
|
+
},
|
|
4043
|
+
"expires-in": {
|
|
4044
|
+
type: "string",
|
|
4045
|
+
description: "Optional duration like 30d, 6h. Omit for no expiry."
|
|
4046
|
+
}
|
|
4047
|
+
},
|
|
4048
|
+
async run({ args }) {
|
|
4049
|
+
const ownerAuth = loadAuth();
|
|
4050
|
+
if (!ownerAuth?.email || !ownerAuth.access_token) {
|
|
4051
|
+
throw new CliError("Run `apes login <email>` first \u2014 only the nest's owner can set its YOLO-policy.");
|
|
4052
|
+
}
|
|
4053
|
+
const nestAuthPath = join9(NEST_DATA_DIR, ".config", "apes", "auth.json");
|
|
4054
|
+
if (!existsSync11(nestAuthPath)) {
|
|
4055
|
+
throw new CliError("Nest not enrolled. Run `apes nest enroll` first.");
|
|
4056
|
+
}
|
|
4057
|
+
const nestAuth = JSON.parse(readFileSync10(nestAuthPath, "utf8"));
|
|
4058
|
+
if (!nestAuth.email) throw new CliError(`${nestAuthPath} has no email`);
|
|
4059
|
+
const idp = getIdpUrl();
|
|
4060
|
+
if (!idp) throw new CliError("No IdP configured.");
|
|
4061
|
+
const allowPatterns = typeof args.allow === "string" && args.allow ? args.allow.split(",").map((s) => s.trim()).filter(Boolean) : DEFAULT_ALLOW_PATTERNS;
|
|
4062
|
+
const mode = args.mode ?? "allow-list";
|
|
4063
|
+
const expiresAt = parseExpiresIn(args["expires-in"]);
|
|
4064
|
+
consola26.info(`Setting YOLO-policy on ${nestAuth.email}`);
|
|
4065
|
+
consola26.info(` mode: ${mode}`);
|
|
4066
|
+
consola26.info(` allow_patterns:`);
|
|
4067
|
+
for (const p of allowPatterns) consola26.info(` - ${p}`);
|
|
4068
|
+
if (expiresAt) consola26.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
|
|
4069
|
+
const url = `${idp}/api/users/${encodeURIComponent(nestAuth.email)}/yolo-policy`;
|
|
4070
|
+
const res = await fetch(url, {
|
|
4071
|
+
method: "PUT",
|
|
4072
|
+
headers: {
|
|
4073
|
+
"Authorization": `Bearer ${ownerAuth.access_token}`,
|
|
4074
|
+
"Content-Type": "application/json"
|
|
4075
|
+
},
|
|
4076
|
+
body: JSON.stringify({
|
|
4077
|
+
mode,
|
|
4078
|
+
allowPatterns,
|
|
4079
|
+
denyPatterns: [],
|
|
4080
|
+
expiresAt: expiresAt ?? null
|
|
4081
|
+
})
|
|
4082
|
+
});
|
|
4083
|
+
if (!res.ok) {
|
|
4084
|
+
const text = await res.text().catch(() => "");
|
|
4085
|
+
throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
|
|
3975
4086
|
}
|
|
4087
|
+
consola26.success("YOLO-policy applied. Nest-driven agent lifecycle is now zero-prompt.");
|
|
4088
|
+
consola26.info("Test: apes agents spawn <name> via the nest API \u2192 no DDISA prompt.");
|
|
3976
4089
|
}
|
|
3977
4090
|
});
|
|
4091
|
+
function parseExpiresIn(s) {
|
|
4092
|
+
if (!s) return null;
|
|
4093
|
+
const m = s.match(/^(\d+)([hdw])$/);
|
|
4094
|
+
if (!m) throw new CliError(`Invalid --expires-in "${s}" \u2014 expected forms like 30d, 6h, 2w`);
|
|
4095
|
+
const n = Number(m[1]);
|
|
4096
|
+
const unit = m[2];
|
|
4097
|
+
const seconds = unit === "h" ? 3600 : unit === "d" ? 86400 : 7 * 86400;
|
|
4098
|
+
return Math.floor(Date.now() / 1e3) + n * seconds;
|
|
4099
|
+
}
|
|
3978
4100
|
|
|
3979
4101
|
// 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
|
|
4102
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
4103
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
|
|
4104
|
+
import { homedir as homedir11, userInfo as userInfo2 } from "os";
|
|
4105
|
+
import { dirname as dirname3, join as join10 } from "path";
|
|
4106
|
+
import { defineCommand as defineCommand31 } from "citty";
|
|
4107
|
+
import consola27 from "consola";
|
|
3986
4108
|
|
|
3987
4109
|
// src/commands/nest/apes-agents-adapter.ts
|
|
3988
4110
|
var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
|
|
@@ -4049,13 +4171,13 @@ resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
|
|
|
4049
4171
|
// src/commands/nest/install.ts
|
|
4050
4172
|
var PLIST_LABEL = "ai.openape.nest";
|
|
4051
4173
|
function plistPath() {
|
|
4052
|
-
return
|
|
4174
|
+
return join10(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
4053
4175
|
}
|
|
4054
4176
|
function escape2(s) {
|
|
4055
4177
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
4056
4178
|
}
|
|
4057
4179
|
function buildPlist(args) {
|
|
4058
|
-
const logsDir =
|
|
4180
|
+
const logsDir = join10(args.userHome, "Library", "Logs");
|
|
4059
4181
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
4060
4182
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4061
4183
|
<plist version="1.0">
|
|
@@ -4067,7 +4189,7 @@ function buildPlist(args) {
|
|
|
4067
4189
|
<string>${escape2(args.nestBin)}</string>
|
|
4068
4190
|
</array>
|
|
4069
4191
|
<key>WorkingDirectory</key>
|
|
4070
|
-
<string>${escape2(args.
|
|
4192
|
+
<string>${escape2(args.nestHome)}</string>
|
|
4071
4193
|
<key>RunAtLoad</key>
|
|
4072
4194
|
<true/>
|
|
4073
4195
|
<key>KeepAlive</key>
|
|
@@ -4076,8 +4198,8 @@ function buildPlist(args) {
|
|
|
4076
4198
|
<integer>10</integer>
|
|
4077
4199
|
<key>EnvironmentVariables</key>
|
|
4078
4200
|
<dict>
|
|
4079
|
-
<key>HOME</key><string>${escape2(args.
|
|
4080
|
-
<key>PATH</key><string>${escape2(args.
|
|
4201
|
+
<key>HOME</key><string>${escape2(args.nestHome)}</string>
|
|
4202
|
+
<key>PATH</key><string>${escape2(args.userHome)}/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
4081
4203
|
<key>OPENAPE_NEST_PORT</key><string>${args.port}</string>
|
|
4082
4204
|
<key>OPENAPE_APES_BIN</key><string>${escape2(args.apesBin)}</string>
|
|
4083
4205
|
</dict>
|
|
@@ -4090,31 +4212,31 @@ function buildPlist(args) {
|
|
|
4090
4212
|
`;
|
|
4091
4213
|
}
|
|
4092
4214
|
function installAdapter2() {
|
|
4093
|
-
const target =
|
|
4094
|
-
|
|
4215
|
+
const target = join10(homedir11(), ".openape", "shapes", "adapters", "apes-agents.toml");
|
|
4216
|
+
mkdirSync5(dirname3(target), { recursive: true });
|
|
4095
4217
|
let existing = "";
|
|
4096
4218
|
try {
|
|
4097
|
-
existing =
|
|
4219
|
+
existing = readFileSync11(target, "utf8");
|
|
4098
4220
|
} catch {
|
|
4099
4221
|
}
|
|
4100
4222
|
if (existing === APES_AGENTS_ADAPTER_TOML) return false;
|
|
4101
|
-
|
|
4102
|
-
|
|
4223
|
+
writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
|
|
4224
|
+
consola27.success(`Wrote shapes adapter ${target}`);
|
|
4103
4225
|
return true;
|
|
4104
4226
|
}
|
|
4105
4227
|
function findBinary(name) {
|
|
4106
4228
|
for (const dir of [
|
|
4107
|
-
|
|
4229
|
+
join10(homedir11(), ".bun", "bin"),
|
|
4108
4230
|
"/opt/homebrew/bin",
|
|
4109
4231
|
"/usr/local/bin",
|
|
4110
4232
|
"/usr/bin"
|
|
4111
4233
|
]) {
|
|
4112
|
-
const p =
|
|
4113
|
-
if (
|
|
4234
|
+
const p = join10(dir, name);
|
|
4235
|
+
if (existsSync12(p)) return p;
|
|
4114
4236
|
}
|
|
4115
4237
|
throw new Error(`could not locate ${name} on PATH; install it first`);
|
|
4116
4238
|
}
|
|
4117
|
-
var installNestCommand =
|
|
4239
|
+
var installNestCommand = defineCommand31({
|
|
4118
4240
|
meta: {
|
|
4119
4241
|
name: "install",
|
|
4120
4242
|
description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
|
|
@@ -4126,54 +4248,55 @@ var installNestCommand = defineCommand30({
|
|
|
4126
4248
|
}
|
|
4127
4249
|
},
|
|
4128
4250
|
async run({ args }) {
|
|
4129
|
-
const homeDir =
|
|
4251
|
+
const homeDir = homedir11();
|
|
4130
4252
|
const port = Number(args.port ?? 9091);
|
|
4131
4253
|
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
4132
4254
|
throw new Error(`invalid port ${port}`);
|
|
4133
4255
|
}
|
|
4134
4256
|
const nestBin = findBinary("openape-nest");
|
|
4135
4257
|
const apesBin = findBinary("apes");
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4258
|
+
consola27.info(`Installing nest at ${plistPath()}`);
|
|
4259
|
+
consola27.info(` nest binary: ${nestBin}`);
|
|
4260
|
+
consola27.info(` apes binary: ${apesBin}`);
|
|
4261
|
+
consola27.info(` HTTP port: ${port}`);
|
|
4140
4262
|
installAdapter2();
|
|
4141
|
-
|
|
4142
|
-
|
|
4263
|
+
mkdirSync5(join10(homeDir, "Library", "LaunchAgents"), { recursive: true });
|
|
4264
|
+
mkdirSync5(NEST_DATA_DIR, { recursive: true });
|
|
4265
|
+
const desired = buildPlist({ nestBin, apesBin, userHome: homeDir, nestHome: NEST_DATA_DIR, port });
|
|
4143
4266
|
let existing = "";
|
|
4144
4267
|
try {
|
|
4145
|
-
existing =
|
|
4268
|
+
existing = readFileSync11(plistPath(), "utf8");
|
|
4146
4269
|
} catch {
|
|
4147
4270
|
}
|
|
4148
4271
|
if (existing !== desired) {
|
|
4149
|
-
|
|
4150
|
-
|
|
4272
|
+
writeFileSync7(plistPath(), desired, { mode: 420 });
|
|
4273
|
+
consola27.success("Wrote launchd plist");
|
|
4151
4274
|
} else {
|
|
4152
|
-
|
|
4275
|
+
consola27.info("plist already up to date");
|
|
4153
4276
|
}
|
|
4154
4277
|
const uid = userInfo2().uid;
|
|
4155
4278
|
try {
|
|
4156
|
-
|
|
4279
|
+
execFileSync9("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
|
|
4157
4280
|
} catch {
|
|
4158
4281
|
}
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4282
|
+
execFileSync9("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
|
|
4283
|
+
consola27.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
|
|
4284
|
+
consola27.info("");
|
|
4285
|
+
consola27.info("Next steps for zero-prompt spawn \u2014 both one-time:");
|
|
4286
|
+
consola27.info("");
|
|
4287
|
+
consola27.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
|
|
4288
|
+
consola27.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
|
|
4289
|
+
consola27.info("");
|
|
4290
|
+
consola27.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
|
|
4168
4291
|
}
|
|
4169
4292
|
});
|
|
4170
4293
|
|
|
4171
4294
|
// src/commands/nest/status.ts
|
|
4172
4295
|
import process2 from "process";
|
|
4173
|
-
import { defineCommand as
|
|
4174
|
-
import
|
|
4296
|
+
import { defineCommand as defineCommand32 } from "citty";
|
|
4297
|
+
import consola28 from "consola";
|
|
4175
4298
|
var DEFAULT_PORT = 9091;
|
|
4176
|
-
var statusNestCommand =
|
|
4299
|
+
var statusNestCommand = defineCommand32({
|
|
4177
4300
|
meta: {
|
|
4178
4301
|
name: "status",
|
|
4179
4302
|
description: "Print state of the local nest-daemon (agents registered, processes supervised)"
|
|
@@ -4193,8 +4316,8 @@ var statusNestCommand = defineCommand31({
|
|
|
4193
4316
|
} catch (err) {
|
|
4194
4317
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4195
4318
|
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
4196
|
-
|
|
4197
|
-
|
|
4319
|
+
consola28.error(`Nest daemon is not running at http://127.0.0.1:${port}`);
|
|
4320
|
+
consola28.info(" Run: apes nest install");
|
|
4198
4321
|
process2.exit(2);
|
|
4199
4322
|
}
|
|
4200
4323
|
throw err;
|
|
@@ -4203,15 +4326,15 @@ var statusNestCommand = defineCommand31({
|
|
|
4203
4326
|
console.log(JSON.stringify(status, null, 2));
|
|
4204
4327
|
return;
|
|
4205
4328
|
}
|
|
4206
|
-
|
|
4329
|
+
consola28.info(`Nest at http://127.0.0.1:${port} \u2014 ${status.agents} agent(s) registered, ${status.processes.length} supervised`);
|
|
4207
4330
|
if (status.processes.length === 0) {
|
|
4208
|
-
|
|
4331
|
+
consola28.info(" (no processes running)");
|
|
4209
4332
|
return;
|
|
4210
4333
|
}
|
|
4211
4334
|
for (const p of status.processes) {
|
|
4212
4335
|
const uptime = humanDuration(p.uptimeSec);
|
|
4213
4336
|
const crashTag = p.consecutiveCrashes > 0 ? ` \u26A0 ${p.consecutiveCrashes} crash(es)` : "";
|
|
4214
|
-
|
|
4337
|
+
consola28.info(` ${p.name.padEnd(16)} pid=${String(p.pid).padEnd(6)} up=${uptime}${crashTag}`);
|
|
4215
4338
|
}
|
|
4216
4339
|
}
|
|
4217
4340
|
});
|
|
@@ -4223,43 +4346,44 @@ function humanDuration(sec) {
|
|
|
4223
4346
|
}
|
|
4224
4347
|
|
|
4225
4348
|
// 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
|
|
4349
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
4350
|
+
import { existsSync as existsSync13, unlinkSync } from "fs";
|
|
4351
|
+
import { homedir as homedir12, userInfo as userInfo3 } from "os";
|
|
4352
|
+
import { join as join11 } from "path";
|
|
4353
|
+
import { defineCommand as defineCommand33 } from "citty";
|
|
4354
|
+
import consola29 from "consola";
|
|
4232
4355
|
var PLIST_LABEL2 = "ai.openape.nest";
|
|
4233
|
-
var uninstallNestCommand =
|
|
4356
|
+
var uninstallNestCommand = defineCommand33({
|
|
4234
4357
|
meta: {
|
|
4235
4358
|
name: "uninstall",
|
|
4236
4359
|
description: "Stop + remove the local nest-daemon (registry + agents preserved)"
|
|
4237
4360
|
},
|
|
4238
4361
|
async run() {
|
|
4239
4362
|
const uid = userInfo3().uid;
|
|
4240
|
-
const path2 =
|
|
4363
|
+
const path2 = join11(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
|
|
4241
4364
|
try {
|
|
4242
|
-
|
|
4243
|
-
|
|
4365
|
+
execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
|
|
4366
|
+
consola29.success("Nest daemon stopped");
|
|
4244
4367
|
} catch {
|
|
4245
|
-
|
|
4368
|
+
consola29.info("Nest daemon was not loaded");
|
|
4246
4369
|
}
|
|
4247
|
-
if (
|
|
4370
|
+
if (existsSync13(path2)) {
|
|
4248
4371
|
unlinkSync(path2);
|
|
4249
|
-
|
|
4372
|
+
consola29.success(`Removed ${path2}`);
|
|
4250
4373
|
}
|
|
4251
|
-
|
|
4374
|
+
consola29.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
|
|
4252
4375
|
}
|
|
4253
4376
|
});
|
|
4254
4377
|
|
|
4255
4378
|
// src/commands/nest/index.ts
|
|
4256
|
-
var nestCommand =
|
|
4379
|
+
var nestCommand = defineCommand34({
|
|
4257
4380
|
meta: {
|
|
4258
4381
|
name: "nest",
|
|
4259
|
-
description: "Manage the local Nest control-plane daemon (install
|
|
4382
|
+
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
4383
|
},
|
|
4261
4384
|
subCommands: {
|
|
4262
4385
|
install: installNestCommand,
|
|
4386
|
+
enroll: enrollNestCommand,
|
|
4263
4387
|
authorize: authorizeNestCommand,
|
|
4264
4388
|
status: statusNestCommand,
|
|
4265
4389
|
uninstall: uninstallNestCommand
|
|
@@ -4267,15 +4391,15 @@ var nestCommand = defineCommand33({
|
|
|
4267
4391
|
});
|
|
4268
4392
|
|
|
4269
4393
|
// src/commands/adapter/index.ts
|
|
4270
|
-
import { defineCommand as
|
|
4271
|
-
import
|
|
4272
|
-
var adapterCommand =
|
|
4394
|
+
import { defineCommand as defineCommand35 } from "citty";
|
|
4395
|
+
import consola30 from "consola";
|
|
4396
|
+
var adapterCommand = defineCommand35({
|
|
4273
4397
|
meta: {
|
|
4274
4398
|
name: "adapter",
|
|
4275
4399
|
description: "Manage CLI adapters"
|
|
4276
4400
|
},
|
|
4277
4401
|
subCommands: {
|
|
4278
|
-
list:
|
|
4402
|
+
list: defineCommand35({
|
|
4279
4403
|
meta: {
|
|
4280
4404
|
name: "list",
|
|
4281
4405
|
description: "List available adapters"
|
|
@@ -4306,7 +4430,7 @@ var adapterCommand = defineCommand34({
|
|
|
4306
4430
|
`);
|
|
4307
4431
|
return;
|
|
4308
4432
|
}
|
|
4309
|
-
|
|
4433
|
+
consola30.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
4310
4434
|
for (const a of index2.adapters) {
|
|
4311
4435
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
4312
4436
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -4328,7 +4452,7 @@ var adapterCommand = defineCommand34({
|
|
|
4328
4452
|
return;
|
|
4329
4453
|
}
|
|
4330
4454
|
if (local.length === 0) {
|
|
4331
|
-
|
|
4455
|
+
consola30.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
4332
4456
|
return;
|
|
4333
4457
|
}
|
|
4334
4458
|
for (const a of local) {
|
|
@@ -4336,7 +4460,7 @@ var adapterCommand = defineCommand34({
|
|
|
4336
4460
|
}
|
|
4337
4461
|
}
|
|
4338
4462
|
}),
|
|
4339
|
-
install:
|
|
4463
|
+
install: defineCommand35({
|
|
4340
4464
|
meta: {
|
|
4341
4465
|
name: "install",
|
|
4342
4466
|
description: "Install an adapter from the registry"
|
|
@@ -4365,24 +4489,24 @@ var adapterCommand = defineCommand34({
|
|
|
4365
4489
|
for (const id of ids) {
|
|
4366
4490
|
const entry = findAdapter(index, id);
|
|
4367
4491
|
if (!entry) {
|
|
4368
|
-
|
|
4492
|
+
consola30.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
4369
4493
|
continue;
|
|
4370
4494
|
}
|
|
4371
4495
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
4372
4496
|
if (conflicts.length > 0) {
|
|
4373
4497
|
for (const c of conflicts) {
|
|
4374
|
-
|
|
4375
|
-
|
|
4498
|
+
consola30.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
|
|
4499
|
+
consola30.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
|
|
4376
4500
|
}
|
|
4377
4501
|
}
|
|
4378
4502
|
const result = await installAdapter(entry, { local });
|
|
4379
4503
|
const verb = result.updated ? "Updated" : "Installed";
|
|
4380
|
-
|
|
4381
|
-
|
|
4504
|
+
consola30.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
4505
|
+
consola30.info(`Digest: ${result.digest}`);
|
|
4382
4506
|
}
|
|
4383
4507
|
}
|
|
4384
4508
|
}),
|
|
4385
|
-
remove:
|
|
4509
|
+
remove: defineCommand35({
|
|
4386
4510
|
meta: {
|
|
4387
4511
|
name: "remove",
|
|
4388
4512
|
description: "Remove an installed adapter"
|
|
@@ -4405,9 +4529,9 @@ var adapterCommand = defineCommand34({
|
|
|
4405
4529
|
let failed = false;
|
|
4406
4530
|
for (const id of ids) {
|
|
4407
4531
|
if (removeAdapter(id, local)) {
|
|
4408
|
-
|
|
4532
|
+
consola30.success(`Removed adapter: ${id}`);
|
|
4409
4533
|
} else {
|
|
4410
|
-
|
|
4534
|
+
consola30.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4411
4535
|
failed = true;
|
|
4412
4536
|
}
|
|
4413
4537
|
}
|
|
@@ -4415,7 +4539,7 @@ var adapterCommand = defineCommand34({
|
|
|
4415
4539
|
throw new CliError("Some adapters could not be removed");
|
|
4416
4540
|
}
|
|
4417
4541
|
}),
|
|
4418
|
-
info:
|
|
4542
|
+
info: defineCommand35({
|
|
4419
4543
|
meta: {
|
|
4420
4544
|
name: "info",
|
|
4421
4545
|
description: "Show detailed adapter information"
|
|
@@ -4457,7 +4581,7 @@ var adapterCommand = defineCommand34({
|
|
|
4457
4581
|
}
|
|
4458
4582
|
}
|
|
4459
4583
|
}),
|
|
4460
|
-
search:
|
|
4584
|
+
search: defineCommand35({
|
|
4461
4585
|
meta: {
|
|
4462
4586
|
name: "search",
|
|
4463
4587
|
description: "Search adapters in the registry"
|
|
@@ -4489,7 +4613,7 @@ var adapterCommand = defineCommand34({
|
|
|
4489
4613
|
return;
|
|
4490
4614
|
}
|
|
4491
4615
|
if (results.length === 0) {
|
|
4492
|
-
|
|
4616
|
+
consola30.info(`No adapters matching "${query}"`);
|
|
4493
4617
|
return;
|
|
4494
4618
|
}
|
|
4495
4619
|
for (const a of results) {
|
|
@@ -4498,7 +4622,7 @@ var adapterCommand = defineCommand34({
|
|
|
4498
4622
|
}
|
|
4499
4623
|
}
|
|
4500
4624
|
}),
|
|
4501
|
-
update:
|
|
4625
|
+
update: defineCommand35({
|
|
4502
4626
|
meta: {
|
|
4503
4627
|
name: "update",
|
|
4504
4628
|
description: "Update installed adapters"
|
|
@@ -4524,33 +4648,33 @@ var adapterCommand = defineCommand34({
|
|
|
4524
4648
|
const targetId = args.id ? String(args.id) : void 0;
|
|
4525
4649
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
4526
4650
|
if (targets.length === 0) {
|
|
4527
|
-
|
|
4651
|
+
consola30.info("No adapters installed to update.");
|
|
4528
4652
|
return;
|
|
4529
4653
|
}
|
|
4530
4654
|
for (const id of targets) {
|
|
4531
4655
|
const entry = findAdapter(index, id);
|
|
4532
4656
|
if (!entry) {
|
|
4533
|
-
|
|
4657
|
+
consola30.warn(`${id}: not found in registry, skipping`);
|
|
4534
4658
|
continue;
|
|
4535
4659
|
}
|
|
4536
4660
|
const localDigest = getInstalledDigest(id, false);
|
|
4537
4661
|
if (localDigest === entry.digest) {
|
|
4538
|
-
|
|
4662
|
+
consola30.info(`${id}: already up to date`);
|
|
4539
4663
|
continue;
|
|
4540
4664
|
}
|
|
4541
4665
|
if (localDigest && !args.yes) {
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4666
|
+
consola30.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
|
|
4667
|
+
consola30.info(` Old: ${localDigest}`);
|
|
4668
|
+
consola30.info(` New: ${entry.digest}`);
|
|
4669
|
+
consola30.info(" Use --yes to confirm");
|
|
4546
4670
|
continue;
|
|
4547
4671
|
}
|
|
4548
4672
|
const result = await installAdapter(entry);
|
|
4549
|
-
|
|
4673
|
+
consola30.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
4550
4674
|
}
|
|
4551
4675
|
}
|
|
4552
4676
|
}),
|
|
4553
|
-
verify:
|
|
4677
|
+
verify: defineCommand35({
|
|
4554
4678
|
meta: {
|
|
4555
4679
|
name: "verify",
|
|
4556
4680
|
description: "Verify installed adapter against registry digest"
|
|
@@ -4583,7 +4707,7 @@ var adapterCommand = defineCommand34({
|
|
|
4583
4707
|
if (!localDigest)
|
|
4584
4708
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4585
4709
|
if (localDigest === entry.digest) {
|
|
4586
|
-
|
|
4710
|
+
consola30.success(`${id}: digest matches registry`);
|
|
4587
4711
|
} else {
|
|
4588
4712
|
console.log(` Local: ${localDigest}`);
|
|
4589
4713
|
console.log(` Registry: ${entry.digest}`);
|
|
@@ -4595,11 +4719,11 @@ var adapterCommand = defineCommand34({
|
|
|
4595
4719
|
});
|
|
4596
4720
|
|
|
4597
4721
|
// src/commands/run.ts
|
|
4598
|
-
import { execFileSync as
|
|
4599
|
-
import { hostname as
|
|
4722
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
4723
|
+
import { hostname as hostname5 } from "os";
|
|
4600
4724
|
import { basename } from "path";
|
|
4601
|
-
import { defineCommand as
|
|
4602
|
-
import
|
|
4725
|
+
import { defineCommand as defineCommand36 } from "citty";
|
|
4726
|
+
import consola31 from "consola";
|
|
4603
4727
|
function shouldWaitForGrant(args) {
|
|
4604
4728
|
return args.wait === true || process.env.APE_WAIT === "1";
|
|
4605
4729
|
}
|
|
@@ -4636,7 +4760,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4636
4760
|
const statusCmd = `apes grants status ${grant.id}`;
|
|
4637
4761
|
const executeCmd = `apes grants run ${grant.id}`;
|
|
4638
4762
|
if (mode === "human") {
|
|
4639
|
-
|
|
4763
|
+
consola31.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
|
|
4640
4764
|
console.log(` Approve in browser: ${approveUrl}`);
|
|
4641
4765
|
console.log(` Check status: ${statusCmd}`);
|
|
4642
4766
|
console.log(` Run after approval: ${executeCmd}`);
|
|
@@ -4646,7 +4770,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4646
4770
|
return;
|
|
4647
4771
|
}
|
|
4648
4772
|
const maxMin = getPollMaxMinutes();
|
|
4649
|
-
|
|
4773
|
+
consola31.success(`Grant ${grant.id} created (pending approval)`);
|
|
4650
4774
|
console.log(` Approve: ${approveUrl}`);
|
|
4651
4775
|
console.log(` Status: ${statusCmd} [--json]`);
|
|
4652
4776
|
console.log(` Execute: ${executeCmd} --wait`);
|
|
@@ -4668,7 +4792,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4668
4792
|
console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
|
|
4669
4793
|
console.log(" grant be reused on subsequent invocations without re-approval.");
|
|
4670
4794
|
}
|
|
4671
|
-
var runCommand =
|
|
4795
|
+
var runCommand = defineCommand36({
|
|
4672
4796
|
meta: {
|
|
4673
4797
|
name: "run",
|
|
4674
4798
|
description: "Execute a grant-secured command"
|
|
@@ -4757,7 +4881,7 @@ async function runShellMode(command, args) {
|
|
|
4757
4881
|
const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
|
|
4758
4882
|
if (adapterHandled) return;
|
|
4759
4883
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
4760
|
-
const targetHost = args.host ||
|
|
4884
|
+
const targetHost = args.host || hostname5();
|
|
4761
4885
|
try {
|
|
4762
4886
|
const grants = await apiFetch(
|
|
4763
4887
|
`${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
|
|
@@ -4771,7 +4895,7 @@ async function runShellMode(command, args) {
|
|
|
4771
4895
|
}
|
|
4772
4896
|
} catch {
|
|
4773
4897
|
}
|
|
4774
|
-
|
|
4898
|
+
consola31.info(`Requesting ape-shell session grant on ${targetHost}`);
|
|
4775
4899
|
const grant = await apiFetch(grantsUrl, {
|
|
4776
4900
|
method: "POST",
|
|
4777
4901
|
body: {
|
|
@@ -4791,8 +4915,8 @@ async function runShellMode(command, args) {
|
|
|
4791
4915
|
host: targetHost
|
|
4792
4916
|
});
|
|
4793
4917
|
if (shouldWaitForGrant(args)) {
|
|
4794
|
-
|
|
4795
|
-
|
|
4918
|
+
consola31.info(`Grant requested: ${grant.id}`);
|
|
4919
|
+
consola31.info("Waiting for approval...");
|
|
4796
4920
|
const maxWait = 3e5;
|
|
4797
4921
|
const interval = 3e3;
|
|
4798
4922
|
const start = Date.now();
|
|
@@ -4823,13 +4947,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4823
4947
|
try {
|
|
4824
4948
|
resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
|
|
4825
4949
|
} catch (err) {
|
|
4826
|
-
|
|
4950
|
+
consola31.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
|
|
4827
4951
|
return false;
|
|
4828
4952
|
}
|
|
4829
4953
|
try {
|
|
4830
4954
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
4831
4955
|
if (existingGrantId) {
|
|
4832
|
-
|
|
4956
|
+
consola31.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
|
|
4833
4957
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
4834
4958
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
4835
4959
|
return true;
|
|
@@ -4837,7 +4961,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4837
4961
|
} catch {
|
|
4838
4962
|
}
|
|
4839
4963
|
const approval = args.approval ?? "once";
|
|
4840
|
-
|
|
4964
|
+
consola31.info(`Requesting grant for: ${resolved.detail.display}`);
|
|
4841
4965
|
const grant = await createShapesGrant(resolved, {
|
|
4842
4966
|
idp,
|
|
4843
4967
|
approval,
|
|
@@ -4845,19 +4969,19 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4845
4969
|
});
|
|
4846
4970
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
4847
4971
|
const n = grant.similar_grants.similar_grants.length;
|
|
4848
|
-
|
|
4849
|
-
|
|
4972
|
+
consola31.info("");
|
|
4973
|
+
consola31.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
4850
4974
|
}
|
|
4851
4975
|
notifyGrantPending({
|
|
4852
4976
|
grantId: grant.id,
|
|
4853
4977
|
approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
|
|
4854
4978
|
command: resolved.detail?.display || parsed?.raw || "unknown",
|
|
4855
4979
|
audience: resolved.adapter?.cli?.audience ?? "shapes",
|
|
4856
|
-
host: args.host ||
|
|
4980
|
+
host: args.host || hostname5()
|
|
4857
4981
|
});
|
|
4858
4982
|
if (shouldWaitForGrant(args)) {
|
|
4859
|
-
|
|
4860
|
-
|
|
4983
|
+
consola31.info(`Grant requested: ${grant.id}`);
|
|
4984
|
+
consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4861
4985
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
4862
4986
|
if (status !== "approved")
|
|
4863
4987
|
throw new CliError(`Grant ${status}`);
|
|
@@ -4873,7 +4997,7 @@ function execShellCommand(command) {
|
|
|
4873
4997
|
throw new CliError("No command to execute");
|
|
4874
4998
|
try {
|
|
4875
4999
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
4876
|
-
|
|
5000
|
+
execFileSync11(command[0], command.slice(1), {
|
|
4877
5001
|
stdio: "inherit",
|
|
4878
5002
|
env: inheritedEnv
|
|
4879
5003
|
});
|
|
@@ -4931,7 +5055,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
4931
5055
|
try {
|
|
4932
5056
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
4933
5057
|
if (existingGrantId) {
|
|
4934
|
-
|
|
5058
|
+
consola31.info(`Reusing existing grant: ${existingGrantId}`);
|
|
4935
5059
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
4936
5060
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
4937
5061
|
return;
|
|
@@ -4945,17 +5069,17 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
4945
5069
|
});
|
|
4946
5070
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
4947
5071
|
const n = grant.similar_grants.similar_grants.length;
|
|
4948
|
-
|
|
4949
|
-
|
|
5072
|
+
consola31.info("");
|
|
5073
|
+
consola31.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
4950
5074
|
if (grant.similar_grants.widened_details?.length) {
|
|
4951
5075
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
4952
|
-
|
|
5076
|
+
consola31.info(` Broader scope: ${wider}`);
|
|
4953
5077
|
}
|
|
4954
|
-
|
|
5078
|
+
consola31.info("");
|
|
4955
5079
|
}
|
|
4956
5080
|
if (shouldWaitForGrant(args)) {
|
|
4957
|
-
|
|
4958
|
-
|
|
5081
|
+
consola31.info(`Grant requested: ${grant.id}`);
|
|
5082
|
+
consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4959
5083
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
4960
5084
|
if (status !== "approved")
|
|
4961
5085
|
throw new Error(`Grant ${status}`);
|
|
@@ -4974,8 +5098,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4974
5098
|
const idp = getIdpUrl(args.idp);
|
|
4975
5099
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
4976
5100
|
const command = action.split(" ");
|
|
4977
|
-
const targetHost = args.host ||
|
|
4978
|
-
|
|
5101
|
+
const targetHost = args.host || hostname5();
|
|
5102
|
+
consola31.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
4979
5103
|
const grant = await apiFetch(grantsUrl, {
|
|
4980
5104
|
method: "POST",
|
|
4981
5105
|
body: {
|
|
@@ -4992,9 +5116,9 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4992
5116
|
printPendingGrantInfo(grant, idp);
|
|
4993
5117
|
throw new CliExit(getAsyncExitCode());
|
|
4994
5118
|
}
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
5119
|
+
consola31.success(`Grant requested: ${grant.id}`);
|
|
5120
|
+
consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5121
|
+
consola31.info("Waiting for approval...");
|
|
4998
5122
|
const maxWait = 15 * 60 * 1e3;
|
|
4999
5123
|
const interval = 3e3;
|
|
5000
5124
|
const start = Date.now();
|
|
@@ -5002,7 +5126,7 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5002
5126
|
while (Date.now() - start < maxWait) {
|
|
5003
5127
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
5004
5128
|
if (status.status === "approved") {
|
|
5005
|
-
|
|
5129
|
+
consola31.success("Grant approved!");
|
|
5006
5130
|
approved = true;
|
|
5007
5131
|
break;
|
|
5008
5132
|
}
|
|
@@ -5017,15 +5141,15 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5017
5141
|
`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
5142
|
);
|
|
5019
5143
|
}
|
|
5020
|
-
|
|
5144
|
+
consola31.info("Fetching grant token...");
|
|
5021
5145
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
5022
5146
|
method: "POST"
|
|
5023
5147
|
});
|
|
5024
5148
|
if (audience === "escapes") {
|
|
5025
|
-
|
|
5149
|
+
consola31.info(`Executing: ${command.join(" ")}`);
|
|
5026
5150
|
try {
|
|
5027
5151
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
5028
|
-
|
|
5152
|
+
execFileSync11(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
|
|
5029
5153
|
stdio: "inherit",
|
|
5030
5154
|
env: inheritedEnv
|
|
5031
5155
|
});
|
|
@@ -5040,8 +5164,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
5040
5164
|
|
|
5041
5165
|
// src/commands/proxy.ts
|
|
5042
5166
|
import { spawn as spawn2 } from "child_process";
|
|
5043
|
-
import { defineCommand as
|
|
5044
|
-
import
|
|
5167
|
+
import { defineCommand as defineCommand37 } from "citty";
|
|
5168
|
+
import consola32 from "consola";
|
|
5045
5169
|
|
|
5046
5170
|
// src/proxy/config.ts
|
|
5047
5171
|
function buildDefaultProxyConfigToml(opts) {
|
|
@@ -5075,10 +5199,10 @@ note = "VPC-internal hostname suffix"
|
|
|
5075
5199
|
|
|
5076
5200
|
// src/proxy/local-proxy.ts
|
|
5077
5201
|
import { spawn } from "child_process";
|
|
5078
|
-
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as
|
|
5202
|
+
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
5079
5203
|
import { createRequire } from "module";
|
|
5080
5204
|
import { tmpdir as tmpdir3 } from "os";
|
|
5081
|
-
import { dirname as dirname4, join as
|
|
5205
|
+
import { dirname as dirname4, join as join12, resolve as resolve4 } from "path";
|
|
5082
5206
|
var require2 = createRequire(import.meta.url);
|
|
5083
5207
|
function findProxyBin() {
|
|
5084
5208
|
const pkgPath = require2.resolve("@openape/proxy/package.json");
|
|
@@ -5090,9 +5214,9 @@ function findProxyBin() {
|
|
|
5090
5214
|
return resolve4(dirname4(pkgPath), binRel);
|
|
5091
5215
|
}
|
|
5092
5216
|
async function startEphemeralProxy(configToml) {
|
|
5093
|
-
const tmpDir = mkdtempSync3(
|
|
5094
|
-
const configPath =
|
|
5095
|
-
|
|
5217
|
+
const tmpDir = mkdtempSync3(join12(tmpdir3(), "openape-proxy-"));
|
|
5218
|
+
const configPath = join12(tmpDir, "config.toml");
|
|
5219
|
+
writeFileSync8(configPath, configToml, { mode: 384 });
|
|
5096
5220
|
const binPath = findProxyBin();
|
|
5097
5221
|
const child = spawn(process.execPath, [binPath, "-c", configPath], {
|
|
5098
5222
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -5184,10 +5308,10 @@ function resolveProxyConfigOptions() {
|
|
|
5184
5308
|
77
|
|
5185
5309
|
);
|
|
5186
5310
|
}
|
|
5187
|
-
|
|
5311
|
+
consola32.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
|
|
5188
5312
|
return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
|
|
5189
5313
|
}
|
|
5190
|
-
var proxyCommand =
|
|
5314
|
+
var proxyCommand = defineCommand37({
|
|
5191
5315
|
meta: {
|
|
5192
5316
|
name: "proxy",
|
|
5193
5317
|
description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
|
|
@@ -5209,12 +5333,12 @@ var proxyCommand = defineCommand36({
|
|
|
5209
5333
|
let close = null;
|
|
5210
5334
|
if (reuseUrl) {
|
|
5211
5335
|
proxyUrl = reuseUrl;
|
|
5212
|
-
|
|
5336
|
+
consola32.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
|
|
5213
5337
|
} else {
|
|
5214
5338
|
const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
|
|
5215
5339
|
proxyUrl = ephemeral.url;
|
|
5216
5340
|
close = ephemeral.close;
|
|
5217
|
-
|
|
5341
|
+
consola32.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
|
|
5218
5342
|
}
|
|
5219
5343
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
|
|
5220
5344
|
const childEnv = {
|
|
@@ -5246,7 +5370,7 @@ var proxyCommand = defineCommand36({
|
|
|
5246
5370
|
else resolveExit(code ?? 0);
|
|
5247
5371
|
});
|
|
5248
5372
|
child.once("error", (err) => {
|
|
5249
|
-
|
|
5373
|
+
consola32.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
|
|
5250
5374
|
resolveExit(127);
|
|
5251
5375
|
});
|
|
5252
5376
|
});
|
|
@@ -5260,8 +5384,8 @@ function signalNumber(signal) {
|
|
|
5260
5384
|
}
|
|
5261
5385
|
|
|
5262
5386
|
// src/commands/explain.ts
|
|
5263
|
-
import { defineCommand as
|
|
5264
|
-
var explainCommand =
|
|
5387
|
+
import { defineCommand as defineCommand38 } from "citty";
|
|
5388
|
+
var explainCommand = defineCommand38({
|
|
5265
5389
|
meta: {
|
|
5266
5390
|
name: "explain",
|
|
5267
5391
|
description: "Show what permission a command would need"
|
|
@@ -5299,9 +5423,9 @@ var explainCommand = defineCommand37({
|
|
|
5299
5423
|
});
|
|
5300
5424
|
|
|
5301
5425
|
// src/commands/config/get.ts
|
|
5302
|
-
import { defineCommand as
|
|
5303
|
-
import
|
|
5304
|
-
var configGetCommand =
|
|
5426
|
+
import { defineCommand as defineCommand39 } from "citty";
|
|
5427
|
+
import consola33 from "consola";
|
|
5428
|
+
var configGetCommand = defineCommand39({
|
|
5305
5429
|
meta: {
|
|
5306
5430
|
name: "get",
|
|
5307
5431
|
description: "Get a configuration value"
|
|
@@ -5321,7 +5445,7 @@ var configGetCommand = defineCommand38({
|
|
|
5321
5445
|
if (idp)
|
|
5322
5446
|
console.log(idp);
|
|
5323
5447
|
else
|
|
5324
|
-
|
|
5448
|
+
consola33.info("No IdP configured.");
|
|
5325
5449
|
break;
|
|
5326
5450
|
}
|
|
5327
5451
|
case "email": {
|
|
@@ -5329,7 +5453,7 @@ var configGetCommand = defineCommand38({
|
|
|
5329
5453
|
if (auth?.email)
|
|
5330
5454
|
console.log(auth.email);
|
|
5331
5455
|
else
|
|
5332
|
-
|
|
5456
|
+
consola33.info("Not logged in.");
|
|
5333
5457
|
break;
|
|
5334
5458
|
}
|
|
5335
5459
|
default: {
|
|
@@ -5342,7 +5466,7 @@ var configGetCommand = defineCommand38({
|
|
|
5342
5466
|
if (sectionObj && field in sectionObj) {
|
|
5343
5467
|
console.log(sectionObj[field]);
|
|
5344
5468
|
} else {
|
|
5345
|
-
|
|
5469
|
+
consola33.info(`Key "${key}" not set.`);
|
|
5346
5470
|
}
|
|
5347
5471
|
} else {
|
|
5348
5472
|
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
@@ -5353,9 +5477,9 @@ var configGetCommand = defineCommand38({
|
|
|
5353
5477
|
});
|
|
5354
5478
|
|
|
5355
5479
|
// src/commands/config/set.ts
|
|
5356
|
-
import { defineCommand as
|
|
5357
|
-
import
|
|
5358
|
-
var configSetCommand =
|
|
5480
|
+
import { defineCommand as defineCommand40 } from "citty";
|
|
5481
|
+
import consola34 from "consola";
|
|
5482
|
+
var configSetCommand = defineCommand40({
|
|
5359
5483
|
meta: {
|
|
5360
5484
|
name: "set",
|
|
5361
5485
|
description: "Set a configuration value"
|
|
@@ -5391,12 +5515,12 @@ var configSetCommand = defineCommand39({
|
|
|
5391
5515
|
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
5392
5516
|
}
|
|
5393
5517
|
saveConfig(config);
|
|
5394
|
-
|
|
5518
|
+
consola34.success(`Set ${key} = ${value}`);
|
|
5395
5519
|
}
|
|
5396
5520
|
});
|
|
5397
5521
|
|
|
5398
5522
|
// src/commands/fetch/index.ts
|
|
5399
|
-
import { defineCommand as
|
|
5523
|
+
import { defineCommand as defineCommand41 } from "citty";
|
|
5400
5524
|
async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
5401
5525
|
const token = getAuthToken();
|
|
5402
5526
|
if (!token) {
|
|
@@ -5432,13 +5556,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
|
5432
5556
|
throw new CliError(`HTTP ${response.status} ${response.statusText}`);
|
|
5433
5557
|
}
|
|
5434
5558
|
}
|
|
5435
|
-
var fetchCommand =
|
|
5559
|
+
var fetchCommand = defineCommand41({
|
|
5436
5560
|
meta: {
|
|
5437
5561
|
name: "fetch",
|
|
5438
5562
|
description: "Make authenticated HTTP requests"
|
|
5439
5563
|
},
|
|
5440
5564
|
subCommands: {
|
|
5441
|
-
get:
|
|
5565
|
+
get: defineCommand41({
|
|
5442
5566
|
meta: {
|
|
5443
5567
|
name: "get",
|
|
5444
5568
|
description: "GET request with auth token"
|
|
@@ -5464,7 +5588,7 @@ var fetchCommand = defineCommand40({
|
|
|
5464
5588
|
await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
|
|
5465
5589
|
}
|
|
5466
5590
|
}),
|
|
5467
|
-
post:
|
|
5591
|
+
post: defineCommand41({
|
|
5468
5592
|
meta: {
|
|
5469
5593
|
name: "post",
|
|
5470
5594
|
description: "POST request with auth token"
|
|
@@ -5503,8 +5627,8 @@ var fetchCommand = defineCommand40({
|
|
|
5503
5627
|
});
|
|
5504
5628
|
|
|
5505
5629
|
// src/commands/mcp/index.ts
|
|
5506
|
-
import { defineCommand as
|
|
5507
|
-
var mcpCommand =
|
|
5630
|
+
import { defineCommand as defineCommand42 } from "citty";
|
|
5631
|
+
var mcpCommand = defineCommand42({
|
|
5508
5632
|
meta: {
|
|
5509
5633
|
name: "mcp",
|
|
5510
5634
|
description: "Start MCP server for AI agents"
|
|
@@ -5527,48 +5651,48 @@ var mcpCommand = defineCommand41({
|
|
|
5527
5651
|
if (transport !== "stdio" && transport !== "sse") {
|
|
5528
5652
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
5529
5653
|
}
|
|
5530
|
-
const { startMcpServer } = await import("./server-
|
|
5654
|
+
const { startMcpServer } = await import("./server-UKQ5QFOZ.js");
|
|
5531
5655
|
await startMcpServer(transport, port);
|
|
5532
5656
|
}
|
|
5533
5657
|
});
|
|
5534
5658
|
|
|
5535
5659
|
// src/commands/init/index.ts
|
|
5536
|
-
import { existsSync as
|
|
5660
|
+
import { existsSync as existsSync14, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
|
|
5537
5661
|
import { randomBytes } from "crypto";
|
|
5538
|
-
import { execFileSync as
|
|
5539
|
-
import { join as
|
|
5540
|
-
import { defineCommand as
|
|
5541
|
-
import
|
|
5662
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
5663
|
+
import { join as join13 } from "path";
|
|
5664
|
+
import { defineCommand as defineCommand43 } from "citty";
|
|
5665
|
+
import consola35 from "consola";
|
|
5542
5666
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
5543
5667
|
async function downloadTemplate(repo, targetDir) {
|
|
5544
5668
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
5545
5669
|
await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
|
|
5546
5670
|
}
|
|
5547
5671
|
function installDeps(dir) {
|
|
5548
|
-
const hasLockFile = (name) =>
|
|
5672
|
+
const hasLockFile = (name) => existsSync14(join13(dir, name));
|
|
5549
5673
|
if (hasLockFile("pnpm-lock.yaml")) {
|
|
5550
|
-
|
|
5674
|
+
execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5551
5675
|
} else if (hasLockFile("bun.lockb")) {
|
|
5552
|
-
|
|
5676
|
+
execFileSync12("bun", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5553
5677
|
} else {
|
|
5554
|
-
|
|
5678
|
+
execFileSync12("npm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5555
5679
|
}
|
|
5556
5680
|
}
|
|
5557
5681
|
async function promptChoice(message, choices) {
|
|
5558
|
-
const result = await
|
|
5682
|
+
const result = await consola35.prompt(message, { type: "select", options: choices });
|
|
5559
5683
|
if (typeof result === "symbol") {
|
|
5560
5684
|
throw new CliExit(0);
|
|
5561
5685
|
}
|
|
5562
5686
|
return result;
|
|
5563
5687
|
}
|
|
5564
5688
|
async function promptText(message, defaultValue) {
|
|
5565
|
-
const result = await
|
|
5689
|
+
const result = await consola35.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
5566
5690
|
if (typeof result === "symbol") {
|
|
5567
5691
|
throw new CliExit(0);
|
|
5568
5692
|
}
|
|
5569
5693
|
return result || defaultValue || "";
|
|
5570
5694
|
}
|
|
5571
|
-
var initCommand =
|
|
5695
|
+
var initCommand = defineCommand43({
|
|
5572
5696
|
meta: {
|
|
5573
5697
|
name: "init",
|
|
5574
5698
|
description: "Scaffold a new OpenApe project"
|
|
@@ -5610,23 +5734,23 @@ var initCommand = defineCommand42({
|
|
|
5610
5734
|
});
|
|
5611
5735
|
async function initSP(targetDir) {
|
|
5612
5736
|
const dir = targetDir || "my-app";
|
|
5613
|
-
if (
|
|
5737
|
+
if (existsSync14(join13(dir, "package.json"))) {
|
|
5614
5738
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5615
5739
|
}
|
|
5616
|
-
|
|
5740
|
+
consola35.start("Scaffolding SP starter...");
|
|
5617
5741
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
5618
|
-
|
|
5619
|
-
|
|
5742
|
+
consola35.success("Scaffolded from openape-sp-starter");
|
|
5743
|
+
consola35.start("Installing dependencies...");
|
|
5620
5744
|
installDeps(dir);
|
|
5621
|
-
|
|
5622
|
-
const envExample =
|
|
5623
|
-
const envFile =
|
|
5624
|
-
if (
|
|
5745
|
+
consola35.success("Dependencies installed");
|
|
5746
|
+
const envExample = join13(dir, ".env.example");
|
|
5747
|
+
const envFile = join13(dir, ".env");
|
|
5748
|
+
if (existsSync14(envExample) && !existsSync14(envFile)) {
|
|
5625
5749
|
copyFileSync(envExample, envFile);
|
|
5626
|
-
|
|
5750
|
+
consola35.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
5627
5751
|
}
|
|
5628
5752
|
console.log("");
|
|
5629
|
-
|
|
5753
|
+
consola35.box([
|
|
5630
5754
|
`cd ${dir}`,
|
|
5631
5755
|
"npm run dev",
|
|
5632
5756
|
"",
|
|
@@ -5635,7 +5759,7 @@ async function initSP(targetDir) {
|
|
|
5635
5759
|
}
|
|
5636
5760
|
async function initIdP(targetDir) {
|
|
5637
5761
|
const dir = targetDir || "my-idp";
|
|
5638
|
-
if (
|
|
5762
|
+
if (existsSync14(join13(dir, "package.json"))) {
|
|
5639
5763
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5640
5764
|
}
|
|
5641
5765
|
const domain = await promptText("Domain for the IdP", "localhost");
|
|
@@ -5645,15 +5769,15 @@ async function initIdP(targetDir) {
|
|
|
5645
5769
|
"s3 (S3-compatible)"
|
|
5646
5770
|
]);
|
|
5647
5771
|
const adminEmail = await promptText("Admin email");
|
|
5648
|
-
|
|
5772
|
+
consola35.start("Scaffolding IdP starter...");
|
|
5649
5773
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
5650
|
-
|
|
5651
|
-
|
|
5774
|
+
consola35.success("Scaffolded from openape-idp-starter");
|
|
5775
|
+
consola35.start("Installing dependencies...");
|
|
5652
5776
|
installDeps(dir);
|
|
5653
|
-
|
|
5777
|
+
consola35.success("Dependencies installed");
|
|
5654
5778
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
5655
5779
|
const managementToken = randomBytes(32).toString("hex");
|
|
5656
|
-
|
|
5780
|
+
consola35.success("Secrets generated");
|
|
5657
5781
|
const isLocalhost = domain === "localhost";
|
|
5658
5782
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
5659
5783
|
const envContent = [
|
|
@@ -5667,11 +5791,11 @@ async function initIdP(targetDir) {
|
|
|
5667
5791
|
`NUXT_OPENAPE_RP_ID=${domain}`,
|
|
5668
5792
|
`NUXT_OPENAPE_RP_ORIGIN=${origin}`
|
|
5669
5793
|
].join("\n");
|
|
5670
|
-
|
|
5794
|
+
writeFileSync9(join13(dir, ".env"), `${envContent}
|
|
5671
5795
|
`, { mode: 384 });
|
|
5672
|
-
|
|
5796
|
+
consola35.success(".env created");
|
|
5673
5797
|
console.log("");
|
|
5674
|
-
|
|
5798
|
+
consola35.box([
|
|
5675
5799
|
`cd ${dir}`,
|
|
5676
5800
|
"npm run dev",
|
|
5677
5801
|
"",
|
|
@@ -5688,11 +5812,11 @@ async function initIdP(targetDir) {
|
|
|
5688
5812
|
|
|
5689
5813
|
// src/commands/enroll.ts
|
|
5690
5814
|
import { Buffer as Buffer5 } from "buffer";
|
|
5691
|
-
import { existsSync as
|
|
5815
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
5692
5816
|
import { execFile as execFile2 } from "child_process";
|
|
5693
5817
|
import { sign as sign2 } from "crypto";
|
|
5694
|
-
import { defineCommand as
|
|
5695
|
-
import
|
|
5818
|
+
import { defineCommand as defineCommand44 } from "citty";
|
|
5819
|
+
import consola36 from "consola";
|
|
5696
5820
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
5697
5821
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
5698
5822
|
var POLL_INTERVAL = 3e3;
|
|
@@ -5704,7 +5828,7 @@ function openBrowser2(url) {
|
|
|
5704
5828
|
}
|
|
5705
5829
|
async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
5706
5830
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
5707
|
-
const keyContent =
|
|
5831
|
+
const keyContent = readFileSync12(resolvedKey, "utf-8");
|
|
5708
5832
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
5709
5833
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
5710
5834
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -5735,7 +5859,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
5735
5859
|
}
|
|
5736
5860
|
throw new Error("Enrollment timed out. Please check the browser and try again.");
|
|
5737
5861
|
}
|
|
5738
|
-
var enrollCommand =
|
|
5862
|
+
var enrollCommand = defineCommand44({
|
|
5739
5863
|
meta: {
|
|
5740
5864
|
name: "enroll",
|
|
5741
5865
|
description: "Enroll an agent with an Identity Provider"
|
|
@@ -5755,38 +5879,38 @@ var enrollCommand = defineCommand43({
|
|
|
5755
5879
|
}
|
|
5756
5880
|
},
|
|
5757
5881
|
async run({ args }) {
|
|
5758
|
-
const idp = args.idp || await
|
|
5882
|
+
const idp = args.idp || await consola36.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
|
|
5759
5883
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
5760
5884
|
return r;
|
|
5761
5885
|
}) || DEFAULT_IDP_URL2;
|
|
5762
|
-
const agentName = args.name || await
|
|
5886
|
+
const agentName = args.name || await consola36.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
|
|
5763
5887
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
5764
5888
|
return r;
|
|
5765
5889
|
});
|
|
5766
5890
|
if (!agentName) {
|
|
5767
5891
|
throw new CliError("Agent name is required.");
|
|
5768
5892
|
}
|
|
5769
|
-
const keyPath = args.key || await
|
|
5893
|
+
const keyPath = args.key || await consola36.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
|
|
5770
5894
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
5771
5895
|
return r;
|
|
5772
5896
|
}) || DEFAULT_KEY_PATH;
|
|
5773
5897
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
5774
5898
|
let publicKey;
|
|
5775
|
-
if (
|
|
5899
|
+
if (existsSync15(resolvedKey)) {
|
|
5776
5900
|
publicKey = readPublicKey(resolvedKey);
|
|
5777
|
-
|
|
5901
|
+
consola36.success(`Using existing key ${keyPath}`);
|
|
5778
5902
|
} else {
|
|
5779
|
-
|
|
5903
|
+
consola36.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
5780
5904
|
publicKey = generateAndSaveKey(keyPath);
|
|
5781
|
-
|
|
5905
|
+
consola36.success(`Key pair generated at ${keyPath}`);
|
|
5782
5906
|
}
|
|
5783
5907
|
const encodedKey = encodeURIComponent(publicKey);
|
|
5784
5908
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
5785
|
-
|
|
5786
|
-
|
|
5909
|
+
consola36.info("Opening browser for enrollment...");
|
|
5910
|
+
consola36.info(`\u2192 ${idp}/enroll`);
|
|
5787
5911
|
openBrowser2(enrollUrl);
|
|
5788
5912
|
console.log("");
|
|
5789
|
-
const agentEmail = await
|
|
5913
|
+
const agentEmail = await consola36.prompt(
|
|
5790
5914
|
"Agent email (shown in browser after enrollment)",
|
|
5791
5915
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
5792
5916
|
).then((r) => {
|
|
@@ -5796,7 +5920,7 @@ var enrollCommand = defineCommand43({
|
|
|
5796
5920
|
if (!agentEmail) {
|
|
5797
5921
|
throw new CliError("Agent email is required to verify enrollment.");
|
|
5798
5922
|
}
|
|
5799
|
-
|
|
5923
|
+
consola36.start("Verifying enrollment...");
|
|
5800
5924
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
5801
5925
|
saveAuth({
|
|
5802
5926
|
idp,
|
|
@@ -5808,18 +5932,18 @@ var enrollCommand = defineCommand43({
|
|
|
5808
5932
|
config.defaults = { ...config.defaults, idp };
|
|
5809
5933
|
config.agent = { key: keyPath, email: agentEmail };
|
|
5810
5934
|
saveConfig(config);
|
|
5811
|
-
|
|
5812
|
-
|
|
5935
|
+
consola36.success(`Agent enrolled as ${agentEmail}`);
|
|
5936
|
+
consola36.success("Config saved to ~/.config/apes/");
|
|
5813
5937
|
console.log("");
|
|
5814
|
-
|
|
5938
|
+
consola36.info("Verify with: apes whoami");
|
|
5815
5939
|
}
|
|
5816
5940
|
});
|
|
5817
5941
|
|
|
5818
5942
|
// src/commands/register-user.ts
|
|
5819
|
-
import { existsSync as
|
|
5820
|
-
import { defineCommand as
|
|
5821
|
-
import
|
|
5822
|
-
var registerUserCommand =
|
|
5943
|
+
import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
|
|
5944
|
+
import { defineCommand as defineCommand45 } from "citty";
|
|
5945
|
+
import consola37 from "consola";
|
|
5946
|
+
var registerUserCommand = defineCommand45({
|
|
5823
5947
|
meta: {
|
|
5824
5948
|
name: "register-user",
|
|
5825
5949
|
description: "Register a sub-user with SSH key"
|
|
@@ -5855,8 +5979,8 @@ var registerUserCommand = defineCommand44({
|
|
|
5855
5979
|
throw new CliError("No IdP URL configured. Run `apes login` first.");
|
|
5856
5980
|
}
|
|
5857
5981
|
let publicKey = args.key;
|
|
5858
|
-
if (
|
|
5859
|
-
publicKey =
|
|
5982
|
+
if (existsSync16(args.key)) {
|
|
5983
|
+
publicKey = readFileSync13(args.key, "utf-8").trim();
|
|
5860
5984
|
}
|
|
5861
5985
|
if (!publicKey.startsWith("ssh-ed25519 ")) {
|
|
5862
5986
|
throw new CliError("Public key must be in ssh-ed25519 format.");
|
|
@@ -5874,18 +5998,18 @@ var registerUserCommand = defineCommand44({
|
|
|
5874
5998
|
...userType ? { type: userType } : {}
|
|
5875
5999
|
}
|
|
5876
6000
|
});
|
|
5877
|
-
|
|
6001
|
+
consola37.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
|
|
5878
6002
|
}
|
|
5879
6003
|
});
|
|
5880
6004
|
|
|
5881
6005
|
// src/commands/utils/index.ts
|
|
5882
|
-
import { defineCommand as
|
|
6006
|
+
import { defineCommand as defineCommand47 } from "citty";
|
|
5883
6007
|
|
|
5884
6008
|
// src/commands/utils/dig.ts
|
|
5885
|
-
import { defineCommand as
|
|
5886
|
-
import
|
|
6009
|
+
import { defineCommand as defineCommand46 } from "citty";
|
|
6010
|
+
import consola38 from "consola";
|
|
5887
6011
|
import { resolveDDISA as resolveDDISA2 } from "@openape/core";
|
|
5888
|
-
var digCommand =
|
|
6012
|
+
var digCommand = defineCommand46({
|
|
5889
6013
|
meta: {
|
|
5890
6014
|
name: "dig",
|
|
5891
6015
|
description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
|
|
@@ -5958,12 +6082,12 @@ var digCommand = defineCommand45({
|
|
|
5958
6082
|
console.log(` domain: ${domain}`);
|
|
5959
6083
|
console.log("");
|
|
5960
6084
|
if (!result.ddisa.found) {
|
|
5961
|
-
|
|
6085
|
+
consola38.warn(`No DDISA record at _ddisa.${domain}`);
|
|
5962
6086
|
if (result.hint) console.log(`
|
|
5963
6087
|
${result.hint}`);
|
|
5964
6088
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
5965
6089
|
}
|
|
5966
|
-
|
|
6090
|
+
consola38.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
|
|
5967
6091
|
console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
|
|
5968
6092
|
console.log(` IdP URL: ${result.ddisa.idp}`);
|
|
5969
6093
|
if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
|
|
@@ -5973,13 +6097,13 @@ ${result.hint}`);
|
|
|
5973
6097
|
return;
|
|
5974
6098
|
}
|
|
5975
6099
|
if (result.idpDiscovery.ok) {
|
|
5976
|
-
|
|
6100
|
+
consola38.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
|
|
5977
6101
|
if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
|
|
5978
6102
|
if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
|
|
5979
6103
|
if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
|
|
5980
6104
|
if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
|
|
5981
6105
|
} else {
|
|
5982
|
-
|
|
6106
|
+
consola38.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
|
|
5983
6107
|
if (result.hint) console.log(`
|
|
5984
6108
|
${result.hint}`);
|
|
5985
6109
|
throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
|
|
@@ -5988,7 +6112,7 @@ ${result.hint}`);
|
|
|
5988
6112
|
});
|
|
5989
6113
|
|
|
5990
6114
|
// src/commands/utils/index.ts
|
|
5991
|
-
var utilsCommand =
|
|
6115
|
+
var utilsCommand = defineCommand47({
|
|
5992
6116
|
meta: {
|
|
5993
6117
|
name: "utils",
|
|
5994
6118
|
description: "Admin/diagnostic utilities (dig, \u2026)"
|
|
@@ -5999,12 +6123,12 @@ var utilsCommand = defineCommand46({
|
|
|
5999
6123
|
});
|
|
6000
6124
|
|
|
6001
6125
|
// src/commands/sessions/index.ts
|
|
6002
|
-
import { defineCommand as
|
|
6126
|
+
import { defineCommand as defineCommand50 } from "citty";
|
|
6003
6127
|
|
|
6004
6128
|
// src/commands/sessions/list.ts
|
|
6005
|
-
import { defineCommand as
|
|
6006
|
-
import
|
|
6007
|
-
var sessionsListCommand =
|
|
6129
|
+
import { defineCommand as defineCommand48 } from "citty";
|
|
6130
|
+
import consola39 from "consola";
|
|
6131
|
+
var sessionsListCommand = defineCommand48({
|
|
6008
6132
|
meta: {
|
|
6009
6133
|
name: "list",
|
|
6010
6134
|
description: "List your active refresh-token families (one per logged-in device)."
|
|
@@ -6022,7 +6146,7 @@ var sessionsListCommand = defineCommand47({
|
|
|
6022
6146
|
return;
|
|
6023
6147
|
}
|
|
6024
6148
|
if (result.data.length === 0) {
|
|
6025
|
-
|
|
6149
|
+
consola39.info("No active sessions.");
|
|
6026
6150
|
return;
|
|
6027
6151
|
}
|
|
6028
6152
|
for (const f of result.data) {
|
|
@@ -6034,9 +6158,9 @@ var sessionsListCommand = defineCommand47({
|
|
|
6034
6158
|
});
|
|
6035
6159
|
|
|
6036
6160
|
// src/commands/sessions/remove.ts
|
|
6037
|
-
import { defineCommand as
|
|
6038
|
-
import
|
|
6039
|
-
var sessionsRemoveCommand =
|
|
6161
|
+
import { defineCommand as defineCommand49 } from "citty";
|
|
6162
|
+
import consola40 from "consola";
|
|
6163
|
+
var sessionsRemoveCommand = defineCommand49({
|
|
6040
6164
|
meta: {
|
|
6041
6165
|
name: "remove",
|
|
6042
6166
|
description: "Revoke one of your active refresh-token families by id."
|
|
@@ -6052,12 +6176,12 @@ var sessionsRemoveCommand = defineCommand48({
|
|
|
6052
6176
|
const id = String(args.familyId).trim();
|
|
6053
6177
|
if (!id) throw new CliError("familyId required");
|
|
6054
6178
|
await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
6055
|
-
|
|
6179
|
+
consola40.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
|
|
6056
6180
|
}
|
|
6057
6181
|
});
|
|
6058
6182
|
|
|
6059
6183
|
// src/commands/sessions/index.ts
|
|
6060
|
-
var sessionsCommand =
|
|
6184
|
+
var sessionsCommand = defineCommand50({
|
|
6061
6185
|
meta: {
|
|
6062
6186
|
name: "sessions",
|
|
6063
6187
|
description: "Manage your active refresh-token sessions across devices"
|
|
@@ -6069,10 +6193,10 @@ var sessionsCommand = defineCommand49({
|
|
|
6069
6193
|
});
|
|
6070
6194
|
|
|
6071
6195
|
// src/commands/dns-check.ts
|
|
6072
|
-
import { defineCommand as
|
|
6073
|
-
import
|
|
6196
|
+
import { defineCommand as defineCommand51 } from "citty";
|
|
6197
|
+
import consola41 from "consola";
|
|
6074
6198
|
import { resolveDDISA as resolveDDISA3 } from "@openape/core";
|
|
6075
|
-
var dnsCheckCommand =
|
|
6199
|
+
var dnsCheckCommand = defineCommand51({
|
|
6076
6200
|
meta: {
|
|
6077
6201
|
name: "dns-check",
|
|
6078
6202
|
description: "Validate DDISA DNS TXT records for a domain"
|
|
@@ -6086,7 +6210,7 @@ var dnsCheckCommand = defineCommand50({
|
|
|
6086
6210
|
},
|
|
6087
6211
|
async run({ args }) {
|
|
6088
6212
|
const domain = args.domain;
|
|
6089
|
-
|
|
6213
|
+
consola41.start(`Checking _ddisa.${domain}...`);
|
|
6090
6214
|
try {
|
|
6091
6215
|
const result = await resolveDDISA3(domain);
|
|
6092
6216
|
if (!result) {
|
|
@@ -6095,7 +6219,7 @@ var dnsCheckCommand = defineCommand50({
|
|
|
6095
6219
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
6096
6220
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
6097
6221
|
}
|
|
6098
|
-
|
|
6222
|
+
consola41.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
6099
6223
|
console.log("");
|
|
6100
6224
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
6101
6225
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -6104,14 +6228,14 @@ var dnsCheckCommand = defineCommand50({
|
|
|
6104
6228
|
if (result.priority !== void 0)
|
|
6105
6229
|
console.log(` Priority: ${result.priority}`);
|
|
6106
6230
|
console.log("");
|
|
6107
|
-
|
|
6231
|
+
consola41.start(`Verifying IdP at ${result.idp}...`);
|
|
6108
6232
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
6109
6233
|
if (!discoResp.ok) {
|
|
6110
|
-
|
|
6234
|
+
consola41.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
6111
6235
|
return;
|
|
6112
6236
|
}
|
|
6113
6237
|
const disco = await discoResp.json();
|
|
6114
|
-
|
|
6238
|
+
consola41.success(`IdP is reachable`);
|
|
6115
6239
|
console.log(` Issuer: ${disco.issuer}`);
|
|
6116
6240
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
6117
6241
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -6129,7 +6253,7 @@ var dnsCheckCommand = defineCommand50({
|
|
|
6129
6253
|
// src/commands/health.ts
|
|
6130
6254
|
import { exec } from "child_process";
|
|
6131
6255
|
import { promisify } from "util";
|
|
6132
|
-
import { defineCommand as
|
|
6256
|
+
import { defineCommand as defineCommand52 } from "citty";
|
|
6133
6257
|
var execAsync = promisify(exec);
|
|
6134
6258
|
async function resolveApeShellPath() {
|
|
6135
6259
|
try {
|
|
@@ -6165,7 +6289,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
6165
6289
|
}
|
|
6166
6290
|
}
|
|
6167
6291
|
async function runHealth(args) {
|
|
6168
|
-
const version = true ? "1.
|
|
6292
|
+
const version = true ? "1.6.1" : "0.0.0";
|
|
6169
6293
|
const auth = loadAuth();
|
|
6170
6294
|
if (!auth) {
|
|
6171
6295
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -6228,7 +6352,7 @@ async function runHealth(args) {
|
|
|
6228
6352
|
throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
|
|
6229
6353
|
}
|
|
6230
6354
|
}
|
|
6231
|
-
var healthCommand =
|
|
6355
|
+
var healthCommand = defineCommand52({
|
|
6232
6356
|
meta: {
|
|
6233
6357
|
name: "health",
|
|
6234
6358
|
description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
|
|
@@ -6246,8 +6370,8 @@ var healthCommand = defineCommand51({
|
|
|
6246
6370
|
});
|
|
6247
6371
|
|
|
6248
6372
|
// src/commands/workflows.ts
|
|
6249
|
-
import { defineCommand as
|
|
6250
|
-
import
|
|
6373
|
+
import { defineCommand as defineCommand53 } from "citty";
|
|
6374
|
+
import consola42 from "consola";
|
|
6251
6375
|
|
|
6252
6376
|
// src/guides/index.ts
|
|
6253
6377
|
var guides = [
|
|
@@ -6297,7 +6421,7 @@ var guides = [
|
|
|
6297
6421
|
];
|
|
6298
6422
|
|
|
6299
6423
|
// src/commands/workflows.ts
|
|
6300
|
-
var workflowsCommand =
|
|
6424
|
+
var workflowsCommand = defineCommand53({
|
|
6301
6425
|
meta: {
|
|
6302
6426
|
name: "workflows",
|
|
6303
6427
|
description: "Discover workflow guides"
|
|
@@ -6318,7 +6442,7 @@ var workflowsCommand = defineCommand52({
|
|
|
6318
6442
|
if (args.id) {
|
|
6319
6443
|
const guide = guides.find((g) => g.id === String(args.id));
|
|
6320
6444
|
if (!guide) {
|
|
6321
|
-
|
|
6445
|
+
consola42.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
6322
6446
|
throw new CliError(`Guide not found: ${args.id}`);
|
|
6323
6447
|
}
|
|
6324
6448
|
if (args.json) {
|
|
@@ -6358,26 +6482,26 @@ var workflowsCommand = defineCommand52({
|
|
|
6358
6482
|
});
|
|
6359
6483
|
|
|
6360
6484
|
// src/version-check.ts
|
|
6361
|
-
import { existsSync as
|
|
6362
|
-
import { homedir as
|
|
6363
|
-
import { join as
|
|
6364
|
-
import
|
|
6485
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
|
|
6486
|
+
import { homedir as homedir13 } from "os";
|
|
6487
|
+
import { join as join14 } from "path";
|
|
6488
|
+
import consola43 from "consola";
|
|
6365
6489
|
var PACKAGE_NAME = "@openape/apes";
|
|
6366
6490
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6367
|
-
var CACHE_FILE =
|
|
6491
|
+
var CACHE_FILE = join14(homedir13(), ".config", "apes", ".version-check.json");
|
|
6368
6492
|
function readCache() {
|
|
6369
|
-
if (!
|
|
6493
|
+
if (!existsSync17(CACHE_FILE)) return null;
|
|
6370
6494
|
try {
|
|
6371
|
-
return JSON.parse(
|
|
6495
|
+
return JSON.parse(readFileSync14(CACHE_FILE, "utf-8"));
|
|
6372
6496
|
} catch {
|
|
6373
6497
|
return null;
|
|
6374
6498
|
}
|
|
6375
6499
|
}
|
|
6376
6500
|
function writeCache(entry) {
|
|
6377
6501
|
try {
|
|
6378
|
-
const dir =
|
|
6379
|
-
if (!
|
|
6380
|
-
|
|
6502
|
+
const dir = join14(homedir13(), ".config", "apes");
|
|
6503
|
+
if (!existsSync17(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
6504
|
+
writeFileSync10(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
|
|
6381
6505
|
} catch {
|
|
6382
6506
|
}
|
|
6383
6507
|
}
|
|
@@ -6406,7 +6530,7 @@ async function fetchLatestVersion() {
|
|
|
6406
6530
|
}
|
|
6407
6531
|
function warnIfBehind(currentVersion, latest) {
|
|
6408
6532
|
if (compareSemver(currentVersion, latest) < 0) {
|
|
6409
|
-
|
|
6533
|
+
consola43.warn(
|
|
6410
6534
|
`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
6535
|
);
|
|
6412
6536
|
}
|
|
@@ -6438,10 +6562,10 @@ if (shellRewrite) {
|
|
|
6438
6562
|
if (shellRewrite.action === "rewrite") {
|
|
6439
6563
|
process.argv = shellRewrite.argv;
|
|
6440
6564
|
} else if (shellRewrite.action === "version") {
|
|
6441
|
-
console.log(`ape-shell ${"1.
|
|
6565
|
+
console.log(`ape-shell ${"1.6.1"} (OpenApe DDISA shell wrapper)`);
|
|
6442
6566
|
process.exit(0);
|
|
6443
6567
|
} else if (shellRewrite.action === "help") {
|
|
6444
|
-
console.log(`ape-shell ${"1.
|
|
6568
|
+
console.log(`ape-shell ${"1.6.1"} \u2014 OpenApe DDISA shell wrapper`);
|
|
6445
6569
|
console.log("");
|
|
6446
6570
|
console.log("Usage:");
|
|
6447
6571
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -6465,7 +6589,7 @@ if (shellRewrite) {
|
|
|
6465
6589
|
}
|
|
6466
6590
|
}
|
|
6467
6591
|
var debug = process.argv.includes("--debug");
|
|
6468
|
-
var grantsCommand =
|
|
6592
|
+
var grantsCommand = defineCommand54({
|
|
6469
6593
|
meta: {
|
|
6470
6594
|
name: "grants",
|
|
6471
6595
|
description: "Grant management"
|
|
@@ -6486,7 +6610,7 @@ var grantsCommand = defineCommand53({
|
|
|
6486
6610
|
"delegation-revoke": delegationRevokeCommand
|
|
6487
6611
|
}
|
|
6488
6612
|
});
|
|
6489
|
-
var configCommand =
|
|
6613
|
+
var configCommand = defineCommand54({
|
|
6490
6614
|
meta: {
|
|
6491
6615
|
name: "config",
|
|
6492
6616
|
description: "Configuration management"
|
|
@@ -6496,10 +6620,10 @@ var configCommand = defineCommand53({
|
|
|
6496
6620
|
set: configSetCommand
|
|
6497
6621
|
}
|
|
6498
6622
|
});
|
|
6499
|
-
var main =
|
|
6623
|
+
var main = defineCommand54({
|
|
6500
6624
|
meta: {
|
|
6501
6625
|
name: "apes",
|
|
6502
|
-
version: "1.
|
|
6626
|
+
version: "1.6.1",
|
|
6503
6627
|
description: "Unified CLI for OpenApe"
|
|
6504
6628
|
},
|
|
6505
6629
|
subCommands: {
|
|
@@ -6555,20 +6679,20 @@ async function maybeRefreshAuth() {
|
|
|
6555
6679
|
}
|
|
6556
6680
|
}
|
|
6557
6681
|
await maybeRefreshAuth();
|
|
6558
|
-
await maybeWarnStaleVersion("1.
|
|
6682
|
+
await maybeWarnStaleVersion("1.6.1").catch(() => {
|
|
6559
6683
|
});
|
|
6560
6684
|
runMain(main).catch((err) => {
|
|
6561
6685
|
if (err instanceof CliExit) {
|
|
6562
6686
|
process.exit(err.exitCode);
|
|
6563
6687
|
}
|
|
6564
6688
|
if (err instanceof CliError) {
|
|
6565
|
-
|
|
6689
|
+
consola44.error(err.message);
|
|
6566
6690
|
process.exit(err.exitCode);
|
|
6567
6691
|
}
|
|
6568
6692
|
if (debug) {
|
|
6569
|
-
|
|
6693
|
+
consola44.error(err);
|
|
6570
6694
|
} else {
|
|
6571
|
-
|
|
6695
|
+
consola44.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
6572
6696
|
}
|
|
6573
6697
|
process.exit(1);
|
|
6574
6698
|
});
|