@polderlabs/bizar 10.23.4 → 10.23.5

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/cli/bin.mjs CHANGED
@@ -113,7 +113,7 @@ function showHelp() {
113
113
  backup Create / list / verify / delete backups of BizarHarness state
114
114
  restore Restore BizarHarness from a backup
115
115
  validate Validate the Bizar install
116
- setup-provider Configure a provider in ~/.claude/settings.json (since v6.2.2 installer doesn't touch providers)
116
+ setup-provider Configure the global provider used by Bizar and Claude Code
117
117
  release-provenance Generate SBOM + provenance + minisig for a release (audit #83)
118
118
  verify-release Verify a release artifact set against the pinned allowlist
119
119
  spec-list List SDK schemas, policy docs, and mirror sync status (audit #84)
@@ -29,7 +29,8 @@ export function showInstallHelp() {
29
29
  from the repo. Preserves ~/.config/bizar/
30
30
  login state. Combine with --yes to skip prompts.
31
31
  bizar install --deep Alias for --force (clean-install semantics)
32
- bizar install --yes Assume yes for any non-destructive prompt
32
+ bizar install --yes Non-interactive install (CI/script friendly)
33
+ bizar install --non-interactive Alias for --yes
33
34
  bizar install --help Show this help
34
35
 
35
36
  Description:
@@ -59,8 +60,13 @@ export function showInstallHelp() {
59
60
  4. Registers the Bizar MCP server in ~/.claude/settings.json.
60
61
  5. Wires Claude Code lifecycle hooks (SessionStart / PreToolUse /
61
62
  PostToolUse / UserPromptSubmit) under ~/.claude/hooks/.
62
- 6. Runs 'bizar doctor' as a post-install health check.
63
- No API key collection, no interactive prompts.
63
+ 6. In a terminal, confirms the install and securely asks for a provider
64
+ URL and key only when they are not already configured.
65
+ 7. Runs 'bizar doctor' as a post-install health check.
66
+
67
+ Provider settings are global (~/.claude/settings.json), so they work from
68
+ every project. Key input is hidden. Use --yes or --non-interactive to skip
69
+ all prompts; missing provider values then produce actionable guidance.
64
70
  `);
65
71
  }
66
72
 
@@ -2,7 +2,7 @@
2
2
  * Configure Claude Code's provider environment in settings.json.
3
3
  *
4
4
  * Bizar does not maintain a parallel provider registry. Claude Code reads
5
- * ANTHROPIC_BASE_URL, ANTHROPIC_API_KEY, and ANTHROPIC_MODEL directly.
5
+ * ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN, and ANTHROPIC_MODEL directly.
6
6
  */
7
7
  import chalk from 'chalk';
8
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
@@ -41,10 +41,16 @@ export function parseProviderArgs(args = []) {
41
41
 
42
42
  export function updateProviderSettings(current, options) {
43
43
  const next = { ...current, env: { ...(current.env || {}) } };
44
- if (options.gateway !== undefined) next.env.ANTHROPIC_BASE_URL = options.gateway;
45
- if (options.key !== undefined) next.env.ANTHROPIC_API_KEY = options.key;
44
+ if (options.gateway !== undefined) {
45
+ next.env.ANTHROPIC_BASE_URL = options.gateway;
46
+ next.env.BIZAR_MODEL_ROUTER_URL = options.gateway;
47
+ }
48
+ if (options.key !== undefined) next.env.ANTHROPIC_AUTH_TOKEN = options.key;
46
49
  if (options.model !== undefined) next.env.ANTHROPIC_MODEL = options.model;
47
- if (options.removeKey) delete next.env.ANTHROPIC_API_KEY;
50
+ if (options.removeKey) {
51
+ delete next.env.ANTHROPIC_AUTH_TOKEN;
52
+ delete next.env.ANTHROPIC_API_KEY;
53
+ }
48
54
  return next;
49
55
  }
50
56
 
@@ -81,9 +87,9 @@ export async function runSetupProvider(args = []) {
81
87
 
82
88
  if (options.list) {
83
89
  console.log(` Settings: ${path}`);
84
- console.log(` Gateway: ${current.env?.ANTHROPIC_BASE_URL || '(Anthropic default)'}`);
90
+ console.log(` Gateway: ${current.env?.ANTHROPIC_BASE_URL || current.env?.BIZAR_MODEL_ROUTER_URL || '(Anthropic default)'}`);
85
91
  console.log(` Model: ${current.env?.ANTHROPIC_MODEL || '(Claude Code default)'}`);
86
- console.log(` API key: ${redact(current.env?.ANTHROPIC_API_KEY)}`);
92
+ console.log(` API key: ${redact(current.env?.ANTHROPIC_AUTH_TOKEN || current.env?.ANTHROPIC_API_KEY)}`);
87
93
  return { ok: true, path, settings: current };
88
94
  }
89
95
 
@@ -9,6 +9,7 @@ import { runProvision, forceCleanInstall, clearSavedEnv } from '../provision.mjs
9
9
  import { runDoctor } from '../doctor.mjs';
10
10
  import { showBanner, sectionHeading } from './banner.mjs';
11
11
  import { printInstallLocations } from './paths.mjs';
12
+ import { runInteractiveSetup } from './interactive-setup.mjs';
12
13
 
13
14
  /**
14
15
  * Thin orchestrator entry point.
@@ -43,6 +44,15 @@ export async function runInstaller(opts = {}) {
43
44
  showBanner();
44
45
  printInstallLocations({ dryRun, force });
45
46
 
47
+ // A normal TTY install is a guided setup. Automation remains prompt-free
48
+ // via --yes / --non-interactive, and update runs never request credentials.
49
+ let interactive = null;
50
+ if (mode !== 'update' && !dryRun) {
51
+ interactive = await runInteractiveSetup({ enabled: !yes });
52
+ if (!interactive.ok) return { ok: false, interactive };
53
+ if (interactive.cancelled) return { ok: true, cancelled: true, interactive };
54
+ }
55
+
46
56
  // F-183 — pre-provision wipe for forced installs. Force-clean is what
47
57
  // makes `--force` actually a clean install: dirs under ~/.claude/ are
48
58
  // wiped (BIZAR_HOME and third-party state preserved), settings.json
@@ -94,5 +104,5 @@ export async function runInstaller(opts = {}) {
94
104
  }
95
105
  }
96
106
 
97
- return { ...provisionResult, clean };
98
- }
107
+ return { ...provisionResult, clean, interactive };
108
+ }
@@ -0,0 +1,131 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { Writable } from 'node:stream';
5
+ import { createInterface } from 'node:readline/promises';
6
+
7
+ export function providerSettingsPath(env = process.env) {
8
+ const root = env.CLAUDE_CONFIG_DIR?.trim()
9
+ || join(env.HOME?.trim() || homedir(), '.claude');
10
+ return join(root, 'settings.json');
11
+ }
12
+
13
+ export function readProviderSettings(path = providerSettingsPath()) {
14
+ if (!existsSync(path)) return {};
15
+ return JSON.parse(readFileSync(path, 'utf8'));
16
+ }
17
+
18
+ export function detectProviderConfiguration({ env = process.env, settings = {} } = {}) {
19
+ const settingsEnv = settings?.env && typeof settings.env === 'object' ? settings.env : {};
20
+ const url = env.BIZAR_MODEL_ROUTER_URL?.trim()
21
+ || env.ANTHROPIC_BASE_URL?.trim()
22
+ || settingsEnv.BIZAR_MODEL_ROUTER_URL?.trim()
23
+ || settingsEnv.ANTHROPIC_BASE_URL?.trim()
24
+ || '';
25
+ const key = env.ANTHROPIC_AUTH_TOKEN?.trim()
26
+ || env.ANTHROPIC_API_KEY?.trim()
27
+ || settingsEnv.ANTHROPIC_AUTH_TOKEN?.trim()
28
+ || settingsEnv.ANTHROPIC_API_KEY?.trim()
29
+ || '';
30
+ return { url, key, missing: [...(!url ? ['url'] : []), ...(!key ? ['key'] : [])] };
31
+ }
32
+
33
+ export function isValidProviderUrl(value) {
34
+ try {
35
+ const parsed = new URL(value);
36
+ return parsed.protocol === 'https:' || parsed.protocol === 'http:';
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ export async function askLine(prompt, { input = process.stdin, output = process.stdout } = {}) {
43
+ const rl = createInterface({ input, output });
44
+ try {
45
+ return await rl.question(prompt);
46
+ } finally {
47
+ rl.close();
48
+ }
49
+ }
50
+
51
+ /** Read a secret through readline without forwarding its terminal redraws. */
52
+ export async function askSecret(prompt, { input = process.stdin, output = process.stdout } = {}) {
53
+ const muted = new Writable({
54
+ write(_chunk, _encoding, callback) { callback(); },
55
+ });
56
+ const rl = createInterface({ input, output: muted, terminal: true });
57
+ output.write(prompt);
58
+ try {
59
+ return await rl.question('');
60
+ } finally {
61
+ rl.close();
62
+ output.write('\n');
63
+ }
64
+ }
65
+
66
+ function writeLine(output, value = '') {
67
+ output.write(`${value}\n`);
68
+ }
69
+
70
+ /**
71
+ * Guided install preflight. Credentials are placed only in the current
72
+ * process; the provisioner's settings writer persists them globally with the
73
+ * rest of the install. Injected question functions keep the policy testable.
74
+ */
75
+ export async function runInteractiveSetup({
76
+ env = process.env,
77
+ input = process.stdin,
78
+ output = process.stdout,
79
+ enabled = true,
80
+ readSettings = readProviderSettings,
81
+ askText = askLine,
82
+ askHidden = askSecret,
83
+ } = {}) {
84
+ let settings;
85
+ try {
86
+ settings = readSettings(providerSettingsPath(env));
87
+ } catch (error) {
88
+ return { ok: false, error: `Cannot read global Claude settings: ${error.message}` };
89
+ }
90
+
91
+ const detected = detectProviderConfiguration({ env, settings });
92
+ const interactive = enabled && input.isTTY === true && output.isTTY === true;
93
+ if (!interactive) {
94
+ if (detected.missing.length > 0) {
95
+ writeLine(output, ` ! Provider ${detected.missing.join(' and ')} not detected; set ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN or run \`bizar setup-provider\`.`);
96
+ }
97
+ return { ok: true, interactive: false, configured: detected.missing.length === 0, missing: detected.missing };
98
+ }
99
+
100
+ writeLine(output, '');
101
+ writeLine(output, ' Interactive setup');
102
+ const confirmation = (await askText(' Continue with the installation? [Y/n] ', { input, output })).trim().toLowerCase();
103
+ if (confirmation === 'n' || confirmation === 'no') {
104
+ writeLine(output, ' Installation cancelled.');
105
+ return { ok: true, interactive: true, cancelled: true, configured: detected.missing.length === 0 };
106
+ }
107
+
108
+ let url = detected.url;
109
+ let key = detected.key;
110
+ if (url) writeLine(output, ` ✓ Provider URL detected: ${url}`);
111
+ while (!url) {
112
+ const answer = (await askText(' Provider URL (for example https://gateway.example/v1): ', { input, output })).trim();
113
+ if (!isValidProviderUrl(answer)) {
114
+ writeLine(output, ' ! Enter a valid http:// or https:// URL.');
115
+ continue;
116
+ }
117
+ url = answer.replace(/\/+$/, '');
118
+ }
119
+
120
+ if (key) writeLine(output, ' ✓ Provider key detected (hidden)');
121
+ while (!key) {
122
+ key = (await askHidden(' Provider API key (input hidden): ', { input, output })).trim();
123
+ if (!key) writeLine(output, ' ! Provider key cannot be empty.');
124
+ }
125
+
126
+ env.ANTHROPIC_BASE_URL = url;
127
+ env.BIZAR_MODEL_ROUTER_URL = url;
128
+ env.ANTHROPIC_AUTH_TOKEN = key;
129
+ writeLine(output, ' ✓ Provider configuration ready; the key will be stored in global Claude settings.');
130
+ return { ok: true, interactive: true, cancelled: false, configured: true, missing: detected.missing };
131
+ }
@@ -14,10 +14,13 @@ bizar setup-provider --remove-key
14
14
  ```
15
15
 
16
16
  The command edits only `env.ANTHROPIC_BASE_URL`,
17
- `env.ANTHROPIC_API_KEY`, and `env.ANTHROPIC_MODEL` in
17
+ `env.BIZAR_MODEL_ROUTER_URL`, `env.ANTHROPIC_AUTH_TOKEN`, and
18
+ `env.ANTHROPIC_MODEL` in
18
19
  `~/.claude/settings.json` (or `$CLAUDE_CONFIG_DIR/settings.json`).
19
20
  All unrelated settings are preserved. It rejects invalid existing JSON
20
21
  and never prints a full API key.
21
22
 
22
23
  With no arguments it shows help; it does not guess credentials or query
23
- an untrusted model catalog.
24
+ an untrusted model catalog. A normal `bizar install` provides the safer
25
+ interactive path with hidden key entry; this command remains useful for
26
+ automation and explicit changes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "10.23.4",
3
+ "version": "10.23.5",
4
4
  "description": "Autonomous, human-in-the-loop multi-agent harness for Claude Code with guarded workflows, typed SDK primitives, and MCP tools.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * SDK version constant. Keep synchronized with the workspace package versions.
3
3
  */
4
- export declare const SDK_VERSION: "10.23.4";
4
+ export declare const SDK_VERSION: "10.23.5";
5
5
  //# sourceMappingURL=version.d.ts.map
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * SDK version constant. Keep synchronized with the workspace package versions.
3
3
  */
4
- export const SDK_VERSION = "10.23.4";
4
+ export const SDK_VERSION = "10.23.5";
5
5
  //# sourceMappingURL=version.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar-sdk",
3
- "version": "10.23.4",
3
+ "version": "10.23.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",