@openape/apes 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
} from "./chunk-OBF7IMQ2.js";
|
|
64
64
|
|
|
65
65
|
// src/cli.ts
|
|
66
|
-
import
|
|
66
|
+
import consola44 from "consola";
|
|
67
67
|
|
|
68
68
|
// src/ape-shell.ts
|
|
69
69
|
import path from "path";
|
|
@@ -93,7 +93,7 @@ function rewriteApeShellArgs(argv, argv0) {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// src/cli.ts
|
|
96
|
-
import { defineCommand as
|
|
96
|
+
import { defineCommand as defineCommand54, runMain } from "citty";
|
|
97
97
|
|
|
98
98
|
// src/commands/auth/login.ts
|
|
99
99
|
import { Buffer as Buffer2 } from "buffer";
|
|
@@ -379,7 +379,7 @@ async function loginWithPKCE(idp) {
|
|
|
379
379
|
consola2.success(`Logged in as ${payload.email || payload.sub}`);
|
|
380
380
|
}
|
|
381
381
|
async function loginWithKey(idp, keyPath, agentEmail) {
|
|
382
|
-
const { readFileSync:
|
|
382
|
+
const { readFileSync: readFileSync15 } = await import("fs");
|
|
383
383
|
const { sign: sign3 } = await import("crypto");
|
|
384
384
|
const { loadEd25519PrivateKey: loadEd25519PrivateKey2 } = await import("./ssh-key-YBNNG5K5.js");
|
|
385
385
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
@@ -392,7 +392,7 @@ async function loginWithKey(idp, keyPath, agentEmail) {
|
|
|
392
392
|
throw new CliError(`Challenge failed: ${await challengeResp.text()}`);
|
|
393
393
|
}
|
|
394
394
|
const { challenge } = await challengeResp.json();
|
|
395
|
-
const keyContent =
|
|
395
|
+
const keyContent = readFileSync15(keyPath, "utf-8");
|
|
396
396
|
const privateKey = loadEd25519PrivateKey2(keyContent);
|
|
397
397
|
const signature = sign3(null, Buffer2.from(challenge), privateKey).toString("base64");
|
|
398
398
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -3918,24 +3918,259 @@ 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
|
-
// src/commands/nest/
|
|
3924
|
-
import {
|
|
3925
|
-
import {
|
|
3926
|
-
import {
|
|
3923
|
+
// src/commands/nest/authorize.ts
|
|
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";
|
|
3927
3932
|
import { join as join8 } from "path";
|
|
3928
3933
|
import { defineCommand as defineCommand29 } from "citty";
|
|
3929
3934
|
import consola25 from "consola";
|
|
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({
|
|
3944
|
+
meta: {
|
|
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."
|
|
3947
|
+
},
|
|
3948
|
+
args: {
|
|
3949
|
+
name: {
|
|
3950
|
+
type: "string",
|
|
3951
|
+
description: "Override the nest agent name (default: nest-<short-hostname>)"
|
|
3952
|
+
},
|
|
3953
|
+
force: {
|
|
3954
|
+
type: "boolean",
|
|
3955
|
+
description: "Re-enroll even if ~/.openape/nest/.config/apes/auth.json already exists"
|
|
3956
|
+
}
|
|
3957
|
+
},
|
|
3958
|
+
async run({ args }) {
|
|
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}`);
|
|
4003
|
+
consola25.info("");
|
|
4004
|
+
consola25.info("Next: configure the YOLO-policy so the nest can spawn/destroy without prompts:");
|
|
4005
|
+
consola25.info("");
|
|
4006
|
+
consola25.info(" apes nest authorize");
|
|
4007
|
+
}
|
|
4008
|
+
});
|
|
4009
|
+
|
|
4010
|
+
// src/commands/nest/authorize.ts
|
|
4011
|
+
var DEFAULT_ALLOW_PATTERNS = [
|
|
4012
|
+
// Agent lifecycle ops the nest issues against `apes run --as root`
|
|
4013
|
+
"apes agents spawn *",
|
|
4014
|
+
"apes agents destroy *",
|
|
4015
|
+
"apes agents sync",
|
|
4016
|
+
// Bridge invocation the supervisor uses to keep agent processes
|
|
4017
|
+
// running. Pattern is intentionally precise — not a generic
|
|
4018
|
+
// `apes run --as *` wildcard — so a compromised nest can't pivot
|
|
4019
|
+
// to running arbitrary commands as arbitrary users.
|
|
4020
|
+
"apes run --as * -- openape-chat-bridge"
|
|
4021
|
+
];
|
|
4022
|
+
var authorizeNestCommand = defineCommand30({
|
|
4023
|
+
meta: {
|
|
4024
|
+
name: "authorize",
|
|
4025
|
+
description: "Set the YOLO-policy that lets the local nest spawn/destroy without per-call DDISA prompts"
|
|
4026
|
+
},
|
|
4027
|
+
args: {
|
|
4028
|
+
"allow": {
|
|
4029
|
+
type: "string",
|
|
4030
|
+
description: "Override allow_patterns (comma-separated globs). Default: nest-managed agent lifecycle."
|
|
4031
|
+
},
|
|
4032
|
+
"mode": {
|
|
4033
|
+
type: "string",
|
|
4034
|
+
description: "Policy mode (allow-list | deny-list). Default: allow-list \u2014 auto-approve only matched patterns."
|
|
4035
|
+
},
|
|
4036
|
+
"expires-in": {
|
|
4037
|
+
type: "string",
|
|
4038
|
+
description: "Optional duration like 30d, 6h. Omit for no expiry."
|
|
4039
|
+
}
|
|
4040
|
+
},
|
|
4041
|
+
async run({ args }) {
|
|
4042
|
+
const ownerAuth = loadAuth();
|
|
4043
|
+
if (!ownerAuth?.email || !ownerAuth.access_token) {
|
|
4044
|
+
throw new CliError("Run `apes login <email>` first \u2014 only the nest's owner can set its YOLO-policy.");
|
|
4045
|
+
}
|
|
4046
|
+
const nestAuthPath = join9(NEST_DATA_DIR, ".config", "apes", "auth.json");
|
|
4047
|
+
if (!existsSync11(nestAuthPath)) {
|
|
4048
|
+
throw new CliError("Nest not enrolled. Run `apes nest enroll` first.");
|
|
4049
|
+
}
|
|
4050
|
+
const nestAuth = JSON.parse(readFileSync10(nestAuthPath, "utf8"));
|
|
4051
|
+
if (!nestAuth.email) throw new CliError(`${nestAuthPath} has no email`);
|
|
4052
|
+
const idp = getIdpUrl();
|
|
4053
|
+
if (!idp) throw new CliError("No IdP configured.");
|
|
4054
|
+
const allowPatterns = typeof args.allow === "string" && args.allow ? args.allow.split(",").map((s) => s.trim()).filter(Boolean) : DEFAULT_ALLOW_PATTERNS;
|
|
4055
|
+
const mode = args.mode ?? "allow-list";
|
|
4056
|
+
const expiresAt = parseExpiresIn(args["expires-in"]);
|
|
4057
|
+
consola26.info(`Setting YOLO-policy on ${nestAuth.email}`);
|
|
4058
|
+
consola26.info(` mode: ${mode}`);
|
|
4059
|
+
consola26.info(` allow_patterns:`);
|
|
4060
|
+
for (const p of allowPatterns) consola26.info(` - ${p}`);
|
|
4061
|
+
if (expiresAt) consola26.info(` expires_at: ${new Date(expiresAt * 1e3).toISOString()}`);
|
|
4062
|
+
const url = `${idp}/api/users/${encodeURIComponent(nestAuth.email)}/yolo-policy`;
|
|
4063
|
+
const res = await fetch(url, {
|
|
4064
|
+
method: "PUT",
|
|
4065
|
+
headers: {
|
|
4066
|
+
"Authorization": `Bearer ${ownerAuth.access_token}`,
|
|
4067
|
+
"Content-Type": "application/json"
|
|
4068
|
+
},
|
|
4069
|
+
body: JSON.stringify({
|
|
4070
|
+
mode,
|
|
4071
|
+
allowPatterns,
|
|
4072
|
+
denyPatterns: [],
|
|
4073
|
+
expiresAt: expiresAt ?? null
|
|
4074
|
+
})
|
|
4075
|
+
});
|
|
4076
|
+
if (!res.ok) {
|
|
4077
|
+
const text = await res.text().catch(() => "");
|
|
4078
|
+
throw new CliError(`PUT /yolo-policy failed (${res.status}): ${text}`);
|
|
4079
|
+
}
|
|
4080
|
+
consola26.success("YOLO-policy applied. Nest-driven agent lifecycle is now zero-prompt.");
|
|
4081
|
+
consola26.info("Test: apes agents spawn <name> via the nest API \u2192 no DDISA prompt.");
|
|
4082
|
+
}
|
|
4083
|
+
});
|
|
4084
|
+
function parseExpiresIn(s) {
|
|
4085
|
+
if (!s) return null;
|
|
4086
|
+
const m = s.match(/^(\d+)([hdw])$/);
|
|
4087
|
+
if (!m) throw new CliError(`Invalid --expires-in "${s}" \u2014 expected forms like 30d, 6h, 2w`);
|
|
4088
|
+
const n = Number(m[1]);
|
|
4089
|
+
const unit = m[2];
|
|
4090
|
+
const seconds = unit === "h" ? 3600 : unit === "d" ? 86400 : 7 * 86400;
|
|
4091
|
+
return Math.floor(Date.now() / 1e3) + n * seconds;
|
|
4092
|
+
}
|
|
4093
|
+
|
|
4094
|
+
// src/commands/nest/install.ts
|
|
4095
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
4096
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
|
|
4097
|
+
import { homedir as homedir11, userInfo as userInfo2 } from "os";
|
|
4098
|
+
import { dirname as dirname3, join as join10 } from "path";
|
|
4099
|
+
import { defineCommand as defineCommand31 } from "citty";
|
|
4100
|
+
import consola27 from "consola";
|
|
4101
|
+
|
|
4102
|
+
// src/commands/nest/apes-agents-adapter.ts
|
|
4103
|
+
var APES_AGENTS_ADAPTER_TOML = `schema = "openape-shapes/v1"
|
|
4104
|
+
|
|
4105
|
+
# Adapter for the \`apes agents\` subtree \u2014 written by \`apes nest install\`.
|
|
4106
|
+
# A capability-grant with selector \`name=*\` covers any agent name
|
|
4107
|
+
# (selectorValueMatches treats '*' as a glob), letting the nest spawn
|
|
4108
|
+
# and destroy without per-agent DDISA prompts.
|
|
4109
|
+
|
|
4110
|
+
[cli]
|
|
4111
|
+
id = "apes-agents"
|
|
4112
|
+
executable = "apes"
|
|
4113
|
+
audience = "shapes"
|
|
4114
|
+
version = "1"
|
|
4115
|
+
|
|
4116
|
+
# \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
|
|
4117
|
+
# AGENT LIFECYCLE \u2014 spawn / destroy / sync
|
|
4118
|
+
# \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
|
|
4119
|
+
|
|
4120
|
+
[[operation]]
|
|
4121
|
+
id = "agents.spawn"
|
|
4122
|
+
command = ["agents", "spawn"]
|
|
4123
|
+
positionals = ["name"]
|
|
4124
|
+
display = "Spawn agent {name}"
|
|
4125
|
+
action = "create"
|
|
4126
|
+
risk = "high"
|
|
4127
|
+
resource_chain = ["agents:name={name}"]
|
|
4128
|
+
|
|
4129
|
+
[[operation]]
|
|
4130
|
+
id = "agents.destroy"
|
|
4131
|
+
command = ["agents", "destroy"]
|
|
4132
|
+
positionals = ["name"]
|
|
4133
|
+
display = "Destroy agent {name}"
|
|
4134
|
+
action = "delete"
|
|
4135
|
+
risk = "critical"
|
|
4136
|
+
resource_chain = ["agents:name={name}"]
|
|
4137
|
+
|
|
4138
|
+
[[operation]]
|
|
4139
|
+
id = "agents.sync"
|
|
4140
|
+
command = ["agents", "sync"]
|
|
4141
|
+
display = "Sync agent state with troop"
|
|
4142
|
+
action = "edit"
|
|
4143
|
+
risk = "low"
|
|
4144
|
+
resource_chain = ["agents:*"]
|
|
4145
|
+
|
|
4146
|
+
[[operation]]
|
|
4147
|
+
id = "agents.list"
|
|
4148
|
+
command = ["agents", "list"]
|
|
4149
|
+
display = "List agents"
|
|
4150
|
+
action = "list"
|
|
4151
|
+
risk = "low"
|
|
4152
|
+
resource_chain = ["agents:*"]
|
|
4153
|
+
|
|
4154
|
+
[[operation]]
|
|
4155
|
+
id = "agents.allow"
|
|
4156
|
+
command = ["agents", "allow"]
|
|
4157
|
+
positionals = ["name", "peer_email"]
|
|
4158
|
+
display = "Allow agent {name} to accept contact requests from {peer_email}"
|
|
4159
|
+
action = "edit"
|
|
4160
|
+
risk = "medium"
|
|
4161
|
+
resource_chain = ["agents:name={name}", "allowlist:email={peer_email}"]
|
|
4162
|
+
`;
|
|
4163
|
+
|
|
4164
|
+
// src/commands/nest/install.ts
|
|
3930
4165
|
var PLIST_LABEL = "ai.openape.nest";
|
|
3931
4166
|
function plistPath() {
|
|
3932
|
-
return
|
|
4167
|
+
return join10(homedir11(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
3933
4168
|
}
|
|
3934
4169
|
function escape2(s) {
|
|
3935
4170
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3936
4171
|
}
|
|
3937
4172
|
function buildPlist(args) {
|
|
3938
|
-
const logsDir =
|
|
4173
|
+
const logsDir = join10(args.userHome, "Library", "Logs");
|
|
3939
4174
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
3940
4175
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3941
4176
|
<plist version="1.0">
|
|
@@ -3947,7 +4182,7 @@ function buildPlist(args) {
|
|
|
3947
4182
|
<string>${escape2(args.nestBin)}</string>
|
|
3948
4183
|
</array>
|
|
3949
4184
|
<key>WorkingDirectory</key>
|
|
3950
|
-
<string>${escape2(args.
|
|
4185
|
+
<string>${escape2(args.nestHome)}</string>
|
|
3951
4186
|
<key>RunAtLoad</key>
|
|
3952
4187
|
<true/>
|
|
3953
4188
|
<key>KeepAlive</key>
|
|
@@ -3956,8 +4191,8 @@ function buildPlist(args) {
|
|
|
3956
4191
|
<integer>10</integer>
|
|
3957
4192
|
<key>EnvironmentVariables</key>
|
|
3958
4193
|
<dict>
|
|
3959
|
-
<key>HOME</key><string>${escape2(args.
|
|
3960
|
-
<key>PATH</key><string>${escape2(args.
|
|
4194
|
+
<key>HOME</key><string>${escape2(args.nestHome)}</string>
|
|
4195
|
+
<key>PATH</key><string>${escape2(args.userHome)}/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
3961
4196
|
<key>OPENAPE_NEST_PORT</key><string>${args.port}</string>
|
|
3962
4197
|
<key>OPENAPE_APES_BIN</key><string>${escape2(args.apesBin)}</string>
|
|
3963
4198
|
</dict>
|
|
@@ -3969,19 +4204,32 @@ function buildPlist(args) {
|
|
|
3969
4204
|
</plist>
|
|
3970
4205
|
`;
|
|
3971
4206
|
}
|
|
4207
|
+
function installAdapter2() {
|
|
4208
|
+
const target = join10(homedir11(), ".openape", "shapes", "adapters", "apes-agents.toml");
|
|
4209
|
+
mkdirSync5(dirname3(target), { recursive: true });
|
|
4210
|
+
let existing = "";
|
|
4211
|
+
try {
|
|
4212
|
+
existing = readFileSync11(target, "utf8");
|
|
4213
|
+
} catch {
|
|
4214
|
+
}
|
|
4215
|
+
if (existing === APES_AGENTS_ADAPTER_TOML) return false;
|
|
4216
|
+
writeFileSync7(target, APES_AGENTS_ADAPTER_TOML, { mode: 420 });
|
|
4217
|
+
consola27.success(`Wrote shapes adapter ${target}`);
|
|
4218
|
+
return true;
|
|
4219
|
+
}
|
|
3972
4220
|
function findBinary(name) {
|
|
3973
4221
|
for (const dir of [
|
|
3974
|
-
|
|
4222
|
+
join10(homedir11(), ".bun", "bin"),
|
|
3975
4223
|
"/opt/homebrew/bin",
|
|
3976
4224
|
"/usr/local/bin",
|
|
3977
4225
|
"/usr/bin"
|
|
3978
4226
|
]) {
|
|
3979
|
-
const p =
|
|
3980
|
-
if (
|
|
4227
|
+
const p = join10(dir, name);
|
|
4228
|
+
if (existsSync12(p)) return p;
|
|
3981
4229
|
}
|
|
3982
4230
|
throw new Error(`could not locate ${name} on PATH; install it first`);
|
|
3983
4231
|
}
|
|
3984
|
-
var installNestCommand =
|
|
4232
|
+
var installNestCommand = defineCommand31({
|
|
3985
4233
|
meta: {
|
|
3986
4234
|
name: "install",
|
|
3987
4235
|
description: "Install + start the local nest-daemon (idempotent \u2014 re-running just restarts)"
|
|
@@ -3993,29 +4241,31 @@ var installNestCommand = defineCommand29({
|
|
|
3993
4241
|
}
|
|
3994
4242
|
},
|
|
3995
4243
|
async run({ args }) {
|
|
3996
|
-
const homeDir =
|
|
4244
|
+
const homeDir = homedir11();
|
|
3997
4245
|
const port = Number(args.port ?? 9091);
|
|
3998
4246
|
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
3999
4247
|
throw new Error(`invalid port ${port}`);
|
|
4000
4248
|
}
|
|
4001
4249
|
const nestBin = findBinary("openape-nest");
|
|
4002
4250
|
const apesBin = findBinary("apes");
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4251
|
+
consola27.info(`Installing nest at ${plistPath()}`);
|
|
4252
|
+
consola27.info(` nest binary: ${nestBin}`);
|
|
4253
|
+
consola27.info(` apes binary: ${apesBin}`);
|
|
4254
|
+
consola27.info(` HTTP port: ${port}`);
|
|
4255
|
+
installAdapter2();
|
|
4256
|
+
mkdirSync5(join10(homeDir, "Library", "LaunchAgents"), { recursive: true });
|
|
4257
|
+
mkdirSync5(NEST_DATA_DIR, { recursive: true });
|
|
4258
|
+
const desired = buildPlist({ nestBin, apesBin, userHome: homeDir, nestHome: NEST_DATA_DIR, port });
|
|
4009
4259
|
let existing = "";
|
|
4010
4260
|
try {
|
|
4011
|
-
existing =
|
|
4261
|
+
existing = readFileSync11(plistPath(), "utf8");
|
|
4012
4262
|
} catch {
|
|
4013
4263
|
}
|
|
4014
4264
|
if (existing !== desired) {
|
|
4015
|
-
|
|
4016
|
-
|
|
4265
|
+
writeFileSync7(plistPath(), desired, { mode: 420 });
|
|
4266
|
+
consola27.success("Wrote launchd plist");
|
|
4017
4267
|
} else {
|
|
4018
|
-
|
|
4268
|
+
consola27.info("plist already up to date");
|
|
4019
4269
|
}
|
|
4020
4270
|
const uid = userInfo2().uid;
|
|
4021
4271
|
try {
|
|
@@ -4023,25 +4273,23 @@ var installNestCommand = defineCommand29({
|
|
|
4023
4273
|
} catch {
|
|
4024
4274
|
}
|
|
4025
4275
|
execFileSync9("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
consola25.info('(The seed-spawn will fail because "_grant_pattern_seed_" is not a valid');
|
|
4035
|
-
consola25.info("agent name \u2014 that's expected. The grant just needs to be approved-as-always.)");
|
|
4276
|
+
consola27.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
|
|
4277
|
+
consola27.info("");
|
|
4278
|
+
consola27.info("Next steps for zero-prompt spawn \u2014 both one-time:");
|
|
4279
|
+
consola27.info("");
|
|
4280
|
+
consola27.info(" 1. apes nest enroll # register nest as DDISA agent (creates own auth.json)");
|
|
4281
|
+
consola27.info(" 2. apes nest authorize # set YOLO-policy on the nest agent");
|
|
4282
|
+
consola27.info("");
|
|
4283
|
+
consola27.info("After that, every `POST http://127.0.0.1:9091/agents` runs without DDISA prompts.");
|
|
4036
4284
|
}
|
|
4037
4285
|
});
|
|
4038
4286
|
|
|
4039
4287
|
// src/commands/nest/status.ts
|
|
4040
4288
|
import process2 from "process";
|
|
4041
|
-
import { defineCommand as
|
|
4042
|
-
import
|
|
4289
|
+
import { defineCommand as defineCommand32 } from "citty";
|
|
4290
|
+
import consola28 from "consola";
|
|
4043
4291
|
var DEFAULT_PORT = 9091;
|
|
4044
|
-
var statusNestCommand =
|
|
4292
|
+
var statusNestCommand = defineCommand32({
|
|
4045
4293
|
meta: {
|
|
4046
4294
|
name: "status",
|
|
4047
4295
|
description: "Print state of the local nest-daemon (agents registered, processes supervised)"
|
|
@@ -4061,8 +4309,8 @@ var statusNestCommand = defineCommand30({
|
|
|
4061
4309
|
} catch (err) {
|
|
4062
4310
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4063
4311
|
if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
|
|
4064
|
-
|
|
4065
|
-
|
|
4312
|
+
consola28.error(`Nest daemon is not running at http://127.0.0.1:${port}`);
|
|
4313
|
+
consola28.info(" Run: apes nest install");
|
|
4066
4314
|
process2.exit(2);
|
|
4067
4315
|
}
|
|
4068
4316
|
throw err;
|
|
@@ -4071,15 +4319,15 @@ var statusNestCommand = defineCommand30({
|
|
|
4071
4319
|
console.log(JSON.stringify(status, null, 2));
|
|
4072
4320
|
return;
|
|
4073
4321
|
}
|
|
4074
|
-
|
|
4322
|
+
consola28.info(`Nest at http://127.0.0.1:${port} \u2014 ${status.agents} agent(s) registered, ${status.processes.length} supervised`);
|
|
4075
4323
|
if (status.processes.length === 0) {
|
|
4076
|
-
|
|
4324
|
+
consola28.info(" (no processes running)");
|
|
4077
4325
|
return;
|
|
4078
4326
|
}
|
|
4079
4327
|
for (const p of status.processes) {
|
|
4080
4328
|
const uptime = humanDuration(p.uptimeSec);
|
|
4081
4329
|
const crashTag = p.consecutiveCrashes > 0 ? ` \u26A0 ${p.consecutiveCrashes} crash(es)` : "";
|
|
4082
|
-
|
|
4330
|
+
consola28.info(` ${p.name.padEnd(16)} pid=${String(p.pid).padEnd(6)} up=${uptime}${crashTag}`);
|
|
4083
4331
|
}
|
|
4084
4332
|
}
|
|
4085
4333
|
});
|
|
@@ -4092,57 +4340,59 @@ function humanDuration(sec) {
|
|
|
4092
4340
|
|
|
4093
4341
|
// src/commands/nest/uninstall.ts
|
|
4094
4342
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
4095
|
-
import { existsSync as
|
|
4096
|
-
import { homedir as
|
|
4097
|
-
import { join as
|
|
4098
|
-
import { defineCommand as
|
|
4099
|
-
import
|
|
4343
|
+
import { existsSync as existsSync13, unlinkSync } from "fs";
|
|
4344
|
+
import { homedir as homedir12, userInfo as userInfo3 } from "os";
|
|
4345
|
+
import { join as join11 } from "path";
|
|
4346
|
+
import { defineCommand as defineCommand33 } from "citty";
|
|
4347
|
+
import consola29 from "consola";
|
|
4100
4348
|
var PLIST_LABEL2 = "ai.openape.nest";
|
|
4101
|
-
var uninstallNestCommand =
|
|
4349
|
+
var uninstallNestCommand = defineCommand33({
|
|
4102
4350
|
meta: {
|
|
4103
4351
|
name: "uninstall",
|
|
4104
4352
|
description: "Stop + remove the local nest-daemon (registry + agents preserved)"
|
|
4105
4353
|
},
|
|
4106
4354
|
async run() {
|
|
4107
4355
|
const uid = userInfo3().uid;
|
|
4108
|
-
const path2 =
|
|
4356
|
+
const path2 = join11(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
|
|
4109
4357
|
try {
|
|
4110
4358
|
execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
|
|
4111
|
-
|
|
4359
|
+
consola29.success("Nest daemon stopped");
|
|
4112
4360
|
} catch {
|
|
4113
|
-
|
|
4361
|
+
consola29.info("Nest daemon was not loaded");
|
|
4114
4362
|
}
|
|
4115
|
-
if (
|
|
4363
|
+
if (existsSync13(path2)) {
|
|
4116
4364
|
unlinkSync(path2);
|
|
4117
|
-
|
|
4365
|
+
consola29.success(`Removed ${path2}`);
|
|
4118
4366
|
}
|
|
4119
|
-
|
|
4367
|
+
consola29.info("Registry at ~/.openape/nest/agents.json kept \u2014 re-run `apes nest install` to resume supervision.");
|
|
4120
4368
|
}
|
|
4121
4369
|
});
|
|
4122
4370
|
|
|
4123
4371
|
// src/commands/nest/index.ts
|
|
4124
|
-
var nestCommand =
|
|
4372
|
+
var nestCommand = defineCommand34({
|
|
4125
4373
|
meta: {
|
|
4126
4374
|
name: "nest",
|
|
4127
|
-
description: "Manage the local Nest control-plane daemon (install
|
|
4375
|
+
description: "Manage the local Nest control-plane daemon (install / enroll / authorize / status / uninstall). One-time setup: `install` (launchd) \u2192 `enroll` (own DDISA identity) \u2192 `authorize` (YOLO-policy). After that, `apes agents spawn` runs without per-call DDISA approvals."
|
|
4128
4376
|
},
|
|
4129
4377
|
subCommands: {
|
|
4130
4378
|
install: installNestCommand,
|
|
4379
|
+
enroll: enrollNestCommand,
|
|
4380
|
+
authorize: authorizeNestCommand,
|
|
4131
4381
|
status: statusNestCommand,
|
|
4132
4382
|
uninstall: uninstallNestCommand
|
|
4133
4383
|
}
|
|
4134
4384
|
});
|
|
4135
4385
|
|
|
4136
4386
|
// src/commands/adapter/index.ts
|
|
4137
|
-
import { defineCommand as
|
|
4138
|
-
import
|
|
4139
|
-
var adapterCommand =
|
|
4387
|
+
import { defineCommand as defineCommand35 } from "citty";
|
|
4388
|
+
import consola30 from "consola";
|
|
4389
|
+
var adapterCommand = defineCommand35({
|
|
4140
4390
|
meta: {
|
|
4141
4391
|
name: "adapter",
|
|
4142
4392
|
description: "Manage CLI adapters"
|
|
4143
4393
|
},
|
|
4144
4394
|
subCommands: {
|
|
4145
|
-
list:
|
|
4395
|
+
list: defineCommand35({
|
|
4146
4396
|
meta: {
|
|
4147
4397
|
name: "list",
|
|
4148
4398
|
description: "List available adapters"
|
|
@@ -4173,7 +4423,7 @@ var adapterCommand = defineCommand33({
|
|
|
4173
4423
|
`);
|
|
4174
4424
|
return;
|
|
4175
4425
|
}
|
|
4176
|
-
|
|
4426
|
+
consola30.info(`Registry: ${index2.adapters.length} adapters (${index2.generated_at})`);
|
|
4177
4427
|
for (const a of index2.adapters) {
|
|
4178
4428
|
const installed = isInstalled(a.id, false) ? " [installed]" : "";
|
|
4179
4429
|
console.log(` ${a.id.padEnd(12)} ${a.name.padEnd(24)} ${a.category}${installed}`);
|
|
@@ -4195,7 +4445,7 @@ var adapterCommand = defineCommand33({
|
|
|
4195
4445
|
return;
|
|
4196
4446
|
}
|
|
4197
4447
|
if (local.length === 0) {
|
|
4198
|
-
|
|
4448
|
+
consola30.info("No adapters installed. Use `apes adapter list --remote` to see available adapters.");
|
|
4199
4449
|
return;
|
|
4200
4450
|
}
|
|
4201
4451
|
for (const a of local) {
|
|
@@ -4203,7 +4453,7 @@ var adapterCommand = defineCommand33({
|
|
|
4203
4453
|
}
|
|
4204
4454
|
}
|
|
4205
4455
|
}),
|
|
4206
|
-
install:
|
|
4456
|
+
install: defineCommand35({
|
|
4207
4457
|
meta: {
|
|
4208
4458
|
name: "install",
|
|
4209
4459
|
description: "Install an adapter from the registry"
|
|
@@ -4232,24 +4482,24 @@ var adapterCommand = defineCommand33({
|
|
|
4232
4482
|
for (const id of ids) {
|
|
4233
4483
|
const entry = findAdapter(index, id);
|
|
4234
4484
|
if (!entry) {
|
|
4235
|
-
|
|
4485
|
+
consola30.error(`Adapter "${id}" not found in registry. Use \`apes adapter search ${id}\` to search.`);
|
|
4236
4486
|
continue;
|
|
4237
4487
|
}
|
|
4238
4488
|
const conflicts = findConflictingAdapters(entry.executable, id);
|
|
4239
4489
|
if (conflicts.length > 0) {
|
|
4240
4490
|
for (const c of conflicts) {
|
|
4241
|
-
|
|
4242
|
-
|
|
4491
|
+
consola30.warn(`Conflicting adapter found: ${c.path} (id: ${c.adapterId}, executable: ${c.executable})`);
|
|
4492
|
+
consola30.warn(` Remove it with: apes adapter remove ${c.adapterId}`);
|
|
4243
4493
|
}
|
|
4244
4494
|
}
|
|
4245
4495
|
const result = await installAdapter(entry, { local });
|
|
4246
4496
|
const verb = result.updated ? "Updated" : "Installed";
|
|
4247
|
-
|
|
4248
|
-
|
|
4497
|
+
consola30.success(`${verb} ${result.id} \u2192 ${result.path}`);
|
|
4498
|
+
consola30.info(`Digest: ${result.digest}`);
|
|
4249
4499
|
}
|
|
4250
4500
|
}
|
|
4251
4501
|
}),
|
|
4252
|
-
remove:
|
|
4502
|
+
remove: defineCommand35({
|
|
4253
4503
|
meta: {
|
|
4254
4504
|
name: "remove",
|
|
4255
4505
|
description: "Remove an installed adapter"
|
|
@@ -4272,9 +4522,9 @@ var adapterCommand = defineCommand33({
|
|
|
4272
4522
|
let failed = false;
|
|
4273
4523
|
for (const id of ids) {
|
|
4274
4524
|
if (removeAdapter(id, local)) {
|
|
4275
|
-
|
|
4525
|
+
consola30.success(`Removed adapter: ${id}`);
|
|
4276
4526
|
} else {
|
|
4277
|
-
|
|
4527
|
+
consola30.error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4278
4528
|
failed = true;
|
|
4279
4529
|
}
|
|
4280
4530
|
}
|
|
@@ -4282,7 +4532,7 @@ var adapterCommand = defineCommand33({
|
|
|
4282
4532
|
throw new CliError("Some adapters could not be removed");
|
|
4283
4533
|
}
|
|
4284
4534
|
}),
|
|
4285
|
-
info:
|
|
4535
|
+
info: defineCommand35({
|
|
4286
4536
|
meta: {
|
|
4287
4537
|
name: "info",
|
|
4288
4538
|
description: "Show detailed adapter information"
|
|
@@ -4324,7 +4574,7 @@ var adapterCommand = defineCommand33({
|
|
|
4324
4574
|
}
|
|
4325
4575
|
}
|
|
4326
4576
|
}),
|
|
4327
|
-
search:
|
|
4577
|
+
search: defineCommand35({
|
|
4328
4578
|
meta: {
|
|
4329
4579
|
name: "search",
|
|
4330
4580
|
description: "Search adapters in the registry"
|
|
@@ -4356,7 +4606,7 @@ var adapterCommand = defineCommand33({
|
|
|
4356
4606
|
return;
|
|
4357
4607
|
}
|
|
4358
4608
|
if (results.length === 0) {
|
|
4359
|
-
|
|
4609
|
+
consola30.info(`No adapters matching "${query}"`);
|
|
4360
4610
|
return;
|
|
4361
4611
|
}
|
|
4362
4612
|
for (const a of results) {
|
|
@@ -4365,7 +4615,7 @@ var adapterCommand = defineCommand33({
|
|
|
4365
4615
|
}
|
|
4366
4616
|
}
|
|
4367
4617
|
}),
|
|
4368
|
-
update:
|
|
4618
|
+
update: defineCommand35({
|
|
4369
4619
|
meta: {
|
|
4370
4620
|
name: "update",
|
|
4371
4621
|
description: "Update installed adapters"
|
|
@@ -4391,33 +4641,33 @@ var adapterCommand = defineCommand33({
|
|
|
4391
4641
|
const targetId = args.id ? String(args.id) : void 0;
|
|
4392
4642
|
const targets = targetId ? [targetId] : index.adapters.map((a) => a.id).filter((id) => isInstalled(id, false));
|
|
4393
4643
|
if (targets.length === 0) {
|
|
4394
|
-
|
|
4644
|
+
consola30.info("No adapters installed to update.");
|
|
4395
4645
|
return;
|
|
4396
4646
|
}
|
|
4397
4647
|
for (const id of targets) {
|
|
4398
4648
|
const entry = findAdapter(index, id);
|
|
4399
4649
|
if (!entry) {
|
|
4400
|
-
|
|
4650
|
+
consola30.warn(`${id}: not found in registry, skipping`);
|
|
4401
4651
|
continue;
|
|
4402
4652
|
}
|
|
4403
4653
|
const localDigest = getInstalledDigest(id, false);
|
|
4404
4654
|
if (localDigest === entry.digest) {
|
|
4405
|
-
|
|
4655
|
+
consola30.info(`${id}: already up to date`);
|
|
4406
4656
|
continue;
|
|
4407
4657
|
}
|
|
4408
4658
|
if (localDigest && !args.yes) {
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4659
|
+
consola30.warn(`${id}: digest will change \u2014 existing grants for this adapter will be invalidated`);
|
|
4660
|
+
consola30.info(` Old: ${localDigest}`);
|
|
4661
|
+
consola30.info(` New: ${entry.digest}`);
|
|
4662
|
+
consola30.info(" Use --yes to confirm");
|
|
4413
4663
|
continue;
|
|
4414
4664
|
}
|
|
4415
4665
|
const result = await installAdapter(entry);
|
|
4416
|
-
|
|
4666
|
+
consola30.success(`Updated ${result.id} \u2192 ${result.path}`);
|
|
4417
4667
|
}
|
|
4418
4668
|
}
|
|
4419
4669
|
}),
|
|
4420
|
-
verify:
|
|
4670
|
+
verify: defineCommand35({
|
|
4421
4671
|
meta: {
|
|
4422
4672
|
name: "verify",
|
|
4423
4673
|
description: "Verify installed adapter against registry digest"
|
|
@@ -4450,7 +4700,7 @@ var adapterCommand = defineCommand33({
|
|
|
4450
4700
|
if (!localDigest)
|
|
4451
4701
|
throw new Error(`Adapter "${id}" is not installed${local ? " locally" : ""}`);
|
|
4452
4702
|
if (localDigest === entry.digest) {
|
|
4453
|
-
|
|
4703
|
+
consola30.success(`${id}: digest matches registry`);
|
|
4454
4704
|
} else {
|
|
4455
4705
|
console.log(` Local: ${localDigest}`);
|
|
4456
4706
|
console.log(` Registry: ${entry.digest}`);
|
|
@@ -4463,10 +4713,10 @@ var adapterCommand = defineCommand33({
|
|
|
4463
4713
|
|
|
4464
4714
|
// src/commands/run.ts
|
|
4465
4715
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
4466
|
-
import { hostname as
|
|
4716
|
+
import { hostname as hostname5 } from "os";
|
|
4467
4717
|
import { basename } from "path";
|
|
4468
|
-
import { defineCommand as
|
|
4469
|
-
import
|
|
4718
|
+
import { defineCommand as defineCommand36 } from "citty";
|
|
4719
|
+
import consola31 from "consola";
|
|
4470
4720
|
function shouldWaitForGrant(args) {
|
|
4471
4721
|
return args.wait === true || process.env.APE_WAIT === "1";
|
|
4472
4722
|
}
|
|
@@ -4503,7 +4753,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4503
4753
|
const statusCmd = `apes grants status ${grant.id}`;
|
|
4504
4754
|
const executeCmd = `apes grants run ${grant.id}`;
|
|
4505
4755
|
if (mode === "human") {
|
|
4506
|
-
|
|
4756
|
+
consola31.success(`Grant ${grant.id} created \u2014 awaiting your approval`);
|
|
4507
4757
|
console.log(` Approve in browser: ${approveUrl}`);
|
|
4508
4758
|
console.log(` Check status: ${statusCmd}`);
|
|
4509
4759
|
console.log(` Run after approval: ${executeCmd}`);
|
|
@@ -4513,7 +4763,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4513
4763
|
return;
|
|
4514
4764
|
}
|
|
4515
4765
|
const maxMin = getPollMaxMinutes();
|
|
4516
|
-
|
|
4766
|
+
consola31.success(`Grant ${grant.id} created (pending approval)`);
|
|
4517
4767
|
console.log(` Approve: ${approveUrl}`);
|
|
4518
4768
|
console.log(` Status: ${statusCmd} [--json]`);
|
|
4519
4769
|
console.log(` Execute: ${executeCmd} --wait`);
|
|
@@ -4535,7 +4785,7 @@ function printPendingGrantInfo(grant, idp) {
|
|
|
4535
4785
|
console.log(' Tip: Approve as "timed" or "always" in the browser to let this');
|
|
4536
4786
|
console.log(" grant be reused on subsequent invocations without re-approval.");
|
|
4537
4787
|
}
|
|
4538
|
-
var runCommand =
|
|
4788
|
+
var runCommand = defineCommand36({
|
|
4539
4789
|
meta: {
|
|
4540
4790
|
name: "run",
|
|
4541
4791
|
description: "Execute a grant-secured command"
|
|
@@ -4624,7 +4874,7 @@ async function runShellMode(command, args) {
|
|
|
4624
4874
|
const adapterHandled = await tryAdapterModeFromShell(command, idp, args);
|
|
4625
4875
|
if (adapterHandled) return;
|
|
4626
4876
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
4627
|
-
const targetHost = args.host ||
|
|
4877
|
+
const targetHost = args.host || hostname5();
|
|
4628
4878
|
try {
|
|
4629
4879
|
const grants = await apiFetch(
|
|
4630
4880
|
`${grantsUrl}?requester=${encodeURIComponent(auth.email)}&status=approved&limit=20`
|
|
@@ -4638,7 +4888,7 @@ async function runShellMode(command, args) {
|
|
|
4638
4888
|
}
|
|
4639
4889
|
} catch {
|
|
4640
4890
|
}
|
|
4641
|
-
|
|
4891
|
+
consola31.info(`Requesting ape-shell session grant on ${targetHost}`);
|
|
4642
4892
|
const grant = await apiFetch(grantsUrl, {
|
|
4643
4893
|
method: "POST",
|
|
4644
4894
|
body: {
|
|
@@ -4658,8 +4908,8 @@ async function runShellMode(command, args) {
|
|
|
4658
4908
|
host: targetHost
|
|
4659
4909
|
});
|
|
4660
4910
|
if (shouldWaitForGrant(args)) {
|
|
4661
|
-
|
|
4662
|
-
|
|
4911
|
+
consola31.info(`Grant requested: ${grant.id}`);
|
|
4912
|
+
consola31.info("Waiting for approval...");
|
|
4663
4913
|
const maxWait = 3e5;
|
|
4664
4914
|
const interval = 3e3;
|
|
4665
4915
|
const start = Date.now();
|
|
@@ -4690,13 +4940,13 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4690
4940
|
try {
|
|
4691
4941
|
resolved = await resolveCommand(loaded, [normalizedExecutable, ...parsed.argv]);
|
|
4692
4942
|
} catch (err) {
|
|
4693
|
-
|
|
4943
|
+
consola31.debug(`ape-shell: adapter resolve failed for "${parsed.raw}":`, err);
|
|
4694
4944
|
return false;
|
|
4695
4945
|
}
|
|
4696
4946
|
try {
|
|
4697
4947
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
4698
4948
|
if (existingGrantId) {
|
|
4699
|
-
|
|
4949
|
+
consola31.info(`Reusing grant ${existingGrantId} for: ${resolved.detail.display}`);
|
|
4700
4950
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
4701
4951
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
4702
4952
|
return true;
|
|
@@ -4704,7 +4954,7 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4704
4954
|
} catch {
|
|
4705
4955
|
}
|
|
4706
4956
|
const approval = args.approval ?? "once";
|
|
4707
|
-
|
|
4957
|
+
consola31.info(`Requesting grant for: ${resolved.detail.display}`);
|
|
4708
4958
|
const grant = await createShapesGrant(resolved, {
|
|
4709
4959
|
idp,
|
|
4710
4960
|
approval,
|
|
@@ -4712,19 +4962,19 @@ async function tryAdapterModeFromShell(command, idp, args) {
|
|
|
4712
4962
|
});
|
|
4713
4963
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
4714
4964
|
const n = grant.similar_grants.similar_grants.length;
|
|
4715
|
-
|
|
4716
|
-
|
|
4965
|
+
consola31.info("");
|
|
4966
|
+
consola31.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
4717
4967
|
}
|
|
4718
4968
|
notifyGrantPending({
|
|
4719
4969
|
grantId: grant.id,
|
|
4720
4970
|
approveUrl: `${idp}/grant-approval?grant_id=${grant.id}`,
|
|
4721
4971
|
command: resolved.detail?.display || parsed?.raw || "unknown",
|
|
4722
4972
|
audience: resolved.adapter?.cli?.audience ?? "shapes",
|
|
4723
|
-
host: args.host ||
|
|
4973
|
+
host: args.host || hostname5()
|
|
4724
4974
|
});
|
|
4725
4975
|
if (shouldWaitForGrant(args)) {
|
|
4726
|
-
|
|
4727
|
-
|
|
4976
|
+
consola31.info(`Grant requested: ${grant.id}`);
|
|
4977
|
+
consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4728
4978
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
4729
4979
|
if (status !== "approved")
|
|
4730
4980
|
throw new CliError(`Grant ${status}`);
|
|
@@ -4798,7 +5048,7 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
4798
5048
|
try {
|
|
4799
5049
|
const existingGrantId = await findExistingGrant(resolved, idp);
|
|
4800
5050
|
if (existingGrantId) {
|
|
4801
|
-
|
|
5051
|
+
consola31.info(`Reusing existing grant: ${existingGrantId}`);
|
|
4802
5052
|
const token = await fetchGrantToken(idp, existingGrantId);
|
|
4803
5053
|
await verifyAndExecute(token, resolved, existingGrantId);
|
|
4804
5054
|
return;
|
|
@@ -4812,17 +5062,17 @@ async function runAdapterMode(command, rawArgs, args) {
|
|
|
4812
5062
|
});
|
|
4813
5063
|
if (grant.similar_grants?.similar_grants?.length) {
|
|
4814
5064
|
const n = grant.similar_grants.similar_grants.length;
|
|
4815
|
-
|
|
4816
|
-
|
|
5065
|
+
consola31.info("");
|
|
5066
|
+
consola31.info(` Similar grant(s) found (${n}). Your approver can extend an existing grant to cover this request.`);
|
|
4817
5067
|
if (grant.similar_grants.widened_details?.length) {
|
|
4818
5068
|
const wider = grant.similar_grants.widened_details.map((d) => d.permission).join(", ");
|
|
4819
|
-
|
|
5069
|
+
consola31.info(` Broader scope: ${wider}`);
|
|
4820
5070
|
}
|
|
4821
|
-
|
|
5071
|
+
consola31.info("");
|
|
4822
5072
|
}
|
|
4823
5073
|
if (shouldWaitForGrant(args)) {
|
|
4824
|
-
|
|
4825
|
-
|
|
5074
|
+
consola31.info(`Grant requested: ${grant.id}`);
|
|
5075
|
+
consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
4826
5076
|
const status = await waitForGrantStatus(idp, grant.id);
|
|
4827
5077
|
if (status !== "approved")
|
|
4828
5078
|
throw new Error(`Grant ${status}`);
|
|
@@ -4841,8 +5091,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4841
5091
|
const idp = getIdpUrl(args.idp);
|
|
4842
5092
|
const grantsUrl = await getGrantsEndpoint(idp);
|
|
4843
5093
|
const command = action.split(" ");
|
|
4844
|
-
const targetHost = args.host ||
|
|
4845
|
-
|
|
5094
|
+
const targetHost = args.host || hostname5();
|
|
5095
|
+
consola31.info(`Requesting ${audience} grant on ${targetHost}: ${command.join(" ")}`);
|
|
4846
5096
|
const grant = await apiFetch(grantsUrl, {
|
|
4847
5097
|
method: "POST",
|
|
4848
5098
|
body: {
|
|
@@ -4859,9 +5109,9 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4859
5109
|
printPendingGrantInfo(grant, idp);
|
|
4860
5110
|
throw new CliExit(getAsyncExitCode());
|
|
4861
5111
|
}
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
5112
|
+
consola31.success(`Grant requested: ${grant.id}`);
|
|
5113
|
+
consola31.info(`Approve at: ${idp}/grant-approval?grant_id=${grant.id}`);
|
|
5114
|
+
consola31.info("Waiting for approval...");
|
|
4865
5115
|
const maxWait = 15 * 60 * 1e3;
|
|
4866
5116
|
const interval = 3e3;
|
|
4867
5117
|
const start = Date.now();
|
|
@@ -4869,7 +5119,7 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4869
5119
|
while (Date.now() - start < maxWait) {
|
|
4870
5120
|
const status = await apiFetch(`${grantsUrl}/${grant.id}`);
|
|
4871
5121
|
if (status.status === "approved") {
|
|
4872
|
-
|
|
5122
|
+
consola31.success("Grant approved!");
|
|
4873
5123
|
approved = true;
|
|
4874
5124
|
break;
|
|
4875
5125
|
}
|
|
@@ -4884,12 +5134,12 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4884
5134
|
`Grant approval timed out after ${minutes} min (still pending). Check your DDISA inbox at ${idp}/grant-approval?grant_id=${grant.id} \u2014 if approved later, re-run the same \`apes run\` command and it will reuse the grant.`
|
|
4885
5135
|
);
|
|
4886
5136
|
}
|
|
4887
|
-
|
|
5137
|
+
consola31.info("Fetching grant token...");
|
|
4888
5138
|
const { authz_jwt } = await apiFetch(`${grantsUrl}/${grant.id}/token`, {
|
|
4889
5139
|
method: "POST"
|
|
4890
5140
|
});
|
|
4891
5141
|
if (audience === "escapes") {
|
|
4892
|
-
|
|
5142
|
+
consola31.info(`Executing: ${command.join(" ")}`);
|
|
4893
5143
|
try {
|
|
4894
5144
|
const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
|
|
4895
5145
|
execFileSync11(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
|
|
@@ -4907,8 +5157,8 @@ async function runAudienceMode(audience, action, args) {
|
|
|
4907
5157
|
|
|
4908
5158
|
// src/commands/proxy.ts
|
|
4909
5159
|
import { spawn as spawn2 } from "child_process";
|
|
4910
|
-
import { defineCommand as
|
|
4911
|
-
import
|
|
5160
|
+
import { defineCommand as defineCommand37 } from "citty";
|
|
5161
|
+
import consola32 from "consola";
|
|
4912
5162
|
|
|
4913
5163
|
// src/proxy/config.ts
|
|
4914
5164
|
function buildDefaultProxyConfigToml(opts) {
|
|
@@ -4942,10 +5192,10 @@ note = "VPC-internal hostname suffix"
|
|
|
4942
5192
|
|
|
4943
5193
|
// src/proxy/local-proxy.ts
|
|
4944
5194
|
import { spawn } from "child_process";
|
|
4945
|
-
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as
|
|
5195
|
+
import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
|
|
4946
5196
|
import { createRequire } from "module";
|
|
4947
5197
|
import { tmpdir as tmpdir3 } from "os";
|
|
4948
|
-
import { dirname as
|
|
5198
|
+
import { dirname as dirname4, join as join12, resolve as resolve4 } from "path";
|
|
4949
5199
|
var require2 = createRequire(import.meta.url);
|
|
4950
5200
|
function findProxyBin() {
|
|
4951
5201
|
const pkgPath = require2.resolve("@openape/proxy/package.json");
|
|
@@ -4954,12 +5204,12 @@ function findProxyBin() {
|
|
|
4954
5204
|
if (!binRel) {
|
|
4955
5205
|
throw new Error("@openape/proxy is missing the openape-proxy bin entry");
|
|
4956
5206
|
}
|
|
4957
|
-
return resolve4(
|
|
5207
|
+
return resolve4(dirname4(pkgPath), binRel);
|
|
4958
5208
|
}
|
|
4959
5209
|
async function startEphemeralProxy(configToml) {
|
|
4960
|
-
const tmpDir = mkdtempSync3(
|
|
4961
|
-
const configPath =
|
|
4962
|
-
|
|
5210
|
+
const tmpDir = mkdtempSync3(join12(tmpdir3(), "openape-proxy-"));
|
|
5211
|
+
const configPath = join12(tmpDir, "config.toml");
|
|
5212
|
+
writeFileSync8(configPath, configToml, { mode: 384 });
|
|
4963
5213
|
const binPath = findProxyBin();
|
|
4964
5214
|
const child = spawn(process.execPath, [binPath, "-c", configPath], {
|
|
4965
5215
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -5051,10 +5301,10 @@ function resolveProxyConfigOptions() {
|
|
|
5051
5301
|
77
|
|
5052
5302
|
);
|
|
5053
5303
|
}
|
|
5054
|
-
|
|
5304
|
+
consola32.info(`[apes proxy] IdP-mediated mode \u2014 agent=${auth.email}, idp=${auth.idp}`);
|
|
5055
5305
|
return { agentEmail: auth.email, idpUrl: auth.idp, mediated: true };
|
|
5056
5306
|
}
|
|
5057
|
-
var proxyCommand =
|
|
5307
|
+
var proxyCommand = defineCommand37({
|
|
5058
5308
|
meta: {
|
|
5059
5309
|
name: "proxy",
|
|
5060
5310
|
description: "Run a command with HTTPS_PROXY routed through the OpenApe egress proxy."
|
|
@@ -5076,12 +5326,12 @@ var proxyCommand = defineCommand35({
|
|
|
5076
5326
|
let close = null;
|
|
5077
5327
|
if (reuseUrl) {
|
|
5078
5328
|
proxyUrl = reuseUrl;
|
|
5079
|
-
|
|
5329
|
+
consola32.info(`[apes proxy] reusing existing proxy at ${proxyUrl}`);
|
|
5080
5330
|
} else {
|
|
5081
5331
|
const ephemeral = await startEphemeralProxy(buildDefaultProxyConfigToml(resolveProxyConfigOptions()));
|
|
5082
5332
|
proxyUrl = ephemeral.url;
|
|
5083
5333
|
close = ephemeral.close;
|
|
5084
|
-
|
|
5334
|
+
consola32.info(`[apes proxy] started ephemeral proxy at ${proxyUrl}`);
|
|
5085
5335
|
}
|
|
5086
5336
|
const noProxy = process.env.NO_PROXY ?? process.env.no_proxy ?? "127.0.0.1,localhost";
|
|
5087
5337
|
const childEnv = {
|
|
@@ -5113,7 +5363,7 @@ var proxyCommand = defineCommand35({
|
|
|
5113
5363
|
else resolveExit(code ?? 0);
|
|
5114
5364
|
});
|
|
5115
5365
|
child.once("error", (err) => {
|
|
5116
|
-
|
|
5366
|
+
consola32.error(`[apes proxy] failed to spawn '${wrapped[0]}':`, err.message);
|
|
5117
5367
|
resolveExit(127);
|
|
5118
5368
|
});
|
|
5119
5369
|
});
|
|
@@ -5127,8 +5377,8 @@ function signalNumber(signal) {
|
|
|
5127
5377
|
}
|
|
5128
5378
|
|
|
5129
5379
|
// src/commands/explain.ts
|
|
5130
|
-
import { defineCommand as
|
|
5131
|
-
var explainCommand =
|
|
5380
|
+
import { defineCommand as defineCommand38 } from "citty";
|
|
5381
|
+
var explainCommand = defineCommand38({
|
|
5132
5382
|
meta: {
|
|
5133
5383
|
name: "explain",
|
|
5134
5384
|
description: "Show what permission a command would need"
|
|
@@ -5166,9 +5416,9 @@ var explainCommand = defineCommand36({
|
|
|
5166
5416
|
});
|
|
5167
5417
|
|
|
5168
5418
|
// src/commands/config/get.ts
|
|
5169
|
-
import { defineCommand as
|
|
5170
|
-
import
|
|
5171
|
-
var configGetCommand =
|
|
5419
|
+
import { defineCommand as defineCommand39 } from "citty";
|
|
5420
|
+
import consola33 from "consola";
|
|
5421
|
+
var configGetCommand = defineCommand39({
|
|
5172
5422
|
meta: {
|
|
5173
5423
|
name: "get",
|
|
5174
5424
|
description: "Get a configuration value"
|
|
@@ -5188,7 +5438,7 @@ var configGetCommand = defineCommand37({
|
|
|
5188
5438
|
if (idp)
|
|
5189
5439
|
console.log(idp);
|
|
5190
5440
|
else
|
|
5191
|
-
|
|
5441
|
+
consola33.info("No IdP configured.");
|
|
5192
5442
|
break;
|
|
5193
5443
|
}
|
|
5194
5444
|
case "email": {
|
|
@@ -5196,7 +5446,7 @@ var configGetCommand = defineCommand37({
|
|
|
5196
5446
|
if (auth?.email)
|
|
5197
5447
|
console.log(auth.email);
|
|
5198
5448
|
else
|
|
5199
|
-
|
|
5449
|
+
consola33.info("Not logged in.");
|
|
5200
5450
|
break;
|
|
5201
5451
|
}
|
|
5202
5452
|
default: {
|
|
@@ -5209,7 +5459,7 @@ var configGetCommand = defineCommand37({
|
|
|
5209
5459
|
if (sectionObj && field in sectionObj) {
|
|
5210
5460
|
console.log(sectionObj[field]);
|
|
5211
5461
|
} else {
|
|
5212
|
-
|
|
5462
|
+
consola33.info(`Key "${key}" not set.`);
|
|
5213
5463
|
}
|
|
5214
5464
|
} else {
|
|
5215
5465
|
throw new CliError(`Unknown key: "${key}". Use: idp, email, defaults.idp, defaults.approval, agent.key, agent.email`);
|
|
@@ -5220,9 +5470,9 @@ var configGetCommand = defineCommand37({
|
|
|
5220
5470
|
});
|
|
5221
5471
|
|
|
5222
5472
|
// src/commands/config/set.ts
|
|
5223
|
-
import { defineCommand as
|
|
5224
|
-
import
|
|
5225
|
-
var configSetCommand =
|
|
5473
|
+
import { defineCommand as defineCommand40 } from "citty";
|
|
5474
|
+
import consola34 from "consola";
|
|
5475
|
+
var configSetCommand = defineCommand40({
|
|
5226
5476
|
meta: {
|
|
5227
5477
|
name: "set",
|
|
5228
5478
|
description: "Set a configuration value"
|
|
@@ -5258,12 +5508,12 @@ var configSetCommand = defineCommand38({
|
|
|
5258
5508
|
throw new CliError(`Unknown section: "${section}". Use: defaults, agent`);
|
|
5259
5509
|
}
|
|
5260
5510
|
saveConfig(config);
|
|
5261
|
-
|
|
5511
|
+
consola34.success(`Set ${key} = ${value}`);
|
|
5262
5512
|
}
|
|
5263
5513
|
});
|
|
5264
5514
|
|
|
5265
5515
|
// src/commands/fetch/index.ts
|
|
5266
|
-
import { defineCommand as
|
|
5516
|
+
import { defineCommand as defineCommand41 } from "citty";
|
|
5267
5517
|
async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
5268
5518
|
const token = getAuthToken();
|
|
5269
5519
|
if (!token) {
|
|
@@ -5299,13 +5549,13 @@ async function doRequest(method, url, body, contentType, raw, showHeaders) {
|
|
|
5299
5549
|
throw new CliError(`HTTP ${response.status} ${response.statusText}`);
|
|
5300
5550
|
}
|
|
5301
5551
|
}
|
|
5302
|
-
var fetchCommand =
|
|
5552
|
+
var fetchCommand = defineCommand41({
|
|
5303
5553
|
meta: {
|
|
5304
5554
|
name: "fetch",
|
|
5305
5555
|
description: "Make authenticated HTTP requests"
|
|
5306
5556
|
},
|
|
5307
5557
|
subCommands: {
|
|
5308
|
-
get:
|
|
5558
|
+
get: defineCommand41({
|
|
5309
5559
|
meta: {
|
|
5310
5560
|
name: "get",
|
|
5311
5561
|
description: "GET request with auth token"
|
|
@@ -5331,7 +5581,7 @@ var fetchCommand = defineCommand39({
|
|
|
5331
5581
|
await doRequest("GET", String(args.url), void 0, "application/json", Boolean(args.raw), Boolean(args.headers));
|
|
5332
5582
|
}
|
|
5333
5583
|
}),
|
|
5334
|
-
post:
|
|
5584
|
+
post: defineCommand41({
|
|
5335
5585
|
meta: {
|
|
5336
5586
|
name: "post",
|
|
5337
5587
|
description: "POST request with auth token"
|
|
@@ -5370,8 +5620,8 @@ var fetchCommand = defineCommand39({
|
|
|
5370
5620
|
});
|
|
5371
5621
|
|
|
5372
5622
|
// src/commands/mcp/index.ts
|
|
5373
|
-
import { defineCommand as
|
|
5374
|
-
var mcpCommand =
|
|
5623
|
+
import { defineCommand as defineCommand42 } from "citty";
|
|
5624
|
+
var mcpCommand = defineCommand42({
|
|
5375
5625
|
meta: {
|
|
5376
5626
|
name: "mcp",
|
|
5377
5627
|
description: "Start MCP server for AI agents"
|
|
@@ -5394,25 +5644,25 @@ var mcpCommand = defineCommand40({
|
|
|
5394
5644
|
if (transport !== "stdio" && transport !== "sse") {
|
|
5395
5645
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
5396
5646
|
}
|
|
5397
|
-
const { startMcpServer } = await import("./server-
|
|
5647
|
+
const { startMcpServer } = await import("./server-PHANS7PS.js");
|
|
5398
5648
|
await startMcpServer(transport, port);
|
|
5399
5649
|
}
|
|
5400
5650
|
});
|
|
5401
5651
|
|
|
5402
5652
|
// src/commands/init/index.ts
|
|
5403
|
-
import { existsSync as
|
|
5653
|
+
import { existsSync as existsSync14, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
|
|
5404
5654
|
import { randomBytes } from "crypto";
|
|
5405
5655
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
5406
|
-
import { join as
|
|
5407
|
-
import { defineCommand as
|
|
5408
|
-
import
|
|
5656
|
+
import { join as join13 } from "path";
|
|
5657
|
+
import { defineCommand as defineCommand43 } from "citty";
|
|
5658
|
+
import consola35 from "consola";
|
|
5409
5659
|
var DEFAULT_IDP_URL = "https://id.openape.at";
|
|
5410
5660
|
async function downloadTemplate(repo, targetDir) {
|
|
5411
5661
|
const { downloadTemplate: gigetDownload } = await import("giget");
|
|
5412
5662
|
await gigetDownload(`gh:${repo}`, { dir: targetDir, force: false });
|
|
5413
5663
|
}
|
|
5414
5664
|
function installDeps(dir) {
|
|
5415
|
-
const hasLockFile = (name) =>
|
|
5665
|
+
const hasLockFile = (name) => existsSync14(join13(dir, name));
|
|
5416
5666
|
if (hasLockFile("pnpm-lock.yaml")) {
|
|
5417
5667
|
execFileSync12("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
|
|
5418
5668
|
} else if (hasLockFile("bun.lockb")) {
|
|
@@ -5422,20 +5672,20 @@ function installDeps(dir) {
|
|
|
5422
5672
|
}
|
|
5423
5673
|
}
|
|
5424
5674
|
async function promptChoice(message, choices) {
|
|
5425
|
-
const result = await
|
|
5675
|
+
const result = await consola35.prompt(message, { type: "select", options: choices });
|
|
5426
5676
|
if (typeof result === "symbol") {
|
|
5427
5677
|
throw new CliExit(0);
|
|
5428
5678
|
}
|
|
5429
5679
|
return result;
|
|
5430
5680
|
}
|
|
5431
5681
|
async function promptText(message, defaultValue) {
|
|
5432
|
-
const result = await
|
|
5682
|
+
const result = await consola35.prompt(message, { type: "text", default: defaultValue, placeholder: defaultValue });
|
|
5433
5683
|
if (typeof result === "symbol") {
|
|
5434
5684
|
throw new CliExit(0);
|
|
5435
5685
|
}
|
|
5436
5686
|
return result || defaultValue || "";
|
|
5437
5687
|
}
|
|
5438
|
-
var initCommand =
|
|
5688
|
+
var initCommand = defineCommand43({
|
|
5439
5689
|
meta: {
|
|
5440
5690
|
name: "init",
|
|
5441
5691
|
description: "Scaffold a new OpenApe project"
|
|
@@ -5477,23 +5727,23 @@ var initCommand = defineCommand41({
|
|
|
5477
5727
|
});
|
|
5478
5728
|
async function initSP(targetDir) {
|
|
5479
5729
|
const dir = targetDir || "my-app";
|
|
5480
|
-
if (
|
|
5730
|
+
if (existsSync14(join13(dir, "package.json"))) {
|
|
5481
5731
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5482
5732
|
}
|
|
5483
|
-
|
|
5733
|
+
consola35.start("Scaffolding SP starter...");
|
|
5484
5734
|
await downloadTemplate("openape-ai/openape-sp-starter", dir);
|
|
5485
|
-
|
|
5486
|
-
|
|
5735
|
+
consola35.success("Scaffolded from openape-sp-starter");
|
|
5736
|
+
consola35.start("Installing dependencies...");
|
|
5487
5737
|
installDeps(dir);
|
|
5488
|
-
|
|
5489
|
-
const envExample =
|
|
5490
|
-
const envFile =
|
|
5491
|
-
if (
|
|
5738
|
+
consola35.success("Dependencies installed");
|
|
5739
|
+
const envExample = join13(dir, ".env.example");
|
|
5740
|
+
const envFile = join13(dir, ".env");
|
|
5741
|
+
if (existsSync14(envExample) && !existsSync14(envFile)) {
|
|
5492
5742
|
copyFileSync(envExample, envFile);
|
|
5493
|
-
|
|
5743
|
+
consola35.success(`\`.env\` created (using Free IdP at ${DEFAULT_IDP_URL})`);
|
|
5494
5744
|
}
|
|
5495
5745
|
console.log("");
|
|
5496
|
-
|
|
5746
|
+
consola35.box([
|
|
5497
5747
|
`cd ${dir}`,
|
|
5498
5748
|
"npm run dev",
|
|
5499
5749
|
"",
|
|
@@ -5502,7 +5752,7 @@ async function initSP(targetDir) {
|
|
|
5502
5752
|
}
|
|
5503
5753
|
async function initIdP(targetDir) {
|
|
5504
5754
|
const dir = targetDir || "my-idp";
|
|
5505
|
-
if (
|
|
5755
|
+
if (existsSync14(join13(dir, "package.json"))) {
|
|
5506
5756
|
throw new CliError(`Directory "${dir}" already contains a project.`);
|
|
5507
5757
|
}
|
|
5508
5758
|
const domain = await promptText("Domain for the IdP", "localhost");
|
|
@@ -5512,15 +5762,15 @@ async function initIdP(targetDir) {
|
|
|
5512
5762
|
"s3 (S3-compatible)"
|
|
5513
5763
|
]);
|
|
5514
5764
|
const adminEmail = await promptText("Admin email");
|
|
5515
|
-
|
|
5765
|
+
consola35.start("Scaffolding IdP starter...");
|
|
5516
5766
|
await downloadTemplate("openape-ai/openape-idp-starter", dir);
|
|
5517
|
-
|
|
5518
|
-
|
|
5767
|
+
consola35.success("Scaffolded from openape-idp-starter");
|
|
5768
|
+
consola35.start("Installing dependencies...");
|
|
5519
5769
|
installDeps(dir);
|
|
5520
|
-
|
|
5770
|
+
consola35.success("Dependencies installed");
|
|
5521
5771
|
const sessionSecret = randomBytes(32).toString("hex");
|
|
5522
5772
|
const managementToken = randomBytes(32).toString("hex");
|
|
5523
|
-
|
|
5773
|
+
consola35.success("Secrets generated");
|
|
5524
5774
|
const isLocalhost = domain === "localhost";
|
|
5525
5775
|
const origin = isLocalhost ? "http://localhost:3000" : `https://${domain}`;
|
|
5526
5776
|
const envContent = [
|
|
@@ -5534,11 +5784,11 @@ async function initIdP(targetDir) {
|
|
|
5534
5784
|
`NUXT_OPENAPE_RP_ID=${domain}`,
|
|
5535
5785
|
`NUXT_OPENAPE_RP_ORIGIN=${origin}`
|
|
5536
5786
|
].join("\n");
|
|
5537
|
-
|
|
5787
|
+
writeFileSync9(join13(dir, ".env"), `${envContent}
|
|
5538
5788
|
`, { mode: 384 });
|
|
5539
|
-
|
|
5789
|
+
consola35.success(".env created");
|
|
5540
5790
|
console.log("");
|
|
5541
|
-
|
|
5791
|
+
consola35.box([
|
|
5542
5792
|
`cd ${dir}`,
|
|
5543
5793
|
"npm run dev",
|
|
5544
5794
|
"",
|
|
@@ -5555,11 +5805,11 @@ async function initIdP(targetDir) {
|
|
|
5555
5805
|
|
|
5556
5806
|
// src/commands/enroll.ts
|
|
5557
5807
|
import { Buffer as Buffer5 } from "buffer";
|
|
5558
|
-
import { existsSync as
|
|
5808
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
5559
5809
|
import { execFile as execFile2 } from "child_process";
|
|
5560
5810
|
import { sign as sign2 } from "crypto";
|
|
5561
|
-
import { defineCommand as
|
|
5562
|
-
import
|
|
5811
|
+
import { defineCommand as defineCommand44 } from "citty";
|
|
5812
|
+
import consola36 from "consola";
|
|
5563
5813
|
var DEFAULT_IDP_URL2 = "https://id.openape.at";
|
|
5564
5814
|
var DEFAULT_KEY_PATH = "~/.ssh/id_ed25519";
|
|
5565
5815
|
var POLL_INTERVAL = 3e3;
|
|
@@ -5571,7 +5821,7 @@ function openBrowser2(url) {
|
|
|
5571
5821
|
}
|
|
5572
5822
|
async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
5573
5823
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
5574
|
-
const keyContent =
|
|
5824
|
+
const keyContent = readFileSync12(resolvedKey, "utf-8");
|
|
5575
5825
|
const privateKey = loadEd25519PrivateKey(keyContent);
|
|
5576
5826
|
const challengeUrl = await getAgentChallengeEndpoint(idp);
|
|
5577
5827
|
const authenticateUrl = await getAgentAuthenticateEndpoint(idp);
|
|
@@ -5602,7 +5852,7 @@ async function pollForEnrollment(idp, agentEmail, keyPath) {
|
|
|
5602
5852
|
}
|
|
5603
5853
|
throw new Error("Enrollment timed out. Please check the browser and try again.");
|
|
5604
5854
|
}
|
|
5605
|
-
var enrollCommand =
|
|
5855
|
+
var enrollCommand = defineCommand44({
|
|
5606
5856
|
meta: {
|
|
5607
5857
|
name: "enroll",
|
|
5608
5858
|
description: "Enroll an agent with an Identity Provider"
|
|
@@ -5622,38 +5872,38 @@ var enrollCommand = defineCommand42({
|
|
|
5622
5872
|
}
|
|
5623
5873
|
},
|
|
5624
5874
|
async run({ args }) {
|
|
5625
|
-
const idp = args.idp || await
|
|
5875
|
+
const idp = args.idp || await consola36.prompt("IdP URL", { type: "text", default: DEFAULT_IDP_URL2, placeholder: DEFAULT_IDP_URL2 }).then((r) => {
|
|
5626
5876
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
5627
5877
|
return r;
|
|
5628
5878
|
}) || DEFAULT_IDP_URL2;
|
|
5629
|
-
const agentName = args.name || await
|
|
5879
|
+
const agentName = args.name || await consola36.prompt("Agent name", { type: "text", placeholder: "deploy-bot" }).then((r) => {
|
|
5630
5880
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
5631
5881
|
return r;
|
|
5632
5882
|
});
|
|
5633
5883
|
if (!agentName) {
|
|
5634
5884
|
throw new CliError("Agent name is required.");
|
|
5635
5885
|
}
|
|
5636
|
-
const keyPath = args.key || await
|
|
5886
|
+
const keyPath = args.key || await consola36.prompt("Ed25519 key", { type: "text", default: DEFAULT_KEY_PATH, placeholder: DEFAULT_KEY_PATH }).then((r) => {
|
|
5637
5887
|
if (typeof r === "symbol") throw new CliExit(0);
|
|
5638
5888
|
return r;
|
|
5639
5889
|
}) || DEFAULT_KEY_PATH;
|
|
5640
5890
|
const resolvedKey = resolveKeyPath(keyPath);
|
|
5641
5891
|
let publicKey;
|
|
5642
|
-
if (
|
|
5892
|
+
if (existsSync15(resolvedKey)) {
|
|
5643
5893
|
publicKey = readPublicKey(resolvedKey);
|
|
5644
|
-
|
|
5894
|
+
consola36.success(`Using existing key ${keyPath}`);
|
|
5645
5895
|
} else {
|
|
5646
|
-
|
|
5896
|
+
consola36.start(`Generating Ed25519 key pair at ${keyPath}...`);
|
|
5647
5897
|
publicKey = generateAndSaveKey(keyPath);
|
|
5648
|
-
|
|
5898
|
+
consola36.success(`Key pair generated at ${keyPath}`);
|
|
5649
5899
|
}
|
|
5650
5900
|
const encodedKey = encodeURIComponent(publicKey);
|
|
5651
5901
|
const enrollUrl = `${idp}/enroll?name=${encodeURIComponent(agentName)}&key=${encodedKey}`;
|
|
5652
|
-
|
|
5653
|
-
|
|
5902
|
+
consola36.info("Opening browser for enrollment...");
|
|
5903
|
+
consola36.info(`\u2192 ${idp}/enroll`);
|
|
5654
5904
|
openBrowser2(enrollUrl);
|
|
5655
5905
|
console.log("");
|
|
5656
|
-
const agentEmail = await
|
|
5906
|
+
const agentEmail = await consola36.prompt(
|
|
5657
5907
|
"Agent email (shown in browser after enrollment)",
|
|
5658
5908
|
{ type: "text", placeholder: `agent+${agentName}@...` }
|
|
5659
5909
|
).then((r) => {
|
|
@@ -5663,7 +5913,7 @@ var enrollCommand = defineCommand42({
|
|
|
5663
5913
|
if (!agentEmail) {
|
|
5664
5914
|
throw new CliError("Agent email is required to verify enrollment.");
|
|
5665
5915
|
}
|
|
5666
|
-
|
|
5916
|
+
consola36.start("Verifying enrollment...");
|
|
5667
5917
|
const { token, expiresIn } = await pollForEnrollment(idp, agentEmail, keyPath);
|
|
5668
5918
|
saveAuth({
|
|
5669
5919
|
idp,
|
|
@@ -5675,18 +5925,18 @@ var enrollCommand = defineCommand42({
|
|
|
5675
5925
|
config.defaults = { ...config.defaults, idp };
|
|
5676
5926
|
config.agent = { key: keyPath, email: agentEmail };
|
|
5677
5927
|
saveConfig(config);
|
|
5678
|
-
|
|
5679
|
-
|
|
5928
|
+
consola36.success(`Agent enrolled as ${agentEmail}`);
|
|
5929
|
+
consola36.success("Config saved to ~/.config/apes/");
|
|
5680
5930
|
console.log("");
|
|
5681
|
-
|
|
5931
|
+
consola36.info("Verify with: apes whoami");
|
|
5682
5932
|
}
|
|
5683
5933
|
});
|
|
5684
5934
|
|
|
5685
5935
|
// src/commands/register-user.ts
|
|
5686
|
-
import { existsSync as
|
|
5687
|
-
import { defineCommand as
|
|
5688
|
-
import
|
|
5689
|
-
var registerUserCommand =
|
|
5936
|
+
import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
|
|
5937
|
+
import { defineCommand as defineCommand45 } from "citty";
|
|
5938
|
+
import consola37 from "consola";
|
|
5939
|
+
var registerUserCommand = defineCommand45({
|
|
5690
5940
|
meta: {
|
|
5691
5941
|
name: "register-user",
|
|
5692
5942
|
description: "Register a sub-user with SSH key"
|
|
@@ -5722,8 +5972,8 @@ var registerUserCommand = defineCommand43({
|
|
|
5722
5972
|
throw new CliError("No IdP URL configured. Run `apes login` first.");
|
|
5723
5973
|
}
|
|
5724
5974
|
let publicKey = args.key;
|
|
5725
|
-
if (
|
|
5726
|
-
publicKey =
|
|
5975
|
+
if (existsSync16(args.key)) {
|
|
5976
|
+
publicKey = readFileSync13(args.key, "utf-8").trim();
|
|
5727
5977
|
}
|
|
5728
5978
|
if (!publicKey.startsWith("ssh-ed25519 ")) {
|
|
5729
5979
|
throw new CliError("Public key must be in ssh-ed25519 format.");
|
|
@@ -5741,18 +5991,18 @@ var registerUserCommand = defineCommand43({
|
|
|
5741
5991
|
...userType ? { type: userType } : {}
|
|
5742
5992
|
}
|
|
5743
5993
|
});
|
|
5744
|
-
|
|
5994
|
+
consola37.success(`User registered: ${result.email} (type: ${result.type}, owner: ${result.owner})`);
|
|
5745
5995
|
}
|
|
5746
5996
|
});
|
|
5747
5997
|
|
|
5748
5998
|
// src/commands/utils/index.ts
|
|
5749
|
-
import { defineCommand as
|
|
5999
|
+
import { defineCommand as defineCommand47 } from "citty";
|
|
5750
6000
|
|
|
5751
6001
|
// src/commands/utils/dig.ts
|
|
5752
|
-
import { defineCommand as
|
|
5753
|
-
import
|
|
6002
|
+
import { defineCommand as defineCommand46 } from "citty";
|
|
6003
|
+
import consola38 from "consola";
|
|
5754
6004
|
import { resolveDDISA as resolveDDISA2 } from "@openape/core";
|
|
5755
|
-
var digCommand =
|
|
6005
|
+
var digCommand = defineCommand46({
|
|
5756
6006
|
meta: {
|
|
5757
6007
|
name: "dig",
|
|
5758
6008
|
description: "Resolve DDISA IdP for a domain or email (admin/diag tool)"
|
|
@@ -5825,12 +6075,12 @@ var digCommand = defineCommand44({
|
|
|
5825
6075
|
console.log(` domain: ${domain}`);
|
|
5826
6076
|
console.log("");
|
|
5827
6077
|
if (!result.ddisa.found) {
|
|
5828
|
-
|
|
6078
|
+
consola38.warn(`No DDISA record at _ddisa.${domain}`);
|
|
5829
6079
|
if (result.hint) console.log(`
|
|
5830
6080
|
${result.hint}`);
|
|
5831
6081
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
5832
6082
|
}
|
|
5833
|
-
|
|
6083
|
+
consola38.success(`_ddisa.${domain} \u2192 ${result.ddisa.idp}`);
|
|
5834
6084
|
console.log(` Version: ${result.ddisa.version || "ddisa1"}`);
|
|
5835
6085
|
console.log(` IdP URL: ${result.ddisa.idp}`);
|
|
5836
6086
|
if (result.ddisa.mode) console.log(` Mode: ${result.ddisa.mode}`);
|
|
@@ -5840,13 +6090,13 @@ ${result.hint}`);
|
|
|
5840
6090
|
return;
|
|
5841
6091
|
}
|
|
5842
6092
|
if (result.idpDiscovery.ok) {
|
|
5843
|
-
|
|
6093
|
+
consola38.success(`IdP reachable (${result.idpDiscovery.status ?? 200})`);
|
|
5844
6094
|
if (result.idpDiscovery.issuer) console.log(` Issuer: ${result.idpDiscovery.issuer}`);
|
|
5845
6095
|
if (result.idpDiscovery.ddisaVersion) console.log(` DDISA: v${result.idpDiscovery.ddisaVersion}`);
|
|
5846
6096
|
if (result.idpDiscovery.authMethods?.length) console.log(` Auth: ${result.idpDiscovery.authMethods.join(", ")}`);
|
|
5847
6097
|
if (result.idpDiscovery.grantTypes?.length) console.log(` Grants: ${result.idpDiscovery.grantTypes.join(", ")}`);
|
|
5848
6098
|
} else {
|
|
5849
|
-
|
|
6099
|
+
consola38.warn(`IdP discovery failed${result.idpDiscovery.status ? ` (HTTP ${result.idpDiscovery.status})` : ""}`);
|
|
5850
6100
|
if (result.hint) console.log(`
|
|
5851
6101
|
${result.hint}`);
|
|
5852
6102
|
throw new CliError(`IdP at ${result.ddisa.idp} not reachable`);
|
|
@@ -5855,7 +6105,7 @@ ${result.hint}`);
|
|
|
5855
6105
|
});
|
|
5856
6106
|
|
|
5857
6107
|
// src/commands/utils/index.ts
|
|
5858
|
-
var utilsCommand =
|
|
6108
|
+
var utilsCommand = defineCommand47({
|
|
5859
6109
|
meta: {
|
|
5860
6110
|
name: "utils",
|
|
5861
6111
|
description: "Admin/diagnostic utilities (dig, \u2026)"
|
|
@@ -5866,12 +6116,12 @@ var utilsCommand = defineCommand45({
|
|
|
5866
6116
|
});
|
|
5867
6117
|
|
|
5868
6118
|
// src/commands/sessions/index.ts
|
|
5869
|
-
import { defineCommand as
|
|
6119
|
+
import { defineCommand as defineCommand50 } from "citty";
|
|
5870
6120
|
|
|
5871
6121
|
// src/commands/sessions/list.ts
|
|
5872
|
-
import { defineCommand as
|
|
5873
|
-
import
|
|
5874
|
-
var sessionsListCommand =
|
|
6122
|
+
import { defineCommand as defineCommand48 } from "citty";
|
|
6123
|
+
import consola39 from "consola";
|
|
6124
|
+
var sessionsListCommand = defineCommand48({
|
|
5875
6125
|
meta: {
|
|
5876
6126
|
name: "list",
|
|
5877
6127
|
description: "List your active refresh-token families (one per logged-in device)."
|
|
@@ -5889,7 +6139,7 @@ var sessionsListCommand = defineCommand46({
|
|
|
5889
6139
|
return;
|
|
5890
6140
|
}
|
|
5891
6141
|
if (result.data.length === 0) {
|
|
5892
|
-
|
|
6142
|
+
consola39.info("No active sessions.");
|
|
5893
6143
|
return;
|
|
5894
6144
|
}
|
|
5895
6145
|
for (const f of result.data) {
|
|
@@ -5901,9 +6151,9 @@ var sessionsListCommand = defineCommand46({
|
|
|
5901
6151
|
});
|
|
5902
6152
|
|
|
5903
6153
|
// src/commands/sessions/remove.ts
|
|
5904
|
-
import { defineCommand as
|
|
5905
|
-
import
|
|
5906
|
-
var sessionsRemoveCommand =
|
|
6154
|
+
import { defineCommand as defineCommand49 } from "citty";
|
|
6155
|
+
import consola40 from "consola";
|
|
6156
|
+
var sessionsRemoveCommand = defineCommand49({
|
|
5907
6157
|
meta: {
|
|
5908
6158
|
name: "remove",
|
|
5909
6159
|
description: "Revoke one of your active refresh-token families by id."
|
|
@@ -5919,12 +6169,12 @@ var sessionsRemoveCommand = defineCommand47({
|
|
|
5919
6169
|
const id = String(args.familyId).trim();
|
|
5920
6170
|
if (!id) throw new CliError("familyId required");
|
|
5921
6171
|
await apiFetch(`/api/me/sessions/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
5922
|
-
|
|
6172
|
+
consola40.success(`Session ${id} revoked. The device using it will need to \`apes login\` again on its next refresh.`);
|
|
5923
6173
|
}
|
|
5924
6174
|
});
|
|
5925
6175
|
|
|
5926
6176
|
// src/commands/sessions/index.ts
|
|
5927
|
-
var sessionsCommand =
|
|
6177
|
+
var sessionsCommand = defineCommand50({
|
|
5928
6178
|
meta: {
|
|
5929
6179
|
name: "sessions",
|
|
5930
6180
|
description: "Manage your active refresh-token sessions across devices"
|
|
@@ -5936,10 +6186,10 @@ var sessionsCommand = defineCommand48({
|
|
|
5936
6186
|
});
|
|
5937
6187
|
|
|
5938
6188
|
// src/commands/dns-check.ts
|
|
5939
|
-
import { defineCommand as
|
|
5940
|
-
import
|
|
6189
|
+
import { defineCommand as defineCommand51 } from "citty";
|
|
6190
|
+
import consola41 from "consola";
|
|
5941
6191
|
import { resolveDDISA as resolveDDISA3 } from "@openape/core";
|
|
5942
|
-
var dnsCheckCommand =
|
|
6192
|
+
var dnsCheckCommand = defineCommand51({
|
|
5943
6193
|
meta: {
|
|
5944
6194
|
name: "dns-check",
|
|
5945
6195
|
description: "Validate DDISA DNS TXT records for a domain"
|
|
@@ -5953,7 +6203,7 @@ var dnsCheckCommand = defineCommand49({
|
|
|
5953
6203
|
},
|
|
5954
6204
|
async run({ args }) {
|
|
5955
6205
|
const domain = args.domain;
|
|
5956
|
-
|
|
6206
|
+
consola41.start(`Checking _ddisa.${domain}...`);
|
|
5957
6207
|
try {
|
|
5958
6208
|
const result = await resolveDDISA3(domain);
|
|
5959
6209
|
if (!result) {
|
|
@@ -5962,7 +6212,7 @@ var dnsCheckCommand = defineCommand49({
|
|
|
5962
6212
|
console.log(` _ddisa.${domain} TXT "v=ddisa1 idp=https://id.${domain}"`);
|
|
5963
6213
|
throw new CliError(`No DDISA record found for ${domain}`);
|
|
5964
6214
|
}
|
|
5965
|
-
|
|
6215
|
+
consola41.success(`_ddisa.${domain} \u2192 ${result.idp}`);
|
|
5966
6216
|
console.log("");
|
|
5967
6217
|
console.log(` Version: ${result.version || "ddisa1"}`);
|
|
5968
6218
|
console.log(` IdP URL: ${result.idp}`);
|
|
@@ -5971,14 +6221,14 @@ var dnsCheckCommand = defineCommand49({
|
|
|
5971
6221
|
if (result.priority !== void 0)
|
|
5972
6222
|
console.log(` Priority: ${result.priority}`);
|
|
5973
6223
|
console.log("");
|
|
5974
|
-
|
|
6224
|
+
consola41.start(`Verifying IdP at ${result.idp}...`);
|
|
5975
6225
|
const discoResp = await fetch(`${result.idp}/.well-known/openid-configuration`);
|
|
5976
6226
|
if (!discoResp.ok) {
|
|
5977
|
-
|
|
6227
|
+
consola41.warn(`IdP discovery failed (${discoResp.status}). Is the IdP running at ${result.idp}?`);
|
|
5978
6228
|
return;
|
|
5979
6229
|
}
|
|
5980
6230
|
const disco = await discoResp.json();
|
|
5981
|
-
|
|
6231
|
+
consola41.success(`IdP is reachable`);
|
|
5982
6232
|
console.log(` Issuer: ${disco.issuer}`);
|
|
5983
6233
|
console.log(` DDISA: v${disco.ddisa_version || "?"}`);
|
|
5984
6234
|
if (disco.ddisa_auth_methods_supported) {
|
|
@@ -5996,7 +6246,7 @@ var dnsCheckCommand = defineCommand49({
|
|
|
5996
6246
|
// src/commands/health.ts
|
|
5997
6247
|
import { exec } from "child_process";
|
|
5998
6248
|
import { promisify } from "util";
|
|
5999
|
-
import { defineCommand as
|
|
6249
|
+
import { defineCommand as defineCommand52 } from "citty";
|
|
6000
6250
|
var execAsync = promisify(exec);
|
|
6001
6251
|
async function resolveApeShellPath() {
|
|
6002
6252
|
try {
|
|
@@ -6032,7 +6282,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
6032
6282
|
}
|
|
6033
6283
|
}
|
|
6034
6284
|
async function runHealth(args) {
|
|
6035
|
-
const version = true ? "1.
|
|
6285
|
+
const version = true ? "1.6.0" : "0.0.0";
|
|
6036
6286
|
const auth = loadAuth();
|
|
6037
6287
|
if (!auth) {
|
|
6038
6288
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -6095,7 +6345,7 @@ async function runHealth(args) {
|
|
|
6095
6345
|
throw new CliError(`IdP ${auth.idp} unreachable: ${idpProbe.error}`, 1);
|
|
6096
6346
|
}
|
|
6097
6347
|
}
|
|
6098
|
-
var healthCommand =
|
|
6348
|
+
var healthCommand = defineCommand52({
|
|
6099
6349
|
meta: {
|
|
6100
6350
|
name: "health",
|
|
6101
6351
|
description: "Report CLI diagnostic state (auth, IdP, grants, binaries)"
|
|
@@ -6113,8 +6363,8 @@ var healthCommand = defineCommand50({
|
|
|
6113
6363
|
});
|
|
6114
6364
|
|
|
6115
6365
|
// src/commands/workflows.ts
|
|
6116
|
-
import { defineCommand as
|
|
6117
|
-
import
|
|
6366
|
+
import { defineCommand as defineCommand53 } from "citty";
|
|
6367
|
+
import consola42 from "consola";
|
|
6118
6368
|
|
|
6119
6369
|
// src/guides/index.ts
|
|
6120
6370
|
var guides = [
|
|
@@ -6164,7 +6414,7 @@ var guides = [
|
|
|
6164
6414
|
];
|
|
6165
6415
|
|
|
6166
6416
|
// src/commands/workflows.ts
|
|
6167
|
-
var workflowsCommand =
|
|
6417
|
+
var workflowsCommand = defineCommand53({
|
|
6168
6418
|
meta: {
|
|
6169
6419
|
name: "workflows",
|
|
6170
6420
|
description: "Discover workflow guides"
|
|
@@ -6185,7 +6435,7 @@ var workflowsCommand = defineCommand51({
|
|
|
6185
6435
|
if (args.id) {
|
|
6186
6436
|
const guide = guides.find((g) => g.id === String(args.id));
|
|
6187
6437
|
if (!guide) {
|
|
6188
|
-
|
|
6438
|
+
consola42.info(`Available: ${guides.map((g) => g.id).join(", ")}`);
|
|
6189
6439
|
throw new CliError(`Guide not found: ${args.id}`);
|
|
6190
6440
|
}
|
|
6191
6441
|
if (args.json) {
|
|
@@ -6225,26 +6475,26 @@ var workflowsCommand = defineCommand51({
|
|
|
6225
6475
|
});
|
|
6226
6476
|
|
|
6227
6477
|
// src/version-check.ts
|
|
6228
|
-
import { existsSync as
|
|
6229
|
-
import { homedir as
|
|
6230
|
-
import { join as
|
|
6231
|
-
import
|
|
6478
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
|
|
6479
|
+
import { homedir as homedir13 } from "os";
|
|
6480
|
+
import { join as join14 } from "path";
|
|
6481
|
+
import consola43 from "consola";
|
|
6232
6482
|
var PACKAGE_NAME = "@openape/apes";
|
|
6233
6483
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6234
|
-
var CACHE_FILE =
|
|
6484
|
+
var CACHE_FILE = join14(homedir13(), ".config", "apes", ".version-check.json");
|
|
6235
6485
|
function readCache() {
|
|
6236
|
-
if (!
|
|
6486
|
+
if (!existsSync17(CACHE_FILE)) return null;
|
|
6237
6487
|
try {
|
|
6238
|
-
return JSON.parse(
|
|
6488
|
+
return JSON.parse(readFileSync14(CACHE_FILE, "utf-8"));
|
|
6239
6489
|
} catch {
|
|
6240
6490
|
return null;
|
|
6241
6491
|
}
|
|
6242
6492
|
}
|
|
6243
6493
|
function writeCache(entry) {
|
|
6244
6494
|
try {
|
|
6245
|
-
const dir =
|
|
6246
|
-
if (!
|
|
6247
|
-
|
|
6495
|
+
const dir = join14(homedir13(), ".config", "apes");
|
|
6496
|
+
if (!existsSync17(dir)) mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
6497
|
+
writeFileSync10(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
|
|
6248
6498
|
} catch {
|
|
6249
6499
|
}
|
|
6250
6500
|
}
|
|
@@ -6273,7 +6523,7 @@ async function fetchLatestVersion() {
|
|
|
6273
6523
|
}
|
|
6274
6524
|
function warnIfBehind(currentVersion, latest) {
|
|
6275
6525
|
if (compareSemver(currentVersion, latest) < 0) {
|
|
6276
|
-
|
|
6526
|
+
consola43.warn(
|
|
6277
6527
|
`apes ${currentVersion} is behind latest @openape/apes@${latest}. Run \`npm i -g @openape/apes@latest\` to update. (Suppress with APES_NO_UPDATE_CHECK=1.)`
|
|
6278
6528
|
);
|
|
6279
6529
|
}
|
|
@@ -6305,10 +6555,10 @@ if (shellRewrite) {
|
|
|
6305
6555
|
if (shellRewrite.action === "rewrite") {
|
|
6306
6556
|
process.argv = shellRewrite.argv;
|
|
6307
6557
|
} else if (shellRewrite.action === "version") {
|
|
6308
|
-
console.log(`ape-shell ${"1.
|
|
6558
|
+
console.log(`ape-shell ${"1.6.0"} (OpenApe DDISA shell wrapper)`);
|
|
6309
6559
|
process.exit(0);
|
|
6310
6560
|
} else if (shellRewrite.action === "help") {
|
|
6311
|
-
console.log(`ape-shell ${"1.
|
|
6561
|
+
console.log(`ape-shell ${"1.6.0"} \u2014 OpenApe DDISA shell wrapper`);
|
|
6312
6562
|
console.log("");
|
|
6313
6563
|
console.log("Usage:");
|
|
6314
6564
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -6332,7 +6582,7 @@ if (shellRewrite) {
|
|
|
6332
6582
|
}
|
|
6333
6583
|
}
|
|
6334
6584
|
var debug = process.argv.includes("--debug");
|
|
6335
|
-
var grantsCommand =
|
|
6585
|
+
var grantsCommand = defineCommand54({
|
|
6336
6586
|
meta: {
|
|
6337
6587
|
name: "grants",
|
|
6338
6588
|
description: "Grant management"
|
|
@@ -6353,7 +6603,7 @@ var grantsCommand = defineCommand52({
|
|
|
6353
6603
|
"delegation-revoke": delegationRevokeCommand
|
|
6354
6604
|
}
|
|
6355
6605
|
});
|
|
6356
|
-
var configCommand =
|
|
6606
|
+
var configCommand = defineCommand54({
|
|
6357
6607
|
meta: {
|
|
6358
6608
|
name: "config",
|
|
6359
6609
|
description: "Configuration management"
|
|
@@ -6363,10 +6613,10 @@ var configCommand = defineCommand52({
|
|
|
6363
6613
|
set: configSetCommand
|
|
6364
6614
|
}
|
|
6365
6615
|
});
|
|
6366
|
-
var main =
|
|
6616
|
+
var main = defineCommand54({
|
|
6367
6617
|
meta: {
|
|
6368
6618
|
name: "apes",
|
|
6369
|
-
version: "1.
|
|
6619
|
+
version: "1.6.0",
|
|
6370
6620
|
description: "Unified CLI for OpenApe"
|
|
6371
6621
|
},
|
|
6372
6622
|
subCommands: {
|
|
@@ -6422,20 +6672,20 @@ async function maybeRefreshAuth() {
|
|
|
6422
6672
|
}
|
|
6423
6673
|
}
|
|
6424
6674
|
await maybeRefreshAuth();
|
|
6425
|
-
await maybeWarnStaleVersion("1.
|
|
6675
|
+
await maybeWarnStaleVersion("1.6.0").catch(() => {
|
|
6426
6676
|
});
|
|
6427
6677
|
runMain(main).catch((err) => {
|
|
6428
6678
|
if (err instanceof CliExit) {
|
|
6429
6679
|
process.exit(err.exitCode);
|
|
6430
6680
|
}
|
|
6431
6681
|
if (err instanceof CliError) {
|
|
6432
|
-
|
|
6682
|
+
consola44.error(err.message);
|
|
6433
6683
|
process.exit(err.exitCode);
|
|
6434
6684
|
}
|
|
6435
6685
|
if (debug) {
|
|
6436
|
-
|
|
6686
|
+
consola44.error(err);
|
|
6437
6687
|
} else {
|
|
6438
|
-
|
|
6688
|
+
consola44.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
6439
6689
|
}
|
|
6440
6690
|
process.exit(1);
|
|
6441
6691
|
});
|