privateer-agent 0.6.2 → 0.6.3
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/bin/privateer-launch.mjs +76 -5
- package/package.json +2 -2
package/bin/privateer-launch.mjs
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
//
|
|
13
13
|
// Ported from the original bash launcher; behaviour is intended to match exactly.
|
|
14
14
|
|
|
15
|
-
import { spawn } from "node:child_process";
|
|
15
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
16
16
|
import fs from "node:fs";
|
|
17
17
|
import os from "node:os";
|
|
18
18
|
import path from "node:path";
|
|
@@ -80,7 +80,8 @@ if (sub === "update") {
|
|
|
80
80
|
}
|
|
81
81
|
} else {
|
|
82
82
|
console.log("Updating privateer-agent to the latest release…");
|
|
83
|
-
|
|
83
|
+
// npm is npm.cmd on Windows; Node >=18.20 needs a shell to spawn a .cmd (EINVAL otherwise).
|
|
84
|
+
runToCompletion(isWin ? "npm.cmd" : "npm", ["install", "-g", "privateer-agent@latest"], { shell: isWin });
|
|
84
85
|
}
|
|
85
86
|
// runToCompletion exits via the child's exit handler.
|
|
86
87
|
}
|
|
@@ -96,6 +97,12 @@ else if (sub === "daemon") {
|
|
|
96
97
|
|
|
97
98
|
// --- normal launch: install the moat, then exec Pi's TUI -------------------
|
|
98
99
|
else {
|
|
100
|
+
// Windows has no bash out of the box, but Privateer's command tool needs one. If a
|
|
101
|
+
// real bash isn't reachable, stop here with a clear, actionable message — otherwise
|
|
102
|
+
// the user boots fine and only hits a cryptic `'bash' is not recognized` the first
|
|
103
|
+
// time the agent tries to run a command. Unix always has a shell, so this is a no-op.
|
|
104
|
+
ensureShellOrExit();
|
|
105
|
+
|
|
99
106
|
const AGENT_DIR = path.join(PRIVATEER_HOME, "agent");
|
|
100
107
|
const EXT_DIR = path.join(AGENT_DIR, "extensions");
|
|
101
108
|
fs.mkdirSync(EXT_DIR, { recursive: true });
|
|
@@ -176,6 +183,62 @@ else {
|
|
|
176
183
|
}
|
|
177
184
|
|
|
178
185
|
// --- helpers ---------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
// Preflight: on Windows, make sure a bash the command tool can use actually exists.
|
|
188
|
+
// Mirrors pi-coding-agent's resolver (utils/shell.js getShellConfig): an explicit
|
|
189
|
+
// shellPath override wins, then Git Bash in Program Files, then any bash.exe on PATH.
|
|
190
|
+
// If none resolve, print branded install guidance and exit before the TUI loads.
|
|
191
|
+
function ensureShellOrExit() {
|
|
192
|
+
if (!isWin) return; // macOS/Linux always ship /bin/sh (bash); nothing to check.
|
|
193
|
+
|
|
194
|
+
// Respect an explicit shellPath in the agent settings — if the user set one, defer
|
|
195
|
+
// to Pi's own resolver (it validates the path and reports its own error).
|
|
196
|
+
try {
|
|
197
|
+
const s = JSON.parse(fs.readFileSync(path.join(PRIVATEER_HOME, "agent", "settings.json"), "utf8"));
|
|
198
|
+
if (s && typeof s.shellPath === "string" && s.shellPath.trim()) return;
|
|
199
|
+
} catch { /* no settings file / no override — fall through to detection */ }
|
|
200
|
+
|
|
201
|
+
if (findWindowsBash()) return; // a usable bash is reachable — carry on.
|
|
202
|
+
|
|
203
|
+
const msg = [
|
|
204
|
+
"",
|
|
205
|
+
" ⚓ Privateer needs a bash shell to run commands, and Windows doesn't ship one.",
|
|
206
|
+
"",
|
|
207
|
+
" Fix it with any ONE of these, then run `privateer` again:",
|
|
208
|
+
"",
|
|
209
|
+
" 1. Install Git for Windows (recommended) — bundles Git Bash where Privateer",
|
|
210
|
+
" looks first, no config needed: https://git-scm.com/download/win",
|
|
211
|
+
"",
|
|
212
|
+
" 2. Use WSL — run Privateer inside a WSL shell, or install it with",
|
|
213
|
+
" `wsl --install` from an admin PowerShell.",
|
|
214
|
+
"",
|
|
215
|
+
" 3. Already have Cygwin/MSYS2? Add its bash.exe to PATH, or set \"shellPath\"",
|
|
216
|
+
" to your bash.exe in your Privateer settings.json.",
|
|
217
|
+
"",
|
|
218
|
+
" After installing, open a NEW terminal (PATH changes don't reach open windows).",
|
|
219
|
+
"",
|
|
220
|
+
].join("\n");
|
|
221
|
+
process.stderr.write(msg + "\n");
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Return the path to a usable bash.exe on Windows, or null. Same search order as
|
|
226
|
+
// pi-coding-agent: Git Bash under %ProgramFiles%[(x86)], then `where bash.exe`.
|
|
227
|
+
function findWindowsBash() {
|
|
228
|
+
const candidates = [];
|
|
229
|
+
if (process.env.ProgramFiles) candidates.push(path.join(process.env.ProgramFiles, "Git", "bin", "bash.exe"));
|
|
230
|
+
if (process.env["ProgramFiles(x86)"]) candidates.push(path.join(process.env["ProgramFiles(x86)"], "Git", "bin", "bash.exe"));
|
|
231
|
+
for (const c of candidates) if (fs.existsSync(c)) return c;
|
|
232
|
+
try {
|
|
233
|
+
const r = spawnSync("where", ["bash.exe"], { encoding: "utf8", timeout: 5000, windowsHide: true });
|
|
234
|
+
if (r.status === 0 && r.stdout) {
|
|
235
|
+
const first = r.stdout.trim().split(/\r?\n/)[0];
|
|
236
|
+
if (first && fs.existsSync(first)) return first;
|
|
237
|
+
}
|
|
238
|
+
} catch { /* `where` unavailable — treat as not found */ }
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
|
|
179
242
|
function haveTinfoilKey() {
|
|
180
243
|
if (process.env.TINFOIL_API_KEY) return true;
|
|
181
244
|
try { return /^TINFOIL_API_KEY=.+/m.test(fs.readFileSync(ENV_FILE, "utf8")); }
|
|
@@ -206,9 +269,17 @@ function refreshUpdateCache() {
|
|
|
206
269
|
.then((j) => write(String(j?.tag_name || "").replace(/^v/, "")))
|
|
207
270
|
.catch(() => { /* offline — keep stale cache */ });
|
|
208
271
|
} else {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
272
|
+
// On Windows `npm` is npm.cmd; Node >=18.20 throws EINVAL synchronously when
|
|
273
|
+
// spawning a .cmd without a shell, so run through a shell there and guard the
|
|
274
|
+
// call — this is a fire-and-forget cache refresh and must never break launch.
|
|
275
|
+
let p;
|
|
276
|
+
try {
|
|
277
|
+
p = spawn(isWin ? "npm.cmd" : "npm", ["view", "privateer-agent", "version"], {
|
|
278
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
279
|
+
shell: isWin,
|
|
280
|
+
windowsHide: true,
|
|
281
|
+
});
|
|
282
|
+
} catch { return; /* no npm / spawn refused — keep stale cache */ }
|
|
212
283
|
let out = "";
|
|
213
284
|
p.stdout.on("data", (d) => { out += d; });
|
|
214
285
|
p.on("close", () => write(out.trim()));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "privateer-agent",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "Privateer — a provider-agnostic, safe-by-default terminal coding agent with TEE/Tinfoil attestation, rebuilt on the Pi toolkit. Bring your own model across 20 providers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"venice"
|
|
29
29
|
],
|
|
30
30
|
"bin": {
|
|
31
|
-
"privateer": "./bin/privateer-
|
|
31
|
+
"privateer": "./bin/privateer-launch.mjs"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|