@dragonmastery/tamer 0.34.1 → 0.35.1

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 (68) hide show
  1. package/dist/{apply-BsVQ9cbK.mjs → apply-Cz1lxAOp.mjs} +11 -11
  2. package/dist/{apply-BsVQ9cbK.mjs.map → apply-Cz1lxAOp.mjs.map} +1 -1
  3. package/dist/{applyTarget-BJlVrED9.mjs → applyTarget-BI6QAVwA.mjs} +2 -2
  4. package/dist/{applyTarget-BJlVrED9.mjs.map → applyTarget-BI6QAVwA.mjs.map} +1 -1
  5. package/dist/bootstrap-CsL4IT9Z.mjs +34 -0
  6. package/dist/bootstrap-CsL4IT9Z.mjs.map +1 -0
  7. package/dist/{cloudflareSnapshot-CMbPQIsN.mjs → cloudflareSnapshot-CpNFH4zd.mjs} +4 -4
  8. package/dist/{cloudflareSnapshot-CMbPQIsN.mjs.map → cloudflareSnapshot-CpNFH4zd.mjs.map} +1 -1
  9. package/dist/{deploy-dNygCucr.mjs → deploy-D2tQrtN9.mjs} +7 -7
  10. package/dist/{deploy-dNygCucr.mjs.map → deploy-D2tQrtN9.mjs.map} +1 -1
  11. package/dist/{destroy-C0cKkpWe.mjs → destroy-CEv14T0X.mjs} +18 -12
  12. package/dist/destroy-CEv14T0X.mjs.map +1 -0
  13. package/dist/{destroy-tenant-hyEb1gEz.mjs → destroy-tenant-Ugu7Y1EH.mjs} +2 -2
  14. package/dist/{destroy-tenant-hyEb1gEz.mjs.map → destroy-tenant-Ugu7Y1EH.mjs.map} +1 -1
  15. package/dist/{dev-Bug5l5OZ.mjs → dev-vGQIP7tD.mjs} +6 -6
  16. package/dist/{dev-Bug5l5OZ.mjs.map → dev-vGQIP7tD.mjs.map} +1 -1
  17. package/dist/{dns-records.sync-FyzKl-Ph.mjs → dns-records.sync-PoahWQjY.mjs} +2 -2
  18. package/dist/{dns-records.sync-FyzKl-Ph.mjs.map → dns-records.sync-PoahWQjY.mjs.map} +1 -1
  19. package/dist/{doctor-BwuEPYM6.mjs → doctor-BnsOxqnA.mjs} +2 -2
  20. package/dist/{doctor-BwuEPYM6.mjs.map → doctor-BnsOxqnA.mjs.map} +1 -1
  21. package/dist/{drift-DENAf1Oe.mjs → drift-C3wIrNyg.mjs} +4 -4
  22. package/dist/{drift-B5y9LF5X.mjs → drift-C7pgj4Iw.mjs} +5 -5
  23. package/dist/{drift-B5y9LF5X.mjs.map → drift-C7pgj4Iw.mjs.map} +1 -1
  24. package/dist/{emit-DTpaIMkn.mjs → emit-vpibELHm.mjs} +3 -3
  25. package/dist/{emit-DTpaIMkn.mjs.map → emit-vpibELHm.mjs.map} +1 -1
  26. package/dist/{events-B7Lencm8.mjs → events-yk4J3l0M.mjs} +2 -2
  27. package/dist/{events-B7Lencm8.mjs.map → events-yk4J3l0M.mjs.map} +1 -1
  28. package/dist/{generator-ZTEeHlft.mjs → generator-7b97QsuM.mjs} +2 -2
  29. package/dist/{generator-ZTEeHlft.mjs.map → generator-7b97QsuM.mjs.map} +1 -1
  30. package/dist/{import-Ciq9MN3v.mjs → import-CZ6509_t.mjs} +3 -3
  31. package/dist/{import-Ciq9MN3v.mjs.map → import-CZ6509_t.mjs.map} +1 -1
  32. package/dist/{migrate-DWyUVN1I.mjs → migrate-DDBMzPtR.mjs} +4 -4
  33. package/dist/{migrate-DWyUVN1I.mjs.map → migrate-DDBMzPtR.mjs.map} +1 -1
  34. package/dist/{plan-Blxn-yKr.mjs → plan-Dot2VV7R.mjs} +8 -8
  35. package/dist/{plan-Blxn-yKr.mjs.map → plan-Dot2VV7R.mjs.map} +1 -1
  36. package/dist/{provision-tenant-BHDWTC2U.mjs → provision-tenant-C2EL79oz.mjs} +6 -5
  37. package/dist/provision-tenant-C2EL79oz.mjs.map +1 -0
  38. package/dist/{registry-BlHEOKlN.mjs → registry-D-vo1TTV.mjs} +2 -2
  39. package/dist/{registry-BlHEOKlN.mjs.map → registry-D-vo1TTV.mjs.map} +1 -1
  40. package/dist/{stackOutputs-CkpNSng8.mjs → stackOutputs-CuLa76D4.mjs} +2 -2
  41. package/dist/{stackOutputs-CkpNSng8.mjs.map → stackOutputs-CuLa76D4.mjs.map} +1 -1
  42. package/dist/{status-PlMHsDzT.mjs → status-_k3a77Fw.mjs} +4 -4
  43. package/dist/{status-PlMHsDzT.mjs.map → status-_k3a77Fw.mjs.map} +1 -1
  44. package/dist/{sync-YKZ5HD8v.mjs → sync-C5RjLfgk.mjs} +5 -5
  45. package/dist/{sync-YKZ5HD8v.mjs.map → sync-C5RjLfgk.mjs.map} +1 -1
  46. package/dist/tamer.mjs +141 -93
  47. package/dist/tamer.mjs.map +1 -1
  48. package/dist/tamerArtifactsR2-7qUbhOLD.mjs +51 -0
  49. package/dist/tamerArtifactsR2-7qUbhOLD.mjs.map +1 -0
  50. package/dist/{types-CADr4Kck.mjs → types-CqmFeIBf.mjs} +4 -4
  51. package/dist/{types-CADr4Kck.mjs.map → types-CqmFeIBf.mjs.map} +1 -1
  52. package/dist/{verifyPlanFile-BBAwWzUo.mjs → verifyPlanFile-DSO-yS64.mjs} +2 -2
  53. package/dist/{verifyPlanFile-BBAwWzUo.mjs.map → verifyPlanFile-DSO-yS64.mjs.map} +1 -1
  54. package/dist/{wfp-delete-CQc9tveU.mjs → wfp-delete-C-1pIY3Z.mjs} +2 -2
  55. package/dist/{wfp-delete-CQc9tveU.mjs.map → wfp-delete-C-1pIY3Z.mjs.map} +1 -1
  56. package/dist/{wfp-put-CVw8cloM.mjs → wfp-put-D0tDs0J5.mjs} +2 -2
  57. package/dist/{wfp-put-CVw8cloM.mjs.map → wfp-put-D0tDs0J5.mjs.map} +1 -1
  58. package/dist/{worker-route-CqBDvjgo.mjs → worker-route-RexL6xCk.mjs} +2 -2
  59. package/dist/{worker-route-CqBDvjgo.mjs.map → worker-route-RexL6xCk.mjs.map} +1 -1
  60. package/dist/{workers-y9RTOzbC.mjs → workers-CS0tlbdk.mjs} +2 -2
  61. package/dist/{workers-y9RTOzbC.mjs.map → workers-CS0tlbdk.mjs.map} +1 -1
  62. package/package.json +6 -2
  63. package/dist/bootstrap-CV6OT-c9.mjs +0 -33
  64. package/dist/bootstrap-CV6OT-c9.mjs.map +0 -1
  65. package/dist/destroy-C0cKkpWe.mjs.map +0 -1
  66. package/dist/provision-tenant-BHDWTC2U.mjs.map +0 -1
  67. package/dist/tamerArtifactsR2-Ba29OVp9.mjs +0 -52
  68. package/dist/tamerArtifactsR2-Ba29OVp9.mjs.map +0 -1
package/dist/tamer.mjs CHANGED
@@ -5495,12 +5495,19 @@ var CFApiClient = class {
5495
5495
  //#region src/core/secrets/secretsDb.ts
5496
5496
  /** Max audit rows retained per secret name (oldest dropped on insert). */
5497
5497
  const SECRET_HISTORY_CAP = 50;
5498
- /** Cloudflare D1 database for encrypted secrets (`tamer-secrets-dev`, …). */
5499
- function tamerSecretsDatabaseName(env) {
5500
- return `tamer-secrets-${env}`;
5498
+ /**
5499
+ * Account-scoped D1 database for encrypted secrets (`tamer-secrets`).
5500
+ * Each env's secrets are isolated by row key: `{env}:{secretName}`.
5501
+ */
5502
+ function tamerSecretsDatabaseName() {
5503
+ return "tamer-secrets";
5501
5504
  }
5502
- async function findTamerSecretsDatabaseUuid(api, env) {
5503
- const name = tamerSecretsDatabaseName(env);
5505
+ /** Row key for a secret in the shared D1: `{env}:{secretName}`. */
5506
+ function secretRowKey(env, secretName) {
5507
+ return `${env}:${secretName}`;
5508
+ }
5509
+ async function findTamerSecretsDatabaseUuid(api) {
5510
+ const name = tamerSecretsDatabaseName();
5504
5511
  return (await api.d1ListAll()).find((d) => d.name === name)?.uuid;
5505
5512
  }
5506
5513
  const SECRETS_TABLE_DDL = `CREATE TABLE IF NOT EXISTS secrets (
@@ -5520,16 +5527,28 @@ const SECRET_HISTORY_TABLE_DDL = `CREATE TABLE IF NOT EXISTS secret_history (
5520
5527
  updated_by TEXT
5521
5528
  )`;
5522
5529
  /**
5523
- * Create `tamer-secrets-{env}` if missing and ensure `secrets` / `secret_history`
5524
- * tables. Idempotent — safe to call on every bootstrap or first vault touch.
5530
+ * Create `tamer-secrets` (account-scoped) if missing and ensure `secrets` /
5531
+ * `secret_history` tables. Idempotent — safe to call on every bootstrap or
5532
+ * first vault touch.
5525
5533
  */
5526
- async function ensureTamerSecretsDatabase(api, env) {
5527
- let uuid$1 = await findTamerSecretsDatabaseUuid(api, env);
5528
- if (!uuid$1) uuid$1 = (await api.d1Create(tamerSecretsDatabaseName(env))).uuid;
5534
+ async function ensureTamerSecretsDatabase(api) {
5535
+ let uuid$1 = await findTamerSecretsDatabaseUuid(api);
5536
+ if (!uuid$1) uuid$1 = (await api.d1Create(tamerSecretsDatabaseName())).uuid;
5529
5537
  await api.d1Query(uuid$1, SECRETS_TABLE_DDL);
5530
5538
  await api.d1Query(uuid$1, SECRET_HISTORY_TABLE_DDL);
5531
5539
  return uuid$1;
5532
5540
  }
5541
+ /**
5542
+ * Delete all secret rows for a specific env from the shared account-scoped D1.
5543
+ * Used by `tamer destroy --wipe-metadata`.
5544
+ */
5545
+ async function deleteEnvSecretRows(api, env) {
5546
+ const uuid$1 = await findTamerSecretsDatabaseUuid(api);
5547
+ if (!uuid$1) return false;
5548
+ await api.d1Query(uuid$1, `DELETE FROM secrets WHERE name LIKE ?`, [`${env}:%`]);
5549
+ await api.d1Query(uuid$1, `DELETE FROM secret_history WHERE name LIKE ?`, [`${env}:%`]);
5550
+ return true;
5551
+ }
5533
5552
 
5534
5553
  //#endregion
5535
5554
  //#region src/core/secrets/masterKey.ts
@@ -6556,9 +6575,21 @@ function stackNameForConfig(config$1) {
6556
6575
  * 5: + `secret` state entries (fingerprints only; additive resource rows).
6557
6576
  */
6558
6577
  const STATE_SCHEMA_VERSION = 5;
6559
- /** Cloudflare D1 database that holds JSON state for an env (`tamer-state-dev`, …). */
6560
- function tamerStateDatabaseName(env) {
6561
- return `tamer-state-${env}`;
6578
+ /**
6579
+ * Account-scoped D1 database that holds JSON state for **all** envs
6580
+ * (`tamer-state`). Each env's state is isolated by row key:
6581
+ * `cfi_state:{env}:{stackName}`.
6582
+ */
6583
+ function tamerStateDatabaseName() {
6584
+ return "tamer-state";
6585
+ }
6586
+ /**
6587
+ * D1 `tamer_kv.k` value for a given env + stack's state row.
6588
+ * Format: `cfi_state:{env}:{stackName}` — env namespacing ensures
6589
+ * cross-env isolation within the shared account-scoped D1.
6590
+ */
6591
+ function stateRowKey(env, stackName) {
6592
+ return `cfi_state:${env}:${stackName}`;
6562
6593
  }
6563
6594
  function createEmptyCfiState(tenantId, env) {
6564
6595
  return {
@@ -6587,24 +6618,28 @@ function migrateRawCfiStateInPlace(raw) {
6587
6618
  if (raw.schemaVersion === 3) raw.schemaVersion = 4;
6588
6619
  if (raw.schemaVersion === 4) raw.schemaVersion = STATE_SCHEMA_VERSION;
6589
6620
  }
6590
- async function findTamerStateDatabaseUuid(api, env) {
6591
- const name = tamerStateDatabaseName(env);
6621
+ async function findTamerStateDatabaseUuid(api) {
6622
+ const name = tamerStateDatabaseName();
6592
6623
  return (await api.d1ListAll()).find((d) => d.name === name)?.uuid;
6593
6624
  }
6594
6625
  /**
6595
- * Create `tamer-state-{env}` if missing, ensure `tamer_kv` table, and seed an
6596
- * initial empty `cfi_state:{stackName}` row when this stack has no row yet.
6597
- * Idempotent re-running for the same stack is a no-op; re-running for a
6598
- * different stack against the same env D1 just adds another row.
6626
+ * Create `tamer-state` (account-scoped) if missing, ensure `tamer_kv` table.
6627
+ * When `env` is provided, also seeds an initial empty
6628
+ * `cfi_state:{env}:{stackName}` row for that stack. When `env` is omitted
6629
+ * (e.g. from `tamer bootstrap`), only creates the D1 + table the first
6630
+ * `tamer apply` will seed the row automatically via the StateManager.
6631
+ *
6632
+ * Idempotent — re-running is always safe.
6599
6633
  */
6600
6634
  async function ensureTamerStateDatabase(api, tenantId, env, stackName = DEFAULT_STACK_NAME) {
6601
- let uuid$1 = await findTamerStateDatabaseUuid(api, env);
6602
- if (!uuid$1) uuid$1 = (await api.d1Create(tamerStateDatabaseName(env))).uuid;
6635
+ let uuid$1 = await findTamerStateDatabaseUuid(api);
6636
+ if (!uuid$1) uuid$1 = (await api.d1Create(tamerStateDatabaseName())).uuid;
6603
6637
  await api.d1Query(uuid$1, `CREATE TABLE IF NOT EXISTS tamer_kv (
6604
6638
  k TEXT PRIMARY KEY,
6605
6639
  v TEXT NOT NULL
6606
6640
  )`);
6607
- const rowKey = `cfi_state:${stackName}`;
6641
+ if (!env) return uuid$1;
6642
+ const rowKey = stateRowKey(env, stackName);
6608
6643
  const { rows } = await api.d1Query(uuid$1, `SELECT v FROM tamer_kv WHERE k = ?`, [rowKey]);
6609
6644
  if (rows.length === 0) {
6610
6645
  const initial = createEmptyCfiState(tenantId, env);
@@ -6619,10 +6654,19 @@ function parseCfiStateJson(json) {
6619
6654
  if (!result.success) throw new Error(`Invalid tamer state JSON: ${result.error.message}`);
6620
6655
  return result.data;
6621
6656
  }
6622
- async function destroyTamerStateDatabase(api, env) {
6623
- const uuid$1 = await findTamerStateDatabaseUuid(api, env);
6657
+ /**
6658
+ * Delete all state rows for a specific env from the shared account-scoped D1.
6659
+ * Used by `tamer destroy --wipe-metadata` to clean up an env's state without
6660
+ * affecting other envs that share the same `tamer-state` database.
6661
+ *
6662
+ * Note: this does NOT delete the D1 database itself — the shared `tamer-state`
6663
+ * database persists for other envs. To remove Tamer entirely from an account,
6664
+ * delete the D1/R2 resources manually via the Cloudflare dashboard.
6665
+ */
6666
+ async function deleteEnvStateRows(api, env) {
6667
+ const uuid$1 = await findTamerStateDatabaseUuid(api);
6624
6668
  if (!uuid$1) return false;
6625
- await api.d1Delete(uuid$1);
6669
+ await api.d1Query(uuid$1, `DELETE FROM tamer_kv WHERE k LIKE ?`, [`cfi_state:${env}:%`]);
6626
6670
  return true;
6627
6671
  }
6628
6672
 
@@ -6705,15 +6749,13 @@ var StateConflictError = class extends Error {
6705
6749
 
6706
6750
  //#endregion
6707
6751
  //#region src/core/state/StateManager.ts
6708
- /** D1 `tamer_kv.k` value for a given stack's state row. */
6709
- function stateRowKey(stackName) {
6710
- return `cfi_state:${stackName}`;
6711
- }
6712
6752
  const OPERATION_HISTORY_CAP = 50;
6713
6753
  /**
6714
6754
  * Authoritative deployment state for an env.
6715
6755
  *
6716
- * - **Non-local:** stored as JSON in Cloudflare D1 (`tamer-state-{env}`).
6756
+ * - **Non-local:** stored as JSON in Cloudflare D1 (`tamer-state`,
6757
+ * account-scoped). Each env's state is isolated by row key:
6758
+ * `cfi_state:{env}:{stackName}`.
6717
6759
  * Call {@link hydrate} before {@link load}, then {@link persist} after mutations.
6718
6760
  * - **local:** in-memory only (no persistence).
6719
6761
  */
@@ -6730,14 +6772,13 @@ var StateManager = class {
6730
6772
  /**
6731
6773
  * @param tenantId `config.tenant.id` — recorded on the state row for
6732
6774
  * diagnostics; not part of the row key.
6733
- * @param env Cloudflare environment name; selects the
6734
- * `tamer-state-{env}` D1 database.
6775
+ * @param env Cloudflare environment name; included in the D1 row key
6776
+ * (`cfi_state:{env}:{stackName}`) for isolation within the
6777
+ * shared account-scoped `tamer-state` database.
6735
6778
  * @param stackName Stack identity (`config.stack.name ?? tenant.slug`).
6736
- * The state row in D1 is keyed `cfi_state:{stackName}`,
6737
- * so multiple stacks coexist in one env D1 without
6738
- * clobbering each other. Defaults to `"default"` —
6739
- * unit tests that synthesize a StateManager without
6740
- * a config get a stable key without extra plumbing.
6779
+ * Combined with env to form the D1 row key, so multiple
6780
+ * stacks and multiple envs coexist in one D1 without
6781
+ * clobbering each other. Defaults to `"default"`.
6741
6782
  */
6742
6783
  constructor(tenantId, env, stackName = DEFAULT_STACK_NAME) {
6743
6784
  this.tenantId = tenantId;
@@ -6755,11 +6796,10 @@ var StateManager = class {
6755
6796
  this.baselineRevision = this.state.revision ?? 0;
6756
6797
  return;
6757
6798
  }
6758
- const name = tamerStateDatabaseName(this.env);
6759
- const uuid$1 = await findTamerStateDatabaseUuid(api, this.env);
6760
- if (!uuid$1) throw new Error(`Tamer state database "${name}" not found. Run: tamer bootstrap --env ${this.env}`);
6799
+ const uuid$1 = await findTamerStateDatabaseUuid(api);
6800
+ if (!uuid$1) throw new Error(`Tamer state database "tamer-state" not found. Run: tamer bootstrap --env ${this.env}`);
6761
6801
  this.tamerStateDbUuid = uuid$1;
6762
- const rowKey = stateRowKey(this.stackName);
6802
+ const rowKey = stateRowKey(this.env, this.stackName);
6763
6803
  const { rows } = await api.d1Query(uuid$1, `SELECT v FROM tamer_kv WHERE k = ?`, [rowKey]);
6764
6804
  if (rows.length === 0) {
6765
6805
  this.state = createEmptyCfiState(this.tenantId, this.env);
@@ -6951,7 +6991,7 @@ var StateManager = class {
6951
6991
  return;
6952
6992
  }
6953
6993
  if (!this.dirty || !this.state || !this.tamerStateDbUuid) return;
6954
- const rowKey = stateRowKey(this.stackName);
6994
+ const rowKey = stateRowKey(this.env, this.stackName);
6955
6995
  const { rows } = await api.d1Query(this.tamerStateDbUuid, `SELECT v FROM tamer_kv WHERE k = ?`, [rowKey]);
6956
6996
  let remoteRev = 0;
6957
6997
  if (rows.length > 0) {
@@ -7333,7 +7373,8 @@ function namingFromConfig(config$1) {
7333
7373
  //#endregion
7334
7374
  //#region src/core/secrets/SecretsVault.ts
7335
7375
  /**
7336
- * Encrypted secrets vault backed by `tamer-secrets-{env}` D1.
7376
+ * Encrypted secrets vault backed by account-scoped `tamer-secrets` D1.
7377
+ * Each env's secrets are isolated by row key: `{env}:{secretName}`.
7337
7378
  * Stores ciphertext only — never plaintext.
7338
7379
  */
7339
7380
  var SecretsVault = class {
@@ -7343,10 +7384,19 @@ var SecretsVault = class {
7343
7384
  this.env = env;
7344
7385
  this.databaseId = databaseId;
7345
7386
  }
7387
+ /** Convert a logical secret name to its D1 row key: `{env}:{name}`. */
7388
+ key(name) {
7389
+ return secretRowKey(this.env, name);
7390
+ }
7391
+ /** Reverse: strip the `{env}:` prefix from a D1 row key. */
7392
+ unkey(rowKey) {
7393
+ const prefix = `${this.env}:`;
7394
+ return rowKey.startsWith(prefix) ? rowKey.slice(prefix.length) : rowKey;
7395
+ }
7346
7396
  async dbId() {
7347
7397
  if (this.databaseId) return this.databaseId;
7348
- const uuid$1 = await findTamerSecretsDatabaseUuid(this.api, this.env);
7349
- if (!uuid$1) throw new Error(`secrets vault not provisioned for env "${this.env}" (expected D1 ${tamerSecretsDatabaseName(this.env)}); run tamer bootstrap`);
7398
+ const uuid$1 = await findTamerSecretsDatabaseUuid(this.api);
7399
+ if (!uuid$1) throw new Error(`secrets vault not provisioned (expected D1 "tamer-secrets"); run tamer bootstrap`);
7350
7400
  this.databaseId = uuid$1;
7351
7401
  return uuid$1;
7352
7402
  }
@@ -7354,6 +7404,7 @@ var SecretsVault = class {
7354
7404
  const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
7355
7405
  const updatedBy = options?.updatedBy;
7356
7406
  const db = await this.dbId();
7407
+ const key = this.key(name);
7357
7408
  await this.api.d1Query(db, `INSERT INTO secrets (
7358
7409
  name, ciphertext, iv, wrapped_dek, dek_iv, value_hash, updated_at, updated_by
7359
7410
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
@@ -7365,7 +7416,7 @@ var SecretsVault = class {
7365
7416
  value_hash = excluded.value_hash,
7366
7417
  updated_at = excluded.updated_at,
7367
7418
  updated_by = excluded.updated_by`, [
7368
- name,
7419
+ key,
7369
7420
  blobParam(encrypted.ciphertext),
7370
7421
  blobParam(encrypted.iv),
7371
7422
  blobParam(encrypted.wrappedDek),
@@ -7376,7 +7427,7 @@ var SecretsVault = class {
7376
7427
  ]);
7377
7428
  await this.api.d1Query(db, `INSERT INTO secret_history (name, value_hash, updated_at, updated_by)
7378
7429
  VALUES (?, ?, ?, ?)`, [
7379
- name,
7430
+ key,
7380
7431
  valueHash,
7381
7432
  updatedAt,
7382
7433
  updatedBy ?? null
@@ -7391,24 +7442,24 @@ var SecretsVault = class {
7391
7442
  LIMIT -1 OFFSET ?
7392
7443
  )
7393
7444
  )`, [
7394
- name,
7395
- name,
7445
+ key,
7446
+ key,
7396
7447
  SECRET_HISTORY_CAP
7397
7448
  ]);
7398
7449
  }
7399
7450
  async get(name) {
7400
7451
  const db = await this.dbId();
7401
7452
  const { rows } = await this.api.d1Query(db, `SELECT name, ciphertext, iv, wrapped_dek, dek_iv, value_hash, updated_at, updated_by
7402
- FROM secrets WHERE name = ?`, [name]);
7453
+ FROM secrets WHERE name = ?`, [this.key(name)]);
7403
7454
  if (rows.length === 0) return void 0;
7404
- return rowToVaultSecret(rows[0]);
7455
+ return rowToVaultSecret(rows[0], this.env);
7405
7456
  }
7406
7457
  async list() {
7407
7458
  const db = await this.dbId();
7408
7459
  const { rows } = await this.api.d1Query(db, `SELECT name, value_hash, updated_at, updated_by
7409
- FROM secrets ORDER BY name`);
7460
+ FROM secrets WHERE name LIKE ? ORDER BY name`, [`${this.env}:%`]);
7410
7461
  return rows.map((row) => ({
7411
- name: String(row.name),
7462
+ name: this.unkey(String(row.name)),
7412
7463
  valueHash: row.value_hash,
7413
7464
  updatedAt: String(row.updated_at),
7414
7465
  updatedBy: row.updated_by != null ? String(row.updated_by) : void 0
@@ -7416,7 +7467,7 @@ var SecretsVault = class {
7416
7467
  }
7417
7468
  async delete(name) {
7418
7469
  const db = await this.dbId();
7419
- const { rows } = await this.api.d1Query(db, `DELETE FROM secrets WHERE name = ? RETURNING name`, [name]);
7470
+ const { rows } = await this.api.d1Query(db, `DELETE FROM secrets WHERE name = ? RETURNING name`, [this.key(name)]);
7420
7471
  return rows.length > 0;
7421
7472
  }
7422
7473
  /** Append an audit row (e.g. after `secrets get`) without changing the secret. */
@@ -7426,9 +7477,10 @@ var SecretsVault = class {
7426
7477
  const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
7427
7478
  const updatedBy = options?.updatedBy;
7428
7479
  const db = await this.dbId();
7480
+ const key = this.key(name);
7429
7481
  await this.api.d1Query(db, `INSERT INTO secret_history (name, value_hash, updated_at, updated_by)
7430
7482
  VALUES (?, ?, ?, ?)`, [
7431
- name,
7483
+ key,
7432
7484
  record$1.valueHash,
7433
7485
  updatedAt,
7434
7486
  updatedBy ?? null
@@ -7443,8 +7495,8 @@ var SecretsVault = class {
7443
7495
  LIMIT -1 OFFSET ?
7444
7496
  )
7445
7497
  )`, [
7446
- name,
7447
- name,
7498
+ key,
7499
+ key,
7448
7500
  SECRET_HISTORY_CAP
7449
7501
  ]);
7450
7502
  }
@@ -7453,18 +7505,20 @@ var SecretsVault = class {
7453
7505
  const { rows } = await this.api.d1Query(db, `SELECT name, value_hash, updated_at, updated_by
7454
7506
  FROM secret_history
7455
7507
  WHERE name = ?
7456
- ORDER BY updated_at DESC`, [name]);
7508
+ ORDER BY updated_at DESC`, [this.key(name)]);
7457
7509
  return rows.map((row) => ({
7458
- name: String(row.name),
7510
+ name: this.unkey(String(row.name)),
7459
7511
  valueHash: row.value_hash,
7460
7512
  updatedAt: String(row.updated_at),
7461
7513
  updatedBy: row.updated_by != null ? String(row.updated_by) : void 0
7462
7514
  }));
7463
7515
  }
7464
7516
  };
7465
- function rowToVaultSecret(row) {
7517
+ function rowToVaultSecret(row, env) {
7518
+ const rawName = String(row.name);
7519
+ const prefix = `${env}:`;
7466
7520
  return {
7467
- name: String(row.name),
7521
+ name: rawName.startsWith(prefix) ? rawName.slice(prefix.length) : rawName,
7468
7522
  ciphertext: blobFromRow(row.ciphertext),
7469
7523
  iv: blobFromRow(row.iv),
7470
7524
  wrappedDek: blobFromRow(row.wrapped_dek),
@@ -7512,7 +7566,7 @@ async function createSecretsContext(options) {
7512
7566
  const accountId = config$1.account_id ?? cloudflareAccountIdFromEnv();
7513
7567
  if (!accountId) throw new Error("account_id required in config or CLOUDFLARE_ACCOUNT_ID env var");
7514
7568
  const api = new CFApiClient(accountId);
7515
- const vault = new SecretsVault(api, env, await ensureTamerSecretsDatabase(api, env));
7569
+ const vault = new SecretsVault(api, env, await ensureTamerSecretsDatabase(api));
7516
7570
  const state = new StateManager(config$1.tenant.id, env, stackNameForConfig(config$1));
7517
7571
  await state.hydrate(api);
7518
7572
  const masterKey = readMasterKeyFromEnv(env);
@@ -7593,7 +7647,7 @@ function displayPathFromCwd(absOrRel) {
7593
7647
  async function createDeploySecretsResources(api, env) {
7594
7648
  const resolvedEnv = resolveSecretsEnv(env);
7595
7649
  return {
7596
- vault: new SecretsVault(api, resolvedEnv, await ensureTamerSecretsDatabase(api, resolvedEnv)),
7650
+ vault: new SecretsVault(api, resolvedEnv, await ensureTamerSecretsDatabase(api)),
7597
7651
  masterKey: readMasterKeyFromEnv(resolvedEnv)
7598
7652
  };
7599
7653
  }
@@ -7606,8 +7660,8 @@ async function runSecretsInit(options) {
7606
7660
  if (!accountId) throw new Error("account_id required in config or CLOUDFLARE_ACCOUNT_ID env var");
7607
7661
  const masterKey = generateMasterKey();
7608
7662
  const varName = masterKeyEnvVarName(env);
7609
- const uuid$1 = await ensureTamerSecretsDatabase(new CFApiClient(accountId), env);
7610
- console.log(`Secrets vault ready: D1 uuid=${uuid$1} name=${tamerSecretsDatabaseName(env)}`);
7663
+ const uuid$1 = await ensureTamerSecretsDatabase(new CFApiClient(accountId));
7664
+ console.log(`Secrets vault ready: D1 uuid=${uuid$1} name=${tamerSecretsDatabaseName()}`);
7611
7665
  console.log("");
7612
7666
  console.log(`Generated master key for env "${env}" (store in two durable places — CI + password manager):`);
7613
7667
  console.log("");
@@ -8377,10 +8431,7 @@ const DestroyArgsSchema = BaseArgsSchema.extend({
8377
8431
  plan: string().optional(),
8378
8432
  allow_stale: boolean().optional()
8379
8433
  });
8380
- const BootstrapArgsSchema = object({
8381
- env: string().min(1, { error: "env is required for bootstrap" }),
8382
- config: string().optional()
8383
- });
8434
+ const BootstrapArgsSchema = object({ config: string().optional() });
8384
8435
  const DriftArgsSchema = BaseArgsSchema.extend({ json: boolean().optional() });
8385
8436
  const PlanArgsSchema = BaseArgsSchema.extend({
8386
8437
  json: boolean().optional(),
@@ -8638,10 +8689,7 @@ function parseImportArgs(argv) {
8638
8689
  function parseBootstrapArgs(argv) {
8639
8690
  const parsed = BootstrapArgsSchema.safeParse(toOpts(parseArgs(argv)));
8640
8691
  if (!parsed.success) throw new Error(`Invalid arguments: ${formatZodError(parsed.error)}`);
8641
- return {
8642
- env: parsed.data.env,
8643
- configPath: parsed.data.config
8644
- };
8692
+ return { configPath: parsed.data.config };
8645
8693
  }
8646
8694
 
8647
8695
  //#endregion
@@ -8654,14 +8702,14 @@ async function main() {
8654
8702
  try {
8655
8703
  switch (command) {
8656
8704
  case "bootstrap":
8657
- await import("./bootstrap-CV6OT-c9.mjs").then((m) => m.runBootstrap(parseBootstrapArgs(rest)));
8705
+ await import("./bootstrap-CsL4IT9Z.mjs").then((m) => m.runBootstrap(parseBootstrapArgs(rest)));
8658
8706
  break;
8659
8707
  case "sync":
8660
- await import("./sync-YKZ5HD8v.mjs").then((m) => m.runSync(parseSyncArgs(rest)));
8708
+ await import("./sync-C5RjLfgk.mjs").then((m) => m.runSync(parseSyncArgs(rest)));
8661
8709
  break;
8662
8710
  case "apply": {
8663
8711
  const a = parseApplyArgs(rest);
8664
- await import("./apply-BsVQ9cbK.mjs").then((m) => m.runApply({
8712
+ await import("./apply-Cz1lxAOp.mjs").then((m) => m.runApply({
8665
8713
  env: a.env,
8666
8714
  addShard: a.addShard,
8667
8715
  configPath: a.configPath,
@@ -8673,11 +8721,11 @@ async function main() {
8673
8721
  break;
8674
8722
  }
8675
8723
  case "dev":
8676
- await import("./dev-Bug5l5OZ.mjs").then((m) => m.runDev(parseDevArgs(rest)));
8724
+ await import("./dev-vGQIP7tD.mjs").then((m) => m.runDev(parseDevArgs(rest)));
8677
8725
  break;
8678
8726
  case "deploy": {
8679
8727
  const d = parseDeployArgs(rest);
8680
- await import("./deploy-dNygCucr.mjs").then((m) => m.runDeploy({
8728
+ await import("./deploy-D2tQrtN9.mjs").then((m) => m.runDeploy({
8681
8729
  worker: d.worker,
8682
8730
  env: d.env,
8683
8731
  configPath: d.configPath,
@@ -8686,23 +8734,23 @@ async function main() {
8686
8734
  break;
8687
8735
  }
8688
8736
  case "migrate":
8689
- await import("./migrate-DWyUVN1I.mjs").then((m) => m.runMigrate(parseMigrateArgs(rest)));
8737
+ await import("./migrate-DDBMzPtR.mjs").then((m) => m.runMigrate(parseMigrateArgs(rest)));
8690
8738
  break;
8691
8739
  case "types":
8692
- await import("./types-CADr4Kck.mjs").then((m) => m.runTypes(parseTypesArgs(rest)));
8740
+ await import("./types-CqmFeIBf.mjs").then((m) => m.runTypes(parseTypesArgs(rest)));
8693
8741
  break;
8694
8742
  case "status":
8695
- await import("./status-PlMHsDzT.mjs").then((m) => m.runStatus(parseStatusArgs(rest)));
8743
+ await import("./status-_k3a77Fw.mjs").then((m) => m.runStatus(parseStatusArgs(rest)));
8696
8744
  break;
8697
8745
  case "events":
8698
- await import("./events-B7Lencm8.mjs").then((m) => m.runEvents(parseEventsArgs(rest)));
8746
+ await import("./events-yk4J3l0M.mjs").then((m) => m.runEvents(parseEventsArgs(rest)));
8699
8747
  break;
8700
8748
  case "drift":
8701
- exitStatus = await import("./drift-DENAf1Oe.mjs").then((m) => m.runDrift(parseDriftArgs(rest)));
8749
+ exitStatus = await import("./drift-C3wIrNyg.mjs").then((m) => m.runDrift(parseDriftArgs(rest)));
8702
8750
  break;
8703
8751
  case "plan": {
8704
8752
  const p = parsePlanArgs(rest);
8705
- exitStatus = await import("./plan-Blxn-yKr.mjs").then((m) => m.runPlan({
8753
+ exitStatus = await import("./plan-Dot2VV7R.mjs").then((m) => m.runPlan({
8706
8754
  env: p.env,
8707
8755
  configPath: p.configPath,
8708
8756
  json: p.json,
@@ -8714,14 +8762,14 @@ async function main() {
8714
8762
  break;
8715
8763
  }
8716
8764
  case "import":
8717
- await import("./import-Ciq9MN3v.mjs").then((m) => m.runImport(parseImportArgs(rest)));
8765
+ await import("./import-CZ6509_t.mjs").then((m) => m.runImport(parseImportArgs(rest)));
8718
8766
  break;
8719
8767
  case "doctor":
8720
- exitStatus = await import("./doctor-BwuEPYM6.mjs").then((m) => m.runDoctor(parseDoctorArgs(rest)));
8768
+ exitStatus = await import("./doctor-BnsOxqnA.mjs").then((m) => m.runDoctor(parseDoctorArgs(rest)));
8721
8769
  break;
8722
8770
  case "provision-tenant": {
8723
8771
  const p = parseProvisionTenantArgs(rest);
8724
- await import("./provision-tenant-BHDWTC2U.mjs").then((m) => m.runProvisionTenant({
8772
+ await import("./provision-tenant-C2EL79oz.mjs").then((m) => m.runProvisionTenant({
8725
8773
  env: p.env,
8726
8774
  product: p.product,
8727
8775
  workspace: p.workspace,
@@ -8738,7 +8786,7 @@ async function main() {
8738
8786
  }
8739
8787
  case "destroy-tenant": {
8740
8788
  const t = parseDestroyTenantArgs(rest);
8741
- await import("./destroy-tenant-hyEb1gEz.mjs").then((m) => m.runDestroyTenant({
8789
+ await import("./destroy-tenant-Ugu7Y1EH.mjs").then((m) => m.runDestroyTenant({
8742
8790
  env: t.env,
8743
8791
  product: t.product,
8744
8792
  workspace: t.workspace,
@@ -8751,7 +8799,7 @@ async function main() {
8751
8799
  }
8752
8800
  case "destroy": {
8753
8801
  const d = parseDestroyArgs(rest);
8754
- await import("./destroy-C0cKkpWe.mjs").then((m) => m.runDestroy({
8802
+ await import("./destroy-CEv14T0X.mjs").then((m) => m.runDestroy({
8755
8803
  env: d.env,
8756
8804
  force: d.force,
8757
8805
  skipWorkers: d.skipWorkers,
@@ -8766,12 +8814,12 @@ async function main() {
8766
8814
  case "wfp": {
8767
8815
  const [sub, ...wfpRest] = rest;
8768
8816
  if (sub === "put") {
8769
- const { parseWfpPutArgs, runWfpPut } = await import("./wfp-put-CVw8cloM.mjs");
8817
+ const { parseWfpPutArgs, runWfpPut } = await import("./wfp-put-D0tDs0J5.mjs");
8770
8818
  await runWfpPut(parseWfpPutArgs(wfpRest));
8771
8819
  break;
8772
8820
  }
8773
8821
  if (sub === "delete") {
8774
- const { parseWfpDeleteArgs, runWfpDelete } = await import("./wfp-delete-CQc9tveU.mjs");
8822
+ const { parseWfpDeleteArgs, runWfpDelete } = await import("./wfp-delete-C-1pIY3Z.mjs");
8775
8823
  await runWfpDelete(parseWfpDeleteArgs(wfpRest));
8776
8824
  break;
8777
8825
  }
@@ -8791,7 +8839,7 @@ tamer — Cloudflare Workers infrastructure manager
8791
8839
  Usage: tamer <command> [options]
8792
8840
 
8793
8841
  Commands:
8794
- bootstrap Create per-env Tamer metadata: tamer-state-<env> (D1) and tamer-artifacts-<env> (R2)
8842
+ bootstrap Create account-scoped Tamer metadata: tamer-state (D1), tamer-artifacts (R2), tamer-secrets (D1). Run once per account.
8795
8843
  sync Sync state from Cloudflare API
8796
8844
  apply Provision missing resources
8797
8845
  dev Start wrangler dev (use --all for every worker)
@@ -8871,5 +8919,5 @@ Environment variables (same as Wrangler):
8871
8919
  main();
8872
8920
 
8873
8921
  //#endregion
8874
- export { rewriteIntraStackServiceTargets as A, getWorkers as B, tamerStateDatabaseName as C, mergedWorkerConfigForEnv as D, mergeWorkerConfigForResourcePick as E, ensureTamerSecretsDatabase as F, tamerSecretsDatabaseName as I, CFApiClient as L, wranglerConfigCliArgs as M, effectiveDispatchNamespaceName as N, resolveDeployedWorkerName as O, isEphemeralEnv as P, cloudflareAccountIdFromEnv as R, ensureTamerStateDatabase as S, buildIntraStackScriptNameMap as T, loadConfig as V, tenantDispatchScriptName as _, reconcileSecrets as a, createEmptyCfiState as b, vaultReaderFromMap as c, requiredSecretsForWorker as d, fetchStackImports as f, parseTenantShardRoles as g, StateManager as h, pushSecretsForDeploy as i, resolveReferencesInString as j, resolveWorkerConfig as k, createDeploySecretsResources as l, scanConfigForImports as m, parseSecretsArgs as n, secretsDrift as o, importedStackNames as p, runSecrets as r, secretsPlanItems as s, SECRETS_USAGE as t, namingFromConfig as u, tenantShardDatabaseName as v, stackNameForConfig as w, destroyTamerStateDatabase as x, tenantStateKey as y, cloudflareApiTokenFromEnv as z };
8922
+ export { resolveReferencesInString as A, loadConfig as B, stackNameForConfig as C, resolveDeployedWorkerName as D, mergedWorkerConfigForEnv as E, ensureTamerSecretsDatabase as F, CFApiClient as I, cloudflareAccountIdFromEnv as L, effectiveDispatchNamespaceName as M, isEphemeralEnv as N, resolveWorkerConfig as O, deleteEnvSecretRows as P, cloudflareApiTokenFromEnv as R, ensureTamerStateDatabase as S, mergeWorkerConfigForResourcePick as T, tenantDispatchScriptName as _, reconcileSecrets as a, createEmptyCfiState as b, vaultReaderFromMap as c, requiredSecretsForWorker as d, fetchStackImports as f, parseTenantShardRoles as g, StateManager as h, pushSecretsForDeploy as i, wranglerConfigCliArgs as j, rewriteIntraStackServiceTargets as k, createDeploySecretsResources as l, scanConfigForImports as m, parseSecretsArgs as n, secretsDrift as o, importedStackNames as p, runSecrets as r, secretsPlanItems as s, SECRETS_USAGE as t, namingFromConfig as u, tenantShardDatabaseName as v, buildIntraStackScriptNameMap as w, deleteEnvStateRows as x, tenantStateKey as y, getWorkers as z };
8875
8923
  //# sourceMappingURL=tamer.mjs.map