bazilion 0.14.0 → 0.14.2

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.
Files changed (52) hide show
  1. package/README.md +18 -6
  2. package/dist/cli.js +288 -89
  3. package/dist/cli.js.map +1 -1
  4. package/dist/daemon.js +514 -76
  5. package/dist/daemon.js.map +1 -1
  6. package/dist/web/client/assets/{ConfirmDialog-CyL9A20V.js → ConfirmDialog-_bmG-wVG.js} +1 -1
  7. package/dist/web/client/assets/{FieldRow-D6REbYUa.js → FieldRow-Do1cBkRc.js} +1 -1
  8. package/dist/web/client/assets/{ProductionTeamPolicyEditor-DBKnOezm.js → ProductionTeamPolicyEditor-XP-QJul4.js} +1 -1
  9. package/dist/web/client/assets/{ProfileCommunicationEditor-4RK2laqO.js → ProfileCommunicationEditor-CwdFPdpk.js} +1 -1
  10. package/dist/web/client/assets/{TeamTabs-CdtNEqt4.js → TeamTabs-DIRiKcqx.js} +1 -1
  11. package/dist/web/client/assets/{TemplatesTabs-CZyss2Y0.js → TemplatesTabs-D4WHvs6b.js} +1 -1
  12. package/dist/web/client/assets/{UnsavedChangesGuard-CEoK-ZcN.js → UnsavedChangesGuard-B1aLqiK5.js} +1 -1
  13. package/dist/web/client/assets/{_id-DIqtdQij.js → _id-3w4PFfma.js} +1 -1
  14. package/dist/web/client/assets/{_id-4ZS-w_Aj.js → _id-BH3v_vI_.js} +1 -1
  15. package/dist/web/client/assets/{_id-DbcC2E7F.js → _id-Cx7o4Zmf.js} +1 -1
  16. package/dist/web/client/assets/{_id-f_r5M0p3.js → _id-DIiVVxBl.js} +1 -1
  17. package/dist/web/client/assets/{activity-CB9Ufth-.js → activity-A_HrG1Tv.js} +1 -1
  18. package/dist/web/client/assets/{agents-D-JGXVg9.js → agents-BeeFCt2c.js} +1 -1
  19. package/dist/web/client/assets/{agents-DS2XafoA.js → agents-C8OVKq3N.js} +1 -1
  20. package/dist/web/client/assets/{approvals-Cke_mrJv.js → approvals-BIdZ9BSn.js} +1 -1
  21. package/dist/web/client/assets/{attention-PYwRM2wC.js → attention-B9bUrxiU.js} +1 -1
  22. package/dist/web/client/assets/chevron-right-VWrk_dTX.js +1 -0
  23. package/dist/web/client/assets/{config-DF9Gqmfa.js → config-By86S7k-.js} +2 -2
  24. package/dist/web/client/assets/{inbox-DC_HdOfI.js → inbox-DlXQCJLe.js} +1 -1
  25. package/dist/web/client/assets/{index-Cp6UHWr2.js → index-CLdmsmiN.js} +2 -2
  26. package/dist/web/client/assets/{learning-BfrQ4YZY.js → learning-BqZIw2nA.js} +1 -1
  27. package/dist/web/client/assets/{login-D4n3AcBR.js → login-CCMPXiny.js} +1 -1
  28. package/dist/web/client/assets/{mcp-CnjQc_JJ.js → mcp-DKSY2yVa.js} +1 -1
  29. package/dist/web/client/assets/{members-wxsVTJdW.js → members-9GuOiq6J.js} +1 -1
  30. package/dist/web/client/assets/{memory-Du60O3pE.js → memory-DiGMI6FC.js} +1 -1
  31. package/dist/web/client/assets/{policy-BOfnkJsL.js → policy-TBGQffw5.js} +1 -1
  32. package/dist/web/client/assets/{routes-B9CuhzUW.js → routes-g3yUOhCb.js} +1 -1
  33. package/dist/web/client/assets/{services-DyvqJqM2.js → services-1Z398CES.js} +1 -1
  34. package/dist/web/client/assets/{skills-DQo7ioW7.js → skills-BP2gciFt.js} +1 -1
  35. package/dist/web/client/assets/{teams-DPggy97H.js → teams-DBIivGT7.js} +1 -1
  36. package/dist/web/client/assets/{teams-DdawcfNR.js → teams-tNoMDJ_M.js} +1 -1
  37. package/dist/web/client/assets/{telegram-DcTeDpP5.js → telegram-CJG_DFdI.js} +1 -1
  38. package/dist/web/client/assets/{tokens-CIEKwZXc.js → tokens-eDWCGQQ2.js} +1 -1
  39. package/dist/web/client/assets/{triggers-Bc2RrUPm.js → triggers-DZME3IcK.js} +1 -1
  40. package/dist/web/client/assets/{welcome-DlhNXUIa.js → welcome-B49sh_uf.js} +1 -1
  41. package/dist/web/client/assets/{wire-constants-BzdHSc6A.js → wire-constants-zZ7Wjb9C.js} +1 -1
  42. package/dist/web/client/assets/{x-DAXGIe_Q.js → x-CHc7_-TH.js} +1 -1
  43. package/dist/web/server/assets/{_tanstack-start-manifest_v-C-OiOZeM.js → _tanstack-start-manifest_v-Duhf0z7H.js} +76 -76
  44. package/dist/web/server/assets/{config-ExacOzHX.js → config-BmLq53tq.js} +33 -11
  45. package/dist/web/server/assets/{config-BlMkHStx.js → config-Wy6hnZ-Z.js} +1 -1
  46. package/dist/web/server/assets/{router-pjH2t8Rl.js → router-CY_pCgP1.js} +4 -3
  47. package/dist/web/server/assets/{welcome-B8nchXdF.js → welcome-BdQGR68o.js} +1 -1
  48. package/dist/web/server/server.js +2 -2
  49. package/dist/worker.js +64 -19
  50. package/dist/worker.js.map +1 -1
  51. package/package.json +6 -6
  52. package/dist/web/client/assets/chevron-right-BG4OeFRQ.js +0 -1
package/dist/daemon.js CHANGED
@@ -19122,6 +19122,7 @@ var init_client2 = __esm({
19122
19122
  });
19123
19123
 
19124
19124
  // ../daemon/src/index.ts
19125
+ import { writeSync as writeSync2 } from "fs";
19125
19126
  import { serve } from "@hono/node-server";
19126
19127
 
19127
19128
  // ../daemon/src/app.ts
@@ -20964,6 +20965,19 @@ function openDb(path) {
20964
20965
  }
20965
20966
  };
20966
20967
  }
20968
+ function openInMemoryDb() {
20969
+ const raw2 = new DatabaseSync(":memory:");
20970
+ applyPragmas(raw2, false);
20971
+ return {
20972
+ raw: wrap(raw2),
20973
+ backupTo(path) {
20974
+ return backup(raw2, path);
20975
+ },
20976
+ close() {
20977
+ raw2.close();
20978
+ }
20979
+ };
20980
+ }
20967
20981
  function inTx(db, fn) {
20968
20982
  return db.raw.transaction(fn)();
20969
20983
  }
@@ -20973,30 +20987,101 @@ import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
20973
20987
  import { dirname, join as join8 } from "path";
20974
20988
  import { fileURLToPath } from "url";
20975
20989
  var migrationsDir = join8(dirname(fileURLToPath(import.meta.url)), "migrations");
20990
+ var schemaMigrationsSql = `
20991
+ CREATE TABLE IF NOT EXISTS schema_migrations (
20992
+ version TEXT PRIMARY KEY,
20993
+ applied_at INTEGER NOT NULL
20994
+ )
20995
+ `;
20996
+ var INCOMPATIBLE_DATABASE_MESSAGE = "Bazilion cannot start because this home uses an incompatible alpha database schema. Preserve the entire Bazilion home directory as a filesystem backup first, keeping bazilion.db and auth.json together. Then perform the reset with `bazilion uninstall --yes` (from the repository root of a source checkout: `pnpm tsx apps/cli/src/index.ts uninstall --yes`) and start Bazilion again. Use `--all` only if you also want to remove logs and installed skills.";
20997
+ var IncompatibleDatabaseError = class extends Error {
20998
+ constructor() {
20999
+ super(INCOMPATIBLE_DATABASE_MESSAGE);
21000
+ this.name = "IncompatibleDatabaseError";
21001
+ }
21002
+ };
21003
+ var canonicalSchemaObjects = null;
21004
+ function migrationFiles() {
21005
+ return readdirSync2(migrationsDir).filter((file) => file.endsWith(".sql")).sort().map((file) => ({
21006
+ version: file.replace(/\.sql$/, ""),
21007
+ sql: readFileSync5(join8(migrationsDir, file), "utf8")
21008
+ }));
21009
+ }
21010
+ function normalizeSql(sql) {
21011
+ return sql.replace(/\s+/g, " ").trim();
21012
+ }
21013
+ function schemaObjects(db) {
21014
+ return db.raw.query(
21015
+ `SELECT type, name, tbl_name, sql
21016
+ FROM sqlite_schema
21017
+ WHERE sql IS NOT NULL AND substr(name, 1, 7) <> 'sqlite_'
21018
+ ORDER BY type, name`
21019
+ ).all();
21020
+ }
21021
+ function schemaPayload(rows) {
21022
+ return rows.map((row) => [row.type, row.name, row.tbl_name, normalizeSql(row.sql)].join("\0")).join("\n");
21023
+ }
21024
+ function expectedSchema(files) {
21025
+ if (canonicalSchemaObjects !== null) return canonicalSchemaObjects;
21026
+ const canonical = openInMemoryDb();
21027
+ try {
21028
+ canonical.raw.exec(schemaMigrationsSql);
21029
+ for (const file of files) canonical.raw.exec(file.sql);
21030
+ canonicalSchemaObjects = schemaObjects(canonical);
21031
+ return canonicalSchemaObjects;
21032
+ } finally {
21033
+ canonical.close();
21034
+ }
21035
+ }
21036
+ function assertMigrationCompatibility(db) {
21037
+ const files = migrationFiles();
21038
+ const expectedVersions = files.map((file) => file.version);
21039
+ let appliedVersions;
21040
+ try {
21041
+ appliedVersions = db.raw.query("SELECT version FROM schema_migrations ORDER BY version").all().map((row) => row.version);
21042
+ } catch {
21043
+ throw new IncompatibleDatabaseError();
21044
+ }
21045
+ const actualObjects = schemaObjects(db);
21046
+ const expectedObjects = expectedSchema(files);
21047
+ if (appliedVersions.length === 0) {
21048
+ const migrationTable = actualObjects.filter(
21049
+ (row) => row.type === "table" && row.name === "schema_migrations"
21050
+ );
21051
+ const expectedMigrationTable = expectedObjects.filter(
21052
+ (row) => row.type === "table" && row.name === "schema_migrations"
21053
+ );
21054
+ if (actualObjects.length !== 1 || migrationTable.length !== 1 || schemaPayload(migrationTable) !== schemaPayload(expectedMigrationTable)) {
21055
+ throw new IncompatibleDatabaseError();
21056
+ }
21057
+ return;
21058
+ }
21059
+ if (appliedVersions.length !== expectedVersions.length || appliedVersions.some((version2, index) => version2 !== expectedVersions[index]) || schemaPayload(actualObjects) !== schemaPayload(expectedObjects)) {
21060
+ throw new IncompatibleDatabaseError();
21061
+ }
21062
+ }
20976
21063
  function runMigrations(db) {
20977
- db.raw.exec(`
20978
- CREATE TABLE IF NOT EXISTS schema_migrations (
20979
- version TEXT PRIMARY KEY,
20980
- applied_at INTEGER NOT NULL
20981
- )
20982
- `);
21064
+ const existingObjects = schemaObjects(db);
21065
+ if (existingObjects.length > 0 && !existingObjects.some((row) => row.type === "table" && row.name === "schema_migrations")) {
21066
+ throw new IncompatibleDatabaseError();
21067
+ }
21068
+ db.raw.exec(schemaMigrationsSql);
21069
+ assertMigrationCompatibility(db);
20983
21070
  const applied = new Set(
20984
21071
  db.raw.query("SELECT version FROM schema_migrations").all().map((r) => r.version)
20985
21072
  );
20986
- const files = readdirSync2(migrationsDir).filter((f) => f.endsWith(".sql")).sort();
20987
- for (const file of files) {
20988
- const version2 = file.replace(/\.sql$/, "");
20989
- if (applied.has(version2)) continue;
20990
- const sql = readFileSync5(join8(migrationsDir, file), "utf8");
21073
+ for (const file of migrationFiles()) {
21074
+ if (applied.has(file.version)) continue;
20991
21075
  const tx = db.raw.transaction(() => {
20992
- db.raw.exec(sql);
21076
+ db.raw.exec(file.sql);
20993
21077
  db.raw.run("INSERT INTO schema_migrations (version, applied_at) VALUES (?, ?)", [
20994
- version2,
21078
+ file.version,
20995
21079
  Date.now()
20996
21080
  ]);
20997
21081
  });
20998
21082
  tx();
20999
21083
  }
21084
+ assertMigrationCompatibility(db);
21000
21085
  }
21001
21086
 
21002
21087
  // ../daemon/src/core/paths.ts
@@ -25150,20 +25235,61 @@ import {
25150
25235
  } from "fs";
25151
25236
  import { dirname as dirname3, isAbsolute, join as join13, relative, resolve as resolve3, sep as sep2 } from "path";
25152
25237
  import { createStore, extractSnippet } from "@tobilu/qmd";
25238
+
25239
+ // ../daemon/src/lib/native-module-error.ts
25240
+ var NODE_MODULE_ABI_MISMATCH = /was compiled against a different Node\.js version using\s+NODE_MODULE_VERSION\s+(\d+)[\s\S]*?requires\s+NODE_MODULE_VERSION\s+(\d+)/i;
25241
+ function formatNativeModuleAbiMismatch(diagnostic, options = {}) {
25242
+ const mismatch = NODE_MODULE_ABI_MISMATCH.exec(diagnostic);
25243
+ if (!mismatch) return void 0;
25244
+ const builtAbi = mismatch[1];
25245
+ const requiredAbi = mismatch[2];
25246
+ const rebuildCommand = /better_sqlite3\.node|better-sqlite3/i.test(diagnostic) ? "`pnpm rebuild better-sqlite3`" : "`pnpm rebuild`";
25247
+ const subject = options.subject ?? "Bazilion";
25248
+ return `${subject} could not load a native dependency built for Node module ABI ${builtAbi}; the current ${process.version} runtime requires ABI ${requiredAbi}. Reinstall Bazilion with this same Node.js version, then restart it. From the repository root of a source checkout, run ${rebuildCommand} (or \`pnpm install --force\`).`;
25249
+ }
25250
+ var NativeModuleAbiMismatchError = class extends Error {
25251
+ code = "native_module_abi_mismatch";
25252
+ constructor(message) {
25253
+ super(message);
25254
+ this.name = "NativeModuleAbiMismatchError";
25255
+ }
25256
+ };
25257
+ function sanitizeNativeModuleError(error2, options = {}) {
25258
+ const diagnostic = error2 instanceof Error ? error2.message : String(error2);
25259
+ const formatted = formatNativeModuleAbiMismatch(diagnostic, options);
25260
+ if (!formatted) return error2;
25261
+ return new NativeModuleAbiMismatchError(formatted);
25262
+ }
25263
+ async function withNativeModuleErrorBoundary(operation, options = {}) {
25264
+ try {
25265
+ return await operation();
25266
+ } catch (error2) {
25267
+ throw sanitizeNativeModuleError(error2, options);
25268
+ }
25269
+ }
25270
+
25271
+ // ../daemon/src/runtime/memory/qmd.ts
25153
25272
  var INDEX_FILENAME = ".qmd-index.sqlite";
25154
25273
  var COLLECTION_NAME = "memory";
25155
25274
  var PATTERN = "**/*.md";
25275
+ var QMD_NATIVE_ERROR_OPTIONS = { subject: "Bazilion memory" };
25156
25276
  var storeCache = /* @__PURE__ */ new Map();
25157
- function getStore(dir) {
25277
+ function getStore(dir, storeFactory) {
25158
25278
  let p = storeCache.get(dir);
25159
25279
  if (!p) {
25160
- p = createStore({
25161
- dbPath: join13(dir, INDEX_FILENAME),
25162
- config: {
25163
- collections: {
25164
- [COLLECTION_NAME]: { path: dir, pattern: PATTERN }
25280
+ p = withNativeModuleErrorBoundary(
25281
+ () => storeFactory({
25282
+ dbPath: join13(dir, INDEX_FILENAME),
25283
+ config: {
25284
+ collections: {
25285
+ [COLLECTION_NAME]: { path: dir, pattern: PATTERN }
25286
+ }
25165
25287
  }
25166
- }
25288
+ }),
25289
+ QMD_NATIVE_ERROR_OPTIONS
25290
+ ).catch((error2) => {
25291
+ storeCache.delete(dir);
25292
+ throw error2;
25167
25293
  });
25168
25294
  storeCache.set(dir, p);
25169
25295
  }
@@ -25346,8 +25472,9 @@ function walkMd(root, dir, prefix, out) {
25346
25472
  }
25347
25473
  }
25348
25474
  }
25349
- function qmdBackend(root) {
25475
+ function qmdBackend(root, options = {}) {
25350
25476
  let canonicalRoot;
25477
+ const storeFactory = options.createStore ?? createStore;
25351
25478
  const safeRoot = () => {
25352
25479
  if (!canonicalRoot) canonicalRoot = initializeSafeRoot(root);
25353
25480
  else assertRootUnchanged(root, canonicalRoot);
@@ -25357,8 +25484,8 @@ function qmdBackend(root) {
25357
25484
  async init() {
25358
25485
  const safe = safeRoot();
25359
25486
  assertSafeTree(safe);
25360
- const store = await getStore(safe);
25361
- await store.update();
25487
+ const store = await getStore(safe, storeFactory);
25488
+ await withNativeModuleErrorBoundary(() => store.update(), QMD_NATIVE_ERROR_OPTIONS);
25362
25489
  },
25363
25490
  async read(key2) {
25364
25491
  const safe = safeRoot();
@@ -25376,19 +25503,22 @@ function qmdBackend(root) {
25376
25503
  const path = safeKey(safe, key2);
25377
25504
  const stats = writeSafeFile(safe, path, content);
25378
25505
  assertSafeTree(safe);
25379
- const store = await getStore(safe);
25380
- await store.update();
25506
+ const store = await getStore(safe, storeFactory);
25507
+ await withNativeModuleErrorBoundary(() => store.update(), QMD_NATIVE_ERROR_OPTIONS);
25381
25508
  return { key: key2, content, updatedAt: stats.mtimeMs };
25382
25509
  },
25383
25510
  async search(query, opts) {
25384
25511
  const limit = opts?.limit ?? 10;
25385
25512
  const safe = safeRoot();
25386
25513
  assertSafeTree(safe);
25387
- const store = await getStore(safe);
25388
- const results = await store.searchLex(query, {
25389
- limit,
25390
- collection: COLLECTION_NAME
25391
- });
25514
+ const store = await getStore(safe, storeFactory);
25515
+ const results = await withNativeModuleErrorBoundary(
25516
+ () => store.searchLex(query, {
25517
+ limit,
25518
+ collection: COLLECTION_NAME
25519
+ }),
25520
+ QMD_NATIVE_ERROR_OPTIONS
25521
+ );
25392
25522
  const hits = [];
25393
25523
  for (const r of results) {
25394
25524
  const prefix = `${COLLECTION_NAME}/`;
@@ -25423,8 +25553,8 @@ function qmdBackend(root) {
25423
25553
  rmSync10(path);
25424
25554
  }
25425
25555
  assertSafeTree(safe);
25426
- const store = await getStore(safe);
25427
- await store.update();
25556
+ const store = await getStore(safe, storeFactory);
25557
+ await withNativeModuleErrorBoundary(() => store.update(), QMD_NATIVE_ERROR_OPTIONS);
25428
25558
  }
25429
25559
  };
25430
25560
  }
@@ -30821,7 +30951,7 @@ async function* spawnWorker(spec, opts) {
30821
30951
  if (!emittedFatal && exit.code !== 0 && exit.code !== null) {
30822
30952
  yield {
30823
30953
  kind: "fatal",
30824
- error: appendDiagnostic(
30954
+ error: formatWorkerExitFailure(
30825
30955
  `worker exited with code ${exit.code}${exit.signal ? ` (${exit.signal})` : ""}`,
30826
30956
  diagnostic
30827
30957
  )
@@ -30829,7 +30959,7 @@ async function* spawnWorker(spec, opts) {
30829
30959
  } else if (!emittedFatal && exit.signal && exit.code === null) {
30830
30960
  yield {
30831
30961
  kind: "fatal",
30832
- error: appendDiagnostic(`worker killed by ${exit.signal}`, diagnostic)
30962
+ error: formatWorkerExitFailure(`worker killed by ${exit.signal}`, diagnostic)
30833
30963
  };
30834
30964
  }
30835
30965
  } finally {
@@ -30924,7 +31054,9 @@ function spawnHosts(spec, opts, signal, onBashApproval, onApiKeyRefreshed) {
30924
31054
  }
30925
31055
  function parseFrame(line, accessTokens) {
30926
31056
  try {
30927
- return redactJsonValue(JSON.parse(line), accessTokens);
31057
+ return normalizeWorkerDiagnosticFrame(
31058
+ redactJsonValue(JSON.parse(line), accessTokens)
31059
+ );
30928
31060
  } catch {
30929
31061
  return {
30930
31062
  kind: "fatal",
@@ -31136,6 +31268,26 @@ async function pumpWorkerStderr(stderr, capture, redactor) {
31136
31268
  function appendDiagnostic(message, diagnostic) {
31137
31269
  return diagnostic ? `${message}: ${diagnostic}` : message;
31138
31270
  }
31271
+ function formatWorkerExitFailure(message, diagnostic) {
31272
+ const formatted = formatWorkerDiagnostic(diagnostic);
31273
+ if (formatted !== diagnostic) return formatted;
31274
+ return appendDiagnostic(message, diagnostic);
31275
+ }
31276
+ function normalizeWorkerDiagnosticFrame(frame) {
31277
+ if (frame.kind === "fatal") {
31278
+ return { ...frame, error: formatWorkerDiagnostic(frame.error) };
31279
+ }
31280
+ if (frame.kind === "event" && frame.event.type === "error") {
31281
+ return {
31282
+ ...frame,
31283
+ event: { ...frame.event, error: formatWorkerDiagnostic(frame.event.error) }
31284
+ };
31285
+ }
31286
+ return frame;
31287
+ }
31288
+ function formatWorkerDiagnostic(diagnostic) {
31289
+ return formatNativeModuleAbiMismatch(diagnostic, { subject: "worker" }) ?? diagnostic;
31290
+ }
31139
31291
 
31140
31292
  // ../daemon/src/lib/api-key.ts
31141
31293
  async function resolveAgentApiKey(db, authToken, agent, opts = {}) {
@@ -35970,8 +36122,20 @@ var _db = null;
35970
36122
  var _paths = null;
35971
36123
  var _authToken = null;
35972
36124
  var _schedulerStarted = false;
36125
+ var INCOMPATIBLE_BOOTSTRAP_IDENTITY_MESSAGE = "Bazilion cannot start because auth.json does not match an active bootstrap credential in bazilion.db. Preserve the entire Bazilion home directory as a filesystem backup first, keeping bazilion.db and auth.json together. Then perform the reset with `bazilion uninstall --yes` (from the repository root of a source checkout: `pnpm tsx apps/cli/src/index.ts uninstall --yes`) and start Bazilion again. Use `--all` only if you also want to remove logs and installed skills.";
36126
+ var IncompatibleBootstrapIdentityError = class extends Error {
36127
+ constructor() {
36128
+ super(INCOMPATIBLE_BOOTSTRAP_IDENTITY_MESSAGE);
36129
+ this.name = "IncompatibleBootstrapIdentityError";
36130
+ }
36131
+ };
35973
36132
  function bootstrap(paths) {
35974
36133
  mkdirSync9(paths.home, { recursive: true });
36134
+ const databaseExists = existsSync17(paths.db);
36135
+ const authFileExists = existsSync17(paths.authFile);
36136
+ if (databaseExists !== authFileExists) {
36137
+ throw new IncompatibleBootstrapIdentityError();
36138
+ }
35975
36139
  for (const d2 of [
35976
36140
  paths.profilesDir,
35977
36141
  paths.agentsDir,
@@ -35982,26 +36146,46 @@ function bootstrap(paths) {
35982
36146
  mkdirSync9(d2, { recursive: true });
35983
36147
  }
35984
36148
  const db = openDb(paths.db);
35985
- runMigrations(db);
35986
- const refreshed = refreshDefaultProfileTemplates(paths);
35987
- if (refreshed.length) {
35988
- console.log(`bazilion: refreshed default profile templates \u2014 [${refreshed.join(", ")}]`);
35989
- }
35990
- if (!existsSync17(paths.authFile)) {
35991
- const created = webTokens_exports.create(db, "bootstrap", { kind: "bootstrap" });
35992
- writeFileSync12(paths.authFile, `${JSON.stringify({ token: created.token }, null, 2)}
36149
+ try {
36150
+ runMigrations(db);
36151
+ let existingAuthToken = null;
36152
+ if (authFileExists) {
36153
+ try {
36154
+ existingAuthToken = readAuthFile(paths.authFile).token;
36155
+ } catch {
36156
+ throw new IncompatibleBootstrapIdentityError();
36157
+ }
36158
+ const bootstrap2 = webTokens_exports.findActiveByToken(db, existingAuthToken);
36159
+ if (bootstrap2?.kind !== "bootstrap") {
36160
+ throw new IncompatibleBootstrapIdentityError();
36161
+ }
36162
+ }
36163
+ const refreshed = refreshDefaultProfileTemplates(paths);
36164
+ if (refreshed.length) {
36165
+ console.log(`bazilion: refreshed default profile templates \u2014 [${refreshed.join(", ")}]`);
36166
+ }
36167
+ if (existingAuthToken === null) {
36168
+ const created = webTokens_exports.create(db, "bootstrap", { kind: "bootstrap" });
36169
+ writeFileSync12(paths.authFile, `${JSON.stringify({ token: created.token }, null, 2)}
35993
36170
  `, {
35994
- mode: 384
35995
- });
36171
+ mode: 384
36172
+ });
36173
+ try {
36174
+ chmodSync(paths.authFile, 384);
36175
+ } catch {
36176
+ }
36177
+ console.log(`bazilion auto-bootstrapped at ${paths.home}`);
36178
+ console.log(`bootstrap token written to ${paths.authFile}`);
36179
+ return { db, authToken: created.token };
36180
+ }
36181
+ return { db, authToken: existingAuthToken };
36182
+ } catch (error2) {
35996
36183
  try {
35997
- chmodSync(paths.authFile, 384);
36184
+ db.close();
35998
36185
  } catch {
35999
36186
  }
36000
- console.log(`bazilion auto-bootstrapped at ${paths.home}`);
36001
- console.log(`bootstrap token written to ${paths.authFile}`);
36002
- return { db, authToken: created.token };
36187
+ throw error2;
36003
36188
  }
36004
- return { db, authToken: readAuthFile(paths.authFile).token };
36005
36189
  }
36006
36190
  function getCtx() {
36007
36191
  if (!_paths) _paths = resolvePaths();
@@ -39947,9 +40131,227 @@ function mutate(c, acknowledged) {
39947
40131
  }
39948
40132
 
39949
40133
  // ../daemon/src/routes/auth-login.ts
39950
- import { spawn as spawn4 } from "child_process";
39951
40134
  import { Hono as Hono4 } from "hono";
39952
40135
  import { deleteCookie, setCookie } from "hono/cookie";
40136
+
40137
+ // ../daemon/src/lib/openai-oauth-prompt.ts
40138
+ import { spawn as spawn4 } from "child_process";
40139
+ import { createServer as createServer2 } from "net";
40140
+ var OPENAI_CODEX_CALLBACK_PORT = 1455;
40141
+ var WEB_OPENAI_LOGIN_TIMEOUT_MS = 10 * 6e4;
40142
+ var DEVICE_CODE_FALLBACK = "`bazilion auth openai login --device-code` (or, from the repository root of a source checkout, `pnpm tsx apps/cli/src/index.ts auth openai login --device-code`)";
40143
+ var OpenAICodexCallbackUnavailableError = class extends Error {
40144
+ constructor(message) {
40145
+ super(message);
40146
+ this.name = "OpenAICodexCallbackUnavailableError";
40147
+ }
40148
+ };
40149
+ function abortReason(signal) {
40150
+ return signal.reason instanceof Error ? signal.reason : new DOMException("OpenAI browser login cancelled", "AbortError");
40151
+ }
40152
+ function callbackAddress(host2, port2) {
40153
+ return host2.includes(":") ? `[${host2}]:${port2}` : `${host2}:${port2}`;
40154
+ }
40155
+ function openAICodexCallbackHost(env = process.env) {
40156
+ return env.PI_OAUTH_CALLBACK_HOST?.trim() || "127.0.0.1";
40157
+ }
40158
+ function callbackUnavailable(error2, host2, port2) {
40159
+ const code = error2 instanceof Error && "code" in error2 && typeof error2.code === "string" ? error2.code : null;
40160
+ const address = callbackAddress(host2, port2);
40161
+ if (code === "EADDRINUSE") {
40162
+ return new OpenAICodexCallbackUnavailableError(
40163
+ `OpenAI browser login cannot start because callback address ${address} is already in use. Stop the process using that port and try again, or run ${DEVICE_CODE_FALLBACK} in a terminal.`
40164
+ );
40165
+ }
40166
+ return new OpenAICodexCallbackUnavailableError(
40167
+ `OpenAI browser login cannot bind callback address ${address}${code ? ` (${code})` : ""}. Check PI_OAUTH_CALLBACK_HOST and try again, or run ${DEVICE_CODE_FALLBACK} in a terminal.`
40168
+ );
40169
+ }
40170
+ function closeServer(server3) {
40171
+ if (!server3.listening) return Promise.resolve();
40172
+ return new Promise((resolve8) => server3.close(() => resolve8()));
40173
+ }
40174
+ async function preflightOpenAICodexCallback(options) {
40175
+ const host2 = options?.host ?? openAICodexCallbackHost();
40176
+ const port2 = options?.port ?? OPENAI_CODEX_CALLBACK_PORT;
40177
+ const server3 = createServer2();
40178
+ try {
40179
+ await new Promise((resolve8, reject2) => {
40180
+ const cleanup = () => {
40181
+ server3.removeListener("error", onError);
40182
+ server3.removeListener("listening", onListening);
40183
+ };
40184
+ const onError = (error2) => {
40185
+ cleanup();
40186
+ reject2(callbackUnavailable(error2, host2, port2));
40187
+ };
40188
+ const onListening = () => {
40189
+ cleanup();
40190
+ resolve8();
40191
+ };
40192
+ server3.once("error", onError);
40193
+ server3.once("listening", onListening);
40194
+ try {
40195
+ server3.listen({ host: host2, port: port2, exclusive: true });
40196
+ } catch (error2) {
40197
+ onError(error2 instanceof Error ? error2 : new Error(String(error2)));
40198
+ }
40199
+ });
40200
+ } finally {
40201
+ await closeServer(server3);
40202
+ }
40203
+ }
40204
+ function createWebOpenAILoginLifetime(requestSignal, timeoutMs = WEB_OPENAI_LOGIN_TIMEOUT_MS) {
40205
+ const controller = new AbortController();
40206
+ let disposed = false;
40207
+ const abort = (reason) => {
40208
+ if (!disposed && !controller.signal.aborted) controller.abort(reason);
40209
+ };
40210
+ const onRequestAbort = () => {
40211
+ if (requestSignal) abort(abortReason(requestSignal));
40212
+ };
40213
+ if (requestSignal?.aborted) onRequestAbort();
40214
+ else requestSignal?.addEventListener("abort", onRequestAbort, { once: true });
40215
+ const timeout = setTimeout(() => {
40216
+ abort(
40217
+ new DOMException(
40218
+ `OpenAI browser login timed out after 10 minutes. Try again, or run ${DEVICE_CODE_FALLBACK}.`,
40219
+ "TimeoutError"
40220
+ )
40221
+ );
40222
+ }, timeoutMs);
40223
+ timeout.unref();
40224
+ return {
40225
+ signal: controller.signal,
40226
+ abort,
40227
+ dispose() {
40228
+ if (disposed) return;
40229
+ disposed = true;
40230
+ clearTimeout(timeout);
40231
+ requestSignal?.removeEventListener("abort", onRequestAbort);
40232
+ }
40233
+ };
40234
+ }
40235
+ function defaultBrowserSpawn(command, args) {
40236
+ return spawn4(command, args, { stdio: "ignore", detached: true });
40237
+ }
40238
+ function browserLaunchFailure(command, detail3) {
40239
+ return new Error(
40240
+ `Could not open the OpenAI sign-in page with ${command}: ${detail3}. Run ${DEVICE_CODE_FALLBACK} in a terminal.`
40241
+ );
40242
+ }
40243
+ function launchOpenAICodexBrowser(url2, onFailure, options) {
40244
+ const platform = options?.platform ?? process.platform;
40245
+ const [command, ...args] = platform === "darwin" ? ["open", url2] : platform === "win32" ? ["rundll32.exe", "url.dll,FileProtocolHandler", url2] : ["xdg-open", url2];
40246
+ let failed = false;
40247
+ const fail3 = (error2) => {
40248
+ if (failed) return;
40249
+ failed = true;
40250
+ onFailure(error2);
40251
+ };
40252
+ try {
40253
+ const child = (options?.spawnBrowser ?? defaultBrowserSpawn)(command, args);
40254
+ child.once("error", (error2) => {
40255
+ fail3(browserLaunchFailure(command, error2.message));
40256
+ });
40257
+ child.once("exit", (code, signal) => {
40258
+ if (code === 0) return;
40259
+ const detail3 = code === null ? `terminated by ${signal ?? "an unknown signal"}` : `exited ${code}`;
40260
+ fail3(browserLaunchFailure(command, detail3));
40261
+ });
40262
+ child.unref();
40263
+ } catch (error2) {
40264
+ fail3(
40265
+ browserLaunchFailure(
40266
+ command,
40267
+ error2 instanceof Error ? error2.message : String(error2)
40268
+ )
40269
+ );
40270
+ }
40271
+ }
40272
+ var webOpenAILoginTail = Promise.resolve();
40273
+ function waitForLoginTurn(turn, signal) {
40274
+ if (!signal) return turn;
40275
+ if (signal.aborted) return Promise.reject(abortReason(signal));
40276
+ return new Promise((resolve8, reject2) => {
40277
+ const onAbort = () => {
40278
+ signal.removeEventListener("abort", onAbort);
40279
+ reject2(abortReason(signal));
40280
+ };
40281
+ signal.addEventListener("abort", onAbort, { once: true });
40282
+ void turn.then(() => {
40283
+ signal.removeEventListener("abort", onAbort);
40284
+ resolve8();
40285
+ });
40286
+ });
40287
+ }
40288
+ async function acquireWebOpenAILogin(signal) {
40289
+ const previous = webOpenAILoginTail;
40290
+ let releaseGate = () => {
40291
+ };
40292
+ const gate = new Promise((resolve8) => {
40293
+ releaseGate = resolve8;
40294
+ });
40295
+ webOpenAILoginTail = previous.then(() => gate);
40296
+ try {
40297
+ await waitForLoginTurn(previous, signal);
40298
+ } catch (error2) {
40299
+ void previous.then(releaseGate);
40300
+ throw error2;
40301
+ }
40302
+ let released = false;
40303
+ return () => {
40304
+ if (released) return;
40305
+ released = true;
40306
+ releaseGate();
40307
+ };
40308
+ }
40309
+ function answerWebOpenAIPrompt(prompt, loginSignal) {
40310
+ if (prompt.type === "select") {
40311
+ const browser = prompt.options.find((option) => option.id === "browser");
40312
+ return browser ? Promise.resolve(browser.id) : Promise.reject(new Error("Pi openai-codex provider does not support browser login"));
40313
+ }
40314
+ if (prompt.type !== "manual_code") {
40315
+ return Promise.reject(
40316
+ new Error(
40317
+ "interactive paste not supported in the web flow \u2014 cancel and try again, or use `bazilion auth openai login`"
40318
+ )
40319
+ );
40320
+ }
40321
+ return new Promise((_resolve, reject2) => {
40322
+ const promptSignal = prompt.signal;
40323
+ if (!promptSignal && !loginSignal) return;
40324
+ let settled = false;
40325
+ const cleanup = () => {
40326
+ promptSignal?.removeEventListener("abort", onPromptAbort);
40327
+ loginSignal?.removeEventListener("abort", onLoginAbort);
40328
+ };
40329
+ const cancel3 = (signal) => {
40330
+ if (settled) return;
40331
+ settled = true;
40332
+ cleanup();
40333
+ reject2(abortReason(signal));
40334
+ };
40335
+ const onPromptAbort = () => {
40336
+ if (promptSignal) cancel3(promptSignal);
40337
+ };
40338
+ const onLoginAbort = () => {
40339
+ if (loginSignal) cancel3(loginSignal);
40340
+ };
40341
+ if (loginSignal?.aborted) {
40342
+ onLoginAbort();
40343
+ return;
40344
+ }
40345
+ if (promptSignal?.aborted) {
40346
+ onPromptAbort();
40347
+ return;
40348
+ }
40349
+ promptSignal?.addEventListener("abort", onPromptAbort, { once: true });
40350
+ loginSignal?.addEventListener("abort", onLoginAbort, { once: true });
40351
+ });
40352
+ }
40353
+
40354
+ // ../daemon/src/routes/auth-login.ts
39953
40355
  var authRouter = new Hono4();
39954
40356
  authRouter.get("/auth/whoami", (c) => {
39955
40357
  const { db } = getCtx();
@@ -39988,21 +40390,30 @@ authRouter.delete("/auth/openai", (c) => {
39988
40390
  });
39989
40391
  authRouter.post("/auth/openai/login", async (c) => {
39990
40392
  const { db, authToken } = getCtx();
40393
+ const lifetime = createWebOpenAILoginLifetime(c.req.raw.signal);
40394
+ let releaseLogin;
39991
40395
  try {
40396
+ releaseLogin = await acquireWebOpenAILogin(lifetime.signal);
40397
+ if (lifetime.signal.aborted) throw lifetime.signal.reason;
40398
+ await preflightOpenAICodexCallback();
40399
+ if (lifetime.signal.aborted) throw lifetime.signal.reason;
39992
40400
  const creds = await loginOpenAICodex({
40401
+ signal: lifetime.signal,
39993
40402
  notify: (event2) => {
39994
- if (event2.type === "auth_url") openBrowser(event2.url);
40403
+ if (event2.type === "auth_url") {
40404
+ launchOpenAICodexBrowser(event2.url, (error2) => lifetime.abort(error2));
40405
+ }
39995
40406
  },
39996
- prompt: (prompt) => prompt.type === "select" ? Promise.resolve(prompt.options[0]?.id ?? "") : Promise.reject(
39997
- new Error(
39998
- "interactive paste not supported in the web flow \u2014 cancel and try again, or use `bazilion auth openai login`"
39999
- )
40000
- )
40407
+ prompt: (prompt) => answerWebOpenAIPrompt(prompt, lifetime.signal)
40001
40408
  });
40409
+ if (lifetime.signal.aborted) throw lifetime.signal.reason;
40002
40410
  saveLoginCredentials(db, authToken, creds);
40003
40411
  return c.json(getStatus(db, authToken));
40004
40412
  } catch (err) {
40005
40413
  return c.json({ error: err.message }, 400);
40414
+ } finally {
40415
+ releaseLogin?.();
40416
+ lifetime.dispose();
40006
40417
  }
40007
40418
  });
40008
40419
  authRouter.post("/providers/test", async (c) => {
@@ -40099,17 +40510,6 @@ authRouter.delete("/sessions/:id", (c) => {
40099
40510
  }
40100
40511
  return c.body(null, 204);
40101
40512
  });
40102
- function openBrowser(url2) {
40103
- const platform = process.platform;
40104
- const [cmd, ...args] = platform === "darwin" ? ["open", url2] : platform === "win32" ? ["cmd", "/c", "start", '""', url2] : ["xdg-open", url2];
40105
- try {
40106
- const child = spawn4(cmd, args, { stdio: "ignore", detached: true });
40107
- child.unref();
40108
- child.on("error", () => {
40109
- });
40110
- } catch {
40111
- }
40112
- }
40113
40513
 
40114
40514
  // ../daemon/src/routes/communication.ts
40115
40515
  import { Hono as Hono5 } from "hono";
@@ -40626,6 +41026,7 @@ function readExisting(path) {
40626
41026
  "starting",
40627
41027
  "listening",
40628
41028
  "restoring",
41029
+ "uninstalling",
40629
41030
  "swapping",
40630
41031
  "installed",
40631
41032
  "recovery-required"
@@ -40643,7 +41044,9 @@ function readExisting(path) {
40643
41044
  if (typeof parsed.startedAt === "number" && Number.isFinite(parsed.startedAt)) {
40644
41045
  startedAt = parsed.startedAt;
40645
41046
  }
40646
- const validOwnerState = owner === "daemon" && state3 !== void 0 && ["starting", "listening"].includes(state3) && (state3 !== "listening" || host2 !== void 0 && port2 !== void 0) || owner === "restore" && state3 !== void 0 && ["restoring", "swapping", "installed", "recovery-required"].includes(state3) && (!["swapping", "recovery-required"].includes(state3) || recoveryPath !== void 0 && hadPrevious !== void 0);
41047
+ const validOwnerState = owner === "daemon" && state3 !== void 0 && ["starting", "listening"].includes(state3) && (state3 !== "listening" || host2 !== void 0 && port2 !== void 0) || owner === "restore" && state3 !== void 0 && ["restoring", "uninstalling", "swapping", "installed", "recovery-required"].includes(
41048
+ state3
41049
+ ) && (!["swapping", "recovery-required"].includes(state3) || recoveryPath !== void 0 && hadPrevious !== void 0);
40647
41050
  if (!validOwnerState) {
40648
41051
  owner = void 0;
40649
41052
  state3 = void 0;
@@ -40737,6 +41140,16 @@ function acquireDaemonLiveness(paths) {
40737
41140
  );
40738
41141
  }
40739
41142
  const livePid = pidIsAlive(existing.pid);
41143
+ if (existing.owner === "restore" && existing.state === "uninstalling") {
41144
+ if (livePid) {
41145
+ throw new Error(
41146
+ `uninstall process ${existing.pid} is modifying this home; wait for it to finish`
41147
+ );
41148
+ }
41149
+ throw new Error(
41150
+ "a previous Bazilion uninstall was interrupted for this home. Re-run the same `bazilion uninstall` command (including its `--all` choice) with the same BAZILION_HOME (or --home) to finish it before starting Bazilion again."
41151
+ );
41152
+ }
40740
41153
  if (existing.owner === "restore" && ["swapping", "recovery-required"].includes(existing.state)) {
40741
41154
  if (livePid) {
40742
41155
  throw new Error(
@@ -42180,20 +42593,30 @@ teamsRouter.put("/:id/topic-format", async (c) => {
42180
42593
  );
42181
42594
  return c.json(teams_exports.get(db, id, paths));
42182
42595
  });
42596
+ var TeamMemoryNotFoundError = class extends Error {
42597
+ };
42183
42598
  async function openMemory(rawId) {
42184
42599
  const { db, paths } = getCtx();
42185
42600
  const team = teams_exports.get(db, rawId, paths);
42186
- if (!team) throw new Error(`team not found: ${rawId}`);
42601
+ if (!team) throw new TeamMemoryNotFoundError(`team not found: ${rawId}`);
42187
42602
  const mem = qmdBackend(join29(team.path, "memory"));
42188
42603
  await mem.init();
42189
42604
  return { mem, team };
42190
42605
  }
42606
+ function teamMemoryError(c, error2) {
42607
+ const safe = sanitizeNativeModuleError(error2, { subject: "Bazilion memory" });
42608
+ const message = safe instanceof Error ? safe.message : String(safe);
42609
+ if (safe instanceof TeamMemoryNotFoundError || message.startsWith("memory entry not found:")) {
42610
+ return c.json({ error: message }, 404);
42611
+ }
42612
+ return c.json({ error: message }, 500);
42613
+ }
42191
42614
  teamsRouter.get("/:id/memory", async (c) => {
42192
42615
  try {
42193
42616
  const { mem } = await openMemory(c.req.param("id"));
42194
42617
  return c.json(await mem.list());
42195
42618
  } catch (err) {
42196
- return c.json({ error: err.message }, 404);
42619
+ return teamMemoryError(c, err);
42197
42620
  }
42198
42621
  });
42199
42622
  teamsRouter.get("/:id/memory/search", async (c) => {
@@ -42204,7 +42627,7 @@ teamsRouter.get("/:id/memory/search", async (c) => {
42204
42627
  const { mem } = await openMemory(c.req.param("id"));
42205
42628
  return c.json(await mem.search(q2, { limit }));
42206
42629
  } catch (err) {
42207
- return c.json({ error: err.message }, 404);
42630
+ return teamMemoryError(c, err);
42208
42631
  }
42209
42632
  });
42210
42633
  teamsRouter.get("/:id/memory/:key{.+}", async (c) => {
@@ -42212,7 +42635,7 @@ teamsRouter.get("/:id/memory/:key{.+}", async (c) => {
42212
42635
  const { mem } = await openMemory(c.req.param("id"));
42213
42636
  return c.json(await mem.read(c.req.param("key")));
42214
42637
  } catch (err) {
42215
- return c.json({ error: err.message }, 404);
42638
+ return teamMemoryError(c, err);
42216
42639
  }
42217
42640
  });
42218
42641
  teamsRouter.put("/:id/memory/:key{.+}", async (c) => {
@@ -42223,7 +42646,7 @@ teamsRouter.put("/:id/memory/:key{.+}", async (c) => {
42223
42646
  const { mem } = await openMemory(c.req.param("id"));
42224
42647
  return c.json(await mem.write(c.req.param("key"), body.content));
42225
42648
  } catch (err) {
42226
- return c.json({ error: err.message }, 500);
42649
+ return teamMemoryError(c, err);
42227
42650
  }
42228
42651
  });
42229
42652
  teamsRouter.delete("/:id/memory/:key{.+}", async (c) => {
@@ -42232,7 +42655,7 @@ teamsRouter.delete("/:id/memory/:key{.+}", async (c) => {
42232
42655
  await mem.remove(c.req.param("key"));
42233
42656
  return c.body(null, 204);
42234
42657
  } catch (err) {
42235
- return c.json({ error: err.message }, 500);
42658
+ return teamMemoryError(c, err);
42236
42659
  }
42237
42660
  });
42238
42661
  function teamPolicyError(c, error2) {
@@ -42628,7 +43051,22 @@ try {
42628
43051
  );
42629
43052
  process.exit(1);
42630
43053
  }
42631
- getCtx();
43054
+ try {
43055
+ getCtx();
43056
+ } catch (error2) {
43057
+ const detail3 = error2 instanceof IncompatibleDatabaseError || error2 instanceof IncompatibleBootstrapIdentityError ? error2.message : error2 instanceof Error ? error2.stack ?? error2.message : String(error2);
43058
+ writeSync2(process.stderr.fd, `bazilion daemon failed to start: ${detail3}
43059
+ `);
43060
+ try {
43061
+ closeCtxForShutdown();
43062
+ } catch {
43063
+ }
43064
+ try {
43065
+ daemonLiveness.stop();
43066
+ } catch {
43067
+ }
43068
+ process.exit(1);
43069
+ }
42632
43070
  var app = createApp();
42633
43071
  var shuttingDown = false;
42634
43072
  var server2 = serve({ fetch: app.fetch, hostname: host, port }, (info) => {