@dmrlzz/davecode 0.4.0 → 0.5.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 (95) hide show
  1. package/dist/{ChatApp-BO7QMBWD.js → ChatApp-YMHKIAAH.js} +6 -6
  2. package/dist/{account-UGNLJPQ4.js → account-5QFYCVYM.js} +3 -3
  3. package/dist/{accounts-NM7IWP53.js → accounts-756BMMAL.js} +5 -5
  4. package/dist/{budget-INAW6FXD.js → budget-PGSNCVVK.js} +4 -4
  5. package/dist/{chat-U4QJQ7M6.js → chat-D3T4YG5D.js} +8 -8
  6. package/dist/{chunk-MOK5D3XJ.js → chunk-6SHOBL2X.js} +2 -2
  7. package/dist/{chunk-Z6NQDJEN.js → chunk-6WUVR3R2.js} +10 -4
  8. package/dist/chunk-6WUVR3R2.js.map +1 -0
  9. package/dist/{chunk-3Q2KCXLN.js → chunk-HENE4DA6.js} +240 -22
  10. package/dist/chunk-HENE4DA6.js.map +1 -0
  11. package/dist/{chunk-GHDOUII5.js → chunk-IOATP7KH.js} +2 -2
  12. package/dist/{chunk-DZ2FDABF.js → chunk-JFWMLYV7.js} +111 -19
  13. package/dist/chunk-JFWMLYV7.js.map +1 -0
  14. package/dist/{chunk-CWBVNS3L.js → chunk-JSRPO2AR.js} +6 -5
  15. package/dist/chunk-JSRPO2AR.js.map +1 -0
  16. package/dist/{chunk-ROIUTNQV.js → chunk-K5AA65RS.js} +2 -2
  17. package/dist/{chunk-GZJ6JSO7.js → chunk-ZNUNYYN6.js} +3 -3
  18. package/dist/{config-YEAPBMBX.js → config-TKGGYEN7.js} +3 -3
  19. package/dist/dashboard/assets/Accounts-DciFUg4y.js +1 -0
  20. package/dist/dashboard/assets/{Badge-CGY6DgGx.js → Badge-DUGKlx3Z.js} +2 -2
  21. package/dist/dashboard/assets/Callout-WgXHDPg0.js +1 -0
  22. package/dist/dashboard/assets/Field-Cmpgg_1z.js +1 -0
  23. package/dist/dashboard/assets/Goals-C6HL1PNE.js +1 -0
  24. package/dist/dashboard/assets/Library-DwpBYTbK.js +1 -0
  25. package/dist/dashboard/assets/Markdown-CGgIydUh.js +71 -0
  26. package/dist/dashboard/assets/Panes-DxFSwhb7.js +8 -0
  27. package/dist/dashboard/assets/Panes-kHJ-D0s7.css +1 -0
  28. package/dist/dashboard/assets/RoutesPage-Cyo0MNd7.js +4 -0
  29. package/dist/dashboard/assets/Runner-CIL7RxpN.js +1 -0
  30. package/dist/dashboard/assets/Settings-B_PEB585.js +1 -0
  31. package/dist/dashboard/assets/Switch-DPYlLseH.js +1 -0
  32. package/dist/dashboard/assets/Tasks-DEfPSaE6.js +2 -0
  33. package/dist/dashboard/assets/Traffic-Dcz-WhsP.js +1 -0
  34. package/dist/dashboard/assets/Workspace-9UCw9q7C.js +1 -0
  35. package/dist/dashboard/assets/arrow-up-DjhzZprp.js +1 -0
  36. package/dist/dashboard/assets/cn-BwE4cei0.js +1 -0
  37. package/dist/dashboard/assets/forms-DVIUAxAc.js +1 -0
  38. package/dist/dashboard/assets/index-BEmBgsbP.css +2 -0
  39. package/dist/dashboard/assets/index-CpztPbom.js +104 -0
  40. package/dist/dashboard/assets/task-errors-r-WYZ_S3.js +1 -0
  41. package/dist/dashboard/assets/trash-BTuDcSaQ.js +1 -0
  42. package/dist/dashboard/index.html +4 -4
  43. package/dist/{dist-RRQGUBL3.js → dist-F3NRJ5FK.js} +18 -2
  44. package/dist/{doctor-MQYK44S2.js → doctor-V4XXVVGK.js} +3 -3
  45. package/dist/{goal-6NUX4AIT.js → goal-RUHYPVPA.js} +3 -3
  46. package/dist/index.js +31 -31
  47. package/dist/{init-AXYIWAB3.js → init-54FKQZ2Z.js} +3 -3
  48. package/dist/{run-QRTLHFB3.js → run-4R25J2VN.js} +3 -3
  49. package/dist/runtime.js +2 -2
  50. package/dist/{start-O5I5TSO2.js → start-GRHW3TGE.js} +3 -3
  51. package/dist/{status-LEA3RVUX.js → status-QR2HNPBK.js} +4 -4
  52. package/dist/{tasks-XOTVICVZ.js → tasks-BHWDV4JS.js} +4 -4
  53. package/package.json +6 -6
  54. package/dist/chunk-3Q2KCXLN.js.map +0 -1
  55. package/dist/chunk-CWBVNS3L.js.map +0 -1
  56. package/dist/chunk-DZ2FDABF.js.map +0 -1
  57. package/dist/chunk-Z6NQDJEN.js.map +0 -1
  58. package/dist/dashboard/assets/Accounts-CeVyX4a3.js +0 -1
  59. package/dist/dashboard/assets/Callout-DauCk_cW.js +0 -1
  60. package/dist/dashboard/assets/Field-OXMnT9Tb.js +0 -1
  61. package/dist/dashboard/assets/Goals-DH-KDh8M.js +0 -1
  62. package/dist/dashboard/assets/Library-C15szLjZ.js +0 -1
  63. package/dist/dashboard/assets/Markdown-Xhao3sgL.js +0 -69
  64. package/dist/dashboard/assets/Panel-Du40pM7n.js +0 -1
  65. package/dist/dashboard/assets/RoutesPage-Cj1OelGO.js +0 -4
  66. package/dist/dashboard/assets/Runner-BK1-ZF7h.js +0 -1
  67. package/dist/dashboard/assets/Settings-C1nzQQhe.js +0 -1
  68. package/dist/dashboard/assets/Switch-bwtIJ00H.js +0 -1
  69. package/dist/dashboard/assets/Tasks-x4s8IiPG.js +0 -2
  70. package/dist/dashboard/assets/Traffic-CV7TaTid.js +0 -1
  71. package/dist/dashboard/assets/Workspace-DAL-MBc0.js +0 -1
  72. package/dist/dashboard/assets/arrow-up-goM9r-I9.js +0 -1
  73. package/dist/dashboard/assets/forms-DtpfI65-.js +0 -1
  74. package/dist/dashboard/assets/index-BCi-Vuvh.js +0 -104
  75. package/dist/dashboard/assets/index-CcJFhc98.css +0 -2
  76. package/dist/dashboard/assets/task-errors-B2pgjUGN.js +0 -1
  77. package/dist/dashboard/assets/trash-BVojETtF.js +0 -1
  78. /package/dist/{ChatApp-BO7QMBWD.js.map → ChatApp-YMHKIAAH.js.map} +0 -0
  79. /package/dist/{account-UGNLJPQ4.js.map → account-5QFYCVYM.js.map} +0 -0
  80. /package/dist/{accounts-NM7IWP53.js.map → accounts-756BMMAL.js.map} +0 -0
  81. /package/dist/{budget-INAW6FXD.js.map → budget-PGSNCVVK.js.map} +0 -0
  82. /package/dist/{chat-U4QJQ7M6.js.map → chat-D3T4YG5D.js.map} +0 -0
  83. /package/dist/{chunk-MOK5D3XJ.js.map → chunk-6SHOBL2X.js.map} +0 -0
  84. /package/dist/{chunk-GHDOUII5.js.map → chunk-IOATP7KH.js.map} +0 -0
  85. /package/dist/{chunk-ROIUTNQV.js.map → chunk-K5AA65RS.js.map} +0 -0
  86. /package/dist/{chunk-GZJ6JSO7.js.map → chunk-ZNUNYYN6.js.map} +0 -0
  87. /package/dist/{config-YEAPBMBX.js.map → config-TKGGYEN7.js.map} +0 -0
  88. /package/dist/{dist-RRQGUBL3.js.map → dist-F3NRJ5FK.js.map} +0 -0
  89. /package/dist/{doctor-MQYK44S2.js.map → doctor-V4XXVVGK.js.map} +0 -0
  90. /package/dist/{goal-6NUX4AIT.js.map → goal-RUHYPVPA.js.map} +0 -0
  91. /package/dist/{init-AXYIWAB3.js.map → init-54FKQZ2Z.js.map} +0 -0
  92. /package/dist/{run-QRTLHFB3.js.map → run-4R25J2VN.js.map} +0 -0
  93. /package/dist/{start-O5I5TSO2.js.map → start-GRHW3TGE.js.map} +0 -0
  94. /package/dist/{status-LEA3RVUX.js.map → status-QR2HNPBK.js.map} +0 -0
  95. /package/dist/{tasks-XOTVICVZ.js.map → tasks-BHWDV4JS.js.map} +0 -0
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  formatTaskTree,
3
3
  summaryLine
4
- } from "./chunk-MOK5D3XJ.js";
4
+ } from "./chunk-6SHOBL2X.js";
5
5
  import {
6
6
  brandBanner
7
- } from "./chunk-ROIUTNQV.js";
7
+ } from "./chunk-K5AA65RS.js";
8
8
  import {
9
9
  Spinner
10
10
  } from "./chunk-5LKOJBUL.js";
@@ -15,7 +15,7 @@ import {
15
15
  import {
16
16
  emptyUsage,
17
17
  renderAccounts
18
- } from "./chunk-GZJ6JSO7.js";
18
+ } from "./chunk-ZNUNYYN6.js";
19
19
  import {
20
20
  bar,
21
21
  formatTokens,
@@ -25,9 +25,9 @@ import {
25
25
  visibleWidth
26
26
  } from "./chunk-SF5ZEAP4.js";
27
27
  import "./chunk-QA3O6RA2.js";
28
- import "./chunk-DZ2FDABF.js";
28
+ import "./chunk-JFWMLYV7.js";
29
29
  import "./chunk-7XVXEO7U.js";
30
- import "./chunk-3Q2KCXLN.js";
30
+ import "./chunk-HENE4DA6.js";
31
31
  import "./chunk-MC7RIPL7.js";
32
32
 
33
33
  // src/tui/ChatApp.tsx
@@ -1042,4 +1042,4 @@ function ChatApp(props) {
1042
1042
  export {
1043
1043
  ChatApp
1044
1044
  };
1045
- //# sourceMappingURL=ChatApp-BO7QMBWD.js.map
1045
+ //# sourceMappingURL=ChatApp-YMHKIAAH.js.map
@@ -7,9 +7,9 @@ import {
7
7
  loginCommand,
8
8
  logoutCommand,
9
9
  signIn
10
- } from "./chunk-GHDOUII5.js";
10
+ } from "./chunk-IOATP7KH.js";
11
11
  import "./chunk-7XVXEO7U.js";
12
- import "./chunk-3Q2KCXLN.js";
12
+ import "./chunk-HENE4DA6.js";
13
13
  export {
14
14
  accountCommand,
15
15
  accountsEnabled,
@@ -20,4 +20,4 @@ export {
20
20
  logoutCommand,
21
21
  signIn
22
22
  };
23
- //# sourceMappingURL=account-UGNLJPQ4.js.map
23
+ //# sourceMappingURL=account-5QFYCVYM.js.map
@@ -11,14 +11,14 @@ import {
11
11
  loginArgs,
12
12
  renderAccountList,
13
13
  subscriptionFromFlags
14
- } from "./chunk-CWBVNS3L.js";
15
- import "./chunk-Z6NQDJEN.js";
14
+ } from "./chunk-JSRPO2AR.js";
15
+ import "./chunk-6WUVR3R2.js";
16
16
  import "./chunk-IC3TDONV.js";
17
17
  import "./chunk-SF5ZEAP4.js";
18
18
  import "./chunk-QA3O6RA2.js";
19
- import "./chunk-DZ2FDABF.js";
19
+ import "./chunk-JFWMLYV7.js";
20
20
  import "./chunk-7XVXEO7U.js";
21
- import "./chunk-3Q2KCXLN.js";
21
+ import "./chunk-HENE4DA6.js";
22
22
  import "./chunk-MC7RIPL7.js";
23
23
  export {
24
24
  accountsAdd,
@@ -34,4 +34,4 @@ export {
34
34
  renderAccountList,
35
35
  subscriptionFromFlags
36
36
  };
37
- //# sourceMappingURL=accounts-NM7IWP53.js.map
37
+ //# sourceMappingURL=accounts-756BMMAL.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  openAccounts
3
- } from "./chunk-Z6NQDJEN.js";
3
+ } from "./chunk-6WUVR3R2.js";
4
4
  import {
5
5
  bar,
6
6
  levelStyle,
@@ -13,11 +13,11 @@ import {
13
13
  } from "./chunk-QA3O6RA2.js";
14
14
  import {
15
15
  detectProjectRoot
16
- } from "./chunk-DZ2FDABF.js";
16
+ } from "./chunk-JFWMLYV7.js";
17
17
  import {
18
18
  EXIT
19
19
  } from "./chunk-7XVXEO7U.js";
20
- import "./chunk-3Q2KCXLN.js";
20
+ import "./chunk-HENE4DA6.js";
21
21
  import "./chunk-MC7RIPL7.js";
22
22
 
23
23
  // src/commands/budget.ts
@@ -112,4 +112,4 @@ export {
112
112
  budgetCommand,
113
113
  renderBudget
114
114
  };
115
- //# sourceMappingURL=budget-INAW6FXD.js.map
115
+ //# sourceMappingURL=budget-PGSNCVVK.js.map
@@ -4,19 +4,19 @@ import {
4
4
  currentAccount,
5
5
  describeUser,
6
6
  signIn
7
- } from "./chunk-GHDOUII5.js";
7
+ } from "./chunk-IOATP7KH.js";
8
8
  import {
9
9
  accountsLogin
10
- } from "./chunk-CWBVNS3L.js";
10
+ } from "./chunk-JSRPO2AR.js";
11
11
  import {
12
12
  openAccounts
13
- } from "./chunk-Z6NQDJEN.js";
13
+ } from "./chunk-6WUVR3R2.js";
14
14
  import {
15
15
  PromptCancelledError
16
16
  } from "./chunk-IC3TDONV.js";
17
17
  import {
18
18
  brandBanner
19
- } from "./chunk-ROIUTNQV.js";
19
+ } from "./chunk-K5AA65RS.js";
20
20
  import "./chunk-SF5ZEAP4.js";
21
21
  import {
22
22
  readPreferences,
@@ -28,7 +28,7 @@ import {
28
28
  findGateway,
29
29
  startRuntime,
30
30
  which
31
- } from "./chunk-DZ2FDABF.js";
31
+ } from "./chunk-JFWMLYV7.js";
32
32
  import {
33
33
  CliError,
34
34
  EXIT
@@ -38,7 +38,7 @@ import {
38
38
  VERSION,
39
39
  loadConfig,
40
40
  writeGlobalRouting
41
- } from "./chunk-3Q2KCXLN.js";
41
+ } from "./chunk-HENE4DA6.js";
42
42
  import {
43
43
  createTheme
44
44
  } from "./chunk-MC7RIPL7.js";
@@ -328,7 +328,7 @@ async function runChat(ctx, opts, projectRoot) {
328
328
  const [{ render }, { createElement }, { ChatApp }] = await Promise.all([
329
329
  import("ink"),
330
330
  import("react"),
331
- import("./ChatApp-BO7QMBWD.js")
331
+ import("./ChatApp-YMHKIAAH.js")
332
332
  ]);
333
333
  const instance = render(
334
334
  createElement(ChatApp, {
@@ -356,4 +356,4 @@ async function runChat(ctx, opts, projectRoot) {
356
356
  export {
357
357
  chatCommand
358
358
  };
359
- //# sourceMappingURL=chat-U4QJQ7M6.js.map
359
+ //# sourceMappingURL=chat-D3T4YG5D.js.map
@@ -7,7 +7,7 @@ import {
7
7
  nextTask,
8
8
  summarize,
9
9
  topologicalOrder
10
- } from "./chunk-3Q2KCXLN.js";
10
+ } from "./chunk-HENE4DA6.js";
11
11
 
12
12
  // src/lib/task-tree.ts
13
13
  function statusGlyph(theme, task, blocked) {
@@ -111,4 +111,4 @@ export {
111
111
  formatTaskTree,
112
112
  formatTaskDetail
113
113
  };
114
- //# sourceMappingURL=chunk-MOK5D3XJ.js.map
114
+ //# sourceMappingURL=chunk-6SHOBL2X.js.map
@@ -2,13 +2,14 @@ import {
2
2
  accountCreateSchema,
3
3
  accountPatchSchema,
4
4
  findGateway
5
- } from "./chunk-DZ2FDABF.js";
5
+ } from "./chunk-JFWMLYV7.js";
6
6
  import {
7
7
  CliError
8
8
  } from "./chunk-7XVXEO7U.js";
9
9
  import {
10
- createEngine
11
- } from "./chunk-3Q2KCXLN.js";
10
+ createEngine,
11
+ endpointRebindIssue
12
+ } from "./chunk-HENE4DA6.js";
12
13
 
13
14
  // src/lib/accounts-backend.ts
14
15
  import { z } from "zod";
@@ -53,6 +54,11 @@ var LocalAccounts = class {
53
54
  const existing = this.engine.accounts.get(id);
54
55
  if (!existing) throw notFound(id);
55
56
  const { secret, ...fields } = parsed.data;
57
+ const rebind = endpointRebindIssue(existing, fields.config, secret !== void 0);
58
+ if (rebind)
59
+ throw new CliError(rebind, {
60
+ hint: "Pass the key again with --secret-env or --secret-stdin."
61
+ });
56
62
  const update = { ...fields };
57
63
  const enabled = fields.enabled ?? existing.enabled;
58
64
  if (!enabled) {
@@ -177,4 +183,4 @@ export {
177
183
  openAccounts,
178
184
  resolveAccount
179
185
  };
180
- //# sourceMappingURL=chunk-Z6NQDJEN.js.map
186
+ //# sourceMappingURL=chunk-6WUVR3R2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/accounts-backend.ts"],"sourcesContent":["import {\n type Account,\n type AccountConnection,\n type AccountUpdateInput,\n type BudgetSnapshot,\n createEngine,\n type DaveConfig,\n type Engine,\n endpointRebindIssue,\n type ProviderKind,\n type QuotaLimits,\n type SubscriptionInfo,\n} from '@davecode/core';\nimport { accountCreateSchema, accountPatchSchema } from '@davecode/server';\nimport { z } from 'zod';\nimport { findGateway, type GatewayClient } from '../client';\nimport { CliError } from '../errors';\n\n/** Body of `POST /api/accounts` (secret is write-only). */\nexport interface AccountCreate {\n provider: ProviderKind;\n label: string;\n enabled?: boolean;\n priority?: number;\n weight?: number;\n limits?: QuotaLimits;\n config?: Record<string, unknown>;\n subscription?: SubscriptionInfo;\n secret?: string;\n}\n\nexport type AccountPatch = Partial<Omit<AccountCreate, 'provider' | 'subscription'>> & {\n /** Replaces the subscription details; `null` clears them. */\n subscription?: SubscriptionInfo | null;\n};\n\n/**\n * Account operations against a running gateway (so the dashboard sees the change live) or, when\n * none is running, directly against `state.db` with the same validation and audit trail.\n */\nexport interface AccountsBackend {\n readonly kind: 'gateway' | 'local';\n readonly config: DaveConfig;\n list(): Promise<Account[]>;\n get(id: string): Promise<Account | undefined>;\n create(input: AccountCreate): Promise<Account>;\n update(id: string, patch: AccountPatch): Promise<Account>;\n remove(id: string): Promise<void>;\n /** Subscription details and remaining capacity per account. */\n budget(): Promise<BudgetSnapshot>;\n /** Whether the account's API key or vendor CLI login is in place. */\n connection(id: string): Promise<AccountConnection>;\n close(): void;\n}\n\nfunction invalid(error: z.ZodError): CliError {\n return new CliError(`Invalid account: ${z.prettifyError(error)}`);\n}\n\nexport class LocalAccounts implements AccountsBackend {\n readonly kind = 'local' as const;\n\n constructor(readonly engine: Engine) {}\n\n get config(): DaveConfig {\n return this.engine.config;\n }\n\n async list(): Promise<Account[]> {\n return this.engine.accounts.list();\n }\n\n async get(id: string): Promise<Account | undefined> {\n return this.engine.accounts.get(id);\n }\n\n async create(input: AccountCreate): Promise<Account> {\n const parsed = accountCreateSchema.safeParse(input);\n if (!parsed.success) throw invalid(parsed.error);\n const { secret, ...fields } = parsed.data;\n const { accounts, keyring, audit, db } = this.engine;\n return db.transaction(() => {\n const created = accounts.create(fields);\n if (secret !== undefined) keyring.set(created.id, secret);\n audit.record({\n actor: 'cli',\n action: 'account.create',\n target: created.id,\n details: { provider: created.provider, label: created.label, hasSecret: !!secret },\n });\n return created;\n })();\n }\n\n async update(id: string, patch: AccountPatch): Promise<Account> {\n const parsed = accountPatchSchema.safeParse(patch);\n if (!parsed.success) throw invalid(parsed.error);\n const existing = this.engine.accounts.get(id);\n if (!existing) throw notFound(id);\n const { secret, ...fields } = parsed.data;\n // The machine's owner may set any config key, but a stored secret still never follows an\n // account to a different endpoint without being supplied again.\n const rebind = endpointRebindIssue(existing, fields.config, secret !== undefined);\n if (rebind)\n throw new CliError(rebind, {\n hint: 'Pass the key again with --secret-env or --secret-stdin.',\n });\n const update: AccountUpdateInput = { ...fields };\n const enabled = fields.enabled ?? existing.enabled;\n if (!enabled) {\n update.status = 'disabled';\n } else if (fields.enabled === true || secret !== undefined) {\n update.status = 'active';\n update.cooldownUntil = null;\n update.lastError = null;\n }\n const { accounts, keyring, audit, db } = this.engine;\n return db.transaction(() => {\n const updated = accounts.update(id, update)!;\n if (secret !== undefined) keyring.set(id, secret);\n audit.record({\n actor: 'cli',\n action: 'account.update',\n target: id,\n details: { fields: Object.keys(fields), secretChanged: secret !== undefined },\n });\n return updated;\n })();\n }\n\n async remove(id: string): Promise<void> {\n const { accounts, sandboxes, chromium, audit } = this.engine;\n if (!accounts.delete(id)) throw notFound(id);\n for (const cleanup of [() => sandboxes.remove(id), () => chromium.remove(id)]) {\n try {\n cleanup();\n } catch {\n // A leftover directory is harmless; the account row is gone.\n }\n }\n audit.record({ actor: 'cli', action: 'account.delete', target: id });\n }\n\n /** Quota windows are rebuilt from state.db; provider-reported limits live in the gateway only. */\n async budget(): Promise<BudgetSnapshot> {\n return this.engine.budget.snapshot();\n }\n\n async connection(id: string): Promise<AccountConnection> {\n const account = this.engine.accounts.get(id);\n if (!account) throw notFound(id);\n return this.engine.connections.check(account);\n }\n\n close(): void {\n this.engine.close();\n }\n}\n\nexport class GatewayAccounts implements AccountsBackend {\n readonly kind = 'gateway' as const;\n\n constructor(\n readonly client: GatewayClient,\n readonly config: DaveConfig,\n ) {}\n\n async list(): Promise<Account[]> {\n return (await this.client.get<{ accounts: Account[] }>('/api/accounts')).accounts;\n }\n\n async get(id: string): Promise<Account | undefined> {\n return (await this.list()).find((a) => a.id === id);\n }\n\n async create(input: AccountCreate): Promise<Account> {\n return (await this.client.post<{ account: Account }>('/api/accounts', input)).account;\n }\n\n async update(id: string, patch: AccountPatch): Promise<Account> {\n return (\n await this.client.patch<{ account: Account }>(\n `/api/accounts/${encodeURIComponent(id)}`,\n patch,\n )\n ).account;\n }\n\n async remove(id: string): Promise<void> {\n await this.client.delete(`/api/accounts/${encodeURIComponent(id)}`);\n }\n\n async budget(): Promise<BudgetSnapshot> {\n return (await this.client.get<{ budget: BudgetSnapshot }>('/api/budget')).budget;\n }\n\n async connection(id: string): Promise<AccountConnection> {\n return (\n await this.client.get<{ connection: AccountConnection }>(\n `/api/accounts/${encodeURIComponent(id)}/connection`,\n )\n ).connection;\n }\n\n close(): void {}\n}\n\nfunction notFound(id: string): CliError {\n return new CliError(`No account with id ${JSON.stringify(id)}`, {\n hint: 'List accounts with `davecode accounts list`.',\n });\n}\n\n/** Prefer the running gateway; fall back to the local database. */\nexport async function openAccounts(options: {\n home: string;\n env: NodeJS.ProcessEnv;\n projectRoot?: string;\n /** Skip the gateway probe (tests, or `--local`). */\n local?: boolean;\n}): Promise<AccountsBackend> {\n const engine = createEngine({\n home: options.home,\n env: options.env,\n ...(options.projectRoot ? { projectRoot: options.projectRoot } : {}),\n });\n if (!options.local) {\n const gateway = await findGateway(engine.config).catch(() => undefined);\n if (gateway) {\n const config = engine.config;\n engine.close();\n return new GatewayAccounts(gateway.client, config);\n }\n }\n return new LocalAccounts(engine);\n}\n\n/** Resolve an account by exact id, or by unique label/id prefix for convenience. */\nexport async function resolveAccount(backend: AccountsBackend, ref: string): Promise<Account> {\n const accounts = await backend.list();\n const exact = accounts.find((a) => a.id === ref);\n if (exact) return exact;\n const matches = accounts.filter(\n (a) => a.label.toLowerCase() === ref.toLowerCase() || a.id.startsWith(ref),\n );\n if (matches.length === 1) return matches[0]!;\n if (matches.length > 1) {\n throw new CliError(`${JSON.stringify(ref)} matches ${matches.length} accounts`, {\n hint: `Use the full id: ${matches.map((a) => a.id).join(', ')}`,\n });\n }\n throw notFound(ref);\n}\n"],"mappings":"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","names":[]}
@@ -116,7 +116,7 @@ import { z as z8 } from "zod";
116
116
  import { randomBytes as randomBytes7 } from "crypto";
117
117
  import { createServer } from "http";
118
118
  import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes8 } from "crypto";
119
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync3, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
119
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync3, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
120
120
  import { dirname as dirname6, join as join6 } from "path";
121
121
  import { z as z9 } from "zod";
122
122
  import { z as z10 } from "zod";
@@ -155,6 +155,8 @@ var ENDPOINTS = {
155
155
  }
156
156
  };
157
157
  var PENDING_MS = 10 * 6e4;
158
+ var PENDING_MAX = 1e3;
159
+ var PENDING_PER_PEER = 20;
158
160
  var SESSION_MS = 8 * 60 * 6e4;
159
161
  var random = () => randomBytes(32).toString("base64url");
160
162
  var digest = (value) => createHash("sha256").update(value).digest("base64url");
@@ -200,10 +202,29 @@ var LoginService = class {
200
202
  logout(session) {
201
203
  if (session) this.sessions.delete(digest(session));
202
204
  }
203
- begin(provider) {
205
+ /**
206
+ * Start a sign-in. `peer` is the connection's address as the HTTP server sees it (never a
207
+ * forwarded header unless the server was configured to trust its proxy). Unfinished sign-ins
208
+ * never block a new one: each peer keeps a few, and when the table is full the oldest goes.
209
+ */
210
+ begin(provider, peer = "unknown") {
204
211
  this.prune();
205
212
  const client = this.options.providers[provider];
206
- if (!client || this.pending.size >= 1e3) throw new LoginError();
213
+ if (!client) throw new LoginError();
214
+ let mine = 0;
215
+ for (const value of this.pending.values()) if (value.peer === peer) mine++;
216
+ for (const [key, value] of this.pending) {
217
+ if (mine < PENDING_PER_PEER) break;
218
+ if (value.peer === peer) {
219
+ this.pending.delete(key);
220
+ mine--;
221
+ }
222
+ }
223
+ while (this.pending.size >= PENDING_MAX) {
224
+ const oldest = this.pending.keys().next().value;
225
+ if (oldest === void 0) break;
226
+ this.pending.delete(oldest);
227
+ }
207
228
  const state = random();
208
229
  const binding = random();
209
230
  const verifier = random();
@@ -211,6 +232,7 @@ var LoginService = class {
211
232
  provider,
212
233
  binding: digest(binding),
213
234
  verifier,
235
+ peer,
214
236
  expires: this.now() + PENDING_MS
215
237
  });
216
238
  const url = new URL(ENDPOINTS[provider].authorize);
@@ -1471,6 +1493,53 @@ ${message}`, options);
1471
1493
  this.source = source;
1472
1494
  }
1473
1495
  };
1496
+ var PROJECT_SETTABLE_KEYS = {
1497
+ logLevel: true,
1498
+ routing: true,
1499
+ goal: true,
1500
+ runner: {
1501
+ maxRepairCycles: true,
1502
+ branchPrefix: true,
1503
+ baseBranch: true,
1504
+ route: true,
1505
+ executor: true,
1506
+ pullRequests: true,
1507
+ commitBrain: true,
1508
+ commandTimeoutMs: true,
1509
+ idlePollMs: true,
1510
+ maxIterations: true,
1511
+ maxTaskTokens: true,
1512
+ validate: true,
1513
+ claudeCli: { accountId: true, model: true, timeoutMs: true },
1514
+ judge: { kind: true, model: true, threshold: true }
1515
+ }
1516
+ };
1517
+ function filterProjectLayer(layer) {
1518
+ const dropped = [];
1519
+ const walk = (value, tree, prefix) => {
1520
+ const out = {};
1521
+ for (const [key, v] of Object.entries(value)) {
1522
+ const rule = Object.hasOwn(tree, key) ? tree[key] : void 0;
1523
+ const path8 = prefix ? `${prefix}.${key}` : key;
1524
+ if (rule === true) {
1525
+ out[key] = v;
1526
+ } else if (rule && isPlainObject(v)) {
1527
+ const inner = walk(v, rule, path8);
1528
+ if (Object.keys(inner).length > 0) out[key] = inner;
1529
+ } else {
1530
+ dropped.push(path8);
1531
+ }
1532
+ }
1533
+ return out;
1534
+ };
1535
+ return { allowed: walk(layer, PROJECT_SETTABLE_KEYS, ""), dropped };
1536
+ }
1537
+ var warned = /* @__PURE__ */ new Set();
1538
+ function defaultWarn(message) {
1539
+ if (warned.has(message)) return;
1540
+ warned.add(message);
1541
+ process.emitWarning(message, "DaveCodeConfigWarning");
1542
+ }
1474
1543
  function isPlainObject(value) {
1475
1544
  return typeof value === "object" && value !== null && !Array.isArray(value);
1476
1545
  }
@@ -1483,7 +1552,7 @@ function deepMerge(base, override) {
1483
1552
  }
1484
1553
  return out;
1485
1554
  }
1486
- function readLayer(path8) {
1555
+ function readLayer(path8, project) {
1487
1556
  let raw;
1488
1557
  try {
1489
1558
  raw = readFileSync(path8, "utf8");
@@ -1498,9 +1567,15 @@ function readLayer(path8) {
1498
1567
  throw new ConfigError(path8, `malformed JSON: ${err.message}`, { cause: err });
1499
1568
  }
1500
1569
  if (!isPlainObject(parsed)) throw new ConfigError(path8, "expected a JSON object");
1501
- const result = configSchema.safeParse(parsed);
1570
+ let layer = parsed;
1571
+ if (project) {
1572
+ const { allowed, dropped } = filterProjectLayer(parsed);
1573
+ if (dropped.length > 0) project(dropped);
1574
+ layer = allowed;
1575
+ }
1576
+ const result = configSchema.safeParse(layer);
1502
1577
  if (!result.success) throw new ConfigError(path8, z4.prettifyError(result.error));
1503
- return parsed;
1578
+ return layer;
1504
1579
  }
1505
1580
  var TRUE_VALUES = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
1506
1581
  var FALSE_VALUES = /* @__PURE__ */ new Set(["0", "false", "no", "off", ""]);
@@ -1558,7 +1633,14 @@ function loadConfig(options = {}) {
1558
1633
  const globalLayer = readLayer(globalPaths(home).config);
1559
1634
  if (globalLayer) merged = deepMerge(merged, globalLayer);
1560
1635
  if (options.projectRoot) {
1561
- const projectLayer = readLayer(projectPaths(options.projectRoot).config);
1636
+ const projectFile = projectPaths(options.projectRoot).config;
1637
+ const warn = options.onWarning ?? defaultWarn;
1638
+ const projectLayer = readLayer(
1639
+ projectFile,
1640
+ (dropped) => warn(
1641
+ `Ignored ${dropped.join(", ")} in ${projectFile}: project config cannot set these keys. Set them in your user config (~/.davecode/config.json) or with DAVECODE_* environment variables.`
1642
+ )
1643
+ );
1562
1644
  if (projectLayer) merged = deepMerge(merged, projectLayer);
1563
1645
  }
1564
1646
  merged = deepMerge(merged, envLayer(env));
@@ -3875,6 +3957,76 @@ function createProviders(options = {}) {
3875
3957
  ];
3876
3958
  return new Map(providers.map((p) => [p.kind, p]));
3877
3959
  }
3960
+ var ENDPOINT_CONFIG_KEYS = ["baseUrl"];
3961
+ var MODEL_KEYS = ["models", "defaultModel"];
3962
+ var API_CONFIG_KEYS = {
3963
+ anthropic: ["baseUrl", ...MODEL_KEYS],
3964
+ openai: ["baseUrl", "includeUsage", ...MODEL_KEYS],
3965
+ gemini: ["baseUrl", ...MODEL_KEYS],
3966
+ "openai-compatible": ["baseUrl", "includeUsage", ...MODEL_KEYS],
3967
+ "claude-cli": [...MODEL_KEYS],
3968
+ "codex-cli": [...MODEL_KEYS],
3969
+ "gemini-cli": [...MODEL_KEYS],
3970
+ "antigravity-cli": [...MODEL_KEYS],
3971
+ "gemini-web": ["models"]
3972
+ };
3973
+ function isStringList(value) {
3974
+ return Array.isArray(value) && value.every((v) => typeof v === "string" && v.length > 0);
3975
+ }
3976
+ function apiConfigIssues(provider, config, existing = {}) {
3977
+ const allowed = API_CONFIG_KEYS[provider];
3978
+ const issues = [];
3979
+ for (const [key, value] of Object.entries(config)) {
3980
+ if (!allowed.includes(key)) {
3981
+ if (key in existing && JSON.stringify(existing[key]) === JSON.stringify(value)) continue;
3982
+ issues.push(
3983
+ `config.${key} cannot be set through the API for ${provider} accounts` + (allowed.length > 0 ? ` (allowed: ${allowed.join(", ")})` : "")
3984
+ );
3985
+ continue;
3986
+ }
3987
+ if (key === "baseUrl") {
3988
+ if (typeof value !== "string" || !isHttpUrl(value)) {
3989
+ issues.push("config.baseUrl must be an http(s) URL without embedded credentials");
3990
+ }
3991
+ } else if (key === "models") {
3992
+ if (!isStringList(value)) issues.push("config.models must be a list of model ids");
3993
+ } else if (key === "defaultModel") {
3994
+ if (typeof value !== "string" || !value) issues.push("config.defaultModel must be a string");
3995
+ } else if (key === "includeUsage" && typeof value !== "boolean") {
3996
+ issues.push("config.includeUsage must be true or false");
3997
+ }
3998
+ }
3999
+ return issues;
4000
+ }
4001
+ function isHttpUrl(value) {
4002
+ try {
4003
+ const url = new URL(value);
4004
+ return (url.protocol === "http:" || url.protocol === "https:") && !url.username && !url.password;
4005
+ } catch {
4006
+ return false;
4007
+ }
4008
+ }
4009
+ function mergeApiConfig(provider, existing, incoming) {
4010
+ const allowed = API_CONFIG_KEYS[provider];
4011
+ const kept = Object.fromEntries(
4012
+ Object.entries(existing).filter(([key]) => !allowed.includes(key))
4013
+ );
4014
+ return { ...kept, ...incoming };
4015
+ }
4016
+ function endpointOf(config) {
4017
+ return ENDPOINT_CONFIG_KEYS.map((key) => {
4018
+ const value = config[key];
4019
+ return typeof value === "string" ? value.trim().replace(/\/+$/, "") : "";
4020
+ }).join("\n");
4021
+ }
4022
+ function endpointChanged(existing, next) {
4023
+ return endpointOf(existing) !== endpointOf(next);
4024
+ }
4025
+ function endpointRebindIssue(existing, nextConfig, secretSupplied) {
4026
+ if (!nextConfig || secretSupplied || !existing.hasSecret) return void 0;
4027
+ if (!endpointChanged(existing.config, nextConfig)) return void 0;
4028
+ return "Changing the endpoint of an account that has a stored secret requires sending the secret again in the same request";
4029
+ }
3878
4030
  var EXPERIMENTAL_PROVIDER_KINDS = ["gemini-web"];
3879
4031
  var SUBSCRIPTION_PROVIDER_KINDS = [
3880
4032
  "claude-cli",
@@ -3883,7 +4035,7 @@ var SUBSCRIPTION_PROVIDER_KINDS = [
3883
4035
  "antigravity-cli",
3884
4036
  "gemini-web"
3885
4037
  ];
3886
- var VERSION = "0.4.0";
4038
+ var VERSION = "0.5.1";
3887
4039
  var QUOTA_WINDOWS = ["1m", "5h", "24h"];
3888
4040
  var WINDOW_MS = {
3889
4041
  "1m": 6e4,
@@ -8070,12 +8222,14 @@ function accountService(env = process.env) {
8070
8222
  }
8071
8223
  var OAUTH_PROVIDERS = ["google", "github"];
8072
8224
  var AccountError = class extends Error {
8073
- constructor(message, status) {
8225
+ constructor(message, status, code) {
8074
8226
  super(message);
8075
8227
  this.status = status;
8228
+ this.code = code;
8076
8229
  this.name = "AccountError";
8077
8230
  }
8078
8231
  status;
8232
+ code;
8079
8233
  };
8080
8234
  var userSchema = z8.object({
8081
8235
  id: z8.string().min(1),
@@ -8211,7 +8365,8 @@ var AccountClient = class {
8211
8365
  const detail = error.success ? error.data.error_description ?? error.data.msg ?? error.data.message : void 0;
8212
8366
  throw new AccountError(
8213
8367
  detail ?? `Sign-in failed (HTTP ${response.status}).`,
8214
- response.status
8368
+ response.status,
8369
+ error.success ? error.data.error : void 0
8215
8370
  );
8216
8371
  }
8217
8372
  return raw;
@@ -8299,6 +8454,16 @@ async function startAccountLogin(options) {
8299
8454
  }
8300
8455
  var AAD = Buffer.from("davecode-account", "utf8");
8301
8456
  var REFRESH_MARGIN_MS = 6e4;
8457
+ var LOCK_STALE_MS = 6e4;
8458
+ var LOCK_WAIT_MS = 3e4;
8459
+ var LOCK_POLL_MS = 50;
8460
+ var INVALID_TOKEN_CODES = /* @__PURE__ */ new Set([
8461
+ "invalid_grant",
8462
+ "refresh_token_not_found",
8463
+ "refresh_token_already_used",
8464
+ "session_not_found",
8465
+ "session_expired"
8466
+ ]);
8302
8467
  var fileSchema = z9.object({
8303
8468
  v: z9.literal(1),
8304
8469
  iv: z9.string(),
@@ -8375,22 +8540,67 @@ var AccountStore = class {
8375
8540
  * or when the service revoked the session; offline, an unexpired session is still returned.
8376
8541
  */
8377
8542
  async current(client, now = Date.now()) {
8378
- const session = this.load();
8379
- if (!session) return void 0;
8380
- if (session.expiresAt - REFRESH_MARGIN_MS > now) return session;
8543
+ const first = this.load();
8544
+ if (!first) return void 0;
8545
+ if (first.expiresAt - REFRESH_MARGIN_MS > now) return first;
8546
+ const release = await this.lock();
8381
8547
  try {
8382
- const fresh = await client.refresh(session.refreshToken);
8383
- this.save(fresh);
8384
- return fresh;
8385
- } catch (err) {
8386
- if (err instanceof AccountError && err.status !== void 0 && err.status < 500) {
8387
- this.clear();
8388
- return void 0;
8548
+ const session = this.load();
8549
+ if (!session) return void 0;
8550
+ if (session.expiresAt - REFRESH_MARGIN_MS > now) return session;
8551
+ try {
8552
+ const fresh = await client.refresh(session.refreshToken);
8553
+ this.save(fresh);
8554
+ return fresh;
8555
+ } catch (err) {
8556
+ if (isInvalidToken(err)) {
8557
+ if (this.load()?.refreshToken === session.refreshToken) this.clear();
8558
+ return this.load();
8559
+ }
8560
+ return session;
8389
8561
  }
8390
- return session;
8562
+ } finally {
8563
+ release();
8564
+ }
8565
+ }
8566
+ /**
8567
+ * A cross-process lock: a file created exclusively next to account.json. A lock older than
8568
+ * LOCK_STALE_MS belongs to a process that died, and is taken over. After LOCK_WAIT_MS the
8569
+ * refresh goes ahead without it (the service tolerates two refreshes racing).
8570
+ */
8571
+ async lock() {
8572
+ const path8 = `${this.path}.lock`;
8573
+ const owner = `${process.pid}:${randomBytes8(8).toString("hex")}`;
8574
+ mkdirSync7(dirname6(path8), { recursive: true });
8575
+ const deadline = Date.now() + LOCK_WAIT_MS;
8576
+ for (; ; ) {
8577
+ try {
8578
+ writeFileSync3(path8, owner, { flag: "wx", mode: 384 });
8579
+ return () => {
8580
+ try {
8581
+ if (readFileSync3(path8, "utf8") === owner) rmSync3(path8, { force: true });
8582
+ } catch {
8583
+ }
8584
+ };
8585
+ } catch (err) {
8586
+ if (err.code !== "EEXIST" && !isWindowsBusy(err))
8587
+ return () => void 0;
8588
+ }
8589
+ try {
8590
+ if (Date.now() - statSync2(path8).mtimeMs > LOCK_STALE_MS) rmSync3(path8, { force: true });
8591
+ } catch {
8592
+ }
8593
+ if (Date.now() >= deadline) return () => void 0;
8594
+ await new Promise((resolve5) => setTimeout(resolve5, LOCK_POLL_MS));
8391
8595
  }
8392
8596
  }
8393
8597
  };
8598
+ function isInvalidToken(err) {
8599
+ if (!(err instanceof AccountError) || err.status === void 0) return false;
8600
+ if (err.code) return INVALID_TOKEN_CODES.has(err.code);
8601
+ return err.status === 400 || err.status === 401;
8602
+ }
8603
+ var isWindowsBusy = (err) => ["EPERM", "EBUSY"].includes(err.code ?? "");
8394
8604
  var BRAND_MARK_SVG = '<svg viewBox="0 0 1214 1214" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M324.3 1096.5C324.6 1095.4 326.5 1088.3 328.5 1080.8C341.4 1031.3 375.8 953.4 419.7 874.0C445.8 826.9 448.4 822.4 471.8 783.0C501.5 733.1 530.1 687.6 556.4 648.5C563.7 637.5 569.7 628.4 569.5 628.2C569.4 628.1 563.1 631.7 555.4 636.3C513.8 661.4 468.0 683.9 411.0 707.5C337.2 737.9 321.7 744.7 296.0 757.2C241.9 783.5 199.1 809.4 152.0 844.3C119.5 868.4 98.8 885.9 71.3 912.4C64.0 919.5 58.0 925.0 58.0 924.7C58.0 923.1 64.7 902.4 68.8 891.0C116.1 762.1 215.0 644.2 351.0 554.8C385.2 532.3 411.6 516.9 445.5 499.4C521.4 460.4 589.4 435.7 636.0 430.0C676.8 425.1 715.1 437.0 743.9 463.6C763.4 481.6 779.0 509.9 785.2 538.5C787.7 550.1 788.7 579.7 787.1 592.5C782.6 626.4 769.7 661.5 746.5 702.0C717.7 752.5 675.6 806.9 627.0 856.4C599.5 884.4 575.9 905.4 542.5 931.6C510.0 957.1 500.7 964.2 432.5 1015.0C409.4 1032.3 382.6 1052.5 373.0 1060.0C354.5 1074.4 327.4 1095.5 325.1 1097.4C324.0 1098.3 323.8 1098.1 324.3 1096.5ZM783.2 1017.8C797.1 999.5 820.2 966.0 831.7 947.5C868.5 888.3 888.8 840.5 903.4 778.5C917.9 716.9 921.2 643.7 912.0 591.0C904.6 549.3 889.7 511.9 866.7 477.5C856.6 462.3 853.1 457.8 837.4 439.6C817.4 416.6 806.6 402.0 800.5 389.5C793.6 375.5 791.7 368.1 791.7 356.0C791.7 335.3 799.3 319.6 819.0 299.9C849.7 269.2 900.2 247.9 956.8 241.9C963.0 241.2 967.7 240.3 967.3 239.9C966.1 238.8 948.5 236.3 933.0 234.9C913.6 233.2 875.2 234.3 859.0 237.0C839.7 240.2 827.8 242.9 813.0 247.5C753.9 265.9 700.6 300.9 659.2 348.5C648.9 360.4 636.6 376.5 632.4 383.5C630.3 387.1 629.0 388.2 624.7 389.6C603.8 396.4 554.6 417.6 513.5 437.5C447.6 469.4 395.4 496.8 341.5 528.1L337.5 530.4L339.5 527.9C340.6 526.6 346.2 520.3 352.0 514.0C374.4 489.6 390.2 470.8 411.2 443.5C429.3 419.9 444.6 398.2 488.5 333.5C510.5 301.0 529.1 278.0 550.9 256.3C593.0 214.1 640.3 183.6 691.5 165.5C727.7 152.8 756.5 147.7 792.5 147.7C854.3 147.8 906.4 164.9 954.1 200.6C967.5 210.8 974.1 214.6 981.5 217.0C991.4 220.1 1022.1 231.9 1038.5 238.8C1061.9 248.7 1099.0 267.4 1097.7 268.7C1097.4 268.9 1089.3 268.5 1079.7 267.9C1033.7 264.7 984.5 274.1 946.2 293.4C932.3 300.4 915.0 313.0 903.4 324.5C881.0 346.6 870.0 369.6 870.0 394.4C870.0 413.7 876.8 433.1 894.7 465.2C908.8 490.3 914.6 501.1 922.8 517.5C961.3 594.3 980.0 662.5 980.0 725.4C980.0 770.9 967.3 818.9 944.4 860.4C927.6 890.8 912.0 911.0 882.5 940.6C856.5 966.7 828.5 989.7 797.0 1010.7C779.6 1022.2 780.0 1022.0 783.2 1017.8Z"/></svg>';
8395
8605
  var LOGO_ART = [
8396
8606
  " \u2584\u2584\u2584\u2588\u2584\u2584\u2584",
@@ -9462,6 +9672,8 @@ export {
9462
9672
  routeSchema,
9463
9673
  configSchema,
9464
9674
  ConfigError,
9675
+ PROJECT_SETTABLE_KEYS,
9676
+ filterProjectLayer,
9465
9677
  deepMerge,
9466
9678
  envLayer,
9467
9679
  loadConfig,
@@ -9486,6 +9698,12 @@ export {
9486
9698
  OpenAIProvider,
9487
9699
  OpenAICompatibleProvider,
9488
9700
  createProviders,
9701
+ ENDPOINT_CONFIG_KEYS,
9702
+ API_CONFIG_KEYS,
9703
+ apiConfigIssues,
9704
+ mergeApiConfig,
9705
+ endpointChanged,
9706
+ endpointRebindIssue,
9489
9707
  EXPERIMENTAL_PROVIDER_KINDS,
9490
9708
  SUBSCRIPTION_PROVIDER_KINDS,
9491
9709
  VERSION,
@@ -9605,4 +9823,4 @@ export {
9605
9823
  GoalStore,
9606
9824
  createGoalService
9607
9825
  };
9608
- //# sourceMappingURL=chunk-3Q2KCXLN.js.map
9826
+ //# sourceMappingURL=chunk-HENE4DA6.js.map