privateer-agent 0.5.0 → 0.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.
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env node
2
+ // Cross-platform Privateer launcher — the single source of launch logic for every
3
+ // platform (macOS, Linux, Windows). `bin/privateer-tui` (unix) and the Windows
4
+ // `privateer.cmd` are thin shims that just pick a Node and run THIS file.
5
+ //
6
+ // It boots Pi's full interactive TUI with the Privateer moat + tool packs. The moat
7
+ // is installed as re-export SHIMS in the agent dir's extensions/, so BOTH this TUI
8
+ // and any subagents it spawns (child processes reading the same agent dir) load the
9
+ // identical set — including our permission gate. One source of truth via discovery
10
+ // (no `-e`, which would double-load vs discovery). Runs in the current directory;
11
+ // model via PRIVATEER_MODEL=provider/id.
12
+ //
13
+ // Ported from the original bash launcher; behaviour is intended to match exactly.
14
+
15
+ import { spawn } from "node:child_process";
16
+ import fs from "node:fs";
17
+ import os from "node:os";
18
+ import path from "node:path";
19
+ import { fileURLToPath, pathToFileURL } from "node:url";
20
+
21
+ const HERE = path.dirname(fileURLToPath(import.meta.url)); // bin/
22
+ const REPO = path.resolve(HERE, "..");
23
+ const isWin = process.platform === "win32";
24
+
25
+ const PRIVATEER_HOME = process.env.PRIVATEER_HOME || path.join(os.homedir(), ".privateer");
26
+ const ENV_FILE = path.join(REPO, ".env"); // dev-only; a real install has none
27
+
28
+ // --- bundle detection ------------------------------------------------------
29
+ // A self-contained bundle ships its own pinned Node at "$REPO/node[.exe]" plus a
30
+ // BUNDLE_INFO.json marker (built by scripts/build-bundle.mjs). When present we use
31
+ // that runtime and never touch system node/npm. Putting the bundle dir on PATH also
32
+ // lets any child that boots via `#!/usr/bin/env node` (Pi's cli.js, the subagent
33
+ // wrapper) resolve the bundled node.
34
+ const bundledNode = path.join(REPO, isWin ? "node.exe" : "node");
35
+ const BUNDLED = fs.existsSync(bundledNode) && fs.existsSync(path.join(REPO, "BUNDLE_INFO.json"));
36
+
37
+ // The node used for child processes. When bundled, the bundled runtime; otherwise the
38
+ // very node already running this script (a suitable >=22, since we booted under it).
39
+ const NODE_BIN = BUNDLED ? bundledNode : process.execPath;
40
+ // Make sure the chosen node's directory is on PATH for shebang-spawned grandchildren.
41
+ process.env.PATH = path.dirname(NODE_BIN) + path.delimiter + (process.env.PATH || "");
42
+
43
+ const args = process.argv.slice(2);
44
+ const sub = args[0];
45
+
46
+ // `privateer --version` — report OUR version, not Pi's. Left to Pi's cli.js it would
47
+ // print the pi-coding-agent version (e.g. 0.80.3); intercept so users see the
48
+ // Privateer release they installed. (The startup banner already shows this version.)
49
+ if (sub === "--version" || sub === "-V") {
50
+ const ver = (p) => { try { return JSON.parse(fs.readFileSync(p, "utf8")).version; } catch { return null; } };
51
+ const pv = ver(path.join(REPO, "package.json")) || "unknown";
52
+ const pi = ver(path.join(REPO, "node_modules", "@earendil-works", "pi-coding-agent", "package.json"));
53
+ console.log(`privateer ${pv}${pi ? ` (pi ${pi})` : ""}`);
54
+ process.exit(0);
55
+ }
56
+
57
+ // Faithfully propagate a child's exit/signal, mirroring bash `exec`.
58
+ function runToCompletion(cmd, cmdArgs, opts = {}) {
59
+ const child = spawn(cmd, cmdArgs, { stdio: "inherit", env: process.env, ...opts });
60
+ child.on("exit", (code, signal) => {
61
+ if (signal) process.kill(process.pid, signal);
62
+ else process.exit(code ?? 0);
63
+ });
64
+ child.on("error", (e) => {
65
+ console.error(`privateer: failed to launch — ${e.message}`);
66
+ process.exit(1);
67
+ });
68
+ }
69
+
70
+ // --- `privateer update` ----------------------------------------------------
71
+ // Fetch the latest release and exit. Bundle installs re-run the download+extract
72
+ // installer; npm installs update the global package.
73
+ if (sub === "update") {
74
+ if (BUNDLED) {
75
+ console.log("Updating Privateer to the latest release…");
76
+ if (isWin) {
77
+ runToCompletion("powershell", ["-NoProfile", "-Command", "irm https://privateer.pro/install.ps1 | iex"]);
78
+ } else {
79
+ runToCompletion("sh", ["-c", "curl -fsSL https://privateer.pro/install.sh | sh"]);
80
+ }
81
+ } else {
82
+ console.log("Updating privateer-agent to the latest release…");
83
+ runToCompletion(isWin ? "npm.cmd" : "npm", ["install", "-g", "privateer-agent@latest"]);
84
+ }
85
+ // runToCompletion exits via the child's exit handler.
86
+ }
87
+
88
+ // --- `privateer daemon [run|install|uninstall|status]` ---------------------
89
+ // The resident background daemon (routines + app-driven headless task spawns). Boots
90
+ // straight into src/daemon via bin/privateer-daemon.mjs — no moat-shim install (the
91
+ // daemon loads the moat as in-code factories, not interactive extensions).
92
+ else if (sub === "daemon") {
93
+ const nodeArgs = fs.existsSync(ENV_FILE) ? [`--env-file=${ENV_FILE}`] : [];
94
+ runToCompletion(NODE_BIN, [...nodeArgs, path.join(REPO, "bin", "privateer-daemon.mjs"), ...args.slice(1)]);
95
+ }
96
+
97
+ // --- normal launch: install the moat, then exec Pi's TUI -------------------
98
+ else {
99
+ const AGENT_DIR = path.join(PRIVATEER_HOME, "agent");
100
+ const EXT_DIR = path.join(AGENT_DIR, "extensions");
101
+ fs.mkdirSync(EXT_DIR, { recursive: true });
102
+
103
+ // Install/refresh the moat + tool-pack shims. Each shim re-exports its target by
104
+ // ABSOLUTE path (as a file:// URL, portable across OSes) so the target's own
105
+ // relative imports resolve from the repo. We remove any shim we previously managed
106
+ // first, so a dropped package can't linger and reload.
107
+ const MANAGED = [
108
+ "privateer-brand", "privateer-context", "privateer-gate", "privateer-account",
109
+ "privateer-models", "privateer-posture", "privateer-tools", "privateer-privacy",
110
+ "pi-privacy", "pi-web-access", "rpiv-web-tools", "pi-mcp-adapter", "pi-hypa", "pi-subagents",
111
+ ];
112
+ for (const name of MANAGED) fs.rmSync(path.join(EXT_DIR, `${name}.ts`), { force: true });
113
+
114
+ const ext = (...p) => path.join(REPO, "extensions", ...p);
115
+ const dep = (...p) => path.join(REPO, "node_modules", ...p);
116
+ const shim = (name, target) =>
117
+ fs.writeFileSync(path.join(EXT_DIR, `${name}.ts`), `export { default } from ${JSON.stringify(pathToFileURL(target).href)};\n`);
118
+
119
+ shim("privateer-brand", ext("privateer-brand.ts")); // banner, ⚓ badge, /signin /signout
120
+ shim("privateer-context", ext("privateer-context.ts")); // PRIVATEER.md context + /init
121
+ shim("privateer-gate", ext("privateer-gate.ts")); // the permission gate (moat)
122
+ shim("privateer-account", ext("privateer-account.ts"));
123
+ shim("privateer-models", ext("privateer-models.ts")); // /models picker w/ privacy shields
124
+ shim("privateer-posture", ext("privateer-posture.ts"));
125
+ shim("privateer-tools", ext("privateer-tools.ts"));
126
+ shim("privateer-privacy", ext("privateer-privacy.ts")); // pi-privacy + account tier resolver
127
+ shim("rpiv-web-tools", dep("@juicesharp", "rpiv-web-tools", "index.ts")); // private web tools
128
+ shim("pi-mcp-adapter", dep("pi-mcp-adapter", "index.ts"));
129
+ shim("pi-hypa", dep("@hypabolic", "pi-hypa", "extensions", "index.ts"));
130
+ shim("pi-subagents", dep("pi-subagents", "src", "extension", "index.ts"));
131
+
132
+ const CLI = dep("@earendil-works", "pi-coding-agent", "dist", "cli.js");
133
+ process.env.PI_CODING_AGENT_DIR = AGENT_DIR;
134
+ // The binary pi-subagents spawns for each child. Point it at OUR cli.js so the child
135
+ // reads this same PI_CODING_AGENT_DIR and DISCOVERS the moat shims (gated + private,
136
+ // no -e injection). Set only when unset so a power user can override.
137
+ if (!process.env.PI_SUBAGENT_PI_BINARY) process.env.PI_SUBAGENT_PI_BINARY = CLI;
138
+ // Suppress Pi's upstream update banner (our banner is the startup surface). Disables
139
+ // ONLY the version fetch — fd/rg can still download on first run.
140
+ if (!process.env.PI_SKIP_VERSION_CHECK) process.env.PI_SKIP_VERSION_CHECK = "1";
141
+
142
+ // Quiet Pi's built-in startup chatter so our banner is the only greeting. Each key is
143
+ // set only when unset, so a user's own settings.json toggle still wins.
144
+ try {
145
+ const sp = path.join(AGENT_DIR, "settings.json");
146
+ let s = {};
147
+ try { s = JSON.parse(fs.readFileSync(sp, "utf8")); } catch { /* new/absent */ }
148
+ let m = false;
149
+ if (s.quietStartup === undefined) { s.quietStartup = true; m = true; }
150
+ if (s.collapseChangelog === undefined) { s.collapseChangelog = true; m = true; }
151
+ if (s.lastChangelogVersion === undefined) { s.lastChangelogVersion = "9999.0.0"; m = true; }
152
+ if (m) fs.writeFileSync(sp, JSON.stringify(s, null, 2) + "\n");
153
+ } catch { /* best-effort */ }
154
+
155
+ // Passive update check: refresh the cached "latest version" at most ~daily, in the
156
+ // background so it never blocks or breaks launch (offline-safe). The banner reads
157
+ // this cache and shows a "↑ vX available · run privateer update" notice. We never
158
+ // auto-install. Fire-and-forget: the event loop stays alive while the TUI child runs.
159
+ refreshUpdateCache();
160
+
161
+ // Default model. Explicit PRIVATEER_MODEL wins; else Tinfoil GLM 5.2 (client-attested
162
+ // TEE, strongest tier) when a Tinfoil key is present; else the signed-in account's
163
+ // NEAR channel; else a cheap OpenRouter fallback.
164
+ const CRED = path.join(PRIVATEER_HOME, "credentials.json");
165
+ const MODEL = process.env.PRIVATEER_MODEL
166
+ ? process.env.PRIVATEER_MODEL
167
+ : haveTinfoilKey()
168
+ ? "tinfoil/glm-5-2"
169
+ : fs.existsSync(CRED)
170
+ ? "privateer/near/zai-org/GLM-5.1-FP8"
171
+ : "openrouter/openai/gpt-4o-mini";
172
+
173
+ // Dev convenience: load provider keys from the repo's .env if present.
174
+ const nodeArgs = fs.existsSync(ENV_FILE) ? [`--env-file=${ENV_FILE}`] : [];
175
+ runToCompletion(NODE_BIN, [...nodeArgs, CLI, "--model", MODEL, ...args]);
176
+ }
177
+
178
+ // --- helpers ---------------------------------------------------------------
179
+ function haveTinfoilKey() {
180
+ if (process.env.TINFOIL_API_KEY) return true;
181
+ try { return /^TINFOIL_API_KEY=.+/m.test(fs.readFileSync(ENV_FILE, "utf8")); }
182
+ catch { return false; }
183
+ }
184
+
185
+ function refreshUpdateCache() {
186
+ const cache = path.join(PRIVATEER_HOME, "update-check.json");
187
+ try {
188
+ const stat = fs.statSync(cache);
189
+ if (Date.now() - stat.mtimeMs < 24 * 60 * 60 * 1000) return; // fresh (<1 day)
190
+ } catch { /* missing — refresh */ }
191
+
192
+ const write = (latest) => {
193
+ if (!/^[0-9]/.test(latest || "")) return;
194
+ try {
195
+ fs.mkdirSync(PRIVATEER_HOME, { recursive: true });
196
+ fs.writeFileSync(cache, JSON.stringify({ latest }) + "\n");
197
+ } catch { /* best-effort */ }
198
+ };
199
+
200
+ if (BUNDLED) {
201
+ // No npm in a bundle — read the latest tag off GitHub Releases.
202
+ fetch("https://api.github.com/repos/privateer-agent/privateer-agent/releases/latest", {
203
+ headers: { "User-Agent": "privateer-cli", Accept: "application/vnd.github+json" },
204
+ })
205
+ .then((r) => (r.ok ? r.json() : null))
206
+ .then((j) => write(String(j?.tag_name || "").replace(/^v/, "")))
207
+ .catch(() => { /* offline — keep stale cache */ });
208
+ } else {
209
+ const p = spawn(isWin ? "npm.cmd" : "npm", ["view", "privateer-agent", "version"], {
210
+ stdio: ["ignore", "pipe", "ignore"],
211
+ });
212
+ let out = "";
213
+ p.stdout.on("data", (d) => { out += d; });
214
+ p.on("close", () => write(out.trim()));
215
+ p.on("error", () => { /* no npm — ignore */ });
216
+ p.unref();
217
+ }
218
+ }
package/bin/privateer-tui CHANGED
@@ -1,15 +1,10 @@
1
1
  #!/usr/bin/env bash
2
- # Launch Pi's full interactive TUI with the Privateer moat + tool packs. The moat is
3
- # installed as re-export SHIMS in the agent dir's extensions/, so BOTH this TUI and
4
- # any subagents it spawns (which run as child `pi` processes reading the agent dir)
5
- # load the identical setincluding our permission gate. So subagent tool calls are
6
- # gated too (the gate runs bypass-within-restricted-tools headlessly; danger.ts still
7
- # blocks destructive shell). One source of truth via discovery — no -e (which would
8
- # double-load vs discovery).
9
- #
10
- # Runs in the current directory. Model via PRIVATEER_MODEL=provider/id.
2
+ # Thin unix shim: pick a Node, then hand off to the cross-platform launcher
3
+ # (bin/privateer-launch.mjs), which is the single source of launch logic shared with
4
+ # the Windows privateer.cmd. All the real work moat-shim install, model pick,
5
+ # update/daemon dispatch, exec into Pi's TUI lives in the launcher.
11
6
  set -euo pipefail
12
- # Resolve through symlinks so REPO points at the real repo, not the symlink dir.
7
+ # Resolve through symlinks so REPO points at the real bundle/repo dir.
13
8
  SOURCE="${BASH_SOURCE[0]}"
14
9
  while [ -h "$SOURCE" ]; do
15
10
  DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
@@ -18,16 +13,9 @@ while [ -h "$SOURCE" ]; do
18
13
  done
19
14
  REPO="$(cd -P "$(dirname "$SOURCE")/.." && pwd)"
20
15
 
21
- # `privateer update` pull the latest release from npm and exit. Handled here in the
22
- # launcher (not the TUI) so it works even when a broken install won't boot. npm rewrites
23
- # the global bin in place; replacing it while this process runs is safe on unix.
24
- if [ "${1:-}" = "update" ]; then
25
- echo "Updating privateer-agent to the latest release…"
26
- npm install -g privateer-agent@latest
27
- exit $?
28
- fi
29
-
16
+ # Bundled runtime wins; else a system node >=22; else an nvm node; else PATH `node`.
30
17
  pick_node() {
18
+ if [ -x "$REPO/node" ] && [ -f "$REPO/BUNDLE_INFO.json" ]; then echo "$REPO/node"; return; fi
31
19
  if command -v node >/dev/null 2>&1 \
32
20
  && node -e 'process.exit((+process.versions.node.split(".")[0]) >= 22 ? 0 : 1)' 2>/dev/null; then
33
21
  command -v node; return
@@ -38,123 +26,4 @@ pick_node() {
38
26
  echo "node"
39
27
  }
40
28
 
41
- # `privateer daemon [run|install|uninstall|status]` — the resident background daemon
42
- # (routines + app-driven headless task spawns). Handled HERE, before the Pi TUI exec
43
- # and before the moat-shim install (the daemon doesn't need the interactive extension
44
- # set), so it boots straight into src/daemon via bin/privateer-daemon.mjs.
45
- if [ "${1:-}" = "daemon" ]; then
46
- shift
47
- DAEMON_NODE="$(pick_node)"
48
- DAEMON_ENV_ARGS=""
49
- [ -f "$REPO/.env" ] && DAEMON_ENV_ARGS="--env-file=$REPO/.env"
50
- exec "$DAEMON_NODE" ${DAEMON_ENV_ARGS:+"$DAEMON_ENV_ARGS"} "$REPO/bin/privateer-daemon.mjs" "$@"
51
- fi
52
-
53
- AGENT_DIR="${PRIVATEER_HOME:-$HOME/.privateer}/agent"
54
- EXT_DIR="$AGENT_DIR/extensions"
55
- mkdir -p "$EXT_DIR"
56
-
57
- # Install/refresh the moat + tool-pack shims. Each shim re-exports its target by
58
- # ABSOLUTE path so the target's own relative imports resolve from the repo (a plain
59
- # symlink would resolve them relative to the shim's location and break). We remove
60
- # any shim we previously managed first, so a dropped package can't linger and reload.
61
- MANAGED="privateer-brand privateer-context privateer-gate privateer-account privateer-posture privateer-tools privateer-privacy pi-privacy pi-web-access rpiv-web-tools pi-mcp-adapter pi-hypa pi-subagents"
62
- for name in $MANAGED; do rm -f "$EXT_DIR/$name.ts"; done
63
- shim() { printf 'export { default } from "%s";\n' "$2" > "$EXT_DIR/$1.ts"; }
64
- # Branding + the account sign-in surface (banner, ⚓ badge, /signin /signout).
65
- shim privateer-brand "$REPO/extensions/privateer-brand.ts"
66
- # PRIVATEER.md project-context loading (like AGENTS.md/CLAUDE.md) + the /init command.
67
- shim privateer-context "$REPO/extensions/privateer-context.ts"
68
- shim privateer-gate "$REPO/extensions/privateer-gate.ts"
69
- shim privateer-account "$REPO/extensions/privateer-account.ts"
70
- shim privateer-posture "$REPO/extensions/privateer-posture.ts"
71
- shim privateer-tools "$REPO/extensions/privateer-tools.ts"
72
- # pi-privacy wrapped with the account-channel tier resolver (privateer/near… = TEE),
73
- # so it replaces loading pi-privacy's default entry.
74
- shim privateer-privacy "$REPO/extensions/privateer-privacy.ts"
75
- # Privacy-aligned web tools: pluggable backends (SearXNG self-hosted = private; no
76
- # WebView). Pick the backend with /web-tools, or set SEARXNG_URL. Replaces
77
- # pi-web-access (opened a WebView + defaulted to Exa, a 3rd-party search).
78
- shim rpiv-web-tools "$REPO/node_modules/@juicesharp/rpiv-web-tools/index.ts"
79
- shim pi-mcp-adapter "$REPO/node_modules/pi-mcp-adapter/index.ts"
80
- shim pi-hypa "$REPO/node_modules/@hypabolic/pi-hypa/extensions/index.ts"
81
- shim pi-subagents "$REPO/node_modules/pi-subagents/src/extension/index.ts"
82
-
83
- CLI="$REPO/node_modules/@earendil-works/pi-coding-agent/dist/cli.js"
84
- export PI_CODING_AGENT_DIR="$AGENT_DIR"
85
- # The binary pi-subagents spawns for each subagent child. Without this it falls back
86
- # to `pi` on PATH (getPiSpawnCommand) — which a Privateer install does NOT provide, so
87
- # every subagent spawn would fail with ENOENT. Point it at OUR bundled cli.js (it's
88
- # executable, `#!/usr/bin/env node`): the child then reads this same PI_CODING_AGENT_DIR
89
- # and DISCOVERS the moat shims above — so subagents run gated + private, no -e injection
90
- # (hence no double-load). Set only when unset so a power user can override it.
91
- export PI_SUBAGENT_PI_BINARY="${PI_SUBAGENT_PI_BINARY:-$CLI}"
92
- # Suppress Pi's upstream "Update Available — run `pi update`" banner. It's noise on a
93
- # Privateer install (our banner is the startup surface), it leaks the "pi" name, and
94
- # `pi update` would fight our npm-managed install. PI_SKIP_VERSION_CHECK disables ONLY
95
- # the version fetch — unlike PI_OFFLINE it still lets fd/rg download on first run.
96
- export PI_SKIP_VERSION_CHECK=1
97
- NODE_BIN="$(pick_node)"
98
-
99
- # Quiet Pi's built-in startup chatter so our banner is the only greeting. Each key is
100
- # set only when unset, so a user's own settings.json toggle (or --verbose) still wins:
101
- # quietStartup — hide the [Extensions]/[Context]/[Skills]/… resource listing
102
- # collapseChangelog — never dump Pi's "What's New" changelog wall into the chat
103
- # lastChangelogVersion — sentinel so Pi finds no "new" entries to show on upgrade;
104
- # release notes live in the npm README / GitHub releases instead
105
- "$NODE_BIN" -e '
106
- const fs = require("fs"), p = process.argv[1];
107
- let s = {}; try { s = JSON.parse(fs.readFileSync(p, "utf8")); } catch {}
108
- let m = false;
109
- if (s.quietStartup === undefined) { s.quietStartup = true; m = true; }
110
- if (s.collapseChangelog === undefined) { s.collapseChangelog = true; m = true; }
111
- if (s.lastChangelogVersion === undefined) { s.lastChangelogVersion = "9999.0.0"; m = true; }
112
- if (m) fs.writeFileSync(p, JSON.stringify(s, null, 2) + "\n");
113
- ' "$AGENT_DIR/settings.json" 2>/dev/null || true
114
-
115
- # Passive update check: refresh the cached "latest npm version" at most ~daily, in the
116
- # BACKGROUND so it never blocks or breaks launch (offline-safe — a failed fetch just
117
- # leaves the stale cache in place). The banner (privateer-brand) reads this cache and
118
- # shows a one-line "↑ vX available · run privateer update" notice when we're behind. We
119
- # never auto-install — the user stays in control of when new code lands (the whole point
120
- # of an attestable tool). Detached with </dev/null &, so it outlives our exec into node.
121
- UPDATE_CACHE="${PRIVATEER_HOME:-$HOME/.privateer}/update-check.json"
122
- if [ -z "$(find "$UPDATE_CACHE" -mtime -1 2>/dev/null)" ]; then
123
- (
124
- latest="$(npm view privateer-agent version 2>/dev/null || true)"
125
- case "$latest" in
126
- [0-9]*) printf '{"latest":"%s"}\n' "$latest" > "$UPDATE_CACHE.tmp" 2>/dev/null \
127
- && mv -f "$UPDATE_CACHE.tmp" "$UPDATE_CACHE" 2>/dev/null ;;
128
- esac
129
- ) </dev/null >/dev/null 2>&1 &
130
- fi
131
-
132
- # Default model. An explicit PRIVATEER_MODEL always wins. Otherwise prefer Tinfoil's
133
- # GLM 5.2 when a Tinfoil key is available: verifiable TEE inference with CLIENT-side
134
- # attestation (the live TLS key is bound to the enclave's quote), the strongest privacy
135
- # tier we offer — stronger than the account's server-proxied NEAR channel. Failing that,
136
- # use the signed-in Privateer account's NEAR confidential-compute channel; with neither,
137
- # fall back to a cheap OpenRouter model. The Tinfoil key may sit in the ambient env or in
138
- # the dev .env the launcher loads below, so check both.
139
- CRED="${PRIVATEER_HOME:-$HOME/.privateer}/credentials.json"
140
- have_tinfoil_key() {
141
- [ -n "${TINFOIL_API_KEY:-}" ] && return 0
142
- [ -f "$REPO/.env" ] && grep -qE '^TINFOIL_API_KEY=.+' "$REPO/.env"
143
- }
144
- if [ -n "${PRIVATEER_MODEL:-}" ]; then
145
- MODEL="$PRIVATEER_MODEL"
146
- elif have_tinfoil_key; then
147
- MODEL="tinfoil/glm-5-2"
148
- elif [ -f "$CRED" ]; then
149
- MODEL="privateer/near/zai-org/GLM-5.1-FP8"
150
- else
151
- MODEL="openrouter/openai/gpt-4o-mini"
152
- fi
153
-
154
- # Dev convenience: load provider keys from the repo's .env if present. A real install
155
- # has none — so only pass the flag when the file exists (using --env-file-if-exists
156
- # unconditionally would print "…/.env not found. Continuing without it." on every
157
- # launch, muddying the startup banner). No .env → rely on the ambient environment.
158
- ENV_ARGS=""
159
- [ -f "$REPO/.env" ] && ENV_ARGS="--env-file=$REPO/.env"
160
- exec "$NODE_BIN" ${ENV_ARGS:+"$ENV_ARGS"} "$CLI" --model "$MODEL" "$@"
29
+ exec "$(pick_node)" "$REPO/bin/privateer-launch.mjs" "$@"
@@ -0,0 +1,6 @@
1
+ @echo off
2
+ REM Windows entry point for a Privateer bundle. Mirrors the unix bin/privateer-tui
3
+ REM shim: run the bundled Node against the shared cross-platform launcher. %~dp0 is
4
+ REM this file's dir (<app>\bin\), so ..\node.exe is the bundled runtime.
5
+ "%~dp0..\node.exe" "%~dp0privateer-launch.mjs" %*
6
+ exit /b %errorlevel%