botinabox 2.16.17 → 2.16.18

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.
@@ -41,6 +41,17 @@ export declare class SecretStore {
41
41
  set(input: SecretInput): Promise<SecretMeta>;
42
42
  get(name: string, environment?: string): Promise<string | null>;
43
43
  getMeta(name: string, environment?: string): Promise<SecretMeta | null>;
44
+ /**
45
+ * Fetch the most recently created live row for (name, environment).
46
+ *
47
+ * `set()` keeps this to a single row in steady state, but we still pick the
48
+ * newest in JS rather than rely on a SQL `LIMIT 1` (nondeterministic without
49
+ * an ORDER BY) or a dialect/version-specific `orderBy` form: legacy data
50
+ * written before the upsert fix can still have duplicate live rows, and JS
51
+ * sorting resolves them deterministically across SQLite and Postgres. (Same
52
+ * cross-dialect-sort rationale as the chat memory resolver.)
53
+ */
54
+ private _latestRow;
44
55
  list(): Promise<SecretMeta[]>;
45
56
  rotate(name: string, newValue: string, environment?: string): Promise<void>;
46
57
  delete(name: string, environment?: string): Promise<void>;
package/dist/index.js CHANGED
@@ -7524,35 +7524,65 @@ var SecretStore = class {
7524
7524
  this.encKey = encryptionKey ? deriveEncKey(encryptionKey) : null;
7525
7525
  }
7526
7526
  async set(input) {
7527
- const id = uuidv42();
7528
- const data = { ...input, id };
7529
- if (this.encKey && data.value) {
7530
- data.value = encryptValue(data.value, this.encKey);
7527
+ const environment = input.environment ?? "production";
7528
+ const value = this.encKey && input.value ? encryptValue(input.value, this.encKey) : input.value;
7529
+ const existing = await this.db.query("secrets", {
7530
+ where: { name: input.name, environment },
7531
+ filters: [{ col: "deleted_at", op: "isNull" }],
7532
+ limit: 1
7533
+ });
7534
+ if (existing.length > 0) {
7535
+ const id2 = existing[0].id;
7536
+ const changes = {
7537
+ value,
7538
+ environment,
7539
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
7540
+ };
7541
+ if (input.type !== void 0) changes.type = input.type;
7542
+ if (input.description !== void 0) changes.description = input.description;
7543
+ await this.db.update("secrets", id2, changes);
7544
+ await this.hooks.emit("secret.updated", { name: input.name });
7545
+ const updated = await this.db.get("secrets", id2);
7546
+ return this._toMeta(updated);
7531
7547
  }
7532
- await this.db.insert("secrets", data);
7548
+ const id = uuidv42();
7549
+ await this.db.insert("secrets", { ...input, id, value, environment });
7533
7550
  await this.hooks.emit("secret.created", { name: input.name });
7534
7551
  const inserted = await this.db.get("secrets", id);
7535
7552
  return this._toMeta(inserted);
7536
7553
  }
7537
7554
  async get(name, environment = "production") {
7538
- const rows = await this.db.query("secrets", {
7539
- where: { name, environment },
7540
- filters: [{ col: "deleted_at", op: "isNull" }],
7541
- limit: 1
7542
- });
7543
- if (rows.length === 0) return null;
7555
+ const row = await this._latestRow(name, environment);
7556
+ if (!row) return null;
7544
7557
  await this.hooks.emit("secret.accessed", { name, environment });
7545
- const raw = rows[0].value ?? null;
7558
+ const raw = row.value ?? null;
7546
7559
  if (raw && this.encKey) return decryptValue(raw, this.encKey);
7547
7560
  return raw;
7548
7561
  }
7549
7562
  async getMeta(name, environment = "production") {
7563
+ const row = await this._latestRow(name, environment);
7564
+ return row ? this._toMeta(row) : null;
7565
+ }
7566
+ /**
7567
+ * Fetch the most recently created live row for (name, environment).
7568
+ *
7569
+ * `set()` keeps this to a single row in steady state, but we still pick the
7570
+ * newest in JS rather than rely on a SQL `LIMIT 1` (nondeterministic without
7571
+ * an ORDER BY) or a dialect/version-specific `orderBy` form: legacy data
7572
+ * written before the upsert fix can still have duplicate live rows, and JS
7573
+ * sorting resolves them deterministically across SQLite and Postgres. (Same
7574
+ * cross-dialect-sort rationale as the chat memory resolver.)
7575
+ */
7576
+ async _latestRow(name, environment) {
7550
7577
  const rows = await this.db.query("secrets", {
7551
7578
  where: { name, environment },
7552
- filters: [{ col: "deleted_at", op: "isNull" }],
7553
- limit: 1
7579
+ filters: [{ col: "deleted_at", op: "isNull" }]
7554
7580
  });
7555
- return rows.length > 0 ? this._toMeta(rows[0]) : null;
7581
+ if (rows.length === 0) return void 0;
7582
+ rows.sort(
7583
+ (a, b) => String(b.created_at ?? "").localeCompare(String(a.created_at ?? ""))
7584
+ );
7585
+ return rows[0];
7556
7586
  }
7557
7587
  async list() {
7558
7588
  const rows = await this.db.query("secrets", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botinabox",
3
- "version": "2.16.17",
3
+ "version": "2.16.18",
4
4
  "description": "Bot in a Box — framework for building multi-agent bots",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",