privateer-agent 0.3.2 → 0.3.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/README.md CHANGED
@@ -12,6 +12,9 @@
12
12
  <a href="https://www.npmjs.com/package/privateer-agent">
13
13
  <img src="https://img.shields.io/npm/v/privateer-agent" alt="npm" />
14
14
  </a>
15
+ <a href="https://github.com/privateer-agent/privateer-agent/releases">
16
+ <img src="https://img.shields.io/badge/changelog-what's%20new-5b8def" alt="Changelog" />
17
+ </a>
15
18
  <img src="https://img.shields.io/badge/node-%E2%89%A522.19-brightgreen" alt="Node >= 22.19" />
16
19
  <img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License" />
17
20
  <img src="https://img.shields.io/badge/permissions-safe%20by%20default-2ea44f" alt="Safe by default" />
@@ -247,6 +250,12 @@ npm run typecheck
247
250
  npm test
248
251
  ```
249
252
 
253
+ ## Changelog
254
+
255
+ Release notes and what's new in each version live on the
256
+ [**GitHub releases page**](https://github.com/privateer-agent/privateer-agent/releases).
257
+ Privateer keeps its startup clean — the app won't dump a changelog into your terminal.
258
+
250
259
  ## License
251
260
 
252
261
  [MIT](LICENSE) © Patrick
package/bin/privateer-tui CHANGED
@@ -59,15 +59,27 @@ shim pi-subagents "$REPO/node_modules/pi-subagents/src/extension/index.ts"
59
59
 
60
60
  CLI="$REPO/node_modules/@earendil-works/pi-coding-agent/dist/cli.js"
61
61
  export PI_CODING_AGENT_DIR="$AGENT_DIR"
62
+ # Suppress Pi's upstream "Update Available — run `pi update`" banner. It's noise on a
63
+ # Privateer install (our banner is the startup surface), it leaks the "pi" name, and
64
+ # `pi update` would fight our npm-managed install. PI_SKIP_VERSION_CHECK disables ONLY
65
+ # the version fetch — unlike PI_OFFLINE it still lets fd/rg download on first run.
66
+ export PI_SKIP_VERSION_CHECK=1
62
67
  NODE_BIN="$(pick_node)"
63
68
 
64
- # Quiet Pi's built-in startup resource listing ([Extensions]/[Context]/[Skills]/…) by
65
- # default our banner is the intended startup surface, and the extension list is noise.
66
- # Only set it when unset, so a later `--verbose` or a user's own settings toggle sticks.
69
+ # Quiet Pi's built-in startup chatter so our banner is the only greeting. Each key is
70
+ # set only when unset, so a user's own settings.json toggle (or --verbose) still wins:
71
+ # quietStartup — hide the [Extensions]/[Context]/[Skills]/… resource listing
72
+ # collapseChangelog — never dump Pi's "What's New" changelog wall into the chat
73
+ # lastChangelogVersion — sentinel so Pi finds no "new" entries to show on upgrade;
74
+ # release notes live in the npm README / GitHub releases instead
67
75
  "$NODE_BIN" -e '
68
76
  const fs = require("fs"), p = process.argv[1];
69
77
  let s = {}; try { s = JSON.parse(fs.readFileSync(p, "utf8")); } catch {}
70
- if (s.quietStartup === undefined) { s.quietStartup = true; fs.writeFileSync(p, JSON.stringify(s, null, 2) + "\n"); }
78
+ let m = false;
79
+ if (s.quietStartup === undefined) { s.quietStartup = true; m = true; }
80
+ if (s.collapseChangelog === undefined) { s.collapseChangelog = true; m = true; }
81
+ if (s.lastChangelogVersion === undefined) { s.lastChangelogVersion = "9999.0.0"; m = true; }
82
+ if (m) fs.writeFileSync(p, JSON.stringify(s, null, 2) + "\n");
71
83
  ' "$AGENT_DIR/settings.json" 2>/dev/null || true
72
84
 
73
85
  # Default model: when signed in to a Privateer account, default to GLM 5.1 on the
@@ -9,12 +9,15 @@
9
9
  // device-code flow the account channel needs.
10
10
  //
11
11
  // Pi already owns /login and /logout for PROVIDER auth (and /whoami), so we do NOT
12
- // shadow them. /signin drives the Privateer *account* device flow directly which
13
- // has to exist separately, because the `privateer` inference provider only registers
14
- // once credentials exist, so a first-time user can't reach it through provider auth.
12
+ // shadow them. The account provider now registers unconditionally (see
13
+ // makeAccountProvider), so Privateer appears under Pi's /login "Use a subscription"
14
+ // list and a first-time user CAN sign in through provider auth. /signin remains as a
15
+ // friendlier, dedicated shortcut that drives the same account device-code flow
16
+ // directly — one obvious command instead of /login → pick a provider.
15
17
  //
16
18
  // On a successful /signin we hot-register the account provider so privateer/* models
17
- // appear immediately, without a restart.
19
+ // appear immediately (the account catalog refreshes to the live listing without a
20
+ // restart).
18
21
 
19
22
  import { readFileSync } from "node:fs";
20
23
  import { homedir } from "node:os";
@@ -54,7 +57,7 @@ const ANCHOR = [
54
57
  " .-----. ", // lock body top (the shackle's base)
55
58
  " | o | ", // lock body + keyhole
56
59
  " '--+--' ", // lock body base, shank exits
57
- " ---+--- ", // stock (crossbar)
60
+ " /\\ | /\\ ", // stock — arms flare from the shank (each \\ is one backslash)
58
61
  " \\ | / ", // arms
59
62
  " \\_|_/ ", // flukes
60
63
  ];
@@ -150,7 +153,6 @@ function accountBadge(): string {
150
153
  export default function privateerBrand(pi: any): void {
151
154
  let currentModelProvider: string | undefined;
152
155
  let ctxRef: any = null;
153
- let greeted = false;
154
156
 
155
157
  const setHeader = (ctx: any) =>
156
158
  ctx?.ui?.setHeader?.(() => headerComponent(currentModelProvider));
@@ -227,13 +229,8 @@ export default function privateerBrand(pi: any): void {
227
229
  if (!ctx?.hasUI) return; // headless (print/json): no banner or prompts
228
230
  ctx?.ui?.setTitle?.("Privateer");
229
231
  refresh(ctx);
230
- if (!priv.hasCredentials() && !greeted) {
231
- greeted = true;
232
- ctx?.ui?.notify?.(
233
- "Not signed in — run /signin to connect your Privateer account (adds hosted models + remote access).",
234
- "info",
235
- );
236
- }
232
+ // No startup notify here: the banner's account line already surfaces the
233
+ // "not signed in · /signin" prompt, so a second line would just be noise.
237
234
  });
238
235
 
239
236
  // Keep the header's account line in sync with the picked model (the "this model
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "privateer-agent",
3
- "version": "0.3.2",
3
+ "version": "0.3.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",
@@ -136,8 +136,16 @@ export async function accountPosture(modelId: string): Promise<AccountPosture> {
136
136
  }
137
137
  }
138
138
 
139
- // Extension factory: registers the account provider when a machine login exists. No
140
- // login → nothing registered (BYO-key only).
139
+ // Extension factory: registers the account provider so `/login` can offer it.
140
+ //
141
+ // We register UNCONDITIONALLY (not only when a machine login already exists). Pi's
142
+ // `/login` builds its "Use a subscription" list from the OAuth providers registered
143
+ // here (authStorage.getOAuthProviders()); a not-yet-logged-in machine has no
144
+ // credentials, so gating registration on hasCredentials() left `/login` with no
145
+ // Privateer option — the classic chicken-and-egg where you can't log in because
146
+ // you're not logged in. The OAuth provider's login() itself runs the device-code
147
+ // flow when hasCredentials() is false (see privateerOAuthProvider.login), so first
148
+ // login works entirely through Pi once the provider is present.
141
149
  //
142
150
  // Registration is SYNCHRONOUS (seeded with the fallback), then refined once the live
143
151
  // catalog is fetched. This matters: Pi flushes provider registrations made during the
@@ -151,7 +159,7 @@ export function makeAccountProvider() {
151
159
  return (pi: {
152
160
  registerProvider?: (name: string, config: unknown) => void;
153
161
  }): void => {
154
- if (!hasCredentials() || typeof pi.registerProvider !== "function") return;
162
+ if (typeof pi.registerProvider !== "function") return;
155
163
  const register = (ids: string[]): void =>
156
164
  pi.registerProvider!("privateer", {
157
165
  name: "Privateer account",