@openape/apes 1.3.1 → 1.5.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 consola43 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 defineCommand53, 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: readFileSync14 } = 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 = readFileSync14(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);
|
|
@@ -3917,16 +3917,365 @@ var agentsCommand = defineCommand28({
|
|
|
3917
3917
|
}
|
|
3918
3918
|
});
|
|
3919
3919
|
|
|
3920
|
-
// src/commands/
|
|
3920
|
+
// src/commands/nest/index.ts
|
|
3921
|
+
import { defineCommand as defineCommand33 } from "citty";
|
|
3922
|
+
|
|
3923
|
+
// src/commands/nest/authorize.ts
|
|
3924
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
3921
3925
|
import { defineCommand as defineCommand29 } from "citty";
|
|
3922
3926
|
import consola25 from "consola";
|
|
3923
|
-
var
|
|
3927
|
+
var authorizeNestCommand = defineCommand29({
|
|
3928
|
+
meta: {
|
|
3929
|
+
name: "authorize",
|
|
3930
|
+
description: "Request the always-capability-grant the nest needs for zero-prompt spawn/destroy"
|
|
3931
|
+
},
|
|
3932
|
+
args: {
|
|
3933
|
+
"reason": {
|
|
3934
|
+
type: "string",
|
|
3935
|
+
description: "Reason shown in the DDISA approval UI"
|
|
3936
|
+
},
|
|
3937
|
+
"wait": {
|
|
3938
|
+
type: "boolean",
|
|
3939
|
+
description: "Block until the grant is approved (default: print URL + exit 0)"
|
|
3940
|
+
}
|
|
3941
|
+
},
|
|
3942
|
+
async run({ args }) {
|
|
3943
|
+
const reason = args.reason ?? "nest-managed agent lifecycle (spawn / destroy / sync) \u2014 approve as Always";
|
|
3944
|
+
consola25.info("Requesting capability-grant for `apes-agents` (selector name=* covers all agent names)...");
|
|
3945
|
+
consola25.info("");
|
|
3946
|
+
consola25.info("When the IdP approval page opens, choose **Always** so the nest can re-use the grant on every spawn.");
|
|
3947
|
+
consola25.info("");
|
|
3948
|
+
const cmdArgs = [
|
|
3949
|
+
"grants",
|
|
3950
|
+
"request-capability",
|
|
3951
|
+
"apes-agents",
|
|
3952
|
+
"--resource",
|
|
3953
|
+
"agents:*",
|
|
3954
|
+
"--selector",
|
|
3955
|
+
"name=*",
|
|
3956
|
+
"--action",
|
|
3957
|
+
"create",
|
|
3958
|
+
"--action",
|
|
3959
|
+
"delete",
|
|
3960
|
+
"--action",
|
|
3961
|
+
"edit",
|
|
3962
|
+
"--action",
|
|
3963
|
+
"list",
|
|
3964
|
+
"--approval=always",
|
|
3965
|
+
"--reason",
|
|
3966
|
+
reason,
|
|
3967
|
+
"--run-as",
|
|
3968
|
+
"root"
|
|
3969
|
+
];
|
|
3970
|
+
if (args.wait) cmdArgs.push("--wait");
|
|
3971
|
+
try {
|
|
3972
|
+
execFileSync9("apes", cmdArgs, { stdio: "inherit" });
|
|
3973
|
+
} catch (err) {
|
|
3974
|
+
throw new Error(err instanceof Error ? err.message : String(err));
|
|
3975
|
+
}
|
|
3976
|
+
}
|
|
3977
|
+
});
|
|
3978
|
+
|
|
3979
|
+
// src/commands/nest/install.ts
|
|
3980
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
3981
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
3982
|
+
import { homedir as homedir10, userInfo as userInfo2 } from "os";
|
|
3983
|
+
import { dirname as dirname3, join as join8 } from "path";
|
|
3984
|
+
import { defineCommand as defineCommand30 } from "citty";
|
|
3985
|
+
import consola26 from "consola";
|
|
3986
|
+
|
|
3987
|
+
// src/commands/nest/apes-agents-adapter.ts
|
|
3988
|
+
var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
|
|
3989
|
+
|
|
3990
|
+
# Adapter for the \`apes agents\` subtree \u2014 written by \`apes nest install\`.
|
|
3991
|
+
# A capability-grant with selector \`name=*\` covers any agent name
|
|
3992
|
+
# (selectorValueMatches treats '*' as a glob), letting the nest spawn
|
|
3993
|
+
# and destroy without per-agent DDISA prompts.
|
|
3994
|
+
|
|
3995
|
+
[cli]
|
|
3996
|
+
id = "apes-agents"
|
|
3997
|
+
executable = "apes"
|
|
3998
|
+
audience = "shapes"
|
|
3999
|
+
version = "1"
|
|
4000
|
+
|
|
4001
|
+
# \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
4002
|
+
# AGENT LIFECYCLE \u2014 spawn / destroy / sync
|
|
4003
|
+
# \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
4004
|
+
|
|
4005
|
+
[[operation]]
|
|
4006
|
+
id = "agents.spawn"
|
|
4007
|
+
command = ["agents", "spawn"]
|
|
4008
|
+
positionals = ["name"]
|
|
4009
|
+
display = "Spawn agent {name}"
|
|
4010
|
+
action = "create"
|
|
4011
|
+
risk = "high"
|
|
4012
|
+
resource_chain = ["agents:name={name}"]
|
|
4013
|
+
|
|
4014
|
+
[[operation]]
|
|
4015
|
+
id = "agents.destroy"
|
|
4016
|
+
command = ["agents", "destroy"]
|
|
4017
|
+
positionals = ["name"]
|
|
4018
|
+
display = "Destroy agent {name}"
|
|
4019
|
+
action = "delete"
|
|
4020
|
+
risk = "critical"
|
|
4021
|
+
resource_chain = ["agents:name={name}"]
|
|
4022
|
+
|
|
4023
|
+
[[operation]]
|
|
4024
|
+
id = "agents.sync"
|
|
4025
|
+
command = ["agents", "sync"]
|
|
4026
|
+
display = "Sync agent state with troop"
|
|
4027
|
+
action = "edit"
|
|
4028
|
+
risk = "low"
|
|
4029
|
+
resource_chain = ["agents:*"]
|
|
4030
|
+
|
|
4031
|
+
[[operation]]
|
|
4032
|
+
id = "agents.list"
|
|
4033
|
+
command = ["agents", "list"]
|
|
4034
|
+
display = "List agents"
|
|
4035
|
+
action = "list"
|
|
4036
|
+
risk = "low"
|
|
4037
|
+
resource_chain = ["agents:*"]
|
|
4038
|
+
|
|
4039
|
+
[[operation]]
|
|
4040
|
+
id = "agents.allow"
|
|
4041
|
+
command = ["agents", "allow"]
|
|
4042
|
+
positionals = ["name", "peer_email"]
|
|
4043
|
+
display = "Allow agent {name} to accept contact requests from {peer_email}"
|
|
4044
|
+
action = "edit"
|
|
4045
|
+
risk = "medium"
|
|
4046
|
+
resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
|
|
4047
|
+
`;
|
|
4048
|
+
|
|
4049
|
+
// src/commands/nest/install.ts
|
|
4050
|
+
var PLIST_LABEL = "ai.openape.nest";
|
|
4051
|
+
function plistPath() {
|
|
4052
|
+
return join8(homedir10(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
4053
|
+
}
|
|
4054
|
+
function escape2(s) {
|
|
4055
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
4056
|
+
}
|
|
4057
|
+
function buildPlist(args) {
|
|
4058
|
+
const logsDir = join8(args.homeDir, "Library", "Logs");
|
|
4059
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
4060
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
4061
|
+
<plist version="1.0">
|
|
4062
|
+
<dict>
|
|
4063
|
+
<key>Label</key>
|
|
4064
|
+
<string>${escape2(PLIST_LABEL)}</string>
|
|
4065
|
+
<key>ProgramArguments</key>
|
|
4066
|
+
<array>
|
|
4067
|
+
<string>${escape2(args.nestBin)}</string>
|
|
4068
|
+
</array>
|
|
4069
|
+
<key>WorkingDirectory</key>
|
|
4070
|
+
<string>${escape2(args.homeDir)}</string>
|
|
4071
|
+
<key>RunAtLoad</key>
|
|
4072
|
+
<true/>
|
|
4073
|
+
<key>KeepAlive</key>
|
|
4074
|
+
<true/>
|
|
4075
|
+
<key>ThrottleInterval</key>
|
|
4076
|
+
<integer>10</integer>
|
|
4077
|
+
<key>EnvironmentVariables</key>
|
|
4078
|
+
<dict>
|
|
4079
|
+
<key>HOME</key><string>${escape2(args.homeDir)}</string>
|
|
4080
|
+
<key>PATH</key><string>${escape2(args.homeDir)}/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
4081
|
+
<key>OPENAPE_NEST_PORT</key><string>${args.port}</string>
|
|
4082
|
+
<key>OPENAPE_APES_BIN</key><string>${escape2(args.apesBin)}</string>
|
|
4083
|
+
</dict>
|
|
4084
|
+
<key>StandardOutPath</key>
|
|
4085
|
+
<string>${escape2(logsDir)}/openape-nest.log</string>
|
|
4086
|
+
<key>StandardErrorPath</key>
|
|
4087
|
+
<string>${escape2(logsDir)}/openape-nest.log</string>
|
|
4088
|
+
</dict>
|
|
4089
|
+
</plist>
|
|
4090
|
+
`;
|
|
4091
|
+
}
|
|
4092
|
+
function installAdapter2() {
|
|
4093
|
+
const target = join8(homedir10(), ".openape", "shapes", "adapters", "apes-agents.toml");
|
|
4094
|
+
mkdirSync4(dirname3(target), { recursive: true });
|
|
4095
|
+
let existing = "";
|
|
4096
|
+
try {
|
|
4097
|
+
existing = readFileSync10(target, "utf8");
|
|
4098
|
+
} catch {
|
|
4099
|
+
}
|
|
4100
|
+
if (existing === APES_AGENTS_ADAPTER_TOML) return false;
|
|
4101
|
+
writeFileSync6(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
|
|
4102
|
+
consola26.success(`Wrote shapes adapter ${target}`);
|
|
4103
|
+
return true;
|
|
4104
|
+
}
|
|
4105
|
+
function findBinary(name) {
|
|
4106
|
+
for (const dir of [
|
|
4107
|
+
join8(homedir10(), ".bun", "bin"),
|
|
4108
|
+
"/opt/homebrew/bin",
|
|
4109
|
+
"/usr/local/bin",
|
|
4110
|
+
"/usr/bin"
|
|
4111
|
+
]) {
|
|
4112
|
+
const p = join8(dir, name);
|
|
4113
|
+
if (existsSync10(p)) return p;
|
|
4114
|
+
}
|
|
4115
|
+
throw new Error(`could not locate ${name} on PATH; install it first`);
|
|
4116
|
+
}
|
|
4117
|
+
var installNestCommand = defineCommand30({
|
|
4118
|
+
meta: {
|
|
4119
|
+
name: "install",
|
|
4120
|
+
description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
|
|
4121
|
+
},
|
|
4122
|
+
args: {
|
|
4123
|
+
port: {
|
|
4124
|
+
type: "string",
|
|
4125
|
+
description: "Port for the nest HTTP API (default: 9091)"
|
|
4126
|
+
}
|
|
4127
|
+
},
|
|
4128
|
+
async run({ args }) {
|
|
4129
|
+
const homeDir = homedir10();
|
|
4130
|
+
const port = Number(args.port ?? 9091);
|
|
4131
|
+
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
4132
|
+
throw new Error(`invalid port ${port}`);
|
|
4133
|
+
}
|
|
4134
|
+
const nestBin = findBinary("openape-nest");
|
|
4135
|
+
const apesBin = findBinary("apes");
|
|
4136
|
+
consola26.info(`Installing nest at ${plistPath()}`);
|
|
4137
|
+
consola26.info(` nest binary: ${nestBin}`);
|
|
4138
|
+
consola26.info(` apes binary: ${apesBin}`);
|
|
4139
|
+
consola26.info(` HTTP port: ${port}`);
|
|
4140
|
+
installAdapter2();
|
|
4141
|
+
mkdirSync4(join8(homeDir, "Library", "LaunchAgents"), { recursive: true });
|
|
4142
|
+
const desired = buildPlist({ nestBin, apesBin, homeDir, port });
|
|
4143
|
+
let existing = "";
|
|
4144
|
+
try {
|
|
4145
|
+
existing = readFileSync10(plistPath(), "utf8");
|
|
4146
|
+
} catch {
|
|
4147
|
+
}
|
|
4148
|
+
if (existing !== desired) {
|
|
4149
|
+
writeFileSync6(plistPath(), desired, { mode: 420 });
|
|
4150
|
+
consola26.success("Wrote launchd plist");
|
|
4151
|
+
} else {
|
|
4152
|
+
consola26.info("plist already up to date");
|
|
4153
|
+
}
|
|
4154
|
+
const uid = userInfo2().uid;
|
|
4155
|
+
try {
|
|
4156
|
+
execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
|
|
4157
|
+
} catch {
|
|
4158
|
+
}
|
|
4159
|
+
execFileSync10("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
|
|
4160
|
+
consola26.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
|
|
4161
|
+
consola26.info("");
|
|
4162
|
+
consola26.info("Next: request the capability-grant that lets the nest spawn/destroy any agent without per-call approval:");
|
|
4163
|
+
consola26.info("");
|
|
4164
|
+
consola26.info(" apes nest authorize");
|
|
4165
|
+
consola26.info("");
|
|
4166
|
+
consola26.info("That requests an `apes-agents` capability-grant covering all agent names (selector glob `name=*`)");
|
|
4167
|
+
consola26.info("from your DDISA inbox; approve it once as `always` and the nest API runs silently from then on.");
|
|
4168
|
+
}
|
|
4169
|
+
});
|
|
4170
|
+
|
|
4171
|
+
// src/commands/nest/status.ts
|
|
4172
|
+
import process2 from "process";
|
|
4173
|
+
import { defineCommand as defineCommand31 } from "citty";
|
|
4174
|
+
import consola27 from "consola";
|
|
4175
|
+
var DEFAULT_PORT = 9091;
|
|
4176
|
+
var statusNestCommand = defineCommand31({
|
|
4177
|
+
meta: {
|
|
4178
|
+
name: "status",
|
|
4179
|
+
description: "Print state of the local nest-daemon (agents registered, processes supervised)"
|
|
4180
|
+
},
|
|
4181
|
+
args: {
|
|
4182
|
+
port: { type: "string", description: "Override nest port (default: 9091)" },
|
|
4183
|
+
json: { type: "boolean", description: "JSON output for scripts" }
|
|
4184
|
+
},
|
|
4185
|
+
async run({ args }) {
|
|
4186
|
+
const port = Number(args.port ?? process2.env.OPENAPE_NEST_PORT ?? DEFAULT_PORT);
|
|
4187
|
+
const url = `http://127.0.0.1:${port}/status`;
|
|
4188
|
+
let status;
|
|
4189
|
+
try {
|
|
4190
|
+
const res = await fetch(url);
|
|
4191
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
4192
|
+
status = await res.json();
|
|
4193
|
+
} catch (err) {
|
|
4194
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4195
|
+
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
4196
|
+
consola27.error(`Nest daemon is not running at http://127.0.0.1:${port}`);
|
|
4197
|
+
consola27.info(" Run: apes nest install");
|
|
4198
|
+
process2.exit(2);
|
|
4199
|
+
}
|
|
4200
|
+
throw err;
|
|
4201
|
+
}
|
|
4202
|
+
if (args.json) {
|
|
4203
|
+
console.log(JSON.stringify(status, null, 2));
|
|
4204
|
+
return;
|
|
4205
|
+
}
|
|
4206
|
+
consola27.info(`Nest at http://127.0.0.1:${port} \u2014 ${status.agents} agent(s) registered, ${status.processes.length} supervised`);
|
|
4207
|
+
if (status.processes.length === 0) {
|
|
4208
|
+
consola27.info(" (no processes running)");
|
|
4209
|
+
return;
|
|
4210
|
+
}
|
|
4211
|
+
for (const p of status.processes) {
|
|
4212
|
+
const uptime = humanDuration(p.uptimeSec);
|
|
4213
|
+
const crashTag = p.consecutiveCrashes > 0 ? ` \u26A0 ${p.consecutiveCrashes} crash(es)` : "";
|
|
4214
|
+
consola27.info(` ${p.name.padEnd(16)} pid=${String(p.pid).padEnd(6)} up=${uptime}${crashTag}`);
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
});
|
|
4218
|
+
function humanDuration(sec) {
|
|
4219
|
+
if (sec < 60) return `${sec}s`;
|
|
4220
|
+
if (sec < 3600) return `${Math.floor(sec / 60)}m`;
|
|
4221
|
+
if (sec < 86400) return `${Math.floor(sec / 3600)}h`;
|
|
4222
|
+
return `${Math.floor(sec / 86400)}d`;
|
|
4223
|
+
}
|
|
4224
|
+
|
|
4225
|
+
// src/commands/nest/uninstall.ts
|
|
4226
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
4227
|
+
import { existsSync as existsSync11, unlinkSync } from "fs";
|
|
4228
|
+
import { homedir as homedir11, userInfo as userInfo3 } from "os";
|
|
4229
|
+
import { join as join9 } from "path";
|
|
4230
|
+
import { defineCommand as defineCommand32 } from "citty";
|
|
4231
|
+
import consola28 from "consola";
|
|
4232
|
+
var PLIST_LABEL2 = "ai.openape.nest";
|
|
4233
|
+
var uninstallNestCommand = defineCommand32({
|
|
4234
|
+
meta: {
|
|
4235
|
+
name: "uninstall",
|
|
4236
|
+
description: "Stop + remove the local nest-daemon (registry + agents preserved)"
|
|
4237
|
+
},
|
|
4238
|
+
async run() {
|
|
4239
|
+
const uid = userInfo3().uid;
|
|
4240
|
+
const path2 = join9(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
|
|
4241
|
+
try {
|
|
4242
|
+
execFileSync11("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
|
|
4243
|
+
consola28.success("Nest daemon stopped");
|
|
4244
|
+
} catch {
|
|
4245
|
+
consola28.info("Nest daemon was not loaded");
|
|
4246
|
+
}
|
|
4247
|
+
if (existsSync11(path2)) {
|
|
4248
|
+
unlinkSync(path2);
|
|
4249
|
+
consola28.success(`Removed ${path2}`);
|
|
4250
|
+
}
|
|
4251
|
+
consola28.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
|
|
4252
|
+
}
|
|
4253
|
+
});
|
|
4254
|
+
|
|
4255
|
+
// src/commands/nest/index.ts
|
|
4256
|
+
var nestCommand = defineCommand33({
|
|
4257
|
+
meta: {
|
|
4258
|
+
name: "nest",
|
|
4259
|
+
description: "Manage the local Nest control-plane daemon (install, authorize, status, uninstall). The Nest hosts agents on this computer \u2014 once installed + authorized, `apes agents spawn` is fast (no per-spawn DDISA approvals) and per-agent launchd plists are replaced by a single supervised process tree."
|
|
4260
|
+
},
|
|
4261
|
+
subCommands: {
|
|
4262
|
+
install: installNestCommand,
|
|
4263
|
+
authorize: authorizeNestCommand,
|
|
4264
|
+
status: statusNestCommand,
|
|
4265
|
+
uninstall: uninstallNestCommand
|
|
4266
|
+
}
|
|
4267
|
+
});
|
|
4268
|
+
|
|
4269
|
+
// src/commands/adapter/index.ts
|
|
4270
|
+
import { defineCommand as defineCommand34 } from "citty";
|
|
4271
|
+
import consola29 from "consola";
|
|
4272
|
+
var adapterCommand = defineCommand34({
|
|
3924
4273
|
meta: {
|
|
3925
4274
|
name: "adapter",
|
|
3926
4275
|
description: "Manage CLI adapters"
|
|
3927
4276
|
},
|
|
3928
4277
|
subCommands: {
|
|
3929
|
-
list:
|
|
4278
|
+
list: defineCommand34({
|
|
3930
4279
|
meta: {
|
|
3931
4280
|
name: "list",
|
|
3932
4281
|
description: "List available adapters"
|
|
@@ -3957,7 +4306,7 @@ var adapterCommand = defineCommand29({
|
|
|
3957
4306
|
`);
|
|
3958
4307
|
return;
|
|
3959
4308
|
}
|
|
3960
|
-
|
|
4309
|
+
consola29.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
3961
4310
|
for (const a of index2.adapters) {
|
|
3962
4311
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
3963
4312
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -3979,7 +4328,7 @@ var adapterCommand = defineCommand29({
|
|
|
3979
4328
|
return;
|
|
3980
4329
|
}
|
|
3981
4330
|
if (local.length === 0) {
|
|
3982
|
-
|
|
4331
|
+
consola29.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
3983
4332
|
return;
|
|
3984
4333
|
}
|
|
3985
4334
|
for (const a of local) {
|
|
@@ -3987,7 +4336,7 @@ var adapterCommand = defineCommand29({
|
|
|
3987
4336
|
}
|
|
3988
4337
|
}
|
|
3989
4338
|
}),
|
|
3990
|
-
install:
|
|
4339
|
+
install: defineCommand34({
|
|
3991
4340
|
meta: {
|
|
3992
4341
|
name: "install",
|
|
3993
4342
|
description: "Install an adapter from the registry"
|
|
@@ -4016,24 +4365,24 @@ var adapterCommand = defineCommand29({
|
|
|
4016
4365
|
for (const id of ids) {
|
|
4017
4366
|
const entry = findAdapter(index, id);
|
|
4018
4367
|
if (!entry) {
|
|
4019
|
-
|
|
4368
|
+
consola29.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
4020
4369
|
continue;
|
|
4021
4370
|
}
|
|
4022
4371
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
4023
4372
|
if (conflicts.length > 0) {
|
|
4024
4373
|
for (const c of conflicts) {
|
|
4025
|
-
|
|
4026
|
-
|
|
4374
|
+
consola29.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
|
|
4375
|
+
consola29.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
|
|
4027
4376
|
}
|
|
4028
4377
|
}
|
|
4029
4378
|
const result = await installAdapter(entry, { local });
|
|
4030
4379
|
const verb = result.updated ? "Updated" : "Installed";
|
|
4031
|
-
|
|
4032
|
-
|
|
4380
|
+
consola29.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
4381
|
+
consola29.info(`Digest: ${result.digest}`);
|
|
4033
4382
|
}
|
|
4034
4383
|
}
|
|
4035
4384
|
}),
|
|
4036
|
-
remove:
|
|
4385
|
+
remove: defineCommand34({
|
|
4037
4386
|
meta: {
|
|
4038
4387
|
name: "remove",
|
|
4039
4388
|
description: "Remove an installed adapter"
|
|
@@ -4056,9 +4405,9 @@ var adapterCommand = defineCommand29({
|
|
|
4056
4405
|
let failed = false;
|
|
4057
4406
|
for (const id of ids) {
|
|
4058
4407
|
if (removeAdapter(id, local)) {
|
|
4059
|
-
|
|
4408
|
+
consola29.success(`Removed adapter: ${id}`);
|
|
4060
4409
|
} else {
|
|
4061
|
-
|
|
4410
|
+
consola29.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4062
4411
|
failed = true;
|
|
4063
4412
|
}
|
|
4064
4413
|
}
|
|
@@ -4066,7 +4415,7 @@ var adapterCommand = defineCommand29({
|
|
|
4066
4415
|
throw new CliError("Some adapters could not be removed");
|
|
4067
4416
|
}
|
|
4068
4417
|
}),
|
|
4069
|
-
info:
|
|
4418
|
+
info: defineCommand34({
|
|
4070
4419
|
meta: {
|
|
4071
4420
|
name: "info",
|
|
4072
4421
|
description: "Show detailed adapter information"
|
|
@@ -4108,7 +4457,7 @@ var adapterCommand = defineCommand29({
|
|
|
4108
4457
|
}
|
|
4109
4458
|
}
|
|
4110
4459
|
}),
|
|
4111
|
-
search:
|
|
4460
|
+
search: defineCommand34({
|
|
4112
4461
|
meta: {
|
|
4113
4462
|
name: "search",
|
|
4114
4463
|
description: "Search adapters in the registry"
|
|
@@ -4140,7 +4489,7 @@ var adapterCommand = defineCommand29({
|
|
|
4140
4489
|
return;
|
|
4141
4490
|
}
|
|
4142
4491
|
if (results.length === 0) {
|
|
4143
|
-
|
|
4492
|
+
consola29.info(`No adapters matching "${query}"`);
|
|
4144
4493
|
return;
|
|
4145
4494
|
}
|
|
4146
4495
|
for (const a of results) {
|
|
@@ -4149,7 +4498,7 @@ var adapterCommand = defineCommand29({
|
|
|
4149
4498
|
}
|
|
4150
4499
|
}
|
|
4151
4500
|
}),
|
|
4152
|
-
update:
|
|
4501
|
+
update: defineCommand34({
|
|
4153
4502
|
meta: {
|
|
4154
4503
|
name: "update",
|
|
4155
4504
|
description: "Update installed adapters"
|
|
@@ -4175,33 +4524,33 @@ var adapterCommand = defineCommand29({
|
|
|
4175
4524
|
const targetId = args.id ? String(args.id) : void 0;
|
|
4176
4525
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
4177
4526
|
if (targets.length === 0) {
|
|
4178
|
-
|
|
4527
|
+
consola29.info("No adapters installed to update.");
|
|
4179
4528
|
return;
|
|
4180
4529
|
}
|
|
4181
4530
|
for (const id of targets) {
|
|
4182
4531
|
const entry = findAdapter(index, id);
|
|
4183
4532
|
if (!entry) {
|
|
4184
|
-
|
|
4533
|
+
consola29.warn(`${id}: not found in registry, skipping`);
|
|
4185
4534
|
continue;
|
|
4186
4535
|
}
|
|
4187
4536
|
const localDigest = getInstalledDigest(id, false);
|
|
4188
4537
|
if (localDigest === entry.digest) {
|
|
4189
|
-
|
|
4538
|
+
consola29.info(`${id}: already up to date`);
|
|
4190
4539
|
continue;
|
|
4191
4540
|
}
|
|
4192
4541
|
if (localDigest && !args.yes) {
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4542
|
+
consola29.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
|
|
4543
|
+
consola29.info(` Old: ${localDigest}`);
|
|
4544
|
+
consola29.info(` New: ${entry.digest}`);
|
|
4545
|
+
consola29.info(" Use --yes to confirm");
|
|
4197
4546
|
continue;
|
|
4198
4547
|
}
|
|
4199
4548
|
const result = await installAdapter(entry);
|
|
4200
|
-
|
|
4549
|
+
consola29.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
4201
4550
|
}
|
|
4202
4551
|
}
|
|
4203
4552
|
}),
|
|
4204
|
-
verify:
|
|
4553
|
+
verify: defineCommand34({
|
|
4205
4554
|
meta: {
|
|
4206
4555
|
name: "verify",
|
|
4207
4556
|
description: "Verify installed adapter against registry digest"
|
|
@@ -4234,7 +4583,7 @@ var adapterCommand = defineCommand29({
|
|
|
4234
4583
|
if (!localDigest)
|
|
4235
4584
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4236
4585
|
if (localDigest === entry.digest) {
|
|
4237
|
-
|
|
4586
|
+
consola29.success(`${id}: digest matches registry`);
|
|
4238
4587
|
} else {
|
|
4239
4588
|
console.log(` Local: ${localDigest}`);
|
|
4240
4589
|
console.log(` Registry: ${entry.digest}`);
|
|
@@ -4246,11 +4595,11 @@ var adapterCommand = defineCommand29({
|
|
|
4246
4595
|
});
|
|
4247
4596
|
|
|
4248
4597
|
// src/commands/run.ts
|
|
4249
|
-
import { execFileSync as
|
|
4598
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
4250
4599
|
import { hostname as hostname4 } from "os";
|
|
4251
4600
|
import { basename } from "path";
|
|
4252
|
-
import { defineCommand as
|
|
4253
|
-
import
|
|
4601
|
+
import { defineCommand as defineCommand35 } from "citty";
|
|
4602
|
+
import consola30 from "consola";
|
|
4254
4603
|
function shouldWaitForGrant(args) {
|
|
4255
4604
|
return args.wait === true || process.env.APE_WAIT === "1";
|
|
4256
4605
|
}
|
|
@@ -4287,7 +4636,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4287
4636
|
const statusCmd = `apes grants status ${grant.id}`;
|
|
4288
4637
|
const executeCmd = `apes grants run ${grant.id}`;
|
|
4289
4638
|
if (mode === "human") {
|
|
4290
|
-
|
|
4639
|
+
consola30.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
|
|
4291
4640
|
console.log(` Approve in browser: ${approveUrl}`);
|
|
4292
4641
|
console.log(` Check status: ${statusCmd}`);
|
|
4293
4642
|
console.log(` Run after approval: ${executeCmd}`);
|
|
@@ -4297,7 +4646,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4297
4646
|
return;
|
|
4298
4647
|
}
|
|
4299
4648
|
const maxMin = getPollMaxMinutes();
|
|
4300
|
-
|
|
4649
|
+
consola30.success(`Grant ${grant.id} created (pending approval)`);
|
|
4301
4650
|
console.log(` Approve: ${approveUrl}`);
|
|
4302
4651
|
console.log(` Status: ${statusCmd} [--json]`);
|
|
4303
4652
|
console.log(` Execute: ${executeCmd} --wait`);
|
|
@@ -4319,7 +4668,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4319
4668
|
console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
|
|
4320
4669
|
console.log(" grant be reused on subsequent invocations without re-approval.");
|
|
4321
4670
|
}
|
|
4322
|
-
var runCommand =
|
|
4671
|
+
var runCommand = defineCommand35({
|
|
4323
4672
|
meta: {
|
|
4324
4673
|
name: "run",
|
|
4325
4674
|
description: "Execute a grant-secured command"
|
|
@@ -4422,7 +4771,7 @@ async function runShellMode(command, args) {
|
|
|
4422
4771
|
}
|
|
4423
4772
|
} catch {
|
|
4424
4773
|
}
|
|
4425
|
-
|
|
4774
|
+
consola30.info(`Requesting ape-shell session grant on ${targetHost}`);
|
|
4426
4775
|
const grant = await apiFetch(grantsUrl, {
|
|
4427
4776
|
method: "POST",
|
|
4428
4777
|
body: {
|
|
@@ -4442,8 +4791,8 @@ async function runShellMode(command, args) {
|
|
|
4442
4791
|
host: targetHost
|
|
4443
4792
|
});
|
|
4444
4793
|
if (shouldWaitForGrant(args)) {
|
|
4445
|
-
|
|
4446
|
-
|
|
4794
|
+
consola30.info(`Grant requested: ${grant.id}`);
|
|
4795
|
+
consola30.info("Waiting for approval...");
|
|
4447
4796
|
const maxWait = 3e5;
|
|
4448
4797
|
const interval = 3e3;
|
|
4449
4798
|
const start = Date.now();
|
|
@@ -4474,13 +4823,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4474
4823
|
try {
|
|
4475
4824
|
resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
|
|
4476
4825
|
} catch (err) {
|
|
4477
|
-
|
|
4826
|
+
consola30.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
|
|
4478
4827
|
return false;
|
|
4479
4828
|
}
|
|
4480
4829
|
try {
|
|
4481
4830
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
4482
4831
|
if (existingGrantId) {
|
|
4483
|
-
|
|
4832
|
+
consola30.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
|
|
4484
4833
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
4485
4834
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
4486
4835
|
return true;
|
|
@@ -4488,7 +4837,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4488
4837
|
} catch {
|
|
4489
4838
|
}
|
|
4490
4839
|
const approval = args.approval ?? "once";
|
|
4491
|
-
|
|
4840
|
+
consola30.info(`Requesting grant for: ${resolved.detail.display}`);
|
|
4492
4841
|
const grant = await createShapesGrant(resolved, {
|
|
4493
4842
|
idp,
|
|
4494
4843
|
approval,
|
|
@@ -4496,8 +4845,8 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4496
4845
|
});
|
|
4497
4846
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
4498
4847
|
const n = grant.similar_grants.similar_grants.length;
|
|
4499
|
-
|
|
4500
|
-
|
|
4848
|
+
consola30.info("");
|
|
4849
|
+
consola30.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
4501
4850
|
}
|
|
4502
4851
|
notifyGrantPending({
|
|
4503
4852
|
grantId: grant.id,
|
|
@@ -4507,8 +4856,8 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4507
4856
|
host: args.host || hostname4()
|
|
4508
4857
|
});
|
|
4509
4858
|
if (shouldWaitForGrant(args)) {
|
|
4510
|
-
|
|
4511
|
-
|
|
4859
|
+
consola30.info(`Grant requested: ${grant.id}`);
|
|
4860
|
+
consola30.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4512
4861
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
4513
4862
|
if (status !== "approved")
|
|
4514
4863
|
throw new CliError(`Grant ${status}`);
|
|
@@ -4524,7 +4873,7 @@ function execShellCommand(command) {
|
|
|
4524
4873
|
throw new CliError("No command to execute");
|
|
4525
4874
|
try {
|
|
4526
4875
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
4527
|
-
|
|
4876
|
+
execFileSync12(command[0], command.slice(1), {
|
|
4528
4877
|
stdio: "inherit",
|
|
4529
4878
|
env: inheritedEnv
|
|
4530
4879
|
});
|
|
@@ -4582,7 +4931,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
4582
4931
|
try {
|
|
4583
4932
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
4584
4933
|
if (existingGrantId) {
|
|
4585
|
-
|
|
4934
|
+
consola30.info(`Reusing existing grant: ${existingGrantId}`);
|
|
4586
4935
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
4587
4936
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
4588
4937
|
return;
|
|
@@ -4596,17 +4945,17 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
4596
4945
|
});
|
|
4597
4946
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
4598
4947
|
const n = grant.similar_grants.similar_grants.length;
|
|
4599
|
-
|
|
4600
|
-
|
|
4948
|
+
consola30.info("");
|
|
4949
|
+
consola30.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
4601
4950
|
if (grant.similar_grants.widened_details?.length) {
|
|
4602
4951
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
4603
|
-
|
|
4952
|
+
consola30.info(` Broader scope: ${wider}`);
|
|
4604
4953
|
}
|
|
4605
|
-
|
|
4954
|
+
consola30.info("");
|
|
4606
4955
|
}
|
|
4607
4956
|
if (shouldWaitForGrant(args)) {
|
|
4608
|
-
|
|
4609
|
-
|
|
4957
|
+
consola30.info(`Grant requested: ${grant.id}`);
|
|
4958
|
+
consola30.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4610
4959
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
4611
4960
|
if (status !== "approved")
|
|
4612
4961
|
throw new Error(`Grant ${status}`);
|
|
@@ -4626,7 +4975,7 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4626
4975
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
4627
4976
|
const command = action.split(" ");
|
|
4628
4977
|
const targetHost = args.host || hostname4();
|
|
4629
|
-
|
|
4978
|
+
consola30.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
4630
4979
|
const grant = await apiFetch(grantsUrl, {
|
|
4631
4980
|
method: "POST",
|
|
4632
4981
|
body: {
|
|
@@ -4643,9 +4992,9 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4643
4992
|
printPendingGrantInfo(grant, idp);
|
|
4644
4993
|
throw new CliExit(getAsyncExitCode());
|
|
4645
4994
|
}
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4995
|
+
consola30.success(`Grant requested: ${grant.id}`);
|
|
4996
|
+
consola30.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4997
|
+
consola30.info("Waiting for approval...");
|
|
4649
4998
|
const maxWait = 15 * 60 * 1e3;
|
|
4650
4999
|
const interval = 3e3;
|
|
4651
5000
|
const start = Date.now();
|
|
@@ -4653,7 +5002,7 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4653
5002
|
while (Date.now() - start < maxWait) {
|
|
4654
5003
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
4655
5004
|
if (status.status === "approved") {
|
|
4656
|
-
|
|
5005
|
+
consola30.success("Grant approved!");
|
|
4657
5006
|
approved = true;
|
|
4658
5007
|
break;
|
|
4659
5008
|
}
|
|
@@ -4668,15 +5017,15 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4668
5017
|
`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.`
|
|
4669
5018
|
);
|
|
4670
5019
|
}
|
|
4671
|
-
|
|
5020
|
+
consola30.info("Fetching grant token...");
|
|
4672
5021
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
4673
5022
|
method: "POST"
|
|
4674
5023
|
});
|
|
4675
5024
|
if (audience === "escapes") {
|
|
4676
|
-
|
|
5025
|
+
consola30.info(`Executing: ${command.join(" ")}`);
|
|
4677
5026
|
try {
|
|
4678
5027
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
4679
|
-
|
|
5028
|
+
execFileSync12(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
|
|
4680
5029
|
stdio: "inherit",
|
|
4681
5030
|
env: inheritedEnv
|
|
4682
5031
|
});
|
|
@@ -4691,8 +5040,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4691
5040
|
|
|
4692
5041
|
// src/commands/proxy.ts
|
|
4693
5042
|
import { spawn as spawn2 } from "child_process";
|
|
4694
|
-
import { defineCommand as
|
|
4695
|
-
import
|
|
5043
|
+
import { defineCommand as defineCommand36 } from "citty";
|
|
5044
|
+
import consola31 from "consola";
|
|
4696
5045
|
|
|
4697
5046
|
// src/proxy/config.ts
|
|
4698
5047
|
function buildDefaultProxyConfigToml(opts) {
|
|
@@ -4726,10 +5075,10 @@ note = "VPC-internal hostname suffix"
|
|
|
4726
5075
|
|
|
4727
5076
|
// src/proxy/local-proxy.ts
|
|
4728
5077
|
import { spawn } from "child_process";
|
|
4729
|
-
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as
|
|
5078
|
+
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "fs";
|
|
4730
5079
|
import { createRequire } from "module";
|
|
4731
5080
|
import { tmpdir as tmpdir3 } from "os";
|
|
4732
|
-
import { dirname as
|
|
5081
|
+
import { dirname as dirname4, join as join10, resolve as resolve4 } from "path";
|
|
4733
5082
|
var require2 = createRequire(import.meta.url);
|
|
4734
5083
|
function findProxyBin() {
|
|
4735
5084
|
const pkgPath = require2.resolve("@openape/proxy/package.json");
|
|
@@ -4738,12 +5087,12 @@ function findProxyBin() {
|
|
|
4738
5087
|
if (!binRel) {
|
|
4739
5088
|
throw new Error("@openape/proxy is missing the openape-proxy bin entry");
|
|
4740
5089
|
}
|
|
4741
|
-
return resolve4(
|
|
5090
|
+
return resolve4(dirname4(pkgPath), binRel);
|
|
4742
5091
|
}
|
|
4743
5092
|
async function startEphemeralProxy(configToml) {
|
|
4744
|
-
const tmpDir = mkdtempSync3(
|
|
4745
|
-
const configPath =
|
|
4746
|
-
|
|
5093
|
+
const tmpDir = mkdtempSync3(join10(tmpdir3(), "openape-proxy-"));
|
|
5094
|
+
const configPath = join10(tmpDir, "config.toml");
|
|
5095
|
+
writeFileSync7(configPath, configToml, { mode: 384 });
|
|
4747
5096
|
const binPath = findProxyBin();
|
|
4748
5097
|
const child = spawn(process.execPath, [binPath, "-c", configPath], {
|
|
4749
5098
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -4835,10 +5184,10 @@ function resolveProxyConfigOptions() {
|
|
|
4835
5184
|
77
|
|
4836
5185
|
);
|
|
4837
5186
|
}
|
|
4838
|
-
|
|
5187
|
+
consola31.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
|
|
4839
5188
|
return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
|
|
4840
5189
|
}
|
|
4841
|
-
var proxyCommand =
|
|
5190
|
+
var proxyCommand = defineCommand36({
|
|
4842
5191
|
meta: {
|
|
4843
5192
|
name: "proxy",
|
|
4844
5193
|
description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
|
|
@@ -4860,12 +5209,12 @@ var proxyCommand = defineCommand31({
|
|
|
4860
5209
|
let close = null;
|
|
4861
5210
|
if (reuseUrl) {
|
|
4862
5211
|
proxyUrl = reuseUrl;
|
|
4863
|
-
|
|
5212
|
+
consola31.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
|
|
4864
5213
|
} else {
|
|
4865
5214
|
const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
|
|
4866
5215
|
proxyUrl = ephemeral.url;
|
|
4867
5216
|
close = ephemeral.close;
|
|
4868
|
-
|
|
5217
|
+
consola31.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
|
|
4869
5218
|
}
|
|
4870
5219
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
|
|
4871
5220
|
const childEnv = {
|
|
@@ -4897,7 +5246,7 @@ var proxyCommand = defineCommand31({
|
|
|
4897
5246
|
else resolveExit(code ?? 0);
|
|
4898
5247
|
});
|
|
4899
5248
|
child.once("error", (err) => {
|
|
4900
|
-
|
|
5249
|
+
consola31.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
|
|
4901
5250
|
resolveExit(127);
|
|
4902
5251
|
});
|
|
4903
5252
|
});
|
|
@@ -4911,8 +5260,8 @@ function signalNumber(signal) {
|
|
|
4911
5260
|
}
|
|
4912
5261
|
|
|
4913
5262
|
// src/commands/explain.ts
|
|
4914
|
-
import { defineCommand as
|
|
4915
|
-
var explainCommand =
|
|
5263
|
+
import { defineCommand as defineCommand37 } from "citty";
|
|
5264
|
+
var explainCommand = defineCommand37({
|
|
4916
5265
|
meta: {
|
|
4917
5266
|
name: "explain",
|
|
4918
5267
|
description: "Show what permission a command would need"
|
|
@@ -4950,9 +5299,9 @@ var explainCommand = defineCommand32({
|
|
|
4950
5299
|
});
|
|
4951
5300
|
|
|
4952
5301
|
// src/commands/config/get.ts
|
|
4953
|
-
import { defineCommand as
|
|
4954
|
-
import
|
|
4955
|
-
var configGetCommand =
|
|
5302
|
+
import { defineCommand as defineCommand38 } from "citty";
|
|
5303
|
+
import consola32 from "consola";
|
|
5304
|
+
var configGetCommand = defineCommand38({
|
|
4956
5305
|
meta: {
|
|
4957
5306
|
name: "get",
|
|
4958
5307
|
description: "Get a configuration value"
|
|
@@ -4972,7 +5321,7 @@ var configGetCommand = defineCommand33({
|
|
|
4972
5321
|
if (idp)
|
|
4973
5322
|
console.log(idp);
|
|
4974
5323
|
else
|
|
4975
|
-
|
|
5324
|
+
consola32.info("No IdP configured.");
|
|
4976
5325
|
break;
|
|
4977
5326
|
}
|
|
4978
5327
|
case "email": {
|
|
@@ -4980,7 +5329,7 @@ var configGetCommand = defineCommand33({
|
|
|
4980
5329
|
if (auth?.email)
|
|
4981
5330
|
console.log(auth.email);
|
|
4982
5331
|
else
|
|
4983
|
-
|
|
5332
|
+
consola32.info("Not logged in.");
|
|
4984
5333
|
break;
|
|
4985
5334
|
}
|
|
4986
5335
|
default: {
|
|
@@ -4993,7 +5342,7 @@ var configGetCommand = defineCommand33({
|
|
|
4993
5342
|
if (sectionObj && field in sectionObj) {
|
|
4994
5343
|
console.log(sectionObj[field]);
|
|
4995
5344
|
} else {
|
|
4996
|
-
|
|
5345
|
+
consola32.info(`Key "${key}" not set.`);
|
|
4997
5346
|
}
|
|
4998
5347
|
} else {
|
|
4999
5348
|
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
@@ -5004,9 +5353,9 @@ var configGetCommand = defineCommand33({
|
|
|
5004
5353
|
});
|
|
5005
5354
|
|
|
5006
5355
|
// src/commands/config/set.ts
|
|
5007
|
-
import { defineCommand as
|
|
5008
|
-
import
|
|
5009
|
-
var configSetCommand =
|
|
5356
|
+
import { defineCommand as defineCommand39 } from "citty";
|
|
5357
|
+
import consola33 from "consola";
|
|
5358
|
+
var configSetCommand = defineCommand39({
|
|
5010
5359
|
meta: {
|
|
5011
5360
|
name: "set",
|
|
5012
5361
|
description: "Set a configuration value"
|
|
@@ -5042,12 +5391,12 @@ var configSetCommand = defineCommand34({
|
|
|
5042
5391
|
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
5043
5392
|
}
|
|
5044
5393
|
saveConfig(config);
|
|
5045
|
-
|
|
5394
|
+
consola33.success(`Set ${key} = ${value}`);
|
|
5046
5395
|
}
|
|
5047
5396
|
});
|
|
5048
5397
|
|
|
5049
5398
|
// src/commands/fetch/index.ts
|
|
5050
|
-
import { defineCommand as
|
|
5399
|
+
import { defineCommand as defineCommand40 } from "citty";
|
|
5051
5400
|
async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
5052
5401
|
const token = getAuthToken();
|
|
5053
5402
|
if (!token) {
|
|
@@ -5083,13 +5432,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
|
5083
5432
|
throw new CliError(`HTTP ${response.status} ${response.statusText}`);
|
|
5084
5433
|
}
|
|
5085
5434
|
}
|
|
5086
|
-
var fetchCommand =
|
|
5435
|
+
var fetchCommand = defineCommand40({
|
|
5087
5436
|
meta: {
|
|
5088
5437
|
name: "fetch",
|
|
5089
5438
|
description: "Make authenticated HTTP requests"
|
|
5090
5439
|
},
|
|
5091
5440
|
subCommands: {
|
|
5092
|
-
get:
|
|
5441
|
+
get: defineCommand40({
|
|
5093
5442
|
meta: {
|
|
5094
5443
|
name: "get",
|
|
5095
5444
|
description: "GET request with auth token"
|
|
@@ -5115,7 +5464,7 @@ var fetchCommand = defineCommand35({
|
|
|
5115
5464
|
await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
|
|
5116
5465
|
}
|
|
5117
5466
|
}),
|
|
5118
|
-
post:
|
|
5467
|
+
post: defineCommand40({
|
|
5119
5468
|
meta: {
|
|
5120
5469
|
name: "post",
|
|
5121
5470
|
description: "POST request with auth token"
|
|
@@ -5154,8 +5503,8 @@ var fetchCommand = defineCommand35({
|
|
|
5154
5503
|
});
|
|
5155
5504
|
|
|
5156
5505
|
// src/commands/mcp/index.ts
|
|
5157
|
-
import { defineCommand as
|
|
5158
|
-
var mcpCommand =
|
|
5506
|
+
import { defineCommand as defineCommand41 } from "citty";
|
|
5507
|
+
var mcpCommand = defineCommand41({
|
|
5159
5508
|
meta: {
|
|
5160
5509
|
name: "mcp",
|
|
5161
5510
|
description: "Start MCP server for AI agents"
|
|
@@ -5178,48 +5527,48 @@ var mcpCommand = defineCommand36({
|
|
|
5178
5527
|
if (transport !== "stdio" && transport !== "sse") {
|
|
5179
5528
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
5180
5529
|
}
|
|
5181
|
-
const { startMcpServer } = await import("./server-
|
|
5530
|
+
const { startMcpServer } = await import("./server-AK2JBMJD.js");
|
|
5182
5531
|
await startMcpServer(transport, port);
|
|
5183
5532
|
}
|
|
5184
5533
|
});
|
|
5185
5534
|
|
|
5186
5535
|
// src/commands/init/index.ts
|
|
5187
|
-
import { existsSync as
|
|
5536
|
+
import { existsSync as existsSync12, copyFileSync, writeFileSync as writeFileSync8 } from "fs";
|
|
5188
5537
|
import { randomBytes } from "crypto";
|
|
5189
|
-
import { execFileSync as
|
|
5190
|
-
import { join as
|
|
5191
|
-
import { defineCommand as
|
|
5192
|
-
import
|
|
5538
|
+
import { execFileSync as execFileSync13 } from "child_process";
|
|
5539
|
+
import { join as join11 } from "path";
|
|
5540
|
+
import { defineCommand as defineCommand42 } from "citty";
|
|
5541
|
+
import consola34 from "consola";
|
|
5193
5542
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
5194
5543
|
async function downloadTemplate(repo, targetDir) {
|
|
5195
5544
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
5196
5545
|
await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
|
|
5197
5546
|
}
|
|
5198
5547
|
function installDeps(dir) {
|
|
5199
|
-
const hasLockFile = (name) =>
|
|
5548
|
+
const hasLockFile = (name) => existsSync12(join11(dir, name));
|
|
5200
5549
|
if (hasLockFile("pnpm-lock.yaml")) {
|
|
5201
|
-
|
|
5550
|
+
execFileSync13("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5202
5551
|
} else if (hasLockFile("bun.lockb")) {
|
|
5203
|
-
|
|
5552
|
+
execFileSync13("bun", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5204
5553
|
} else {
|
|
5205
|
-
|
|
5554
|
+
execFileSync13("npm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5206
5555
|
}
|
|
5207
5556
|
}
|
|
5208
5557
|
async function promptChoice(message, choices) {
|
|
5209
|
-
const result = await
|
|
5558
|
+
const result = await consola34.prompt(message, { type: "select", options: choices });
|
|
5210
5559
|
if (typeof result === "symbol") {
|
|
5211
5560
|
throw new CliExit(0);
|
|
5212
5561
|
}
|
|
5213
5562
|
return result;
|
|
5214
5563
|
}
|
|
5215
5564
|
async function promptText(message, defaultValue) {
|
|
5216
|
-
const result = await
|
|
5565
|
+
const result = await consola34.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
5217
5566
|
if (typeof result === "symbol") {
|
|
5218
5567
|
throw new CliExit(0);
|
|
5219
5568
|
}
|
|
5220
5569
|
return result || defaultValue || "";
|
|
5221
5570
|
}
|
|
5222
|
-
var initCommand =
|
|
5571
|
+
var initCommand = defineCommand42({
|
|
5223
5572
|
meta: {
|
|
5224
5573
|
name: "init",
|
|
5225
5574
|
description: "Scaffold a new OpenApe project"
|
|
@@ -5261,23 +5610,23 @@ var initCommand = defineCommand37({
|
|
|
5261
5610
|
});
|
|
5262
5611
|
async function initSP(targetDir) {
|
|
5263
5612
|
const dir = targetDir || "my-app";
|
|
5264
|
-
if (
|
|
5613
|
+
if (existsSync12(join11(dir, "package.json"))) {
|
|
5265
5614
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5266
5615
|
}
|
|
5267
|
-
|
|
5616
|
+
consola34.start("Scaffolding SP starter...");
|
|
5268
5617
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
5269
|
-
|
|
5270
|
-
|
|
5618
|
+
consola34.success("Scaffolded from openape-sp-starter");
|
|
5619
|
+
consola34.start("Installing dependencies...");
|
|
5271
5620
|
installDeps(dir);
|
|
5272
|
-
|
|
5273
|
-
const envExample =
|
|
5274
|
-
const envFile =
|
|
5275
|
-
if (
|
|
5621
|
+
consola34.success("Dependencies installed");
|
|
5622
|
+
const envExample = join11(dir, ".env.example");
|
|
5623
|
+
const envFile = join11(dir, ".env");
|
|
5624
|
+
if (existsSync12(envExample) && !existsSync12(envFile)) {
|
|
5276
5625
|
copyFileSync(envExample, envFile);
|
|
5277
|
-
|
|
5626
|
+
consola34.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
5278
5627
|
}
|
|
5279
5628
|
console.log("");
|
|
5280
|
-
|
|
5629
|
+
consola34.box([
|
|
5281
5630
|
`cd ${dir}`,
|
|
5282
5631
|
"npm run dev",
|
|
5283
5632
|
"",
|
|
@@ -5286,7 +5635,7 @@ async function initSP(targetDir) {
|
|
|
5286
5635
|
}
|
|
5287
5636
|
async function initIdP(targetDir) {
|
|
5288
5637
|
const dir = targetDir || "my-idp";
|
|
5289
|
-
if (
|
|
5638
|
+
if (existsSync12(join11(dir, "package.json"))) {
|
|
5290
5639
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5291
5640
|
}
|
|
5292
5641
|
const domain = await promptText("Domain for the IdP", "localhost");
|
|
@@ -5296,15 +5645,15 @@ async function initIdP(targetDir) {
|
|
|
5296
5645
|
"s3 (S3-compatible)"
|
|
5297
5646
|
]);
|
|
5298
5647
|
const adminEmail = await promptText("Admin email");
|
|
5299
|
-
|
|
5648
|
+
consola34.start("Scaffolding IdP starter...");
|
|
5300
5649
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
5301
|
-
|
|
5302
|
-
|
|
5650
|
+
consola34.success("Scaffolded from openape-idp-starter");
|
|
5651
|
+
consola34.start("Installing dependencies...");
|
|
5303
5652
|
installDeps(dir);
|
|
5304
|
-
|
|
5653
|
+
consola34.success("Dependencies installed");
|
|
5305
5654
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
5306
5655
|
const managementToken = randomBytes(32).toString("hex");
|
|
5307
|
-
|
|
5656
|
+
consola34.success("Secrets generated");
|
|
5308
5657
|
const isLocalhost = domain === "localhost";
|
|
5309
5658
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
5310
5659
|
const envContent = [
|
|
@@ -5318,11 +5667,11 @@ async function initIdP(targetDir) {
|
|
|
5318
5667
|
`NUXT_OPENAPE_RP_ID=${domain}`,
|
|
5319
5668
|
`NUXT_OPENAPE_RP_ORIGIN=${origin}`
|
|
5320
5669
|
].join("\n");
|
|
5321
|
-
|
|
5670
|
+
writeFileSync8(join11(dir, ".env"), `${envContent}
|
|
5322
5671
|
`, { mode: 384 });
|
|
5323
|
-
|
|
5672
|
+
consola34.success(".env created");
|
|
5324
5673
|
console.log("");
|
|
5325
|
-
|
|
5674
|
+
consola34.box([
|
|
5326
5675
|
`cd ${dir}`,
|
|
5327
5676
|
"npm run dev",
|
|
5328
5677
|
"",
|
|
@@ -5339,11 +5688,11 @@ async function initIdP(targetDir) {
|
|
|
5339
5688
|
|
|
5340
5689
|
// src/commands/enroll.ts
|
|
5341
5690
|
import { Buffer as Buffer5 } from "buffer";
|
|
5342
|
-
import { existsSync as
|
|
5691
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "fs";
|
|
5343
5692
|
import { execFile as execFile2 } from "child_process";
|
|
5344
5693
|
import { sign as sign2 } from "crypto";
|
|
5345
|
-
import { defineCommand as
|
|
5346
|
-
import
|
|
5694
|
+
import { defineCommand as defineCommand43 } from "citty";
|
|
5695
|
+
import consola35 from "consola";
|
|
5347
5696
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
5348
5697
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
5349
5698
|
var POLL_INTERVAL = 3e3;
|
|
@@ -5355,7 +5704,7 @@ function openBrowser2(url) {
|
|
|
5355
5704
|
}
|
|
5356
5705
|
async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
5357
5706
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
5358
|
-
const keyContent =
|
|
5707
|
+
const keyContent = readFileSync11(resolvedKey, "utf-8");
|
|
5359
5708
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
5360
5709
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
5361
5710
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -5386,7 +5735,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
5386
5735
|
}
|
|
5387
5736
|
throw new Error("Enrollment timed out. Please check the browser and try again.");
|
|
5388
5737
|
}
|
|
5389
|
-
var enrollCommand =
|
|
5738
|
+
var enrollCommand = defineCommand43({
|
|
5390
5739
|
meta: {
|
|
5391
5740
|
name: "enroll",
|
|
5392
5741
|
description: "Enroll an agent with an Identity Provider"
|
|
@@ -5406,38 +5755,38 @@ var enrollCommand = defineCommand38({
|
|
|
5406
5755
|
}
|
|
5407
5756
|
},
|
|
5408
5757
|
async run({ args }) {
|
|
5409
|
-
const idp = args.idp || await
|
|
5758
|
+
const idp = args.idp || await consola35.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
|
|
5410
5759
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
5411
5760
|
return r;
|
|
5412
5761
|
}) || DEFAULT_IDP_URL2;
|
|
5413
|
-
const agentName = args.name || await
|
|
5762
|
+
const agentName = args.name || await consola35.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
|
|
5414
5763
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
5415
5764
|
return r;
|
|
5416
5765
|
});
|
|
5417
5766
|
if (!agentName) {
|
|
5418
5767
|
throw new CliError("Agent name is required.");
|
|
5419
5768
|
}
|
|
5420
|
-
const keyPath = args.key || await
|
|
5769
|
+
const keyPath = args.key || await consola35.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
|
|
5421
5770
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
5422
5771
|
return r;
|
|
5423
5772
|
}) || DEFAULT_KEY_PATH;
|
|
5424
5773
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
5425
5774
|
let publicKey;
|
|
5426
|
-
if (
|
|
5775
|
+
if (existsSync13(resolvedKey)) {
|
|
5427
5776
|
publicKey = readPublicKey(resolvedKey);
|
|
5428
|
-
|
|
5777
|
+
consola35.success(`Using existing key ${keyPath}`);
|
|
5429
5778
|
} else {
|
|
5430
|
-
|
|
5779
|
+
consola35.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
5431
5780
|
publicKey = generateAndSaveKey(keyPath);
|
|
5432
|
-
|
|
5781
|
+
consola35.success(`Key pair generated at ${keyPath}`);
|
|
5433
5782
|
}
|
|
5434
5783
|
const encodedKey = encodeURIComponent(publicKey);
|
|
5435
5784
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
5436
|
-
|
|
5437
|
-
|
|
5785
|
+
consola35.info("Opening browser for enrollment...");
|
|
5786
|
+
consola35.info(`\u2192 ${idp}/enroll`);
|
|
5438
5787
|
openBrowser2(enrollUrl);
|
|
5439
5788
|
console.log("");
|
|
5440
|
-
const agentEmail = await
|
|
5789
|
+
const agentEmail = await consola35.prompt(
|
|
5441
5790
|
"Agent email (shown in browser after enrollment)",
|
|
5442
5791
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
5443
5792
|
).then((r) => {
|
|
@@ -5447,7 +5796,7 @@ var enrollCommand = defineCommand38({
|
|
|
5447
5796
|
if (!agentEmail) {
|
|
5448
5797
|
throw new CliError("Agent email is required to verify enrollment.");
|
|
5449
5798
|
}
|
|
5450
|
-
|
|
5799
|
+
consola35.start("Verifying enrollment...");
|
|
5451
5800
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
5452
5801
|
saveAuth({
|
|
5453
5802
|
idp,
|
|
@@ -5459,18 +5808,18 @@ var enrollCommand = defineCommand38({
|
|
|
5459
5808
|
config.defaults = { ...config.defaults, idp };
|
|
5460
5809
|
config.agent = { key: keyPath, email: agentEmail };
|
|
5461
5810
|
saveConfig(config);
|
|
5462
|
-
|
|
5463
|
-
|
|
5811
|
+
consola35.success(`Agent enrolled as ${agentEmail}`);
|
|
5812
|
+
consola35.success("Config saved to ~/.config/apes/");
|
|
5464
5813
|
console.log("");
|
|
5465
|
-
|
|
5814
|
+
consola35.info("Verify with: apes whoami");
|
|
5466
5815
|
}
|
|
5467
5816
|
});
|
|
5468
5817
|
|
|
5469
5818
|
// src/commands/register-user.ts
|
|
5470
|
-
import { existsSync as
|
|
5471
|
-
import { defineCommand as
|
|
5472
|
-
import
|
|
5473
|
-
var registerUserCommand =
|
|
5819
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
5820
|
+
import { defineCommand as defineCommand44 } from "citty";
|
|
5821
|
+
import consola36 from "consola";
|
|
5822
|
+
var registerUserCommand = defineCommand44({
|
|
5474
5823
|
meta: {
|
|
5475
5824
|
name: "register-user",
|
|
5476
5825
|
description: "Register a sub-user with SSH key"
|
|
@@ -5506,8 +5855,8 @@ var registerUserCommand = defineCommand39({
|
|
|
5506
5855
|
throw new CliError("No IdP URL configured. Run `apes login` first.");
|
|
5507
5856
|
}
|
|
5508
5857
|
let publicKey = args.key;
|
|
5509
|
-
if (
|
|
5510
|
-
publicKey =
|
|
5858
|
+
if (existsSync14(args.key)) {
|
|
5859
|
+
publicKey = readFileSync12(args.key, "utf-8").trim();
|
|
5511
5860
|
}
|
|
5512
5861
|
if (!publicKey.startsWith("ssh-ed25519 ")) {
|
|
5513
5862
|
throw new CliError("Public key must be in ssh-ed25519 format.");
|
|
@@ -5525,18 +5874,18 @@ var registerUserCommand = defineCommand39({
|
|
|
5525
5874
|
...userType ? { type: userType } : {}
|
|
5526
5875
|
}
|
|
5527
5876
|
});
|
|
5528
|
-
|
|
5877
|
+
consola36.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
|
|
5529
5878
|
}
|
|
5530
5879
|
});
|
|
5531
5880
|
|
|
5532
5881
|
// src/commands/utils/index.ts
|
|
5533
|
-
import { defineCommand as
|
|
5882
|
+
import { defineCommand as defineCommand46 } from "citty";
|
|
5534
5883
|
|
|
5535
5884
|
// src/commands/utils/dig.ts
|
|
5536
|
-
import { defineCommand as
|
|
5537
|
-
import
|
|
5885
|
+
import { defineCommand as defineCommand45 } from "citty";
|
|
5886
|
+
import consola37 from "consola";
|
|
5538
5887
|
import { resolveDDISA as resolveDDISA2 } from "@openape/core";
|
|
5539
|
-
var digCommand =
|
|
5888
|
+
var digCommand = defineCommand45({
|
|
5540
5889
|
meta: {
|
|
5541
5890
|
name: "dig",
|
|
5542
5891
|
description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
|
|
@@ -5609,12 +5958,12 @@ var digCommand = defineCommand40({
|
|
|
5609
5958
|
console.log(` domain: ${domain}`);
|
|
5610
5959
|
console.log("");
|
|
5611
5960
|
if (!result.ddisa.found) {
|
|
5612
|
-
|
|
5961
|
+
consola37.warn(`No DDISA record at _ddisa.${domain}`);
|
|
5613
5962
|
if (result.hint) console.log(`
|
|
5614
5963
|
${result.hint}`);
|
|
5615
5964
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
5616
5965
|
}
|
|
5617
|
-
|
|
5966
|
+
consola37.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
|
|
5618
5967
|
console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
|
|
5619
5968
|
console.log(` IdP URL: ${result.ddisa.idp}`);
|
|
5620
5969
|
if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
|
|
@@ -5624,13 +5973,13 @@ ${result.hint}`);
|
|
|
5624
5973
|
return;
|
|
5625
5974
|
}
|
|
5626
5975
|
if (result.idpDiscovery.ok) {
|
|
5627
|
-
|
|
5976
|
+
consola37.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
|
|
5628
5977
|
if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
|
|
5629
5978
|
if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
|
|
5630
5979
|
if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
|
|
5631
5980
|
if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
|
|
5632
5981
|
} else {
|
|
5633
|
-
|
|
5982
|
+
consola37.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
|
|
5634
5983
|
if (result.hint) console.log(`
|
|
5635
5984
|
${result.hint}`);
|
|
5636
5985
|
throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
|
|
@@ -5639,7 +5988,7 @@ ${result.hint}`);
|
|
|
5639
5988
|
});
|
|
5640
5989
|
|
|
5641
5990
|
// src/commands/utils/index.ts
|
|
5642
|
-
var utilsCommand =
|
|
5991
|
+
var utilsCommand = defineCommand46({
|
|
5643
5992
|
meta: {
|
|
5644
5993
|
name: "utils",
|
|
5645
5994
|
description: "Admin/diagnostic utilities (dig, \u2026)"
|
|
@@ -5650,12 +5999,12 @@ var utilsCommand = defineCommand41({
|
|
|
5650
5999
|
});
|
|
5651
6000
|
|
|
5652
6001
|
// src/commands/sessions/index.ts
|
|
5653
|
-
import { defineCommand as
|
|
6002
|
+
import { defineCommand as defineCommand49 } from "citty";
|
|
5654
6003
|
|
|
5655
6004
|
// src/commands/sessions/list.ts
|
|
5656
|
-
import { defineCommand as
|
|
5657
|
-
import
|
|
5658
|
-
var sessionsListCommand =
|
|
6005
|
+
import { defineCommand as defineCommand47 } from "citty";
|
|
6006
|
+
import consola38 from "consola";
|
|
6007
|
+
var sessionsListCommand = defineCommand47({
|
|
5659
6008
|
meta: {
|
|
5660
6009
|
name: "list",
|
|
5661
6010
|
description: "List your active refresh-token families (one per logged-in device)."
|
|
@@ -5673,7 +6022,7 @@ var sessionsListCommand = defineCommand42({
|
|
|
5673
6022
|
return;
|
|
5674
6023
|
}
|
|
5675
6024
|
if (result.data.length === 0) {
|
|
5676
|
-
|
|
6025
|
+
consola38.info("No active sessions.");
|
|
5677
6026
|
return;
|
|
5678
6027
|
}
|
|
5679
6028
|
for (const f of result.data) {
|
|
@@ -5685,9 +6034,9 @@ var sessionsListCommand = defineCommand42({
|
|
|
5685
6034
|
});
|
|
5686
6035
|
|
|
5687
6036
|
// src/commands/sessions/remove.ts
|
|
5688
|
-
import { defineCommand as
|
|
5689
|
-
import
|
|
5690
|
-
var sessionsRemoveCommand =
|
|
6037
|
+
import { defineCommand as defineCommand48 } from "citty";
|
|
6038
|
+
import consola39 from "consola";
|
|
6039
|
+
var sessionsRemoveCommand = defineCommand48({
|
|
5691
6040
|
meta: {
|
|
5692
6041
|
name: "remove",
|
|
5693
6042
|
description: "Revoke one of your active refresh-token families by id."
|
|
@@ -5703,12 +6052,12 @@ var sessionsRemoveCommand = defineCommand43({
|
|
|
5703
6052
|
const id = String(args.familyId).trim();
|
|
5704
6053
|
if (!id) throw new CliError("familyId required");
|
|
5705
6054
|
await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
5706
|
-
|
|
6055
|
+
consola39.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
|
|
5707
6056
|
}
|
|
5708
6057
|
});
|
|
5709
6058
|
|
|
5710
6059
|
// src/commands/sessions/index.ts
|
|
5711
|
-
var sessionsCommand =
|
|
6060
|
+
var sessionsCommand = defineCommand49({
|
|
5712
6061
|
meta: {
|
|
5713
6062
|
name: "sessions",
|
|
5714
6063
|
description: "Manage your active refresh-token sessions across devices"
|
|
@@ -5720,10 +6069,10 @@ var sessionsCommand = defineCommand44({
|
|
|
5720
6069
|
});
|
|
5721
6070
|
|
|
5722
6071
|
// src/commands/dns-check.ts
|
|
5723
|
-
import { defineCommand as
|
|
5724
|
-
import
|
|
6072
|
+
import { defineCommand as defineCommand50 } from "citty";
|
|
6073
|
+
import consola40 from "consola";
|
|
5725
6074
|
import { resolveDDISA as resolveDDISA3 } from "@openape/core";
|
|
5726
|
-
var dnsCheckCommand =
|
|
6075
|
+
var dnsCheckCommand = defineCommand50({
|
|
5727
6076
|
meta: {
|
|
5728
6077
|
name: "dns-check",
|
|
5729
6078
|
description: "Validate DDISA DNS TXT records for a domain"
|
|
@@ -5737,7 +6086,7 @@ var dnsCheckCommand = defineCommand45({
|
|
|
5737
6086
|
},
|
|
5738
6087
|
async run({ args }) {
|
|
5739
6088
|
const domain = args.domain;
|
|
5740
|
-
|
|
6089
|
+
consola40.start(`Checking _ddisa.${domain}...`);
|
|
5741
6090
|
try {
|
|
5742
6091
|
const result = await resolveDDISA3(domain);
|
|
5743
6092
|
if (!result) {
|
|
@@ -5746,7 +6095,7 @@ var dnsCheckCommand = defineCommand45({
|
|
|
5746
6095
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
5747
6096
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
5748
6097
|
}
|
|
5749
|
-
|
|
6098
|
+
consola40.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
5750
6099
|
console.log("");
|
|
5751
6100
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
5752
6101
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -5755,14 +6104,14 @@ var dnsCheckCommand = defineCommand45({
|
|
|
5755
6104
|
if (result.priority !== void 0)
|
|
5756
6105
|
console.log(` Priority: ${result.priority}`);
|
|
5757
6106
|
console.log("");
|
|
5758
|
-
|
|
6107
|
+
consola40.start(`Verifying IdP at ${result.idp}...`);
|
|
5759
6108
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
5760
6109
|
if (!discoResp.ok) {
|
|
5761
|
-
|
|
6110
|
+
consola40.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
5762
6111
|
return;
|
|
5763
6112
|
}
|
|
5764
6113
|
const disco = await discoResp.json();
|
|
5765
|
-
|
|
6114
|
+
consola40.success(`IdP is reachable`);
|
|
5766
6115
|
console.log(` Issuer: ${disco.issuer}`);
|
|
5767
6116
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
5768
6117
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -5780,7 +6129,7 @@ var dnsCheckCommand = defineCommand45({
|
|
|
5780
6129
|
// src/commands/health.ts
|
|
5781
6130
|
import { exec } from "child_process";
|
|
5782
6131
|
import { promisify } from "util";
|
|
5783
|
-
import { defineCommand as
|
|
6132
|
+
import { defineCommand as defineCommand51 } from "citty";
|
|
5784
6133
|
var execAsync = promisify(exec);
|
|
5785
6134
|
async function resolveApeShellPath() {
|
|
5786
6135
|
try {
|
|
@@ -5816,7 +6165,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
5816
6165
|
}
|
|
5817
6166
|
}
|
|
5818
6167
|
async function runHealth(args) {
|
|
5819
|
-
const version = true ? "1.
|
|
6168
|
+
const version = true ? "1.5.0" : "0.0.0";
|
|
5820
6169
|
const auth = loadAuth();
|
|
5821
6170
|
if (!auth) {
|
|
5822
6171
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -5879,7 +6228,7 @@ async function runHealth(args) {
|
|
|
5879
6228
|
throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
|
|
5880
6229
|
}
|
|
5881
6230
|
}
|
|
5882
|
-
var healthCommand =
|
|
6231
|
+
var healthCommand = defineCommand51({
|
|
5883
6232
|
meta: {
|
|
5884
6233
|
name: "health",
|
|
5885
6234
|
description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
|
|
@@ -5897,8 +6246,8 @@ var healthCommand = defineCommand46({
|
|
|
5897
6246
|
});
|
|
5898
6247
|
|
|
5899
6248
|
// src/commands/workflows.ts
|
|
5900
|
-
import { defineCommand as
|
|
5901
|
-
import
|
|
6249
|
+
import { defineCommand as defineCommand52 } from "citty";
|
|
6250
|
+
import consola41 from "consola";
|
|
5902
6251
|
|
|
5903
6252
|
// src/guides/index.ts
|
|
5904
6253
|
var guides = [
|
|
@@ -5948,7 +6297,7 @@ var guides = [
|
|
|
5948
6297
|
];
|
|
5949
6298
|
|
|
5950
6299
|
// src/commands/workflows.ts
|
|
5951
|
-
var workflowsCommand =
|
|
6300
|
+
var workflowsCommand = defineCommand52({
|
|
5952
6301
|
meta: {
|
|
5953
6302
|
name: "workflows",
|
|
5954
6303
|
description: "Discover workflow guides"
|
|
@@ -5969,7 +6318,7 @@ var workflowsCommand = defineCommand47({
|
|
|
5969
6318
|
if (args.id) {
|
|
5970
6319
|
const guide = guides.find((g) => g.id === String(args.id));
|
|
5971
6320
|
if (!guide) {
|
|
5972
|
-
|
|
6321
|
+
consola41.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
5973
6322
|
throw new CliError(`Guide not found: ${args.id}`);
|
|
5974
6323
|
}
|
|
5975
6324
|
if (args.json) {
|
|
@@ -6009,26 +6358,26 @@ var workflowsCommand = defineCommand47({
|
|
|
6009
6358
|
});
|
|
6010
6359
|
|
|
6011
6360
|
// src/version-check.ts
|
|
6012
|
-
import { existsSync as
|
|
6013
|
-
import { homedir as
|
|
6014
|
-
import { join as
|
|
6015
|
-
import
|
|
6361
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
|
|
6362
|
+
import { homedir as homedir12 } from "os";
|
|
6363
|
+
import { join as join12 } from "path";
|
|
6364
|
+
import consola42 from "consola";
|
|
6016
6365
|
var PACKAGE_NAME = "@openape/apes";
|
|
6017
6366
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6018
|
-
var CACHE_FILE =
|
|
6367
|
+
var CACHE_FILE = join12(homedir12(), ".config", "apes", ".version-check.json");
|
|
6019
6368
|
function readCache() {
|
|
6020
|
-
if (!
|
|
6369
|
+
if (!existsSync15(CACHE_FILE)) return null;
|
|
6021
6370
|
try {
|
|
6022
|
-
return JSON.parse(
|
|
6371
|
+
return JSON.parse(readFileSync13(CACHE_FILE, "utf-8"));
|
|
6023
6372
|
} catch {
|
|
6024
6373
|
return null;
|
|
6025
6374
|
}
|
|
6026
6375
|
}
|
|
6027
6376
|
function writeCache(entry) {
|
|
6028
6377
|
try {
|
|
6029
|
-
const dir =
|
|
6030
|
-
if (!
|
|
6031
|
-
|
|
6378
|
+
const dir = join12(homedir12(), ".config", "apes");
|
|
6379
|
+
if (!existsSync15(dir)) mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
6380
|
+
writeFileSync9(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
|
|
6032
6381
|
} catch {
|
|
6033
6382
|
}
|
|
6034
6383
|
}
|
|
@@ -6057,7 +6406,7 @@ async function fetchLatestVersion() {
|
|
|
6057
6406
|
}
|
|
6058
6407
|
function warnIfBehind(currentVersion, latest) {
|
|
6059
6408
|
if (compareSemver(currentVersion, latest) < 0) {
|
|
6060
|
-
|
|
6409
|
+
consola42.warn(
|
|
6061
6410
|
`apes ${currentVersion} is behind latest @openape/apes@${latest}. Run \`npm i -g @openape/apes@latest\` to update. (Suppress with APES_NO_UPDATE_CHECK=1.)`
|
|
6062
6411
|
);
|
|
6063
6412
|
}
|
|
@@ -6089,10 +6438,10 @@ if (shellRewrite) {
|
|
|
6089
6438
|
if (shellRewrite.action === "rewrite") {
|
|
6090
6439
|
process.argv = shellRewrite.argv;
|
|
6091
6440
|
} else if (shellRewrite.action === "version") {
|
|
6092
|
-
console.log(`ape-shell ${"1.
|
|
6441
|
+
console.log(`ape-shell ${"1.5.0"} (OpenApe DDISA shell wrapper)`);
|
|
6093
6442
|
process.exit(0);
|
|
6094
6443
|
} else if (shellRewrite.action === "help") {
|
|
6095
|
-
console.log(`ape-shell ${"1.
|
|
6444
|
+
console.log(`ape-shell ${"1.5.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
6096
6445
|
console.log("");
|
|
6097
6446
|
console.log("Usage:");
|
|
6098
6447
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -6116,7 +6465,7 @@ if (shellRewrite) {
|
|
|
6116
6465
|
}
|
|
6117
6466
|
}
|
|
6118
6467
|
var debug = process.argv.includes("--debug");
|
|
6119
|
-
var grantsCommand =
|
|
6468
|
+
var grantsCommand = defineCommand53({
|
|
6120
6469
|
meta: {
|
|
6121
6470
|
name: "grants",
|
|
6122
6471
|
description: "Grant management"
|
|
@@ -6137,7 +6486,7 @@ var grantsCommand = defineCommand48({
|
|
|
6137
6486
|
"delegation-revoke": delegationRevokeCommand
|
|
6138
6487
|
}
|
|
6139
6488
|
});
|
|
6140
|
-
var configCommand =
|
|
6489
|
+
var configCommand = defineCommand53({
|
|
6141
6490
|
meta: {
|
|
6142
6491
|
name: "config",
|
|
6143
6492
|
description: "Configuration management"
|
|
@@ -6147,10 +6496,10 @@ var configCommand = defineCommand48({
|
|
|
6147
6496
|
set: configSetCommand
|
|
6148
6497
|
}
|
|
6149
6498
|
});
|
|
6150
|
-
var main =
|
|
6499
|
+
var main = defineCommand53({
|
|
6151
6500
|
meta: {
|
|
6152
6501
|
name: "apes",
|
|
6153
|
-
version: "1.
|
|
6502
|
+
version: "1.5.0",
|
|
6154
6503
|
description: "Unified CLI for OpenApe"
|
|
6155
6504
|
},
|
|
6156
6505
|
subCommands: {
|
|
@@ -6166,6 +6515,7 @@ var main = defineCommand48({
|
|
|
6166
6515
|
health: healthCommand,
|
|
6167
6516
|
grants: grantsCommand,
|
|
6168
6517
|
agents: agentsCommand,
|
|
6518
|
+
nest: nestCommand,
|
|
6169
6519
|
admin: adminCommand,
|
|
6170
6520
|
run: runCommand,
|
|
6171
6521
|
proxy: proxyCommand,
|
|
@@ -6205,20 +6555,20 @@ async function maybeRefreshAuth() {
|
|
|
6205
6555
|
}
|
|
6206
6556
|
}
|
|
6207
6557
|
await maybeRefreshAuth();
|
|
6208
|
-
await maybeWarnStaleVersion("1.
|
|
6558
|
+
await maybeWarnStaleVersion("1.5.0").catch(() => {
|
|
6209
6559
|
});
|
|
6210
6560
|
runMain(main).catch((err) => {
|
|
6211
6561
|
if (err instanceof CliExit) {
|
|
6212
6562
|
process.exit(err.exitCode);
|
|
6213
6563
|
}
|
|
6214
6564
|
if (err instanceof CliError) {
|
|
6215
|
-
|
|
6565
|
+
consola43.error(err.message);
|
|
6216
6566
|
process.exit(err.exitCode);
|
|
6217
6567
|
}
|
|
6218
6568
|
if (debug) {
|
|
6219
|
-
|
|
6569
|
+
consola43.error(err);
|
|
6220
6570
|
} else {
|
|
6221
|
-
|
|
6571
|
+
consola43.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
6222
6572
|
}
|
|
6223
6573
|
process.exit(1);
|
|
6224
6574
|
});
|