@polderlabs/bizar 10.19.5 → 10.19.7
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 +2 -1
- package/cli/commands/install.mjs +120 -37
- package/cli/commands/models.mjs +84 -4
- package/cli/commands/util.mjs +8 -11
- package/cli/provision.mjs +6 -5
- package/package.json +1 -1
- package/packages/sdk/dist/version.d.ts +1 -1
- package/packages/sdk/dist/version.js +1 -1
- package/packages/sdk/package.json +1 -1
package/cli/bin.mjs
CHANGED
|
@@ -129,7 +129,8 @@ function showHelp() {
|
|
|
129
129
|
bizar install
|
|
130
130
|
bizar audit
|
|
131
131
|
bizar doctor
|
|
132
|
-
bizar update --
|
|
132
|
+
bizar update --dry-run # Preview what would change
|
|
133
|
+
bizar update --force --yes # Non-interactive full re-emit
|
|
133
134
|
|
|
134
135
|
Run \`bizar <command> --help\` for per-command help.
|
|
135
136
|
|
package/cli/commands/install.mjs
CHANGED
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* install + update command families.
|
|
5
5
|
* v4.4.11+ — 'bizar install' and 'bizar update' share the same code path.
|
|
6
|
+
*
|
|
7
|
+
* v10.19.6 — `bizar update` now honors `--dry-run` / `--force` / `--yes`
|
|
8
|
+
* the same way `bizar install` does. The previous implementation called
|
|
9
|
+
* a legacy `runUpdate(args)` that swallowed every flag; this version
|
|
10
|
+
* routes update through `parseFlags` + `runInstaller` + `runRepair` so
|
|
11
|
+
* the documented flags actually do what they claim.
|
|
6
12
|
*/
|
|
7
13
|
import chalk from 'chalk';
|
|
8
14
|
import { runInstaller } from '../install.mjs';
|
|
9
|
-
import { runUpdate } from '../update.mjs';
|
|
10
15
|
import { runRepair } from '../repair.mjs';
|
|
11
16
|
import { parseFlags } from '../provision.mjs';
|
|
12
17
|
|
|
@@ -61,35 +66,53 @@ export function showInstallHelp() {
|
|
|
61
66
|
|
|
62
67
|
export function showUpdateHelp() {
|
|
63
68
|
console.log(`
|
|
64
|
-
bizar update — Update @
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
bizar update — Update @polderlabs/bizar (CLI + SDK + agents +
|
|
70
|
+
skills + hooks + commands). Refreshing Claude Code itself is also
|
|
71
|
+
handled when the bundled install.sh runs under --force.
|
|
72
|
+
|
|
67
73
|
Usage:
|
|
68
|
-
bizar update
|
|
69
|
-
bizar update --check Only print current vs. latest; do not update
|
|
70
|
-
bizar update --channel=stable|beta Pick the npm dist-tag (default: stable)
|
|
74
|
+
bizar update Refresh every Bizar-managed surface
|
|
71
75
|
bizar update --dry-run Print what would happen, change nothing
|
|
72
|
-
bizar update --force
|
|
73
|
-
|
|
76
|
+
bizar update --force | --deep Full clean re-emit: wipe Bizar-managed
|
|
77
|
+
dirs, back up settings env vars,
|
|
78
|
+
re-sync everything from the repo
|
|
79
|
+
bizar update --yes | -y Assume yes for any non-destructive prompt
|
|
80
|
+
bizar update --non-interactive Alias for --yes
|
|
74
81
|
bizar update --help Show this help
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
82
|
+
|
|
83
|
+
Behavior (v10.19.6+):
|
|
84
|
+
Single unified provisioner — the same code path as 'bizar install'
|
|
85
|
+
with mode=update. Every step is idempotent; re-running is safe.
|
|
86
|
+
|
|
87
|
+
1. Re-emits skills, commands, rules, hooks, agents, and workflows
|
|
88
|
+
from the repo source into ~/.claude/ (or $CLAUDE_CONFIG_DIR),
|
|
89
|
+
overwriting only the Bizar-managed surface and pruning stale
|
|
90
|
+
entries (F-141).
|
|
91
|
+
2. Writes the install marker so subsequent runs short-circuit when
|
|
92
|
+
nothing has changed.
|
|
93
|
+
3. With --force, re-runs the F-183 clean-install flow: wipes
|
|
94
|
+
~/.claude/{agents,skills,commands,hooks,rules,workflows,plugins}/
|
|
95
|
+
and ~/.agents/, stashes the prior settings.json env block into
|
|
96
|
+
BIZAR_SAVED_ENV, then re-emits settings.json with the operator's
|
|
97
|
+
ANTHROPIC_* and BIZAR_* keys union-merged back in (so gateway
|
|
98
|
+
URL, auth token, and BIZAR_HOME are preserved across the wipe).
|
|
99
|
+
4. Runs 'bizar doctor' after a successful update so config
|
|
100
|
+
regressions surface before the next Claude Code session.
|
|
101
|
+
5. Repairs stale bin symlinks so the operator picks up the new code
|
|
102
|
+
on the next shell prompt.
|
|
103
|
+
|
|
104
|
+
Idempotency note:
|
|
105
|
+
When the SDK on disk matches the published npm version, every step
|
|
106
|
+
above is a no-op — settings.json is rewritten only if its hash
|
|
107
|
+
drifted, sync targets are skipped when the file set is unchanged,
|
|
108
|
+
and the bin symlink repair short-circuits. A clean re-run is fast.
|
|
109
|
+
|
|
88
110
|
Examples:
|
|
89
|
-
bizar update
|
|
90
|
-
bizar update --
|
|
91
|
-
bizar update --
|
|
92
|
-
bizar update --
|
|
111
|
+
bizar update Refresh the managed surface
|
|
112
|
+
bizar update --dry-run Preview every step
|
|
113
|
+
bizar update --force Full clean re-emit, preserved env
|
|
114
|
+
bizar update --force --yes Same, no prompts (script-friendly)
|
|
115
|
+
|
|
93
116
|
Errors:
|
|
94
117
|
Network failures (registry offline / DNS) and npm permission issues
|
|
95
118
|
are surfaced with the raw npm output. The provisioner never silently
|
|
@@ -97,7 +120,34 @@ export function showUpdateHelp() {
|
|
|
97
120
|
`);
|
|
98
121
|
}
|
|
99
122
|
|
|
123
|
+
// ── Shared post-install teardown ────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Run `runRepair({})` after install/update and surface any repointed
|
|
127
|
+
* bin symlinks to the operator. Both `install()` and `update()` share
|
|
128
|
+
* this block; the previous `bizar update` skipped it entirely (audit
|
|
129
|
+
* A6), so a freshly-upgraded Bizar left stale bin symlinks pointing at
|
|
130
|
+
* the prior install until the operator manually re-sourced their shell.
|
|
131
|
+
*
|
|
132
|
+
* @param {object} [opts]
|
|
133
|
+
* @param {(opts: object) => Promise<{ ok: boolean, fixed: string[], notes?: string[] }>} [opts.runRepair]
|
|
134
|
+
* Dependency-injected for tests; defaults to the real `runRepair`.
|
|
135
|
+
*/
|
|
136
|
+
async function runPostInstallerRepair({ runRepair: runRepairDep = runRepair } = {}) {
|
|
137
|
+
try {
|
|
138
|
+
const r = await runRepairDep({});
|
|
139
|
+
if (r.fixed.length > 0) {
|
|
140
|
+
console.log(chalk.cyan('\n Repair: repointed stale bin symlinks:'));
|
|
141
|
+
for (const f of r.fixed) console.log(` ${f}`);
|
|
142
|
+
console.log(chalk.dim(' Re-run your shell or `hash -r` to pick up the new path.'));
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
console.log(chalk.dim(` Repair skipped: ${err.message}`));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
100
149
|
// ── Command runners ────────────────────────────────────────────────────────────
|
|
150
|
+
|
|
101
151
|
export async function install(args, isHelpRequest) {
|
|
102
152
|
if (isHelpRequest) {
|
|
103
153
|
showInstallHelp();
|
|
@@ -122,16 +172,10 @@ export async function install(args, isHelpRequest) {
|
|
|
122
172
|
}
|
|
123
173
|
// v4.4.3 — After install, repair any stale bin symlinks so the
|
|
124
174
|
// user picks up the new code.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
for (const f of r.fixed) console.log(` ${f}`);
|
|
130
|
-
console.log(chalk.dim(' Re-run your shell or `hash -r` to pick up the new path.'));
|
|
131
|
-
}
|
|
132
|
-
} catch (err) {
|
|
133
|
-
console.log(chalk.dim(` Repair skipped: ${err.message}`));
|
|
134
|
-
}
|
|
175
|
+
await runPostInstallerRepair();
|
|
176
|
+
// v10.19.6 — propagate install failure to the parent shell so
|
|
177
|
+
// `bizar install && bizar doctor` short-circuits on install errors.
|
|
178
|
+
if (!result?.ok) process.exit(1);
|
|
135
179
|
}
|
|
136
180
|
|
|
137
181
|
export async function update(args, isHelpRequest) {
|
|
@@ -139,7 +183,46 @@ export async function update(args, isHelpRequest) {
|
|
|
139
183
|
showUpdateHelp();
|
|
140
184
|
return;
|
|
141
185
|
}
|
|
142
|
-
|
|
186
|
+
// v10.19.6 — route update through the same flag-parsing + installer
|
|
187
|
+
// pipeline as install so `--dry-run`, `--force`, `--yes`, etc. do
|
|
188
|
+
// what they claim. See `runUpdateWithFlags` for the testable
|
|
189
|
+
// dependency-injected core.
|
|
190
|
+
await runUpdateWithFlags({ args });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Testable core of `update()`. Default arguments bind to the real
|
|
195
|
+
* `runInstaller` / `parseFlags` / `runRepair` from this module; tests
|
|
196
|
+
* inject stubs to assert the wiring without touching disk.
|
|
197
|
+
*
|
|
198
|
+
* Sequence (mirrors install() so the audit's documented "Runs doctor +
|
|
199
|
+
* runs repair" promises become true):
|
|
200
|
+
* 1. `parseFlags(args)` → `{ mode, dryRun, force, yes }`
|
|
201
|
+
* 2. `runInstaller({ mode, dryRun, force, yes })` — runs the
|
|
202
|
+
* provisioner, then post-install `runDoctor({ silent: true })`.
|
|
203
|
+
* 3. `runRepair({})` — repoint stale bin symlinks.
|
|
204
|
+
* 4. If `runInstaller` returned `{ ok: false }`, exit(1) so scripts
|
|
205
|
+
* that gate on the exit code (`bizar update && bizar doctor`) see
|
|
206
|
+
* the failure.
|
|
207
|
+
*
|
|
208
|
+
* @param {object} [opts]
|
|
209
|
+
* @param {string[]} [opts.args]
|
|
210
|
+
* @param {(opts: { mode: string, dryRun: boolean, force: boolean, yes: boolean }) => Promise<{ ok?: boolean }>} [opts.runInstaller]
|
|
211
|
+
* @param {(argv: string[]) => { mode: string, dryRun: boolean, force: boolean, yes: boolean }} [opts.parseFlags]
|
|
212
|
+
* @param {(opts: { dryRun?: boolean }) => Promise<{ ok: boolean, fixed: string[] }>} [opts.runRepair]
|
|
213
|
+
* @returns {Promise<{ ok?: boolean }>}
|
|
214
|
+
*/
|
|
215
|
+
export async function runUpdateWithFlags({
|
|
216
|
+
args = [],
|
|
217
|
+
runInstaller: runInstallerDep = runInstaller,
|
|
218
|
+
parseFlags: parseFlagsDep = parseFlags,
|
|
219
|
+
runRepair: runRepairDep = runRepair,
|
|
220
|
+
} = {}) {
|
|
221
|
+
const { mode, dryRun, force, yes } = parseFlagsDep(args);
|
|
222
|
+
const result = await runInstallerDep({ mode, dryRun, force, yes });
|
|
223
|
+
await runPostInstallerRepair({ runRepair: runRepairDep });
|
|
224
|
+
if (!result?.ok) process.exit(1);
|
|
225
|
+
return result;
|
|
143
226
|
}
|
|
144
227
|
|
|
145
228
|
// ── run() entry point (used by bin.mjs dispatcher) ──────────────────────────────
|
package/cli/commands/models.mjs
CHANGED
|
@@ -227,7 +227,15 @@ function normalizedModelIdentity(id) {
|
|
|
227
227
|
};
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Build a per-model profile object from a Models.dev catalog row.
|
|
232
|
+
*
|
|
233
|
+
* Phase 1 (v10.19.7): in addition to the long-standing `name` / `family` /
|
|
234
|
+
* `capabilities` / `limits` / `metadata` fields, propagate Models.dev's
|
|
235
|
+
* `description` and `summary` onto the profile so Phase 3's status screen
|
|
236
|
+
* (10.19.9) can render the description without re-querying the catalog.
|
|
237
|
+
*/
|
|
238
|
+
export function toCapabilityProfile(gatewayId, match, matchType, confidence) {
|
|
231
239
|
const limit = match.limit && typeof match.limit === 'object' ? match.limit : {};
|
|
232
240
|
const modalities = match.modalities && typeof match.modalities === 'object' ? match.modalities : {};
|
|
233
241
|
return {
|
|
@@ -235,6 +243,8 @@ function toCapabilityProfile(gatewayId, match, matchType, confidence) {
|
|
|
235
243
|
baseModel: match.id,
|
|
236
244
|
name: typeof match.name === 'string' ? match.name : match.id,
|
|
237
245
|
family: typeof match.family === 'string' ? match.family : null,
|
|
246
|
+
description: typeof match.description === 'string' ? match.description : null,
|
|
247
|
+
summary: typeof match.summary === 'string' ? match.summary : null,
|
|
238
248
|
capabilities: {
|
|
239
249
|
attachment: match.attachment === true,
|
|
240
250
|
reasoning: match.reasoning === true,
|
|
@@ -265,6 +275,15 @@ function toCapabilityProfile(gatewayId, match, matchType, confidence) {
|
|
|
265
275
|
* Enrich gateway-discovered models with Models.dev profiles. Exact IDs win.
|
|
266
276
|
* A normalized/suffix match is accepted only when unique; ambiguous models
|
|
267
277
|
* remain unmatched rather than receiving guessed capabilities.
|
|
278
|
+
*
|
|
279
|
+
* Phase 1 (v10.19.7): when Models.dev has no row for the candidate AND the
|
|
280
|
+
* candidate carries gateway-supplied `_gateway.name` / `_gateway.description`,
|
|
281
|
+
* build a minimal `profile` so the picker row renderer can read
|
|
282
|
+
* `profile.name` / `profile.description` directly without dereferencing
|
|
283
|
+
* `_gateway`. Candidates whose `normalizeModels` output had no `_gateway`
|
|
284
|
+
* fields keep the legacy `profile === null` contract so `capabilityLabel`
|
|
285
|
+
* still returns `'metadata unavailable'` (Phase 2 owns the rewrite that
|
|
286
|
+
* lets a non-null profile render the `'metadata unavailable'` label).
|
|
268
287
|
*/
|
|
269
288
|
export function enrichModelsWithCapabilities(candidates, catalog) {
|
|
270
289
|
const entries = flattenModelsDevCatalog(catalog);
|
|
@@ -286,7 +305,47 @@ export function enrichModelsWithCapabilities(candidates, catalog) {
|
|
|
286
305
|
const profile = toCapabilityProfile(gatewayId, matches[0], 'unique-normalized-id', 0.7);
|
|
287
306
|
return { ...candidate, profile, contextWindow: profile.limits.contextTokens };
|
|
288
307
|
}
|
|
289
|
-
|
|
308
|
+
// Models.dev miss: if the candidate carries gateway-supplied label /
|
|
309
|
+
// description, build a minimal `profile` so the picker row renderer
|
|
310
|
+
// can read `profile.name` / `profile.description` directly. Candidates
|
|
311
|
+
// that arrived from `normalizeModels` WITHOUT any `_gateway` data keep
|
|
312
|
+
// the legacy `profile === null` contract so `capabilityLabel(null)`
|
|
313
|
+
// still returns `'metadata unavailable'` (Phase 2 owns that rewrite).
|
|
314
|
+
const gw = (candidate && typeof candidate._gateway === 'object' && candidate._gateway) || {};
|
|
315
|
+
const gatewayName = typeof gw.name === 'string' ? gw.name
|
|
316
|
+
: (typeof gw.display_name === 'string' ? gw.display_name : null);
|
|
317
|
+
const gatewayDescription = typeof gw.description === 'string' ? gw.description : null;
|
|
318
|
+
if (gatewayName === null && gatewayDescription === null) {
|
|
319
|
+
return { ...candidate, profile: null, contextWindow: null };
|
|
320
|
+
}
|
|
321
|
+
const fallbackProfile = {
|
|
322
|
+
gatewayId,
|
|
323
|
+
baseModel: gatewayId,
|
|
324
|
+
name: gatewayName,
|
|
325
|
+
family: null,
|
|
326
|
+
description: gatewayDescription,
|
|
327
|
+
summary: null,
|
|
328
|
+
capabilities: {
|
|
329
|
+
attachment: false,
|
|
330
|
+
reasoning: false,
|
|
331
|
+
toolCall: false,
|
|
332
|
+
structuredOutput: false,
|
|
333
|
+
temperature: true,
|
|
334
|
+
inputModalities: ['text'],
|
|
335
|
+
outputModalities: ['text'],
|
|
336
|
+
},
|
|
337
|
+
limits: { contextTokens: null, inputTokens: null, outputTokens: null },
|
|
338
|
+
releaseDate: null,
|
|
339
|
+
lastUpdated: null,
|
|
340
|
+
metadata: {
|
|
341
|
+
source: 'gateway-fallback',
|
|
342
|
+
sourceUrl: null,
|
|
343
|
+
retrievedAt: new Date().toISOString(),
|
|
344
|
+
matchType: 'gateway-fallback',
|
|
345
|
+
confidence: 0,
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
return { ...candidate, profile: fallbackProfile, contextWindow: null };
|
|
290
349
|
});
|
|
291
350
|
}
|
|
292
351
|
|
|
@@ -379,7 +438,24 @@ async function fetchOnce({ doFetch, url, authToken, timeoutMs }) {
|
|
|
379
438
|
}
|
|
380
439
|
}
|
|
381
440
|
|
|
382
|
-
|
|
441
|
+
/**
|
|
442
|
+
* Normalize the gateway `/models` response into the candidate-pool shape
|
|
443
|
+
* consumed by the picker.
|
|
444
|
+
*
|
|
445
|
+
* Phase 1 (v10.19.7): in addition to the existing `id` / `owned_by` / `kind`
|
|
446
|
+
* fields, preserve the gateway's optional `name` / `display_name` /
|
|
447
|
+
* `description` payload under a new `_gateway` sub-object. The picker row
|
|
448
|
+
* renderer reads `profile.name` / `profile.description`, so when the
|
|
449
|
+
* Models.dev enrichment misses (Phase 2) the renderer can still surface a
|
|
450
|
+
* gateway-supplied label or description rather than an id-derived fallback.
|
|
451
|
+
*
|
|
452
|
+
* NOTE: `_gateway` is in-memory only. `applyModels` never writes it to
|
|
453
|
+
* `model-router.json`; the persisted shape stays as it was before this
|
|
454
|
+
* change. See `models-namespace-sync.test.mjs#normalizeModels does not
|
|
455
|
+
* persist _gateway into userSelected on round-trip` for the regression
|
|
456
|
+
* pin.
|
|
457
|
+
*/
|
|
458
|
+
export function normalizeModels(body) {
|
|
383
459
|
if (!body || typeof body !== 'object') return [];
|
|
384
460
|
const list = Array.isArray(body.data) ? body.data : Array.isArray(body) ? body : [];
|
|
385
461
|
const out = [];
|
|
@@ -388,7 +464,11 @@ function normalizeModels(body) {
|
|
|
388
464
|
const id = typeof m.id === 'string' ? m.id.trim() : '';
|
|
389
465
|
if (!id) continue;
|
|
390
466
|
const owned = typeof m.owned_by === 'string' ? m.owned_by : '';
|
|
391
|
-
|
|
467
|
+
const gw = {};
|
|
468
|
+
if (typeof m.name === 'string' && m.name) gw.name = m.name;
|
|
469
|
+
if (typeof m.display_name === 'string' && m.display_name) gw.display_name = m.display_name;
|
|
470
|
+
if (typeof m.description === 'string' && m.description) gw.description = m.description;
|
|
471
|
+
out.push({ id, owned_by: owned, kind: classifyKind(id), _gateway: gw });
|
|
392
472
|
}
|
|
393
473
|
// Stable order — by id — so the picker does not shuffle between runs.
|
|
394
474
|
out.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
package/cli/commands/util.mjs
CHANGED
|
@@ -182,17 +182,14 @@ export async function runTestGate() {
|
|
|
182
182
|
|
|
183
183
|
export async function run(name, args, isHelpRequest) {
|
|
184
184
|
switch (name) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
await runUpdate(args, {});
|
|
194
|
-
}
|
|
195
|
-
break;
|
|
185
|
+
// NOTE: 'update' is intentionally NOT routed through this dispatcher.
|
|
186
|
+
// `cli/bin.mjs` dispatches 'install' / 'update' directly to
|
|
187
|
+
// `cli/commands/install.mjs`, which owns both commands (they share
|
|
188
|
+
// the same code path). The legacy branch below used to import
|
|
189
|
+
// `runUpdate` from `./install.mjs`, but that module never exported
|
|
190
|
+
// `runUpdate` — the import would throw at runtime. The branch was
|
|
191
|
+
// dead code, deleted in v10.19.6 as part of the `bizar update` audit
|
|
192
|
+
// fix.
|
|
196
193
|
|
|
197
194
|
case 'audit':
|
|
198
195
|
if (isHelpRequest) showAuditHelp();
|
package/cli/provision.mjs
CHANGED
|
@@ -1431,10 +1431,11 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
1431
1431
|
});
|
|
1432
1432
|
}
|
|
1433
1433
|
|
|
1434
|
-
// Back-compat alias — `bizar update` historically
|
|
1435
|
-
//
|
|
1436
|
-
// `
|
|
1437
|
-
// `cli/commands/install.mjs`
|
|
1438
|
-
//
|
|
1434
|
+
// Back-compat alias — `bizar update` historically called `runUpdate(args)`;
|
|
1435
|
+
// install + update now share `runProvision({ mode: 'update', ... })`.
|
|
1436
|
+
// `runUpdate` is retained as an importable export for external SDK
|
|
1437
|
+
// consumers (re-exported from `cli/update.mjs`); `cli/commands/install.mjs`
|
|
1438
|
+
// no longer imports it — its `update()` command routes through `parseFlags`
|
|
1439
|
+
// → `runInstaller({ mode: 'update', ... })` instead.
|
|
1439
1440
|
export const runUpdate = (subargs, opts = {}) =>
|
|
1440
1441
|
runProvision({ ...opts, mode: 'update', subargs });
|
package/package.json
CHANGED