@hasna/todos 0.15.32 → 0.15.34

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/dist/cli/index.js CHANGED
@@ -2123,7 +2123,7 @@ var package_default;
2123
2123
  var init_package = __esm(() => {
2124
2124
  package_default = {
2125
2125
  name: "@hasna/todos",
2126
- version: "0.15.32",
2126
+ version: "0.15.34",
2127
2127
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
2128
2128
  type: "module",
2129
2129
  main: "dist/index.js",
@@ -2197,9 +2197,7 @@ var init_package = __esm(() => {
2197
2197
  "verify:release": "bun run scripts/verify-public-release.ts --mode=review",
2198
2198
  "verify:release-review": "bun run scripts/verify-npm-release-agent-review.ts",
2199
2199
  "verify:attested-container-candidate": "bun run scripts/attested-container-candidate.ts verify",
2200
- "test:attested-container-candidate": "bun test scripts/attested-container-candidate.test.ts scripts/attested-container-compatibility-vector.test.ts",
2201
- "emit:iapp-deployment-compatibility-vector": "bun run scripts/attested-container-compatibility-vector.ts emit",
2202
- "verify:iapp-deployment-compatibility-vector": "bun run scripts/attested-container-compatibility-vector.ts verify",
2200
+ "test:attested-container-candidate": "bun test scripts/attested-container-candidate.test.ts",
2203
2201
  "issue:release-review": "bun run scripts/issue-npm-release-agent-review.ts",
2204
2202
  prepublishOnly: "bun run scripts/verify-public-release.ts --mode=publish",
2205
2203
  postinstall: "mkdir -p $HOME/.hasna/todos $HOME/.hasna/todos/training 2>/dev/null || true"
@@ -5657,40 +5655,38 @@ var init_page_validation = __esm(() => {
5657
5655
 
5658
5656
  // src/cli/cloud-router.ts
5659
5657
  import { resolveStorageClient } from "@hasna/contracts/client/storage";
5660
- import { normalizeStorageMode } from "@hasna/contracts/mode";
5661
5658
  import { randomUUID } from "crypto";
5662
5659
  import { resolve as resolvePath } from "path";
5663
- function cleanMode(value) {
5664
- const normalized = value?.trim().toLowerCase();
5665
- return normalized || null;
5660
+ function assertNoLegacyStorageMode(env = process.env) {
5661
+ for (const key of LEGACY_STORAGE_MODE_KEYS) {
5662
+ if (Object.hasOwn(env, key) && env[key] !== undefined) {
5663
+ throw new Error(`REMOTE_STORAGE_MODE_REMOVED: ${key} was removed. Deployment modes no longer exist: delete the storage-mode variable. ` + `The client uses the on-box SQLite store, or the HTTP API selected by ` + `HASNA_TODOS_API_URL + HASNA_TODOS_API_KEY. ` + `On the server, set HASNA_TODOS_DATABASE_URL to select the postgresql backend, ` + `or leave it unset for sqlite.`);
5664
+ }
5665
+ }
5666
5666
  }
5667
5667
  function resolveTodosCliStorageMode(env = process.env) {
5668
- for (const source of ["HASNA_TODOS_STORAGE_MODE", "TODOS_STORAGE_MODE"]) {
5669
- if (env[source] !== undefined && env[source].trim() === "") {
5670
- throw new Error(`REMOTE_STORAGE_MODE_INVALID: ${source} must not be blank; local SQLite fallback is disabled for invalid routing state`);
5671
- }
5668
+ assertNoLegacyStorageMode(env);
5669
+ const urlValue = env.HASNA_TODOS_API_URL?.trim();
5670
+ const keyValue = env.HASNA_TODOS_API_KEY?.trim();
5671
+ if (urlValue && keyValue) {
5672
+ return {
5673
+ mode: "http",
5674
+ transport: "http",
5675
+ selected: true,
5676
+ source: "HASNA_TODOS_API_URL+HASNA_TODOS_API_KEY"
5677
+ };
5672
5678
  }
5673
- const canonical = cleanMode(env.HASNA_TODOS_STORAGE_MODE);
5674
- const fallback = cleanMode(env.TODOS_STORAGE_MODE);
5675
- for (const [source, value] of [
5676
- ["HASNA_TODOS_STORAGE_MODE", canonical],
5677
- ["TODOS_STORAGE_MODE", fallback]
5678
- ]) {
5679
- if (value && !(value in TRANSPORT_TOKENS)) {
5680
- throw new Error(`REMOTE_STORAGE_MODE_INVALID: ${source}=${value} must be sqlite (local file) or http (hosted /v1 authority); ` + "legacy values local and remote are accepted; " + "local SQLite fallback is disabled for invalid routing state");
5681
- }
5679
+ if (urlValue) {
5680
+ throw new Error("REMOTE_API_KEY_MISSING: remote Todos storage requires HASNA_TODOS_API_KEY; local SQLite fallback is disabled");
5682
5681
  }
5683
- const canonicalTransport = canonical ? TRANSPORT_TOKENS[canonical] : null;
5684
- const fallbackTransport = fallback ? TRANSPORT_TOKENS[fallback] : null;
5685
- if (canonicalTransport && fallbackTransport && canonicalTransport !== fallbackTransport) {
5686
- throw new Error(`REMOTE_STORAGE_MODE_CONFLICT: HASNA_TODOS_STORAGE_MODE=${canonical} conflicts with ` + `TODOS_STORAGE_MODE=${fallback}; local SQLite fallback is disabled`);
5682
+ if (keyValue) {
5683
+ throw new Error("REMOTE_API_URL_MISSING: remote Todos storage requires HASNA_TODOS_API_URL; local SQLite fallback is disabled");
5687
5684
  }
5688
- const transport = canonicalTransport ?? fallbackTransport ?? "sqlite";
5689
5685
  return {
5690
- mode: transport,
5691
- transport,
5692
- selected: transport === "http",
5693
- source: canonical ? "HASNA_TODOS_STORAGE_MODE" : fallback ? "TODOS_STORAGE_MODE" : "default"
5686
+ mode: "sqlite",
5687
+ transport: "sqlite",
5688
+ selected: false,
5689
+ source: "default"
5694
5690
  };
5695
5691
  }
5696
5692
  function requestedTransport(env) {
@@ -5734,7 +5730,7 @@ function getTodosRemoteAuthorityConfigStatus(env = process.env) {
5734
5730
  return {
5735
5731
  selected: true,
5736
5732
  ok: false,
5737
- mode: cleanMode(env.HASNA_TODOS_STORAGE_MODE) ?? cleanMode(env.TODOS_STORAGE_MODE) ?? "invalid",
5733
+ mode: "invalid",
5738
5734
  api_url_configured: Boolean(env.HASNA_TODOS_API_URL?.trim()),
5739
5735
  api_key_configured: Boolean(env.HASNA_TODOS_API_KEY?.trim()),
5740
5736
  v1_base_url: null,
@@ -5780,27 +5776,12 @@ function getTodosRemoteAuthorityConfigStatus(env = process.env) {
5780
5776
  local_fallback: false
5781
5777
  };
5782
5778
  }
5783
- function serverStorageMode(normalize = normalizeStorageMode) {
5784
- const useCache = normalize === normalizeStorageMode;
5785
- if (useCache && cachedServerMode !== null)
5786
- return cachedServerMode;
5787
- for (const candidate of SERVER_MODE_CANDIDATES) {
5788
- try {
5789
- normalize(candidate);
5790
- if (useCache)
5791
- cachedServerMode = candidate;
5792
- return candidate;
5793
- } catch {}
5794
- }
5795
- throw new Error(`REMOTE_STORAGE_MODE_UNSUPPORTED: no known server storage mode is accepted by the installed ` + `@hasna/contracts (tried ${SERVER_MODE_CANDIDATES.join(", ")}); the storage-mode enum has changed. ` + `Add the new server token to SERVER_MODE_CANDIDATES in src/cli/cloud-router.ts; ` + `local SQLite fallback is disabled.`);
5796
- }
5797
5779
  function requireTodosRemoteAuthorityEnv(env) {
5798
5780
  const status = getTodosRemoteAuthorityConfigStatus(env);
5799
5781
  if (!status.ok)
5800
5782
  throw new Error(status.issues[0]);
5801
5783
  return {
5802
5784
  ...env,
5803
- HASNA_TODOS_STORAGE_MODE: serverStorageMode(),
5804
5785
  HASNA_TODOS_API_URL: status.v1_base_url.replace(/\/v1$/, ""),
5805
5786
  HASNA_TODOS_API_KEY: env.HASNA_TODOS_API_KEY.trim()
5806
5787
  };
@@ -5876,7 +5857,7 @@ async function requiredRemoteRoute(client, route, request, recognized404Codes =
5876
5857
  return await request();
5877
5858
  } catch (error) {
5878
5859
  const status = error && typeof error === "object" ? error.status : undefined;
5879
- if (status === 404) {
5860
+ if (status === 404 || status === 405) {
5880
5861
  const body = error && typeof error === "object" ? error.body : undefined;
5881
5862
  const code = body && typeof body === "object" && !Array.isArray(body) ? body.code : undefined;
5882
5863
  if (typeof code === "string" && recognized404Codes.includes(code))
@@ -5892,7 +5873,12 @@ function getTodosCloudClient(env = process.env) {
5892
5873
  const resolved = resolveStorageClient("todos", requireTodosRemoteAuthorityEnv(env), {
5893
5874
  fetchImpl: (input, init) => globalThis.fetch(input, { ...init, redirect: "manual" })
5894
5875
  });
5895
- return resolved.transport === "cloud-http" ? protectRemoteClient(resolved.client) : null;
5876
+ if (resolved.transport === "cloud-http")
5877
+ return protectRemoteClient(resolved.client);
5878
+ const transportName = resolved.transport;
5879
+ if (transportName === "http")
5880
+ return protectRemoteClient(resolved.client);
5881
+ return null;
5896
5882
  }
5897
5883
  function isCloudRouting(env = process.env) {
5898
5884
  return getTodosCloudClient(env) !== null;
@@ -7397,11 +7383,13 @@ async function cloudUpdateTaskList(client, id, patch) {
7397
7383
  return unwrapTaskList(await client.update("task-lists", id, patch));
7398
7384
  }
7399
7385
  async function cloudRenameProject(client, ref, newSlug, name) {
7400
- const id = await cloudResolveProjectRef(client, ref);
7401
- const normalizedSlug = cloudProjectSlug(newSlug);
7402
- if (!normalizedSlug)
7403
- throw new Error("Invalid slug \u2014 must be non-empty kebab-case");
7404
- return client.transport.post(`/projects/${encodeURIComponent(id)}/rename`, { new_slug: normalizedSlug, ...name !== undefined ? { name } : {} });
7386
+ return requiredRemoteRoute(client, "/v1/projects/:id/rename", async () => {
7387
+ const id = await cloudResolveProjectRef(client, ref);
7388
+ const normalizedSlug = cloudProjectSlug(newSlug);
7389
+ if (!normalizedSlug)
7390
+ throw new Error("Invalid slug \u2014 must be non-empty kebab-case");
7391
+ return client.transport.post(`/projects/${encodeURIComponent(id)}/rename`, { new_slug: normalizedSlug, ...name !== undefined ? { name } : {} });
7392
+ });
7405
7393
  }
7406
7394
  async function cloudDeleteTaskList(client, id) {
7407
7395
  try {
@@ -7547,7 +7535,7 @@ async function cloudTimeline(client, options = {}) {
7547
7535
  const limit = options.limit ?? 50;
7548
7536
  return { entries: entries.slice(offset, offset + limit), total, limit, offset };
7549
7537
  }
7550
- var UUID_RE, TRANSPORT_TOKENS, COMPLETION_EVIDENCE_FIELDS, completionCapabilityCache, retryCapabilityCache, taskCreatorCapabilityCache, gitRefCapabilityCache, remoteCommandCapabilityCache, SERVER_MODE_CANDIDATES, cachedServerMode = null, PRIORITY_RANK, listTagsCapabilityCache, PLAN_COMPLETION_PROTECTED_FIELDS, RELATION_HYDRATION_CONCURRENCY = 6;
7538
+ var UUID_RE, COMPLETION_EVIDENCE_FIELDS, completionCapabilityCache, retryCapabilityCache, taskCreatorCapabilityCache, gitRefCapabilityCache, remoteCommandCapabilityCache, LEGACY_STORAGE_MODE_KEYS, PRIORITY_RANK, listTagsCapabilityCache, PLAN_COMPLETION_PROTECTED_FIELDS, RELATION_HYDRATION_CONCURRENCY = 6;
7551
7539
  var init_cloud_router = __esm(() => {
7552
7540
  init_types();
7553
7541
  init_redaction();
@@ -7556,15 +7544,6 @@ var init_cloud_router = __esm(() => {
7556
7544
  init_adoption_validation();
7557
7545
  init_page_validation();
7558
7546
  UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7559
- TRANSPORT_TOKENS = {
7560
- sqlite: "sqlite",
7561
- http: "http",
7562
- local: "sqlite",
7563
- remote: "http",
7564
- self_hosted: "http",
7565
- cloud: "http",
7566
- hybrid: "http"
7567
- };
7568
7547
  COMPLETION_EVIDENCE_FIELDS = [
7569
7548
  "attachment_ids",
7570
7549
  "files_changed",
@@ -7578,7 +7557,12 @@ var init_cloud_router = __esm(() => {
7578
7557
  taskCreatorCapabilityCache = new Map;
7579
7558
  gitRefCapabilityCache = new Map;
7580
7559
  remoteCommandCapabilityCache = new Map;
7581
- SERVER_MODE_CANDIDATES = ["postgres", "cloud", "self_hosted"];
7560
+ LEGACY_STORAGE_MODE_KEYS = [
7561
+ "HASNA_TODOS_STORAGE_MODE",
7562
+ "HASNA_TODOS_MODE",
7563
+ "TODOS_STORAGE_MODE",
7564
+ "TODOS_MODE"
7565
+ ];
7582
7566
  PRIORITY_RANK = { critical: 0, high: 1, medium: 2, low: 3 };
7583
7567
  listTagsCapabilityCache = new Map;
7584
7568
  PLAN_COMPLETION_PROTECTED_FIELDS = [
@@ -7599,8 +7583,10 @@ var init_cloud_router = __esm(() => {
7599
7583
  function applyTodosCliAuthorityEnvironment(authority, env = process.env) {
7600
7584
  if (authority.route !== "local" || authority.selected_by !== "local-only-command")
7601
7585
  return;
7602
- env.HASNA_TODOS_STORAGE_MODE = "sqlite";
7603
- env.TODOS_STORAGE_MODE = "sqlite";
7586
+ env.HASNA_TODOS_API_URL = "";
7587
+ env.HASNA_TODOS_API_KEY = "";
7588
+ env.TODOS_API_URL = "";
7589
+ env.TODOS_API_KEY = "";
7604
7590
  }
7605
7591
  function isTodosCliCommandVisibleForRoute(command, route, remoteCapabilities = new Set) {
7606
7592
  if (route === "local")
@@ -7844,7 +7830,7 @@ function assertInvocationRoutable(invocation) {
7844
7830
  if (command && !owner) {
7845
7831
  const suggestions = nearestCommands(command);
7846
7832
  const didYouMean = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(", ")}?` : "";
7847
- throw new Error(`UNKNOWN_COMMAND: \`${command}\` is not a built-in todos command on the /v1 route.${didYouMean} ` + "Run `todos --help` for the commands available here; verbs contributed by optional packages are local-only. " + "(This is not a connectivity, storage-mode or credential problem.)");
7833
+ throw new Error(`UNKNOWN_COMMAND: \`${command}\` is not a built-in todos command on the /v1 route.${didYouMean} ` + "Run `todos --help` for the commands available here; verbs contributed by optional packages are local-only. " + "(This is not a connectivity or credential problem.)");
7848
7834
  }
7849
7835
  return owner;
7850
7836
  }
@@ -7861,7 +7847,20 @@ function assertRemoteCommandSupported(invocation, owner) {
7861
7847
  }
7862
7848
  }
7863
7849
  function initializeTodosCliAuthority(args = process.argv.slice(2), env = process.env) {
7864
- const mode = resolveTodosCliStorageMode(env);
7850
+ let mode;
7851
+ try {
7852
+ mode = resolveTodosCliStorageMode(env);
7853
+ } catch (error) {
7854
+ const message = error instanceof Error ? error.message : String(error);
7855
+ if (message.includes("REMOTE_STORAGE_MODE_REMOVED"))
7856
+ throw error;
7857
+ const invocation2 = parseInvocation(args);
7858
+ if (isMetadataInvocation(args, invocation2)) {
7859
+ const status = getTodosRemoteAuthorityConfigStatus(env);
7860
+ return { route: "remote-diagnostic", v1_base_url: status.v1_base_url };
7861
+ }
7862
+ throw error;
7863
+ }
7865
7864
  if (!mode.selected)
7866
7865
  return { route: "local", v1_base_url: null };
7867
7866
  const invocation = parseInvocation(args);
@@ -7876,7 +7875,7 @@ function initializeTodosCliAuthority(args = process.argv.slice(2), env = process
7876
7875
  assertRemoteCommandSupported(invocation, owner);
7877
7876
  const client = getTodosCloudClient(env);
7878
7877
  if (!client) {
7879
- throw new Error("REMOTE_API_UNAVAILABLE: remote mode did not resolve an HTTP client; local SQLite fallback is disabled");
7878
+ throw new Error("REMOTE_API_UNAVAILABLE: HTTP routing did not resolve an authenticated /v1 client; local SQLite fallback is disabled");
7880
7879
  }
7881
7880
  return { route: "remote-http", v1_base_url: client.baseUrl };
7882
7881
  }
@@ -12039,6 +12038,7 @@ __export(exports_config2, {
12039
12038
  TODOS_STORAGE_FALLBACK_ENV: () => TODOS_STORAGE_FALLBACK_ENV,
12040
12039
  TODOS_STORAGE_ENV: () => TODOS_STORAGE_ENV,
12041
12040
  STORAGE_TABLES: () => STORAGE_TABLES,
12041
+ REMOVED_STORAGE_MODE_ENV_KEYS: () => REMOVED_STORAGE_MODE_ENV_KEYS,
12042
12042
  CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV: () => CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV,
12043
12043
  CANONICAL_TODOS_RDS_DATABASE: () => CANONICAL_TODOS_RDS_DATABASE,
12044
12044
  CANONICAL_TODOS_RDS_CLUSTER_ENV: () => CANONICAL_TODOS_RDS_CLUSTER_ENV
@@ -12100,25 +12100,36 @@ function assertTodosRemoteStorageConfig(config) {
12100
12100
  if (!isTodosPostgresBackend(config))
12101
12101
  return;
12102
12102
  if (!config.database?.url) {
12103
- throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} is required when ${TODOS_STORAGE_ENV.mode}=${config.mode}`);
12103
+ throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} is required when the postgresql backend is selected`);
12104
12104
  }
12105
12105
  }
12106
12106
  function parseStorageBackend(value) {
12107
12107
  const normalized = clean(value)?.toLowerCase();
12108
12108
  if (!normalized)
12109
12109
  return "sqlite";
12110
- if (normalized === "sqlite" || normalized === "postgres")
12111
- return normalized;
12112
- const legacy = LEGACY_BACKEND_TOKENS[normalized];
12113
- if (legacy)
12114
- return legacy;
12115
- throw new Error(`${TODOS_STORAGE_ENV.mode} must be sqlite or postgres (legacy values local and remote are accepted)`);
12110
+ if (normalized === "sqlite")
12111
+ return "sqlite";
12112
+ if (normalized === "postgres" || normalized === "postgresql")
12113
+ return "postgres";
12114
+ if (["local", "remote", "cloud", "hybrid", "self_hosted"].includes(normalized)) {
12115
+ throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} selects the backend. Deployment modes no longer exist: ` + `delete the storage-mode variable and set ${TODOS_STORAGE_ENV.databaseUrl} to select the ` + `postgresql backend, or leave it unset for sqlite.`);
12116
+ }
12117
+ throw new Error(`Storage backend must be sqlite or postgres`);
12116
12118
  }
12117
12119
  function parseStorageMode(value) {
12118
12120
  return parseStorageBackend(value);
12119
12121
  }
12120
12122
  function getTodosStorageBackend(env = process.env) {
12121
- return parseStorageBackend(readStorageEnv(env, "mode").value);
12123
+ for (const key of REMOVED_STORAGE_MODE_ENV_KEYS) {
12124
+ if (Object.hasOwn(env, key) && env[key] !== undefined) {
12125
+ throw new Error(`${key} was removed. Deployment modes no longer exist: delete the storage-mode variable. ` + `Set ${TODOS_STORAGE_ENV.databaseUrl} to select the postgresql backend, ` + `or leave it unset for sqlite.`);
12126
+ }
12127
+ }
12128
+ if (!getTodosStorageDatabaseUrl(env))
12129
+ return "sqlite";
12130
+ if (isTodosShadowEnabled(env))
12131
+ return "sqlite";
12132
+ return "postgres";
12122
12133
  }
12123
12134
  function getTodosStorageMode(env = process.env) {
12124
12135
  return getTodosStorageBackend(env);
@@ -12192,7 +12203,7 @@ function parsePositiveInteger(value, fallback) {
12192
12203
  }
12193
12204
  return parsed;
12194
12205
  }
12195
- var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos", LEGACY_BACKEND_TOKENS;
12206
+ var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, REMOVED_STORAGE_MODE_ENV_KEYS, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos";
12196
12207
  var init_config2 = __esm(() => {
12197
12208
  TODOS_STORAGE_TABLES = [
12198
12209
  "todos_sync_records",
@@ -12200,7 +12211,6 @@ var init_config2 = __esm(() => {
12200
12211
  ];
12201
12212
  STORAGE_TABLES = TODOS_STORAGE_TABLES;
12202
12213
  TODOS_STORAGE_ENV = {
12203
- mode: "HASNA_TODOS_STORAGE_MODE",
12204
12214
  shadow: "HASNA_TODOS_SHADOW",
12205
12215
  databaseUrl: "HASNA_TODOS_DATABASE_URL",
12206
12216
  databaseSsl: "HASNA_TODOS_DATABASE_SSL",
@@ -12217,7 +12227,6 @@ var init_config2 = __esm(() => {
12217
12227
  syncDryRun: "HASNA_TODOS_SYNC_DRY_RUN"
12218
12228
  };
12219
12229
  TODOS_STORAGE_FALLBACK_ENV = {
12220
- mode: "TODOS_STORAGE_MODE",
12221
12230
  shadow: "TODOS_SHADOW",
12222
12231
  databaseUrl: "TODOS_DATABASE_URL",
12223
12232
  databaseSsl: "TODOS_DATABASE_SSL",
@@ -12233,14 +12242,12 @@ var init_config2 = __esm(() => {
12233
12242
  syncBatchSize: "TODOS_SYNC_BATCH_SIZE",
12234
12243
  syncDryRun: "TODOS_SYNC_DRY_RUN"
12235
12244
  };
12236
- LEGACY_BACKEND_TOKENS = {
12237
- local: "sqlite",
12238
- remote: "postgres",
12239
- postgresql: "postgres",
12240
- hybrid: "postgres",
12241
- self_hosted: "postgres",
12242
- cloud: "postgres"
12243
- };
12245
+ REMOVED_STORAGE_MODE_ENV_KEYS = [
12246
+ "HASNA_TODOS_STORAGE_MODE",
12247
+ "HASNA_TODOS_MODE",
12248
+ "TODOS_STORAGE_MODE",
12249
+ "TODOS_MODE"
12250
+ ];
12244
12251
  });
12245
12252
 
12246
12253
  // src/storage/shadow-outbox-schema.ts
@@ -15383,8 +15390,8 @@ var init_secret_redaction = __esm(() => {
15383
15390
  SECRET_REDACTION_SCHEMA = ["todos", "secret_redaction", "v1"].join(".");
15384
15391
  DEFAULT_PATTERNS = [
15385
15392
  { name: "openai_sk", pattern: /\bsk-[a-zA-Z0-9]{10,}\b/g },
15386
- { name: "github_pat", pattern: /\bghp_[a-zA-Z0-9]{20,}\b/g },
15387
- { name: "github_oauth", pattern: /\bgho_[a-zA-Z0-9]{20,}\b/g },
15393
+ { name: "github_pat", pattern: /\bgh[p]_[a-zA-Z0-9]{20,}\b/g },
15394
+ { name: "github_oauth", pattern: /\bgh[o]_[a-zA-Z0-9]{20,}\b/g },
15388
15395
  { name: "aws_access_key", pattern: /\bAKIA[0-9A-Z]{16}\b/g },
15389
15396
  { name: "bearer_token", pattern: /\bBearer\s+[a-zA-Z0-9\-._~+/]+=*\b/gi },
15390
15397
  { name: "jwt", pattern: /\beyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/g },
@@ -15396,7 +15403,7 @@ var init_secret_redaction = __esm(() => {
15396
15403
  /example\.com/i,
15397
15404
  /your-api-key-here/i,
15398
15405
  /sk-test/i,
15399
- /ghp_xxx/i
15406
+ /gh[p]_xxx/i
15400
15407
  ];
15401
15408
  customRedactors = [];
15402
15409
  });
@@ -31501,7 +31508,7 @@ function postgresTodosSyncSchemaSql(tableName = DEFAULT_TODOS_POSTGRES_SYNC_TABL
31501
31508
  AS $$ SELECT unaccent('unaccent', $1) $$`,
31502
31509
  `CREATE OR REPLACE FUNCTION todos_try_timestamptz(text)
31503
31510
  RETURNS timestamptz
31504
- LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE
31511
+ LANGUAGE plpgsql IMMUTABLE PARALLEL UNSAFE
31505
31512
  SET DateStyle TO 'ISO, YMD'
31506
31513
  AS $$
31507
31514
  BEGIN
@@ -36349,9 +36356,14 @@ class PackageOwnedTodosProjectRegistrationAuthority {
36349
36356
  if (request.max_items !== 1) {
36350
36357
  throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "max_items must be exactly 1 for terminal receipt lookup");
36351
36358
  }
36352
- if (request.authority !== "todos" || request.authority_id !== this.capabilityValue.authority_id || request.tenant_id !== this.capabilityValue.tenant_id || request.corpus_id !== this.capabilityValue.corpus_id) {
36359
+ if (request.authority !== "todos" || request.authority_id !== this.capabilityValue.authority_id || request.tenant_id !== this.capabilityValue.tenant_id) {
36353
36360
  throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH", "receipt lookup does not match this authority capability identity");
36354
36361
  }
36362
+ requireString(request.corpus_id, "corpus_id", {
36363
+ min: 3,
36364
+ max: 128,
36365
+ pattern: AUTHORITY_ROUTE_PATTERN
36366
+ });
36355
36367
  requireString(request.authority_route, "authority_route", {
36356
36368
  min: 3,
36357
36369
  max: 128,
@@ -36382,6 +36394,7 @@ class PackageOwnedTodosProjectRegistrationAuthority {
36382
36394
  }
36383
36395
  const receipt = await this.backend.getReceiptForLookup({
36384
36396
  ...authorityScope(this.capabilityValue),
36397
+ corpus_id: request.corpus_id,
36385
36398
  route: request.authority_route,
36386
36399
  package_version: request.package_version,
36387
36400
  operation_id: request.operation_id,
@@ -36486,9 +36499,15 @@ class PackageOwnedTodosProjectRegistrationAuthority {
36486
36499
  max: 128,
36487
36500
  pattern: PACKAGE_VERSION_PATTERN
36488
36501
  });
36502
+ requireString(sourceRequest.corpus_id, "corpus_id", {
36503
+ min: 3,
36504
+ max: 128,
36505
+ pattern: AUTHORITY_ROUTE_PATTERN
36506
+ });
36489
36507
  assertForwardRequest(sourceRequest, {
36490
36508
  ...this.capabilityValue,
36491
- package_version: sourceRequest.package_version
36509
+ package_version: sourceRequest.package_version,
36510
+ corpus_id: sourceRequest.corpus_id
36492
36511
  });
36493
36512
  const validation = await this.backend.transaction(async (transaction) => {
36494
36513
  const storedSource = await transaction.getReceiptById(sourceReceipt.receipt_id);
@@ -36503,7 +36522,11 @@ class PackageOwnedTodosProjectRegistrationAuthority {
36503
36522
  if (!receiptLineageMatches(storedSource) || !receiptLineageMatches(accepted) || storedSource.outcome === "duplicate_of_accepted" && (storedSource.duplicate_of_receipt_id !== accepted.receipt_id || storedSource.target_id !== accepted.target_id || storedSource.result_revision !== accepted.result_revision || storedSource.result_digest !== accepted.result_digest)) {
36504
36523
  throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_ADOPTION_REJECTED", "receipt authority, operation, request, precondition, or normalized-call lineage changed");
36505
36524
  }
36506
- const binding = await transaction.getBinding(authorityScope(this.capabilityValue), sourceRequest.resource_kind, sourceRequest.target_selector);
36525
+ const binding = await transaction.getBinding({
36526
+ authority_id: sourceRequest.authority_id,
36527
+ tenant_id: sourceRequest.tenant_id,
36528
+ corpus_id: sourceRequest.corpus_id
36529
+ }, sourceRequest.resource_kind, sourceRequest.target_selector);
36507
36530
  if (!binding || binding.state !== "accepted" || binding.operation_id !== sourceRequest.operation_id || binding.step_id !== sourceRequest.step_id || binding.direction !== "forward" || binding.idempotency_key !== sourceRequest.idempotency_key || binding.request_digest !== sourceRequest.request_digest || binding.precondition_digest !== sourceRequest.precondition_digest || binding.normalized_call_digest !== accepted.normalized_call_digest || binding.target_id !== accepted.target_id || binding.accepted_receipt_id !== accepted.receipt_id || binding.result_revision !== accepted.result_revision || binding.result_digest !== accepted.result_digest) {
36508
36531
  throw new TodosProjectRegistrationError("TODOS_PROJECT_REGISTRATION_ADOPTION_REJECTED", "accepted binding does not match the exact accepted receipt and source lineage");
36509
36532
  }
@@ -55738,8 +55761,8 @@ async function handleV1Request(req, url, dependencies = {}) {
55738
55761
  }
55739
55762
  const created = await store.tasks.create(body2, storageContext);
55740
55763
  const persisted = created?.id ? await store.tasks.get(created.id, storageContext) : null;
55741
- if (!persisted || persisted.id !== created.id || (persisted.parent_id ?? null) !== (body2.parent_id ?? null) || (persisted.plan_id ?? null) !== (body2.plan_id ?? null)) {
55742
- return error(500, "TASK_CREATE_PERSISTENCE_UNVERIFIED: task create was acknowledged but authoritative readback did not return the same stored task id, parent_id, and plan_id", { code: "TASK_CREATE_PERSISTENCE_UNVERIFIED" });
55764
+ if (!persisted || persisted.id !== created.id || (persisted.parent_id ?? null) !== (body2.parent_id ?? null) || (persisted.plan_id ?? null) !== (body2.plan_id ?? null) || body2.created_by !== undefined && persisted.created_by !== body2.created_by) {
55765
+ return error(500, "TASK_CREATE_PERSISTENCE_UNVERIFIED: task create was acknowledged but authoritative readback did not return the same stored task id, parent_id, plan_id, and explicit created_by", { code: "TASK_CREATE_PERSISTENCE_UNVERIFIED" });
55743
55766
  }
55744
55767
  return json5({ task: persisted }, 201);
55745
55768
  }
@@ -82428,7 +82451,7 @@ No task claimed (nothing available).`));
82428
82451
  verdict_exit_code: verdict2.verdict,
82429
82452
  fail_on_findings: verdict2.fail_on_findings,
82430
82453
  dry_run: true,
82431
- mode: "remote-http",
82454
+ mode: "http",
82432
82455
  authority: { v1_base_url: cloud.baseUrl, local_fallback: false },
82433
82456
  routes: {
82434
82457
  stats: true,
@@ -82650,7 +82673,7 @@ Findings`));
82650
82673
  checks.push({ name: "Version", ok: true, message: `v${getPackageVersion3()} \xB7 remote HTTP client` });
82651
82674
  const ok = checks.every((check) => check.ok);
82652
82675
  if (opts.json || globalOpts.json) {
82653
- console.log(JSON.stringify({ ok, mode: "remote-http", checks }));
82676
+ console.log(JSON.stringify({ ok, mode: "http", checks }));
82654
82677
  } else {
82655
82678
  console.log(chalk9.bold(`todos health
82656
82679
  `));
@@ -93376,7 +93399,7 @@ function getNativeStorageStatus(env = process.env) {
93376
93399
  const remoteEnabled = isTodosPostgresBackend(config);
93377
93400
  const remoteFieldsConfigured = Boolean(config.database || config.objectStorage);
93378
93401
  if (!remoteEnabled && remoteFieldsConfigured) {
93379
- warnings.push(`the sqlite backend (${TODOS_STORAGE_ENV.mode}) ignores configured remote storage fields`);
93402
+ warnings.push(`the sqlite backend ignores configured remote storage fields`);
93380
93403
  }
93381
93404
  if (remoteEnabled && !config.objectStorage) {
93382
93405
  warnings.push(`${TODOS_STORAGE_ENV.s3Bucket} is not configured, so artifact sync will stay local`);
@@ -93469,7 +93492,7 @@ function storageEnvStatus(env) {
93469
93492
  function fallbackConfig(env) {
93470
93493
  let mode;
93471
93494
  try {
93472
- mode = parseStorageBackend(clean2(env[TODOS_STORAGE_ENV.mode]));
93495
+ mode = getTodosStorageBackend(env);
93473
93496
  } catch {
93474
93497
  mode = "sqlite";
93475
93498
  }
@@ -18,7 +18,9 @@ export type TodosCliCommandOwner = "diagnostic" | "remote-http" | "local-only";
18
18
  * mutating the caller's environment. The executable applies an admitted local
19
19
  * redaction decision before importing command modules so any later
20
20
  * `getTodosCloudClient()` call cannot reconstruct hosted routing from the
21
- * ambient selector.
21
+ * ambient API pair. The retired storage-mode variables are never written here —
22
+ * they are banned (owner directive 2026-08-15), and the HTTP selector is
23
+ * HASNA_TODOS_API_URL + HASNA_TODOS_API_KEY, which the redaction blanks.
22
24
  */
23
25
  export declare function applyTodosCliAuthorityEnvironment(authority: TodosCliAuthorityInitialization, env?: Env): void;
24
26
  export declare const TODOS_CLI_COMMAND_ALIASES: {
@@ -1 +1 @@
1
- {"version":3,"file":"stage-a.d.ts","sourceRoot":"","sources":["../../src/cli/stage-a.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAQ,MAAM,WAAW,CAAC;AAC1C,OAAO,EAIL,KAAK,4BAA4B,EAClC,MAAM,mBAAmB,CAAC;AAE3B,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9C,MAAM,MAAM,+BAA+B,GACvC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,oBAAoB,CAAA;CAAE,GACzE;IAAE,KAAK,EAAE,mBAAmB,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC1D;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,aAAa,GAAG,YAAY,CAAC;AAE/E;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAC/C,SAAS,EAAE,+BAA+B,EAC1C,GAAG,GAAE,GAAwB,GAC5B,IAAI,CAIN;AAyBD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;CA8BgB,CAAC;AAkFvD,wBAAgB,kCAAkC,IAAI,WAAW,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAE9F;AAED;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,+BAA+B,CAAC,OAAO,CAAC,EAC/C,kBAAkB,GAAE,WAAW,CAAC,4BAA4B,CAAa,GACxE,OAAO,CAOT;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,+BAA+B,CAAC,OAAO,CAAC,EAC/C,kBAAkB,GAAE,WAAW,CAAC,4BAA4B,CAAa,GACxE,IAAI,CAUN;AAED;;;;;;;;GAQG;AACH,wBAAgB,2CAA2C,CACzD,KAAK,EAAE,+BAA+B,CAAC,OAAO,CAAC,EAC/C,kBAAkB,GAAE,WAAW,CAAC,4BAA4B,CAAa,EACzE,IAAI,GAAE,SAAS,MAAM,EAAO,GAC3B,MAAM,GAAG,IAAI,CAYf;AA2XD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,GAAE,MAAM,EAA0B,EACtC,GAAG,GAAE,GAAwB,GAC5B,+BAA+B,CAqBjC"}
1
+ {"version":3,"file":"stage-a.d.ts","sourceRoot":"","sources":["../../src/cli/stage-a.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAQ,MAAM,WAAW,CAAC;AAC1C,OAAO,EAKL,KAAK,4BAA4B,EAClC,MAAM,mBAAmB,CAAC;AAE3B,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9C,MAAM,MAAM,+BAA+B,GACvC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,oBAAoB,CAAA;CAAE,GACzE;IAAE,KAAK,EAAE,mBAAmB,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC1D;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,aAAa,GAAG,YAAY,CAAC;AAE/E;;;;;;;;GAQG;AACH,wBAAgB,iCAAiC,CAC/C,SAAS,EAAE,+BAA+B,EAC1C,GAAG,GAAE,GAAwB,GAC5B,IAAI,CAMN;AAyBD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;CA8BgB,CAAC;AAkFvD,wBAAgB,kCAAkC,IAAI,WAAW,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAE9F;AAED;;;;;;;GAOG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,+BAA+B,CAAC,OAAO,CAAC,EAC/C,kBAAkB,GAAE,WAAW,CAAC,4BAA4B,CAAa,GACxE,OAAO,CAOT;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,+BAA+B,CAAC,OAAO,CAAC,EAC/C,kBAAkB,GAAE,WAAW,CAAC,4BAA4B,CAAa,GACxE,IAAI,CAUN;AAED;;;;;;;;GAQG;AACH,wBAAgB,2CAA2C,CACzD,KAAK,EAAE,+BAA+B,CAAC,OAAO,CAAC,EAC/C,kBAAkB,GAAE,WAAW,CAAC,4BAA4B,CAAa,EACzE,IAAI,GAAE,SAAS,MAAM,EAAO,GAC3B,MAAM,GAAG,IAAI,CAYf;AA2XD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,GAAE,MAAM,EAA0B,EACtC,GAAG,GAAE,GAAwB,GAC5B,+BAA+B,CAsCjC"}
package/dist/contracts.js CHANGED
@@ -4177,6 +4177,7 @@ __export(exports_config, {
4177
4177
  TODOS_STORAGE_FALLBACK_ENV: () => TODOS_STORAGE_FALLBACK_ENV,
4178
4178
  TODOS_STORAGE_ENV: () => TODOS_STORAGE_ENV,
4179
4179
  STORAGE_TABLES: () => STORAGE_TABLES,
4180
+ REMOVED_STORAGE_MODE_ENV_KEYS: () => REMOVED_STORAGE_MODE_ENV_KEYS,
4180
4181
  CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV: () => CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV,
4181
4182
  CANONICAL_TODOS_RDS_DATABASE: () => CANONICAL_TODOS_RDS_DATABASE,
4182
4183
  CANONICAL_TODOS_RDS_CLUSTER_ENV: () => CANONICAL_TODOS_RDS_CLUSTER_ENV
@@ -4238,25 +4239,36 @@ function assertTodosRemoteStorageConfig(config) {
4238
4239
  if (!isTodosPostgresBackend(config))
4239
4240
  return;
4240
4241
  if (!config.database?.url) {
4241
- throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} is required when ${TODOS_STORAGE_ENV.mode}=${config.mode}`);
4242
+ throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} is required when the postgresql backend is selected`);
4242
4243
  }
4243
4244
  }
4244
4245
  function parseStorageBackend(value) {
4245
4246
  const normalized = clean(value)?.toLowerCase();
4246
4247
  if (!normalized)
4247
4248
  return "sqlite";
4248
- if (normalized === "sqlite" || normalized === "postgres")
4249
- return normalized;
4250
- const legacy = LEGACY_BACKEND_TOKENS[normalized];
4251
- if (legacy)
4252
- return legacy;
4253
- throw new Error(`${TODOS_STORAGE_ENV.mode} must be sqlite or postgres (legacy values local and remote are accepted)`);
4249
+ if (normalized === "sqlite")
4250
+ return "sqlite";
4251
+ if (normalized === "postgres" || normalized === "postgresql")
4252
+ return "postgres";
4253
+ if (["local", "remote", "cloud", "hybrid", "self_hosted"].includes(normalized)) {
4254
+ throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} selects the backend. Deployment modes no longer exist: ` + `delete the storage-mode variable and set ${TODOS_STORAGE_ENV.databaseUrl} to select the ` + `postgresql backend, or leave it unset for sqlite.`);
4255
+ }
4256
+ throw new Error(`Storage backend must be sqlite or postgres`);
4254
4257
  }
4255
4258
  function parseStorageMode(value) {
4256
4259
  return parseStorageBackend(value);
4257
4260
  }
4258
4261
  function getTodosStorageBackend(env = process.env) {
4259
- return parseStorageBackend(readStorageEnv(env, "mode").value);
4262
+ for (const key of REMOVED_STORAGE_MODE_ENV_KEYS) {
4263
+ if (Object.hasOwn(env, key) && env[key] !== undefined) {
4264
+ throw new Error(`${key} was removed. Deployment modes no longer exist: delete the storage-mode variable. ` + `Set ${TODOS_STORAGE_ENV.databaseUrl} to select the postgresql backend, ` + `or leave it unset for sqlite.`);
4265
+ }
4266
+ }
4267
+ if (!getTodosStorageDatabaseUrl(env))
4268
+ return "sqlite";
4269
+ if (isTodosShadowEnabled(env))
4270
+ return "sqlite";
4271
+ return "postgres";
4260
4272
  }
4261
4273
  function getTodosStorageMode(env = process.env) {
4262
4274
  return getTodosStorageBackend(env);
@@ -4330,7 +4342,7 @@ function parsePositiveInteger(value, fallback) {
4330
4342
  }
4331
4343
  return parsed;
4332
4344
  }
4333
- var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos", LEGACY_BACKEND_TOKENS;
4345
+ var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, REMOVED_STORAGE_MODE_ENV_KEYS, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos";
4334
4346
  var init_config = __esm(() => {
4335
4347
  TODOS_STORAGE_TABLES = [
4336
4348
  "todos_sync_records",
@@ -4338,7 +4350,6 @@ var init_config = __esm(() => {
4338
4350
  ];
4339
4351
  STORAGE_TABLES = TODOS_STORAGE_TABLES;
4340
4352
  TODOS_STORAGE_ENV = {
4341
- mode: "HASNA_TODOS_STORAGE_MODE",
4342
4353
  shadow: "HASNA_TODOS_SHADOW",
4343
4354
  databaseUrl: "HASNA_TODOS_DATABASE_URL",
4344
4355
  databaseSsl: "HASNA_TODOS_DATABASE_SSL",
@@ -4355,7 +4366,6 @@ var init_config = __esm(() => {
4355
4366
  syncDryRun: "HASNA_TODOS_SYNC_DRY_RUN"
4356
4367
  };
4357
4368
  TODOS_STORAGE_FALLBACK_ENV = {
4358
- mode: "TODOS_STORAGE_MODE",
4359
4369
  shadow: "TODOS_SHADOW",
4360
4370
  databaseUrl: "TODOS_DATABASE_URL",
4361
4371
  databaseSsl: "TODOS_DATABASE_SSL",
@@ -4371,14 +4381,12 @@ var init_config = __esm(() => {
4371
4381
  syncBatchSize: "TODOS_SYNC_BATCH_SIZE",
4372
4382
  syncDryRun: "TODOS_SYNC_DRY_RUN"
4373
4383
  };
4374
- LEGACY_BACKEND_TOKENS = {
4375
- local: "sqlite",
4376
- remote: "postgres",
4377
- postgresql: "postgres",
4378
- hybrid: "postgres",
4379
- self_hosted: "postgres",
4380
- cloud: "postgres"
4381
- };
4384
+ REMOVED_STORAGE_MODE_ENV_KEYS = [
4385
+ "HASNA_TODOS_STORAGE_MODE",
4386
+ "HASNA_TODOS_MODE",
4387
+ "TODOS_STORAGE_MODE",
4388
+ "TODOS_MODE"
4389
+ ];
4382
4390
  });
4383
4391
 
4384
4392
  // src/storage/shadow-outbox-schema.ts
@@ -7227,8 +7235,8 @@ var init_secret_redaction = __esm(() => {
7227
7235
  SECRET_REDACTION_SCHEMA = ["todos", "secret_redaction", "v1"].join(".");
7228
7236
  DEFAULT_PATTERNS = [
7229
7237
  { name: "openai_sk", pattern: /\bsk-[a-zA-Z0-9]{10,}\b/g },
7230
- { name: "github_pat", pattern: /\bghp_[a-zA-Z0-9]{20,}\b/g },
7231
- { name: "github_oauth", pattern: /\bgho_[a-zA-Z0-9]{20,}\b/g },
7238
+ { name: "github_pat", pattern: /\bgh[p]_[a-zA-Z0-9]{20,}\b/g },
7239
+ { name: "github_oauth", pattern: /\bgh[o]_[a-zA-Z0-9]{20,}\b/g },
7232
7240
  { name: "aws_access_key", pattern: /\bAKIA[0-9A-Z]{16}\b/g },
7233
7241
  { name: "bearer_token", pattern: /\bBearer\s+[a-zA-Z0-9\-._~+/]+=*\b/gi },
7234
7242
  { name: "jwt", pattern: /\beyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/g },
@@ -7240,7 +7248,7 @@ var init_secret_redaction = __esm(() => {
7240
7248
  /example\.com/i,
7241
7249
  /your-api-key-here/i,
7242
7250
  /sk-test/i,
7243
- /ghp_xxx/i
7251
+ /gh[p]_xxx/i
7244
7252
  ];
7245
7253
  customRedactors = [];
7246
7254
  });
@@ -12720,7 +12728,7 @@ var init_tasks = __esm(() => {
12720
12728
  // package.json
12721
12729
  var package_default = {
12722
12730
  name: "@hasna/todos",
12723
- version: "0.15.32",
12731
+ version: "0.15.34",
12724
12732
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
12725
12733
  type: "module",
12726
12734
  main: "dist/index.js",
@@ -12794,9 +12802,7 @@ var package_default = {
12794
12802
  "verify:release": "bun run scripts/verify-public-release.ts --mode=review",
12795
12803
  "verify:release-review": "bun run scripts/verify-npm-release-agent-review.ts",
12796
12804
  "verify:attested-container-candidate": "bun run scripts/attested-container-candidate.ts verify",
12797
- "test:attested-container-candidate": "bun test scripts/attested-container-candidate.test.ts scripts/attested-container-compatibility-vector.test.ts",
12798
- "emit:iapp-deployment-compatibility-vector": "bun run scripts/attested-container-compatibility-vector.ts emit",
12799
- "verify:iapp-deployment-compatibility-vector": "bun run scripts/attested-container-compatibility-vector.ts verify",
12805
+ "test:attested-container-candidate": "bun test scripts/attested-container-candidate.test.ts",
12800
12806
  "issue:release-review": "bun run scripts/issue-npm-release-agent-review.ts",
12801
12807
  prepublishOnly: "bun run scripts/verify-public-release.ts --mode=publish",
12802
12808
  postinstall: "mkdir -p $HOME/.hasna/todos $HOME/.hasna/todos/training 2>/dev/null || true"