@openape/apes 0.21.2 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
} from "./chunk-IDPV5SNB.js";
|
|
64
64
|
|
|
65
65
|
// src/cli.ts
|
|
66
|
-
import
|
|
66
|
+
import consola36 from "consola";
|
|
67
67
|
|
|
68
68
|
// src/ape-shell.ts
|
|
69
69
|
import path from "path";
|
|
@@ -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: readFileSync8 } = 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 = readFileSync8(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);
|
|
@@ -1748,7 +1748,7 @@ import { defineCommand as defineCommand24 } from "citty";
|
|
|
1748
1748
|
// src/commands/agents/destroy.ts
|
|
1749
1749
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
1750
1750
|
import { mkdtempSync, rmSync, writeFileSync } from "fs";
|
|
1751
|
-
import { tmpdir } from "os";
|
|
1751
|
+
import { tmpdir, userInfo } from "os";
|
|
1752
1752
|
import { join as join2 } from "path";
|
|
1753
1753
|
import { defineCommand as defineCommand20 } from "citty";
|
|
1754
1754
|
import consola18 from "consola";
|
|
@@ -1889,7 +1889,7 @@ echo "OK $NAME uid=$NEXT_UID home=$HOME_DIR"
|
|
|
1889
1889
|
`;
|
|
1890
1890
|
}
|
|
1891
1891
|
function buildDestroyTeardownScript(input) {
|
|
1892
|
-
const { name, homeDir } = input;
|
|
1892
|
+
const { name, homeDir, adminUser } = input;
|
|
1893
1893
|
return `#!/bin/bash
|
|
1894
1894
|
# Best-effort teardown. set -u catches typos; we deliberately do NOT use -e
|
|
1895
1895
|
# because pkill / launchctl are allowed to fail when the user has no live
|
|
@@ -1898,6 +1898,16 @@ set -u
|
|
|
1898
1898
|
|
|
1899
1899
|
NAME=${shQuote(name)}
|
|
1900
1900
|
HOME_DIR=${shQuote(homeDir)}
|
|
1901
|
+
ADMIN_USER=${shQuote(adminUser)}
|
|
1902
|
+
|
|
1903
|
+
# Read the admin password from stdin (line 1). The caller pipes it in.
|
|
1904
|
+
# We never accept it as an argv element so it can't show up in process
|
|
1905
|
+
# listings or escapes' audit log.
|
|
1906
|
+
read -r ADMIN_PASSWORD
|
|
1907
|
+
if [ -z "$ADMIN_PASSWORD" ]; then
|
|
1908
|
+
echo "ERROR: no admin password on stdin (expected one line)." >&2
|
|
1909
|
+
exit 2
|
|
1910
|
+
fi
|
|
1901
1911
|
|
|
1902
1912
|
UID_OF=$(dscl . -read "/Users/$NAME" UniqueID 2>/dev/null | awk '/UniqueID:/ {print $2}')
|
|
1903
1913
|
|
|
@@ -1910,25 +1920,32 @@ if [ -d "$HOME_DIR" ] && [ "$HOME_DIR" != "/" ] && [ "$HOME_DIR" != "" ]; then
|
|
|
1910
1920
|
rm -rf "$HOME_DIR"
|
|
1911
1921
|
fi
|
|
1912
1922
|
|
|
1913
|
-
#
|
|
1914
|
-
#
|
|
1915
|
-
#
|
|
1916
|
-
#
|
|
1917
|
-
#
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1923
|
+
# \`escapes\` is a plain setuid binary \u2014 opendirectoryd sees no audit/PAM
|
|
1924
|
+
# session attached (AUDIT_SESSION_ID=unset) and rejects DirectoryService
|
|
1925
|
+
# writes from this context: a bare \`sysadminctl -deleteUser\` or
|
|
1926
|
+
# \`dscl . -delete\` hangs ~5 minutes and exits with eUndefinedError -14987
|
|
1927
|
+
# at DSRecord.m:563. Passing explicit -adminUser/-adminPassword bypasses
|
|
1928
|
+
# opendirectoryd's implicit "is current session admin?" check and
|
|
1929
|
+
# authenticates against DirectoryService directly \u2014 the delete then
|
|
1930
|
+
# completes in ~1 second.
|
|
1931
|
+
if ! command -v sysadminctl >/dev/null 2>&1; then
|
|
1932
|
+
echo "ERROR: sysadminctl not available; cannot delete user record." >&2
|
|
1933
|
+
exit 1
|
|
1934
|
+
fi
|
|
1935
|
+
|
|
1936
|
+
sysadminctl \\
|
|
1937
|
+
-deleteUser "$NAME" \\
|
|
1938
|
+
-adminUser "$ADMIN_USER" \\
|
|
1939
|
+
-adminPassword "$ADMIN_PASSWORD"
|
|
1940
|
+
SYSAD_EC=$?
|
|
1941
|
+
unset ADMIN_PASSWORD
|
|
1942
|
+
|
|
1943
|
+
if [ $SYSAD_EC -ne 0 ]; then
|
|
1944
|
+
echo "ERROR: sysadminctl -deleteUser failed (exit=$SYSAD_EC)." >&2
|
|
1945
|
+
echo " Common causes: wrong admin password, admin user '$ADMIN_USER'" >&2
|
|
1946
|
+
echo " not in admin group, or target user '$NAME' is the last secure" >&2
|
|
1947
|
+
echo " token holder (run \\\`sysadminctl -secureTokenStatus $NAME\\\`)." >&2
|
|
1948
|
+
exit 1
|
|
1932
1949
|
fi
|
|
1933
1950
|
|
|
1934
1951
|
# Verify the record is actually gone.
|
|
@@ -2122,14 +2139,20 @@ ${consequences.join("\n")}`);
|
|
|
2122
2139
|
if (!escapes) {
|
|
2123
2140
|
throw new CliError("`escapes` not found on PATH; OS teardown requires escapes.");
|
|
2124
2141
|
}
|
|
2142
|
+
const adminUser = userInfo().username;
|
|
2143
|
+
const adminPassword = await collectAdminPassword({ adminUser, force: !!args.force });
|
|
2125
2144
|
const scratch = mkdtempSync(join2(tmpdir(), `apes-destroy-${name}-`));
|
|
2126
2145
|
const scriptPath = join2(scratch, "teardown.sh");
|
|
2127
2146
|
try {
|
|
2128
|
-
const script = buildDestroyTeardownScript({ name, homeDir: `/Users/${name}
|
|
2147
|
+
const script = buildDestroyTeardownScript({ name, homeDir: `/Users/${name}`, adminUser });
|
|
2129
2148
|
writeFileSync(scriptPath, script, { mode: 448 });
|
|
2130
2149
|
consola18.start("Running teardown as root via `apes run --as root --wait`\u2026");
|
|
2131
2150
|
consola18.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
|
|
2132
|
-
execFileSync3(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], {
|
|
2151
|
+
execFileSync3(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], {
|
|
2152
|
+
input: `${adminPassword}
|
|
2153
|
+
`,
|
|
2154
|
+
stdio: ["pipe", "inherit", "inherit"]
|
|
2155
|
+
});
|
|
2133
2156
|
} finally {
|
|
2134
2157
|
rmSync(scratch, { recursive: true, force: true });
|
|
2135
2158
|
}
|
|
@@ -2139,6 +2162,21 @@ ${consequences.join("\n")}`);
|
|
|
2139
2162
|
consola18.success(`Destroyed ${name}.`);
|
|
2140
2163
|
}
|
|
2141
2164
|
});
|
|
2165
|
+
async function collectAdminPassword(opts) {
|
|
2166
|
+
const fromEnv = process.env.APES_ADMIN_PASSWORD;
|
|
2167
|
+
if (fromEnv && fromEnv.length > 0) return fromEnv;
|
|
2168
|
+
if (!process.stdin.isTTY) {
|
|
2169
|
+
throw new CliError(
|
|
2170
|
+
`Admin password required for sysadminctl -deleteUser. No TTY available for the silent prompt; set APES_ADMIN_PASSWORD in the environment (local admin password for ${opts.adminUser}). The teardown reads it from stdin and never stores it.`
|
|
2171
|
+
);
|
|
2172
|
+
}
|
|
2173
|
+
consola18.info(`Local admin password for ${opts.adminUser} (used for sysadminctl -deleteUser; not stored):`);
|
|
2174
|
+
const pw = await consola18.prompt("Admin password", { type: "text", mask: "*" });
|
|
2175
|
+
if (typeof pw === "symbol" || !pw || pw.length === 0) {
|
|
2176
|
+
throw new CliExit(0);
|
|
2177
|
+
}
|
|
2178
|
+
return pw;
|
|
2179
|
+
}
|
|
2142
2180
|
|
|
2143
2181
|
// src/commands/agents/list.ts
|
|
2144
2182
|
import { defineCommand as defineCommand21 } from "citty";
|
|
@@ -3770,7 +3808,7 @@ var mcpCommand = defineCommand32({
|
|
|
3770
3808
|
if (transport !== "stdio" && transport !== "sse") {
|
|
3771
3809
|
throw new Error('Transport must be "stdio" or "sse"');
|
|
3772
3810
|
}
|
|
3773
|
-
const { startMcpServer } = await import("./server-
|
|
3811
|
+
const { startMcpServer } = await import("./server-WBR7VEDY.js");
|
|
3774
3812
|
await startMcpServer(transport, port);
|
|
3775
3813
|
}
|
|
3776
3814
|
});
|
|
@@ -4408,7 +4446,7 @@ async function bestEffortGrantCount(idp) {
|
|
|
4408
4446
|
}
|
|
4409
4447
|
}
|
|
4410
4448
|
async function runHealth(args) {
|
|
4411
|
-
const version = true ? "0.
|
|
4449
|
+
const version = true ? "0.22.1" : "0.0.0";
|
|
4412
4450
|
const auth = loadAuth();
|
|
4413
4451
|
if (!auth) {
|
|
4414
4452
|
throw new CliError("Not logged in. Run `apes login` first.", 1);
|
|
@@ -4600,6 +4638,77 @@ var workflowsCommand = defineCommand43({
|
|
|
4600
4638
|
}
|
|
4601
4639
|
});
|
|
4602
4640
|
|
|
4641
|
+
// src/version-check.ts
|
|
4642
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
4643
|
+
import { homedir as homedir5 } from "os";
|
|
4644
|
+
import { join as join6 } from "path";
|
|
4645
|
+
import consola35 from "consola";
|
|
4646
|
+
var PACKAGE_NAME = "@openape/apes";
|
|
4647
|
+
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4648
|
+
var CACHE_FILE = join6(homedir5(), ".config", "apes", ".version-check.json");
|
|
4649
|
+
function readCache() {
|
|
4650
|
+
if (!existsSync9(CACHE_FILE)) return null;
|
|
4651
|
+
try {
|
|
4652
|
+
return JSON.parse(readFileSync7(CACHE_FILE, "utf-8"));
|
|
4653
|
+
} catch {
|
|
4654
|
+
return null;
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4657
|
+
function writeCache(entry) {
|
|
4658
|
+
try {
|
|
4659
|
+
const dir = join6(homedir5(), ".config", "apes");
|
|
4660
|
+
if (!existsSync9(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
4661
|
+
writeFileSync6(CACHE_FILE, JSON.stringify(entry), { mode: 384 });
|
|
4662
|
+
} catch {
|
|
4663
|
+
}
|
|
4664
|
+
}
|
|
4665
|
+
function compareSemver(a, b) {
|
|
4666
|
+
const pa = a.split(".").map(Number);
|
|
4667
|
+
const pb = b.split(".").map(Number);
|
|
4668
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
4669
|
+
const x = pa[i] ?? 0;
|
|
4670
|
+
const y = pb[i] ?? 0;
|
|
4671
|
+
if (x !== y) return x - y;
|
|
4672
|
+
}
|
|
4673
|
+
return 0;
|
|
4674
|
+
}
|
|
4675
|
+
async function fetchLatestVersion() {
|
|
4676
|
+
try {
|
|
4677
|
+
const res = await fetch(`https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}/latest`, {
|
|
4678
|
+
headers: { Accept: "application/json" },
|
|
4679
|
+
signal: AbortSignal.timeout(2e3)
|
|
4680
|
+
});
|
|
4681
|
+
if (!res.ok) return null;
|
|
4682
|
+
const body = await res.json();
|
|
4683
|
+
return typeof body.version === "string" ? body.version : null;
|
|
4684
|
+
} catch {
|
|
4685
|
+
return null;
|
|
4686
|
+
}
|
|
4687
|
+
}
|
|
4688
|
+
function warnIfBehind(currentVersion, latest) {
|
|
4689
|
+
if (compareSemver(currentVersion, latest) < 0) {
|
|
4690
|
+
consola35.warn(
|
|
4691
|
+
`apes ${currentVersion} is behind latest @openape/apes@${latest}. Run \`npm i -g @openape/apes@latest\` to update. (Suppress with APES_NO_UPDATE_CHECK=1.)`
|
|
4692
|
+
);
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
async function maybeWarnStaleVersion(currentVersion) {
|
|
4696
|
+
if (process.env.APES_NO_UPDATE_CHECK) return;
|
|
4697
|
+
if (!currentVersion || currentVersion === "unknown") return;
|
|
4698
|
+
const cached = readCache();
|
|
4699
|
+
const now = Date.now();
|
|
4700
|
+
if (cached) {
|
|
4701
|
+
warnIfBehind(currentVersion, cached.latest);
|
|
4702
|
+
}
|
|
4703
|
+
if (!cached || now - cached.checkedAt >= CACHE_TTL_MS) {
|
|
4704
|
+
const latest = await fetchLatestVersion();
|
|
4705
|
+
if (latest) {
|
|
4706
|
+
writeCache({ latest, checkedAt: now });
|
|
4707
|
+
if (!cached) warnIfBehind(currentVersion, latest);
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
}
|
|
4711
|
+
|
|
4603
4712
|
// src/cli.ts
|
|
4604
4713
|
process.stdout.on("error", (err) => {
|
|
4605
4714
|
if (err.code === "EPIPE") process.exit(0);
|
|
@@ -4610,10 +4719,10 @@ if (shellRewrite) {
|
|
|
4610
4719
|
if (shellRewrite.action === "rewrite") {
|
|
4611
4720
|
process.argv = shellRewrite.argv;
|
|
4612
4721
|
} else if (shellRewrite.action === "version") {
|
|
4613
|
-
console.log(`ape-shell ${"0.
|
|
4722
|
+
console.log(`ape-shell ${"0.22.1"} (OpenApe DDISA shell wrapper)`);
|
|
4614
4723
|
process.exit(0);
|
|
4615
4724
|
} else if (shellRewrite.action === "help") {
|
|
4616
|
-
console.log(`ape-shell ${"0.
|
|
4725
|
+
console.log(`ape-shell ${"0.22.1"} \u2014 OpenApe DDISA shell wrapper`);
|
|
4617
4726
|
console.log("");
|
|
4618
4727
|
console.log("Usage:");
|
|
4619
4728
|
console.log(" ape-shell Start interactive grant-mediated REPL");
|
|
@@ -4671,7 +4780,7 @@ var configCommand = defineCommand44({
|
|
|
4671
4780
|
var main = defineCommand44({
|
|
4672
4781
|
meta: {
|
|
4673
4782
|
name: "apes",
|
|
4674
|
-
version: "0.
|
|
4783
|
+
version: "0.22.1",
|
|
4675
4784
|
description: "Unified CLI for OpenApe"
|
|
4676
4785
|
},
|
|
4677
4786
|
subCommands: {
|
|
@@ -4726,18 +4835,20 @@ async function maybeRefreshAuth() {
|
|
|
4726
4835
|
}
|
|
4727
4836
|
}
|
|
4728
4837
|
await maybeRefreshAuth();
|
|
4838
|
+
await maybeWarnStaleVersion("0.22.1").catch(() => {
|
|
4839
|
+
});
|
|
4729
4840
|
runMain(main).catch((err) => {
|
|
4730
4841
|
if (err instanceof CliExit) {
|
|
4731
4842
|
process.exit(err.exitCode);
|
|
4732
4843
|
}
|
|
4733
4844
|
if (err instanceof CliError) {
|
|
4734
|
-
|
|
4845
|
+
consola36.error(err.message);
|
|
4735
4846
|
process.exit(err.exitCode);
|
|
4736
4847
|
}
|
|
4737
4848
|
if (debug) {
|
|
4738
|
-
|
|
4849
|
+
consola36.error(err);
|
|
4739
4850
|
} else {
|
|
4740
|
-
|
|
4851
|
+
consola36.error(err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err));
|
|
4741
4852
|
}
|
|
4742
4853
|
process.exit(1);
|
|
4743
4854
|
});
|