@agent-native/core 0.102.2 → 0.103.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 (205) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +12 -0
  3. package/corpus/core/docs/content/local-file-mode.mdx +99 -489
  4. package/corpus/core/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
  5. package/corpus/core/docs/content/locales/ar-SA/template-content.mdx +10 -24
  6. package/corpus/core/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
  7. package/corpus/core/docs/content/locales/de-DE/template-content.mdx +10 -24
  8. package/corpus/core/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
  9. package/corpus/core/docs/content/locales/es-ES/template-content.mdx +10 -24
  10. package/corpus/core/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
  11. package/corpus/core/docs/content/locales/fr-FR/template-content.mdx +10 -24
  12. package/corpus/core/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
  13. package/corpus/core/docs/content/locales/hi-IN/template-content.mdx +10 -24
  14. package/corpus/core/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
  15. package/corpus/core/docs/content/locales/ja-JP/template-content.mdx +10 -24
  16. package/corpus/core/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
  17. package/corpus/core/docs/content/locales/ko-KR/template-content.mdx +10 -24
  18. package/corpus/core/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
  19. package/corpus/core/docs/content/locales/pt-BR/template-content.mdx +10 -24
  20. package/corpus/core/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
  21. package/corpus/core/docs/content/locales/zh-CN/template-content.mdx +9 -24
  22. package/corpus/core/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
  23. package/corpus/core/docs/content/locales/zh-TW/template-content.mdx +9 -24
  24. package/corpus/core/docs/content/template-content.mdx +26 -42
  25. package/corpus/core/package.json +1 -1
  26. package/corpus/core/src/cli/content-local.ts +86 -18
  27. package/corpus/core/src/cli/index.ts +2 -2
  28. package/corpus/core/src/cli/skills-content/content-skill.ts +36 -25
  29. package/corpus/core/src/cli/skills.ts +47 -13
  30. package/corpus/core/src/local-artifacts/index.ts +65 -0
  31. package/corpus/core/src/vite/client.ts +2 -0
  32. package/corpus/templates/assets/app/components/library/GenerationPresetsPanel.tsx +325 -0
  33. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +12 -0
  34. package/corpus/templates/assets/app/i18n/zh-TW.ts +21 -0
  35. package/corpus/templates/assets/app/i18n-data.ts +215 -0
  36. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +19 -479
  37. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1 -1
  38. package/corpus/templates/assets/app/routes/brand-kits.$id_.settings.tsx +446 -0
  39. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +12 -2
  40. package/corpus/templates/clips/app/components/player/settings-panel.tsx +30 -12
  41. package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
  42. package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +22 -3
  43. package/corpus/templates/clips/app/i18n/ar-SA.ts +3 -2
  44. package/corpus/templates/clips/app/i18n/de-DE.ts +4 -2
  45. package/corpus/templates/clips/app/i18n/en-US.ts +3 -2
  46. package/corpus/templates/clips/app/i18n/es-ES.ts +4 -2
  47. package/corpus/templates/clips/app/i18n/fr-FR.ts +3 -2
  48. package/corpus/templates/clips/app/i18n/hi-IN.ts +3 -2
  49. package/corpus/templates/clips/app/i18n/ja-JP.ts +4 -1
  50. package/corpus/templates/clips/app/i18n/ko-KR.ts +3 -2
  51. package/corpus/templates/clips/app/i18n/pt-BR.ts +4 -2
  52. package/corpus/templates/clips/app/i18n/zh-CN.ts +3 -2
  53. package/corpus/templates/clips/app/i18n/zh-TW.ts +3 -2
  54. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +27 -1
  55. package/corpus/templates/clips/server/lib/share-password.ts +6 -3
  56. package/corpus/templates/content/.agents/skills/content/SKILL.md +26 -39
  57. package/corpus/templates/content/AGENTS.md +25 -30
  58. package/corpus/templates/content/actions/_builder-docs-client.ts +23 -0
  59. package/corpus/templates/content/actions/_content-database-source-adapters.ts +21 -3
  60. package/corpus/templates/content/actions/_content-files.ts +338 -0
  61. package/corpus/templates/content/actions/_content-space-access.ts +135 -0
  62. package/corpus/templates/content/actions/_content-spaces.ts +640 -0
  63. package/corpus/templates/content/actions/_database-source-utils.ts +90 -7
  64. package/corpus/templates/content/actions/_database-utils.ts +7 -1
  65. package/corpus/templates/content/actions/_local-file-documents.ts +0 -9
  66. package/corpus/templates/content/actions/_local-folder-source.ts +53 -0
  67. package/corpus/templates/content/actions/_property-utils.ts +9 -4
  68. package/corpus/templates/content/actions/add-database-item.ts +108 -65
  69. package/corpus/templates/content/actions/backfill-content-files.ts +22 -0
  70. package/corpus/templates/content/actions/connect-local-folder-source.ts +221 -0
  71. package/corpus/templates/content/actions/create-content-database.ts +129 -3
  72. package/corpus/templates/content/actions/create-document.ts +65 -55
  73. package/corpus/templates/content/actions/create-inline-content-database.ts +6 -1
  74. package/corpus/templates/content/actions/delete-content-database.ts +3 -0
  75. package/corpus/templates/content/actions/delete-document.ts +7 -11
  76. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +175 -0
  77. package/corpus/templates/content/actions/duplicate-database-item.ts +10 -0
  78. package/corpus/templates/content/actions/duplicate-database-items.ts +13 -0
  79. package/corpus/templates/content/actions/edit-document.ts +2 -28
  80. package/corpus/templates/content/actions/ensure-content-spaces.ts +26 -0
  81. package/corpus/templates/content/actions/export-content-source.ts +47 -9
  82. package/corpus/templates/content/actions/get-document.ts +0 -14
  83. package/corpus/templates/content/actions/import-content-source.ts +43 -3
  84. package/corpus/templates/content/actions/list-content-spaces.ts +103 -0
  85. package/corpus/templates/content/actions/list-documents.ts +12 -9
  86. package/corpus/templates/content/actions/move-document.ts +3 -14
  87. package/corpus/templates/content/actions/pull-document.ts +0 -23
  88. package/corpus/templates/content/actions/remove-local-file-source.ts +50 -29
  89. package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +442 -0
  90. package/corpus/templates/content/actions/reveal-local-source-file.ts +6 -17
  91. package/corpus/templates/content/actions/search-documents.ts +0 -34
  92. package/corpus/templates/content/actions/share-local-file-document.ts +25 -1
  93. package/corpus/templates/content/actions/submit-content-database-form.ts +12 -0
  94. package/corpus/templates/content/actions/sync-local-folder-source.ts +731 -0
  95. package/corpus/templates/content/actions/sync-manifest-local-folder-source.ts +123 -0
  96. package/corpus/templates/content/actions/update-content-database-view.ts +10 -1
  97. package/corpus/templates/content/actions/update-document.ts +0 -15
  98. package/corpus/templates/content/actions/view-screen.ts +2 -50
  99. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +76 -62
  100. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +70 -22
  101. package/corpus/templates/content/app/components/editor/database/grouping.ts +2 -1
  102. package/corpus/templates/content/app/components/editor/database/settings.tsx +23 -20
  103. package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
  104. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +330 -0
  105. package/corpus/templates/content/app/components/editor/database/types.ts +1 -0
  106. package/corpus/templates/content/app/components/editor/database/view-config.ts +3 -1
  107. package/corpus/templates/content/app/components/layout/Layout.tsx +25 -1
  108. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +209 -113
  109. package/corpus/templates/content/app/components/sidebar/select-content-space.ts +31 -0
  110. package/corpus/templates/content/app/hooks/use-content-database.ts +16 -0
  111. package/corpus/templates/content/app/hooks/use-content-spaces.ts +39 -0
  112. package/corpus/templates/content/app/i18n/zh-TW.ts +11 -0
  113. package/corpus/templates/content/app/i18n-data.ts +75 -0
  114. package/corpus/templates/content/app/routes/_app.local-files.tsx +174 -65
  115. package/corpus/templates/content/changelog/2026-07-16-content-callouts-can-now-be-reviewed-and-published-to-builder.md +6 -0
  116. package/corpus/templates/content/changelog/2026-07-16-heading-5-and-heading-6-are-available-from-the-editor-slash-.md +6 -0
  117. package/corpus/templates/content/changelog/2026-07-16-organize-every-workspace-through-a-customizable-files-databa.md +6 -0
  118. package/corpus/templates/content/parity/matrix.md +2 -1
  119. package/corpus/templates/content/parity/matrix.ts +37 -1
  120. package/corpus/templates/content/scripts/dev-database.mjs +7 -5
  121. package/corpus/templates/content/server/db/schema.ts +113 -40
  122. package/corpus/templates/content/server/lib/document-context.ts +15 -1
  123. package/corpus/templates/content/server/lib/notion-sync.ts +6 -0
  124. package/corpus/templates/content/server/plugins/db.ts +153 -0
  125. package/corpus/templates/content/shared/api.ts +17 -3
  126. package/corpus/templates/content/shared/builder-mdx.ts +163 -16
  127. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +59 -0
  128. package/corpus/templates/design/AGENTS.md +6 -1
  129. package/corpus/templates/design/actions/create-design-from-template.ts +19 -7
  130. package/corpus/templates/design/actions/list-design-templates.ts +69 -21
  131. package/corpus/templates/design/actions/navigate.ts +2 -2
  132. package/corpus/templates/design/actions/view-screen.ts +15 -2
  133. package/corpus/templates/design/agent-native.app-skill.json +5 -0
  134. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +281 -56
  135. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +14 -2
  136. package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -2
  137. package/corpus/templates/design/app/i18n/zh-TW.ts +3 -3
  138. package/corpus/templates/design/app/i18n-data.ts +21 -2
  139. package/corpus/templates/design/app/i18n-template-feature.ts +218 -10
  140. package/corpus/templates/design/app/pages/Index.tsx +195 -24
  141. package/corpus/templates/design/app/pages/Templates.tsx +144 -55
  142. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +12 -1
  143. package/corpus/templates/design/changelog/2026-07-14-templates-now-appear-in-the-new-design-picker-with-previews-.md +6 -0
  144. package/corpus/templates/design/changelog/2026-07-15-templates-now-open-immediately-after-copying-even-while-the-.md +6 -0
  145. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
  146. package/corpus/templates/mail/AGENTS.md +23 -18
  147. package/corpus/templates/mail/actions/bulk-archive.ts +32 -28
  148. package/corpus/templates/mail/actions/mark-read.ts +131 -20
  149. package/corpus/templates/mail/actions/mark-thread-read.ts +1 -1
  150. package/corpus/templates/mail/actions/move-email.ts +30 -24
  151. package/corpus/templates/mail/actions/send-email.ts +73 -68
  152. package/corpus/templates/mail/changelog/2026-07-15-unread-inbox-cleanup-now-completes-reliably.md +6 -0
  153. package/corpus/templates/mail/server/handlers/emails.ts +218 -209
  154. package/corpus/templates/mail/server/lib/bulk-mark-read.ts +30 -0
  155. package/corpus/templates/mail/server/lib/email-state.ts +198 -113
  156. package/corpus/templates/mail/server/lib/google-auth.ts +218 -25
  157. package/corpus/templates/mail/server/lib/jobs.ts +69 -74
  158. package/corpus/templates/mail/server/lib/local-email-store.ts +265 -0
  159. package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
  160. package/dist/cli/content-local.d.ts +1 -0
  161. package/dist/cli/content-local.d.ts.map +1 -1
  162. package/dist/cli/content-local.js +75 -17
  163. package/dist/cli/content-local.js.map +1 -1
  164. package/dist/cli/index.js +2 -2
  165. package/dist/cli/index.js.map +1 -1
  166. package/dist/cli/skills-content/content-skill.d.ts +1 -1
  167. package/dist/cli/skills-content/content-skill.d.ts.map +1 -1
  168. package/dist/cli/skills-content/content-skill.js +36 -25
  169. package/dist/cli/skills-content/content-skill.js.map +1 -1
  170. package/dist/cli/skills.d.ts.map +1 -1
  171. package/dist/cli/skills.js +47 -14
  172. package/dist/cli/skills.js.map +1 -1
  173. package/dist/collab/struct-routes.d.ts +1 -1
  174. package/dist/local-artifacts/index.d.ts +10 -0
  175. package/dist/local-artifacts/index.d.ts.map +1 -1
  176. package/dist/local-artifacts/index.js +39 -0
  177. package/dist/local-artifacts/index.js.map +1 -1
  178. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  179. package/dist/vite/client.d.ts.map +1 -1
  180. package/dist/vite/client.js +2 -0
  181. package/dist/vite/client.js.map +1 -1
  182. package/docs/content/local-file-mode.mdx +99 -489
  183. package/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
  184. package/docs/content/locales/ar-SA/template-content.mdx +10 -24
  185. package/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
  186. package/docs/content/locales/de-DE/template-content.mdx +10 -24
  187. package/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
  188. package/docs/content/locales/es-ES/template-content.mdx +10 -24
  189. package/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
  190. package/docs/content/locales/fr-FR/template-content.mdx +10 -24
  191. package/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
  192. package/docs/content/locales/hi-IN/template-content.mdx +10 -24
  193. package/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
  194. package/docs/content/locales/ja-JP/template-content.mdx +10 -24
  195. package/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
  196. package/docs/content/locales/ko-KR/template-content.mdx +10 -24
  197. package/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
  198. package/docs/content/locales/pt-BR/template-content.mdx +10 -24
  199. package/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
  200. package/docs/content/locales/zh-CN/template-content.mdx +9 -24
  201. package/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
  202. package/docs/content/locales/zh-TW/template-content.mdx +9 -24
  203. package/docs/content/template-content.mdx +26 -42
  204. package/package.json +1 -1
  205. package/corpus/templates/assets/app/components/library/EditLibraryDialog.tsx +0 -126
@@ -0,0 +1,265 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+
3
+ import { getDbExec, isPostgres } from "@agent-native/core/db";
4
+ import {
5
+ getSettingsEmitter,
6
+ getUserSetting,
7
+ type StoreWriteOptions,
8
+ } from "@agent-native/core/settings";
9
+ import type { EmailMessage } from "@shared/types.js";
10
+ import { nanoid } from "nanoid";
11
+
12
+ const localEmailMutationLocks = new Map<string, Promise<unknown>>();
13
+ const LOCK_SETTING_KEY = "local-emails-mutation-lock";
14
+ const LOCK_LEASE_MS = 60_000;
15
+ const LOCK_ACQUIRE_TIMEOUT_MS = 10_000;
16
+ const LOCK_RETRY_MS = 25;
17
+ const LOCK_RELEASE_ATTEMPTS = 3;
18
+ const MAX_MUTATION_ATTEMPTS = 8;
19
+
20
+ interface LocalEmailMutationContext {
21
+ ownerKey: string;
22
+ expectedMailboxRaw?: string | null;
23
+ }
24
+
25
+ class LocalEmailWriteConflict extends Error {}
26
+
27
+ const localEmailMutationContext =
28
+ new AsyncLocalStorage<LocalEmailMutationContext>();
29
+
30
+ interface LocalEmailLease {
31
+ token: string;
32
+ expiresAt: number;
33
+ }
34
+
35
+ function settingsTable(): string {
36
+ return isPostgres() ? "public.settings" : "settings";
37
+ }
38
+
39
+ function lockStorageKey(ownerEmail: string): string {
40
+ return `u:${ownerEmail.toLowerCase()}:${LOCK_SETTING_KEY}`;
41
+ }
42
+
43
+ function mailboxStorageKey(ownerEmail: string): string {
44
+ return `u:${ownerEmail}:local-emails`;
45
+ }
46
+
47
+ function parseLease(raw: string | null): LocalEmailLease | null {
48
+ if (!raw) return null;
49
+ try {
50
+ const parsed = JSON.parse(raw) as Partial<LocalEmailLease>;
51
+ if (
52
+ typeof parsed.token === "string" &&
53
+ typeof parsed.expiresAt === "number"
54
+ ) {
55
+ return { token: parsed.token, expiresAt: parsed.expiresAt };
56
+ }
57
+ } catch {}
58
+ return null;
59
+ }
60
+
61
+ async function readLeaseRow(
62
+ ownerEmail: string,
63
+ ): Promise<{ raw: string | null; lease: LocalEmailLease | null }> {
64
+ // Initialize the framework-owned settings table, then bypass the request
65
+ // settings cache so every lease attempt sees the latest committed owner row.
66
+ await getUserSetting(ownerEmail, LOCK_SETTING_KEY);
67
+ const { rows } = await getDbExec().execute({
68
+ sql: `SELECT value FROM ${settingsTable()} WHERE key = ?`,
69
+ args: [lockStorageKey(ownerEmail)],
70
+ });
71
+ const raw = rows.length ? String(rows[0].value ?? rows[0][0]) : null;
72
+ return { raw, lease: parseLease(raw) };
73
+ }
74
+
75
+ async function compareAndSwapLease(
76
+ ownerEmail: string,
77
+ expectedRaw: string | null,
78
+ nextRaw: string,
79
+ ): Promise<boolean> {
80
+ const client = getDbExec();
81
+ const key = lockStorageKey(ownerEmail);
82
+ if (expectedRaw === null) {
83
+ const result = await client.execute({
84
+ sql: isPostgres()
85
+ ? `INSERT INTO ${settingsTable()} (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT (key) DO NOTHING`
86
+ : `INSERT OR IGNORE INTO ${settingsTable()} (key, value, updated_at) VALUES (?, ?, ?)`,
87
+ args: [key, nextRaw, Date.now()],
88
+ });
89
+ return result.rowsAffected === 1;
90
+ }
91
+ const result = await client.execute({
92
+ sql: `UPDATE ${settingsTable()} SET value = ?, updated_at = ? WHERE key = ? AND value = ?`,
93
+ args: [nextRaw, Date.now(), key, expectedRaw],
94
+ });
95
+ return result.rowsAffected === 1;
96
+ }
97
+
98
+ async function acquireDatabaseLease(ownerEmail: string): Promise<string> {
99
+ const token = nanoid(16);
100
+ const deadline = Date.now() + LOCK_ACQUIRE_TIMEOUT_MS;
101
+ while (Date.now() < deadline) {
102
+ const { raw, lease } = await readLeaseRow(ownerEmail);
103
+ const now = Date.now();
104
+ if (!lease || lease.expiresAt <= now) {
105
+ const nextRaw = JSON.stringify({
106
+ token,
107
+ expiresAt: now + LOCK_LEASE_MS,
108
+ } satisfies LocalEmailLease);
109
+ if (await compareAndSwapLease(ownerEmail, raw, nextRaw)) return nextRaw;
110
+ }
111
+ await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
112
+ }
113
+ throw new Error("Timed out waiting for local mailbox mutation lock");
114
+ }
115
+
116
+ async function releaseDatabaseLease(
117
+ ownerEmail: string,
118
+ leaseRaw: string,
119
+ ): Promise<void> {
120
+ for (let attempt = 0; attempt < LOCK_RELEASE_ATTEMPTS; attempt += 1) {
121
+ try {
122
+ await getDbExec().execute({
123
+ sql: `DELETE FROM ${settingsTable()} WHERE key = ? AND value = ?`,
124
+ args: [lockStorageKey(ownerEmail), leaseRaw],
125
+ });
126
+ return;
127
+ } catch (error) {
128
+ if (attempt === LOCK_RELEASE_ATTEMPTS - 1) throw error;
129
+ await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
130
+ }
131
+ }
132
+ }
133
+
134
+ async function readMailboxRow(ownerEmail: string): Promise<string | null> {
135
+ // Initialize the settings table, then bypass its request cache so CAS retry
136
+ // attempts always compare against the latest committed mailbox snapshot.
137
+ await getUserSetting(ownerEmail, "local-emails");
138
+ const { rows } = await getDbExec().execute({
139
+ sql: `SELECT value FROM ${settingsTable()} WHERE key = ?`,
140
+ args: [mailboxStorageKey(ownerEmail)],
141
+ });
142
+ return rows.length ? String(rows[0].value ?? rows[0][0]) : null;
143
+ }
144
+
145
+ function parseEmails(raw: string | null): EmailMessage[] {
146
+ if (!raw) return [];
147
+ try {
148
+ const data = JSON.parse(raw);
149
+ return Array.isArray(data?.emails) ? data.emails : [];
150
+ } catch {
151
+ return [];
152
+ }
153
+ }
154
+
155
+ async function compareAndSwapMailbox(
156
+ ownerEmail: string,
157
+ expectedRaw: string | null,
158
+ nextRaw: string,
159
+ ): Promise<boolean> {
160
+ const client = getDbExec();
161
+ const key = mailboxStorageKey(ownerEmail);
162
+ if (expectedRaw === null) {
163
+ const result = await client.execute({
164
+ sql: isPostgres()
165
+ ? `INSERT INTO ${settingsTable()} (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT (key) DO NOTHING`
166
+ : `INSERT OR IGNORE INTO ${settingsTable()} (key, value, updated_at) VALUES (?, ?, ?)`,
167
+ args: [key, nextRaw, Date.now()],
168
+ });
169
+ return result.rowsAffected === 1;
170
+ }
171
+ const result = await client.execute({
172
+ sql: `UPDATE ${settingsTable()} SET value = ?, updated_at = ? WHERE key = ? AND value = ?`,
173
+ args: [nextRaw, Date.now(), key, expectedRaw],
174
+ });
175
+ return result.rowsAffected === 1;
176
+ }
177
+
178
+ /**
179
+ * Serialize read-modify-write operations on an owner's synthetic mailbox.
180
+ * Local mail is one JSON document, so every writer must participate to avoid
181
+ * replacing a concurrent writer's newer snapshot.
182
+ */
183
+ export function withLocalEmailMutationLock<T>(
184
+ ownerEmail: string,
185
+ mutate: () => Promise<T>,
186
+ ): Promise<T> {
187
+ const key = ownerEmail.toLowerCase();
188
+ const previous = localEmailMutationLocks.get(key) ?? Promise.resolve();
189
+ const runWithDatabaseLease = async () => {
190
+ for (let attempt = 0; attempt < MAX_MUTATION_ATTEMPTS; attempt += 1) {
191
+ const leaseRaw = await acquireDatabaseLease(ownerEmail);
192
+ try {
193
+ return await localEmailMutationContext.run({ ownerKey: key }, mutate);
194
+ } catch (error) {
195
+ if (!(error instanceof LocalEmailWriteConflict)) throw error;
196
+ } finally {
197
+ try {
198
+ await releaseDatabaseLease(ownerEmail, leaseRaw);
199
+ } catch (error) {
200
+ // The lease expires and is token-scoped, so a transient cleanup
201
+ // failure must not turn an already committed mailbox write into an
202
+ // ambiguous failure that callers may retry.
203
+ console.warn(
204
+ "[local-email-store] failed to release mutation lease; it will expire",
205
+ error,
206
+ );
207
+ }
208
+ }
209
+ }
210
+ throw new Error("Local mailbox changed too many times; please retry");
211
+ };
212
+ const next = previous.then(runWithDatabaseLease, runWithDatabaseLease);
213
+ localEmailMutationLocks.set(key, next);
214
+ const cleanup = () => {
215
+ if (localEmailMutationLocks.get(key) === next) {
216
+ localEmailMutationLocks.delete(key);
217
+ }
218
+ };
219
+ next.then(cleanup, cleanup);
220
+ return next;
221
+ }
222
+
223
+ export async function readLocalEmails(
224
+ ownerEmail: string,
225
+ ): Promise<EmailMessage[]> {
226
+ const raw = await readMailboxRow(ownerEmail);
227
+ const context = localEmailMutationContext.getStore();
228
+ if (context?.ownerKey === ownerEmail.toLowerCase()) {
229
+ context.expectedMailboxRaw = raw;
230
+ }
231
+ return parseEmails(raw);
232
+ }
233
+
234
+ export async function writeLocalEmails(
235
+ ownerEmail: string,
236
+ emails: EmailMessage[],
237
+ options?: StoreWriteOptions,
238
+ ): Promise<void> {
239
+ const context = localEmailMutationContext.getStore();
240
+ if (
241
+ context?.ownerKey !== ownerEmail.toLowerCase() ||
242
+ context.expectedMailboxRaw === undefined
243
+ ) {
244
+ throw new Error(
245
+ "Local mailbox writes must run inside withLocalEmailMutationLock after a fresh read",
246
+ );
247
+ }
248
+ const nextRaw = JSON.stringify({ emails });
249
+ if (
250
+ !(await compareAndSwapMailbox(
251
+ ownerEmail,
252
+ context.expectedMailboxRaw,
253
+ nextRaw,
254
+ ))
255
+ ) {
256
+ throw new LocalEmailWriteConflict("Local mailbox changed concurrently");
257
+ }
258
+ context.expectedMailboxRaw = nextRaw;
259
+ getSettingsEmitter().emit("settings", {
260
+ source: "settings",
261
+ type: "change",
262
+ key: mailboxStorageKey(ownerEmail),
263
+ ...(options?.requestSource && { requestSource: options.requestSource }),
264
+ });
265
+ }
@@ -105,6 +105,12 @@ Available operations:
105
105
  - Queue teammate-requested drafts for organization members to review and send
106
106
  - Navigate the UI to specific views or threads
107
107
 
108
+ ## Reliable Mail Mutations
109
+
110
+ For requests to mark all, every, or many unread conversations read in one account, call \`mark-read\` exactly once with \`scope: "all-unread"\` and the exact \`accountEmail\`. Resolve conversations the user wants preserved to exact thread IDs and pass them in \`excludeThreadIds\`. Never loop \`mark-thread-read\` for broad cleanup. Report the returned matched, excluded, changed, and remaining-unread counts; completeness comes from the action's verification read, not merely from a successful tool submission.
111
+
112
+ Use \`mark-thread-read\` for one specific conversation only.
113
+
108
114
  ## Provider APIs Are Escape Hatches, Not Limits
109
115
 
110
116
  Provider-specific Mail actions are shortcuts, not limits. If a first-class action cannot express the exact Gmail, Google Calendar, or CRM endpoint, search query, label/filter setting, request body, pagination mode, account id, payload shape, or API version needed, call \`provider-api-catalog\` and \`provider-api-docs\` as needed, then call \`provider-api-request\` against the provider's real HTTP API.
@@ -12,6 +12,7 @@ export interface ContentLocalLaunchPlan {
12
12
  manifestPath: string;
13
13
  rootPath: string;
14
14
  filePath?: string;
15
+ connectionId: string;
15
16
  url: string;
16
17
  command: string;
17
18
  args: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"content-local.d.ts","sourceRoot":"","sources":["../../src/cli/content-local.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAoCD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,gBAAgB,CA8CtE;AAqGD,wBAAgB,sBAAsB,CAAC,GAAG,SAAgB,UAmBzD;AAOD,wBAAsB,yBAAyB,CAAC,OAAO,EAAE;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA+ClC;AAgCD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,mBA+CnD"}
1
+ {"version":3,"file":"content-local.d.ts","sourceRoot":"","sources":["../../src/cli/content-local.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AA4CD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,gBAAgB,CA8CtE;AAuJD,wBAAgB,sBAAsB,CAAC,GAAG,SAAgB,UAmBzD;AAQD,wBAAsB,yBAAyB,CAAC,OAAO,EAAE;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAkDlC;AAgCD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,mBAmDnD"}
@@ -1,4 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { createHash } from "node:crypto";
2
3
  import fsSync from "node:fs";
3
4
  import fs from "node:fs/promises";
4
5
  import path from "node:path";
@@ -7,7 +8,7 @@ const CONTENT_APP_ID = "content";
7
8
  const MANIFEST_FILE = "agent-native.json";
8
9
  const DEFAULT_PORT = 8083;
9
10
  const DEFAULT_PROFILE = "docs/no-bookkeeping";
10
- const LOCAL_FILE_ID_PREFIX = "local-file:";
11
+ const LOCAL_FOLDER_CONNECTION_PREFIX = "local-folder:";
11
12
  function normalizeSlash(value) {
12
13
  return value.replace(/\\/g, "/");
13
14
  }
@@ -20,8 +21,20 @@ function titleFromPath(value) {
20
21
  .trim()
21
22
  .replace(/\b\w/g, (char) => char.toUpperCase()) || "Content");
22
23
  }
23
- function encodeLocalFileId(filePath) {
24
- return `${LOCAL_FILE_ID_PREFIX}${Buffer.from(filePath, "utf8").toString("base64url")}`;
24
+ function localFolderConnectionId(absoluteRootPath) {
25
+ let canonicalRootPath = path.resolve(absoluteRootPath);
26
+ try {
27
+ canonicalRootPath = fsSync.realpathSync(canonicalRootPath);
28
+ }
29
+ catch (error) {
30
+ if (error.code !== "ENOENT")
31
+ throw error;
32
+ }
33
+ const digest = createHash("sha256")
34
+ .update(canonicalRootPath)
35
+ .digest("base64url")
36
+ .slice(0, 24);
37
+ return `${LOCAL_FOLDER_CONNECTION_PREFIX}${digest}`;
25
38
  }
26
39
  function isRecord(value) {
27
40
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -105,6 +118,7 @@ function resolveTarget(target, cwd) {
105
118
  : undefined;
106
119
  return {
107
120
  absoluteTarget,
121
+ absoluteRootPath: targetDirectory,
108
122
  isFile: stat.isFile(),
109
123
  workspaceRoot,
110
124
  rootPath: normalizeSlash(rootPath),
@@ -127,7 +141,7 @@ async function readManifest(manifestPath) {
127
141
  throw error;
128
142
  }
129
143
  }
130
- function upsertContentManifest(manifest, rootPath, options) {
144
+ function upsertContentManifest(manifest, workspaceRoot, rootPath, options) {
131
145
  const next = {
132
146
  ...manifest,
133
147
  version: manifest.version ?? 1,
@@ -154,14 +168,50 @@ function upsertContentManifest(manifest, rootPath, options) {
154
168
  ? existingRoot.extensions
155
169
  : [".md", ".mdx"],
156
170
  ...(profile ? { profile } : {}),
157
- ...(include && rootIndex === -1 ? { include } : {}),
171
+ ...(include && (rootIndex === -1 || Array.isArray(existingRoot?.include))
172
+ ? {
173
+ include: Array.from(new Set([
174
+ ...(Array.isArray(existingRoot?.include)
175
+ ? existingRoot.include.filter((value) => typeof value === "string")
176
+ : []),
177
+ ...include,
178
+ ])),
179
+ }
180
+ : {}),
181
+ source: {
182
+ ...(isRecord(existingRoot?.source) ? existingRoot.source : {}),
183
+ type: "local-folder",
184
+ connectionId: options.connectionId,
185
+ truthPolicy: "source_primary",
186
+ },
158
187
  };
159
- const roots = rootIndex === -1
188
+ let roots = rootIndex === -1
160
189
  ? [...existingRoots, root]
161
190
  : existingRoots.map((entry, index) => index === rootIndex ? { ...entry, ...root } : entry);
191
+ if (existingContent.mode === "local-files") {
192
+ roots = roots.map((entry) => {
193
+ if (entry === root || typeof entry.path !== "string")
194
+ return entry;
195
+ const existingSource = isRecord(entry.source) ? entry.source : {};
196
+ return {
197
+ ...entry,
198
+ source: {
199
+ ...existingSource,
200
+ type: "local-folder",
201
+ connectionId: typeof existingSource.connectionId === "string" &&
202
+ existingSource.connectionId
203
+ ? existingSource.connectionId
204
+ : localFolderConnectionId(path.resolve(workspaceRoot, entry.path)),
205
+ truthPolicy: typeof existingSource.truthPolicy === "string"
206
+ ? existingSource.truthPolicy
207
+ : "source_primary",
208
+ },
209
+ };
210
+ });
211
+ }
212
+ delete existingContent.mode;
162
213
  apps[CONTENT_APP_ID] = {
163
214
  ...existingContent,
164
- mode: "local-files",
165
215
  roots,
166
216
  hide: Array.isArray(existingContent.hide)
167
217
  ? existingContent.hide
@@ -184,15 +234,19 @@ export function findContentTemplateDir(cwd = process.cwd()) {
184
234
  candidates.push(path.resolve(cliDir, "../../../..", "templates/content"));
185
235
  return (candidates.find((candidate) => fsSync.existsSync(path.join(candidate, "package.json"))) ?? null);
186
236
  }
187
- function launchUrl(port, filePath) {
188
- const base = `http://127.0.0.1:${port}`;
189
- return filePath ? `${base}/page/${encodeLocalFileId(filePath)}` : base;
237
+ function launchUrl(port, connectionId, filePath) {
238
+ const params = new URLSearchParams({ connectionId });
239
+ if (filePath)
240
+ params.set("file", filePath);
241
+ return `http://127.0.0.1:${port}/local-files?${params.toString()}`;
190
242
  }
191
243
  export async function prepareContentLocalLaunch(options) {
192
244
  const cwd = path.resolve(options.cwd ?? process.cwd());
193
245
  const target = resolveTarget(options.target, cwd);
194
246
  const manifestPath = path.join(target.workspaceRoot, MANIFEST_FILE);
195
- const manifest = upsertContentManifest(await readManifest(manifestPath), target.rootPath, {
247
+ const connectionId = localFolderConnectionId(target.absoluteRootPath);
248
+ const manifest = upsertContentManifest(await readManifest(manifestPath), target.workspaceRoot, target.rootPath, {
249
+ connectionId,
196
250
  filePath: target.filePath,
197
251
  profile: options.profile,
198
252
  });
@@ -208,7 +262,8 @@ export async function prepareContentLocalLaunch(options) {
208
262
  manifestPath,
209
263
  rootPath: target.rootPath,
210
264
  filePath: target.filePath,
211
- url: launchUrl(options.port ?? DEFAULT_PORT, target.filePath),
265
+ connectionId,
266
+ url: launchUrl(options.port ?? DEFAULT_PORT, connectionId, target.filePath),
212
267
  command: "corepack",
213
268
  args: [
214
269
  "pnpm",
@@ -222,7 +277,6 @@ export async function prepareContentLocalLaunch(options) {
222
277
  "--strictPort",
223
278
  ],
224
279
  env: {
225
- AGENT_NATIVE_MODE: "local-files",
226
280
  AGENT_NATIVE_MANIFEST_PATH: manifestPath,
227
281
  },
228
282
  manifest,
@@ -248,7 +302,7 @@ function printHelp() {
248
302
  agent-native content <file-or-folder> [options]
249
303
 
250
304
  Options:
251
- --profile <name> Local file profile, for example docs/no-bookkeeping
305
+ --profile <name> Local folder profile, for example docs/no-bookkeeping
252
306
  --port <number> Dev server port (default ${DEFAULT_PORT})
253
307
  --open Open the target in the browser (default)
254
308
  --no-open Start the server without opening the browser
@@ -257,9 +311,13 @@ Options:
257
311
  }
258
312
  export async function runContentLocal(argv) {
259
313
  const parsed = parseContentLocalArgs(argv);
260
- if (parsed.help || !parsed.target) {
314
+ if (parsed.help) {
315
+ printHelp();
316
+ return 0;
317
+ }
318
+ if (!parsed.target) {
261
319
  printHelp();
262
- return parsed.target ? 0 : 1;
320
+ return 1;
263
321
  }
264
322
  const plan = await prepareContentLocalLaunch({
265
323
  target: parsed.target,
@@ -271,7 +329,7 @@ export async function runContentLocal(argv) {
271
329
  console.log(JSON.stringify(plan, null, 2));
272
330
  return 0;
273
331
  }
274
- console.log(`Content local files: ${plan.rootPath}`);
332
+ console.log(`Content folder source: ${plan.rootPath}`);
275
333
  console.log(`Manifest: ${plan.manifestPath}`);
276
334
  console.log(`Opening: ${plan.url}`);
277
335
  if (parsed.open) {
@@ -1 +1 @@
1
- {"version":3,"file":"content-local.js","sourceRoot":"","sources":["../../src/cli/content-local.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport fsSync from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst CONTENT_APP_ID = \"content\";\nconst MANIFEST_FILE = \"agent-native.json\";\nconst DEFAULT_PORT = 8083;\nconst DEFAULT_PROFILE = \"docs/no-bookkeeping\";\nconst LOCAL_FILE_ID_PREFIX = \"local-file:\";\n\nexport interface ContentLocalArgs {\n target?: string;\n open: boolean;\n port: number;\n profile?: string;\n dryRun: boolean;\n json: boolean;\n help: boolean;\n}\n\nexport interface ContentLocalLaunchPlan {\n workspaceRoot: string;\n manifestPath: string;\n rootPath: string;\n filePath?: string;\n url: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n manifest: Record<string, unknown>;\n}\n\nfunction normalizeSlash(value: string) {\n return value.replace(/\\\\/g, \"/\");\n}\n\nfunction titleFromPath(value: string) {\n return (\n path\n .basename(value)\n .replace(/\\.(mdx?|markdown)$/i, \"\")\n .replace(/[-_]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim()\n .replace(/\\b\\w/g, (char) => char.toUpperCase()) || \"Content\"\n );\n}\n\nfunction encodeLocalFileId(filePath: string) {\n return `${LOCAL_FILE_ID_PREFIX}${Buffer.from(filePath, \"utf8\").toString(\n \"base64url\",\n )}`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction stringFlagValue(argv: string[], index: number, flag: string) {\n const value = argv[index + 1];\n if (!value || value.startsWith(\"-\")) {\n throw new Error(`${flag} requires a value`);\n }\n return value;\n}\n\nexport function parseContentLocalArgs(argv: string[]): ContentLocalArgs {\n const args = argv[0] === \"local-files\" ? argv.slice(1) : argv;\n const parsed: ContentLocalArgs = {\n open: true,\n port: DEFAULT_PORT,\n dryRun: false,\n json: false,\n help: false,\n };\n\n for (let index = 0; index < args.length; index += 1) {\n const arg = args[index]!;\n if (arg === \"help\" || arg === \"--help\" || arg === \"-h\") {\n parsed.help = true;\n } else if (arg === \"--open\") {\n parsed.open = true;\n } else if (arg === \"--no-open\") {\n parsed.open = false;\n } else if (arg === \"--dry-run\") {\n parsed.dryRun = true;\n } else if (arg === \"--json\") {\n parsed.json = true;\n } else if (arg === \"--port\") {\n parsed.port = Number.parseInt(stringFlagValue(args, index, arg), 10);\n index += 1;\n } else if (arg.startsWith(\"--port=\")) {\n parsed.port = Number.parseInt(arg.slice(\"--port=\".length), 10);\n } else if (arg === \"--profile\") {\n parsed.profile = stringFlagValue(args, index, arg);\n index += 1;\n } else if (arg.startsWith(\"--profile=\")) {\n parsed.profile = arg.slice(\"--profile=\".length);\n } else if (arg.startsWith(\"-\")) {\n throw new Error(`Unknown option: ${arg}`);\n } else if (!parsed.target) {\n parsed.target = arg;\n } else {\n throw new Error(`Unexpected argument: ${arg}`);\n }\n }\n\n if (!Number.isInteger(parsed.port) || parsed.port <= 0) {\n throw new Error(\"--port must be a positive integer\");\n }\n\n return parsed;\n}\n\nfunction resolveTarget(target: string, cwd: string) {\n const absoluteTarget = path.resolve(cwd, target);\n const stat = fsSync.statSync(absoluteTarget);\n const targetDirectory = stat.isDirectory()\n ? absoluteTarget\n : path.dirname(absoluteTarget);\n let workspaceRoot = path.resolve(cwd);\n let rootPath = normalizeSlash(path.relative(workspaceRoot, targetDirectory));\n\n if (!rootPath || rootPath === \".\" || rootPath.startsWith(\"../\")) {\n workspaceRoot = path.dirname(targetDirectory);\n rootPath = path.basename(targetDirectory);\n }\n\n const filePath = stat.isFile()\n ? normalizeSlash(path.posix.join(rootPath, path.basename(absoluteTarget)))\n : undefined;\n\n return {\n absoluteTarget,\n isFile: stat.isFile(),\n workspaceRoot,\n rootPath: normalizeSlash(rootPath),\n filePath,\n };\n}\n\nasync function readManifest(\n manifestPath: string,\n): Promise<Record<string, unknown>> {\n try {\n const raw = await fs.readFile(manifestPath, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (!isRecord(parsed)) {\n throw new Error(`${MANIFEST_FILE} must contain a JSON object`);\n }\n return parsed;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return { version: 1 };\n }\n throw error;\n }\n}\n\nfunction upsertContentManifest(\n manifest: Record<string, unknown>,\n rootPath: string,\n options: { filePath?: string; profile?: string },\n) {\n const next: Record<string, unknown> = {\n ...manifest,\n version: manifest.version ?? 1,\n };\n const apps = isRecord(next.apps) ? { ...next.apps } : {};\n const existingContent = isRecord(apps[CONTENT_APP_ID])\n ? { ...apps[CONTENT_APP_ID] }\n : {};\n const existingRoots = Array.isArray(existingContent.roots)\n ? existingContent.roots.filter(isRecord).map((root) => ({ ...root }))\n : [];\n const include = options.filePath ? [options.filePath] : undefined;\n const rootIndex = existingRoots.findIndex((entry) => entry.path === rootPath);\n const existingRoot = rootIndex === -1 ? undefined : existingRoots[rootIndex];\n const profile =\n options.profile && !existingRoot?.profile ? options.profile : undefined;\n const root = {\n ...existingRoot,\n name:\n typeof existingRoot?.name === \"string\"\n ? existingRoot.name\n : titleFromPath(rootPath),\n path: rootPath,\n kind: typeof existingRoot?.kind === \"string\" ? existingRoot.kind : \"docs\",\n extensions: Array.isArray(existingRoot?.extensions)\n ? existingRoot.extensions\n : [\".md\", \".mdx\"],\n ...(profile ? { profile } : {}),\n ...(include && rootIndex === -1 ? { include } : {}),\n };\n const roots =\n rootIndex === -1\n ? [...existingRoots, root]\n : existingRoots.map((entry, index) =>\n index === rootIndex ? { ...entry, ...root } : entry,\n );\n\n apps[CONTENT_APP_ID] = {\n ...existingContent,\n mode: \"local-files\",\n roots,\n hide: Array.isArray(existingContent.hide)\n ? existingContent.hide\n : [\"**/_*.md\", \"**/_*.mdx\"],\n };\n next.apps = apps;\n return next;\n}\n\nexport function findContentTemplateDir(cwd = process.cwd()) {\n const cliDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates: string[] = [];\n let current = path.resolve(cwd);\n for (;;) {\n candidates.push(\n path.join(current, \"templates\", \"content\"),\n path.join(current, \"apps\", \"content\"),\n );\n const parent = path.dirname(current);\n if (parent === current) break;\n current = parent;\n }\n candidates.push(path.resolve(cliDir, \"../../../..\", \"templates/content\"));\n return (\n candidates.find((candidate) =>\n fsSync.existsSync(path.join(candidate, \"package.json\")),\n ) ?? null\n );\n}\n\nfunction launchUrl(port: number, filePath?: string) {\n const base = `http://127.0.0.1:${port}`;\n return filePath ? `${base}/page/${encodeLocalFileId(filePath)}` : base;\n}\n\nexport async function prepareContentLocalLaunch(options: {\n target: string;\n cwd?: string;\n port?: number;\n profile?: string;\n dryRun?: boolean;\n}): Promise<ContentLocalLaunchPlan> {\n const cwd = path.resolve(options.cwd ?? process.cwd());\n const target = resolveTarget(options.target, cwd);\n const manifestPath = path.join(target.workspaceRoot, MANIFEST_FILE);\n const manifest = upsertContentManifest(\n await readManifest(manifestPath),\n target.rootPath,\n {\n filePath: target.filePath,\n profile: options.profile,\n },\n );\n const templateDir = findContentTemplateDir(cwd);\n if (!templateDir) {\n throw new Error(\n \"Could not find the Content app template. Run this from the framework checkout or a workspace that includes templates/content.\",\n );\n }\n\n if (!options.dryRun) {\n await fs.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\\n`);\n }\n\n return {\n workspaceRoot: target.workspaceRoot,\n manifestPath,\n rootPath: target.rootPath,\n filePath: target.filePath,\n url: launchUrl(options.port ?? DEFAULT_PORT, target.filePath),\n command: \"corepack\",\n args: [\n \"pnpm\",\n \"--dir\",\n templateDir,\n \"dev\",\n \"--host\",\n \"127.0.0.1\",\n \"--port\",\n String(options.port ?? DEFAULT_PORT),\n \"--strictPort\",\n ],\n env: {\n AGENT_NATIVE_MODE: \"local-files\",\n AGENT_NATIVE_MANIFEST_PATH: manifestPath,\n },\n manifest,\n };\n}\n\nfunction openBrowser(url: string) {\n const command =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"cmd\"\n : \"xdg-open\";\n const args = process.platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : [url];\n const child = spawn(command, args, {\n detached: true,\n stdio: \"ignore\",\n shell: false,\n });\n child.unref();\n}\n\nfunction printHelp() {\n console.log(`Usage:\n agent-native content local-files <file-or-folder> [options]\n agent-native content <file-or-folder> [options]\n\nOptions:\n --profile <name> Local file profile, for example docs/no-bookkeeping\n --port <number> Dev server port (default ${DEFAULT_PORT})\n --open Open the target in the browser (default)\n --no-open Start the server without opening the browser\n --dry-run Print the launch plan without writing ${MANIFEST_FILE}\n --json Print the launch plan as JSON`);\n}\n\nexport async function runContentLocal(argv: string[]) {\n const parsed = parseContentLocalArgs(argv);\n if (parsed.help || !parsed.target) {\n printHelp();\n return parsed.target ? 0 : 1;\n }\n\n const plan = await prepareContentLocalLaunch({\n target: parsed.target,\n port: parsed.port,\n profile: parsed.profile ?? DEFAULT_PROFILE,\n dryRun: parsed.dryRun,\n });\n\n if (parsed.dryRun || parsed.json) {\n console.log(JSON.stringify(plan, null, 2));\n return 0;\n }\n\n console.log(`Content local files: ${plan.rootPath}`);\n console.log(`Manifest: ${plan.manifestPath}`);\n console.log(`Opening: ${plan.url}`);\n\n if (parsed.open) {\n setTimeout(() => openBrowser(plan.url), 1000).unref();\n }\n\n const child = spawn(plan.command, plan.args, {\n cwd: plan.workspaceRoot,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n env: { ...process.env, ...plan.env },\n });\n\n return await new Promise<number>((resolve) => {\n child.on(\"exit\", (code) => resolve(code ?? 0));\n child.on(\"error\", (error) => {\n console.error(error.message);\n resolve(1);\n });\n for (const signal of [\"SIGINT\", \"SIGTERM\", \"SIGHUP\"] as const) {\n process.on(signal, () => {\n child.kill(signal);\n resolve(1);\n });\n }\n });\n}\n"]}
1
+ {"version":3,"file":"content-local.js","sourceRoot":"","sources":["../../src/cli/content-local.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport fsSync from \"node:fs\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst CONTENT_APP_ID = \"content\";\nconst MANIFEST_FILE = \"agent-native.json\";\nconst DEFAULT_PORT = 8083;\nconst DEFAULT_PROFILE = \"docs/no-bookkeeping\";\nconst LOCAL_FOLDER_CONNECTION_PREFIX = \"local-folder:\";\n\nexport interface ContentLocalArgs {\n target?: string;\n open: boolean;\n port: number;\n profile?: string;\n dryRun: boolean;\n json: boolean;\n help: boolean;\n}\n\nexport interface ContentLocalLaunchPlan {\n workspaceRoot: string;\n manifestPath: string;\n rootPath: string;\n filePath?: string;\n connectionId: string;\n url: string;\n command: string;\n args: string[];\n env: Record<string, string>;\n manifest: Record<string, unknown>;\n}\n\nfunction normalizeSlash(value: string) {\n return value.replace(/\\\\/g, \"/\");\n}\n\nfunction titleFromPath(value: string) {\n return (\n path\n .basename(value)\n .replace(/\\.(mdx?|markdown)$/i, \"\")\n .replace(/[-_]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim()\n .replace(/\\b\\w/g, (char) => char.toUpperCase()) || \"Content\"\n );\n}\n\nfunction localFolderConnectionId(absoluteRootPath: string) {\n let canonicalRootPath = path.resolve(absoluteRootPath);\n try {\n canonicalRootPath = fsSync.realpathSync(canonicalRootPath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n }\n const digest = createHash(\"sha256\")\n .update(canonicalRootPath)\n .digest(\"base64url\")\n .slice(0, 24);\n return `${LOCAL_FOLDER_CONNECTION_PREFIX}${digest}`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction stringFlagValue(argv: string[], index: number, flag: string) {\n const value = argv[index + 1];\n if (!value || value.startsWith(\"-\")) {\n throw new Error(`${flag} requires a value`);\n }\n return value;\n}\n\nexport function parseContentLocalArgs(argv: string[]): ContentLocalArgs {\n const args = argv[0] === \"local-files\" ? argv.slice(1) : argv;\n const parsed: ContentLocalArgs = {\n open: true,\n port: DEFAULT_PORT,\n dryRun: false,\n json: false,\n help: false,\n };\n\n for (let index = 0; index < args.length; index += 1) {\n const arg = args[index]!;\n if (arg === \"help\" || arg === \"--help\" || arg === \"-h\") {\n parsed.help = true;\n } else if (arg === \"--open\") {\n parsed.open = true;\n } else if (arg === \"--no-open\") {\n parsed.open = false;\n } else if (arg === \"--dry-run\") {\n parsed.dryRun = true;\n } else if (arg === \"--json\") {\n parsed.json = true;\n } else if (arg === \"--port\") {\n parsed.port = Number.parseInt(stringFlagValue(args, index, arg), 10);\n index += 1;\n } else if (arg.startsWith(\"--port=\")) {\n parsed.port = Number.parseInt(arg.slice(\"--port=\".length), 10);\n } else if (arg === \"--profile\") {\n parsed.profile = stringFlagValue(args, index, arg);\n index += 1;\n } else if (arg.startsWith(\"--profile=\")) {\n parsed.profile = arg.slice(\"--profile=\".length);\n } else if (arg.startsWith(\"-\")) {\n throw new Error(`Unknown option: ${arg}`);\n } else if (!parsed.target) {\n parsed.target = arg;\n } else {\n throw new Error(`Unexpected argument: ${arg}`);\n }\n }\n\n if (!Number.isInteger(parsed.port) || parsed.port <= 0) {\n throw new Error(\"--port must be a positive integer\");\n }\n\n return parsed;\n}\n\nfunction resolveTarget(target: string, cwd: string) {\n const absoluteTarget = path.resolve(cwd, target);\n const stat = fsSync.statSync(absoluteTarget);\n const targetDirectory = stat.isDirectory()\n ? absoluteTarget\n : path.dirname(absoluteTarget);\n let workspaceRoot = path.resolve(cwd);\n let rootPath = normalizeSlash(path.relative(workspaceRoot, targetDirectory));\n\n if (!rootPath || rootPath === \".\" || rootPath.startsWith(\"../\")) {\n workspaceRoot = path.dirname(targetDirectory);\n rootPath = path.basename(targetDirectory);\n }\n\n const filePath = stat.isFile()\n ? normalizeSlash(path.posix.join(rootPath, path.basename(absoluteTarget)))\n : undefined;\n\n return {\n absoluteTarget,\n absoluteRootPath: targetDirectory,\n isFile: stat.isFile(),\n workspaceRoot,\n rootPath: normalizeSlash(rootPath),\n filePath,\n };\n}\n\nasync function readManifest(\n manifestPath: string,\n): Promise<Record<string, unknown>> {\n try {\n const raw = await fs.readFile(manifestPath, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (!isRecord(parsed)) {\n throw new Error(`${MANIFEST_FILE} must contain a JSON object`);\n }\n return parsed;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return { version: 1 };\n }\n throw error;\n }\n}\n\nfunction upsertContentManifest(\n manifest: Record<string, unknown>,\n workspaceRoot: string,\n rootPath: string,\n options: {\n connectionId: string;\n filePath?: string;\n profile?: string;\n },\n) {\n const next: Record<string, unknown> = {\n ...manifest,\n version: manifest.version ?? 1,\n };\n const apps = isRecord(next.apps) ? { ...next.apps } : {};\n const existingContent = isRecord(apps[CONTENT_APP_ID])\n ? { ...apps[CONTENT_APP_ID] }\n : {};\n const existingRoots = Array.isArray(existingContent.roots)\n ? existingContent.roots.filter(isRecord).map((root) => ({ ...root }))\n : [];\n const include = options.filePath ? [options.filePath] : undefined;\n const rootIndex = existingRoots.findIndex((entry) => entry.path === rootPath);\n const existingRoot = rootIndex === -1 ? undefined : existingRoots[rootIndex];\n const profile =\n options.profile && !existingRoot?.profile ? options.profile : undefined;\n const root = {\n ...existingRoot,\n name:\n typeof existingRoot?.name === \"string\"\n ? existingRoot.name\n : titleFromPath(rootPath),\n path: rootPath,\n kind: typeof existingRoot?.kind === \"string\" ? existingRoot.kind : \"docs\",\n extensions: Array.isArray(existingRoot?.extensions)\n ? existingRoot.extensions\n : [\".md\", \".mdx\"],\n ...(profile ? { profile } : {}),\n ...(include && (rootIndex === -1 || Array.isArray(existingRoot?.include))\n ? {\n include: Array.from(\n new Set([\n ...(Array.isArray(existingRoot?.include)\n ? existingRoot.include.filter(\n (value): value is string => typeof value === \"string\",\n )\n : []),\n ...include,\n ]),\n ),\n }\n : {}),\n source: {\n ...(isRecord(existingRoot?.source) ? existingRoot.source : {}),\n type: \"local-folder\",\n connectionId: options.connectionId,\n truthPolicy: \"source_primary\",\n },\n };\n let roots =\n rootIndex === -1\n ? [...existingRoots, root]\n : existingRoots.map((entry, index) =>\n index === rootIndex ? { ...entry, ...root } : entry,\n );\n\n if (existingContent.mode === \"local-files\") {\n roots = roots.map((entry) => {\n if (entry === root || typeof entry.path !== \"string\") return entry;\n const existingSource = isRecord(entry.source) ? entry.source : {};\n return {\n ...entry,\n source: {\n ...existingSource,\n type: \"local-folder\",\n connectionId:\n typeof existingSource.connectionId === \"string\" &&\n existingSource.connectionId\n ? existingSource.connectionId\n : localFolderConnectionId(\n path.resolve(workspaceRoot, entry.path),\n ),\n truthPolicy:\n typeof existingSource.truthPolicy === \"string\"\n ? existingSource.truthPolicy\n : \"source_primary\",\n },\n };\n });\n }\n\n delete existingContent.mode;\n apps[CONTENT_APP_ID] = {\n ...existingContent,\n roots,\n hide: Array.isArray(existingContent.hide)\n ? existingContent.hide\n : [\"**/_*.md\", \"**/_*.mdx\"],\n };\n next.apps = apps;\n return next;\n}\n\nexport function findContentTemplateDir(cwd = process.cwd()) {\n const cliDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates: string[] = [];\n let current = path.resolve(cwd);\n for (;;) {\n candidates.push(\n path.join(current, \"templates\", \"content\"),\n path.join(current, \"apps\", \"content\"),\n );\n const parent = path.dirname(current);\n if (parent === current) break;\n current = parent;\n }\n candidates.push(path.resolve(cliDir, \"../../../..\", \"templates/content\"));\n return (\n candidates.find((candidate) =>\n fsSync.existsSync(path.join(candidate, \"package.json\")),\n ) ?? null\n );\n}\n\nfunction launchUrl(port: number, connectionId: string, filePath?: string) {\n const params = new URLSearchParams({ connectionId });\n if (filePath) params.set(\"file\", filePath);\n return `http://127.0.0.1:${port}/local-files?${params.toString()}`;\n}\n\nexport async function prepareContentLocalLaunch(options: {\n target: string;\n cwd?: string;\n port?: number;\n profile?: string;\n dryRun?: boolean;\n}): Promise<ContentLocalLaunchPlan> {\n const cwd = path.resolve(options.cwd ?? process.cwd());\n const target = resolveTarget(options.target, cwd);\n const manifestPath = path.join(target.workspaceRoot, MANIFEST_FILE);\n const connectionId = localFolderConnectionId(target.absoluteRootPath);\n const manifest = upsertContentManifest(\n await readManifest(manifestPath),\n target.workspaceRoot,\n target.rootPath,\n {\n connectionId,\n filePath: target.filePath,\n profile: options.profile,\n },\n );\n const templateDir = findContentTemplateDir(cwd);\n if (!templateDir) {\n throw new Error(\n \"Could not find the Content app template. Run this from the framework checkout or a workspace that includes templates/content.\",\n );\n }\n\n if (!options.dryRun) {\n await fs.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\\n`);\n }\n\n return {\n workspaceRoot: target.workspaceRoot,\n manifestPath,\n rootPath: target.rootPath,\n filePath: target.filePath,\n connectionId,\n url: launchUrl(options.port ?? DEFAULT_PORT, connectionId, target.filePath),\n command: \"corepack\",\n args: [\n \"pnpm\",\n \"--dir\",\n templateDir,\n \"dev\",\n \"--host\",\n \"127.0.0.1\",\n \"--port\",\n String(options.port ?? DEFAULT_PORT),\n \"--strictPort\",\n ],\n env: {\n AGENT_NATIVE_MANIFEST_PATH: manifestPath,\n },\n manifest,\n };\n}\n\nfunction openBrowser(url: string) {\n const command =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"cmd\"\n : \"xdg-open\";\n const args = process.platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : [url];\n const child = spawn(command, args, {\n detached: true,\n stdio: \"ignore\",\n shell: false,\n });\n child.unref();\n}\n\nfunction printHelp() {\n console.log(`Usage:\n agent-native content local-files <file-or-folder> [options]\n agent-native content <file-or-folder> [options]\n\nOptions:\n --profile <name> Local folder profile, for example docs/no-bookkeeping\n --port <number> Dev server port (default ${DEFAULT_PORT})\n --open Open the target in the browser (default)\n --no-open Start the server without opening the browser\n --dry-run Print the launch plan without writing ${MANIFEST_FILE}\n --json Print the launch plan as JSON`);\n}\n\nexport async function runContentLocal(argv: string[]) {\n const parsed = parseContentLocalArgs(argv);\n if (parsed.help) {\n printHelp();\n return 0;\n }\n if (!parsed.target) {\n printHelp();\n return 1;\n }\n\n const plan = await prepareContentLocalLaunch({\n target: parsed.target,\n port: parsed.port,\n profile: parsed.profile ?? DEFAULT_PROFILE,\n dryRun: parsed.dryRun,\n });\n\n if (parsed.dryRun || parsed.json) {\n console.log(JSON.stringify(plan, null, 2));\n return 0;\n }\n\n console.log(`Content folder source: ${plan.rootPath}`);\n console.log(`Manifest: ${plan.manifestPath}`);\n console.log(`Opening: ${plan.url}`);\n\n if (parsed.open) {\n setTimeout(() => openBrowser(plan.url), 1000).unref();\n }\n\n const child = spawn(plan.command, plan.args, {\n cwd: plan.workspaceRoot,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n env: { ...process.env, ...plan.env },\n });\n\n return await new Promise<number>((resolve) => {\n child.on(\"exit\", (code) => resolve(code ?? 0));\n child.on(\"error\", (error) => {\n console.error(error.message);\n resolve(1);\n });\n for (const signal of [\"SIGINT\", \"SIGTERM\", \"SIGHUP\"] as const) {\n process.on(signal, () => {\n child.kill(signal);\n resolve(1);\n });\n }\n });\n}\n"]}
package/dist/cli/index.js CHANGED
@@ -975,8 +975,8 @@ Usage:
975
975
  --with-github-action also writes the PR Visual
976
976
  Recap workflow into .github/workflows/.
977
977
  agent-native content local-files <file-or-folder>
978
- Launch Content in local-file mode for a local
979
- docs/content folder. Use --no-open, --port N,
978
+ Connect a local docs/content folder to normal
979
+ database-backed Content. Use --no-open, --port N,
980
980
  or --profile docs/no-bookkeeping as needed.
981
981
  agent-native design connect Start a localhost Design bridge for a running
982
982
  dev server. Use --url, --port, --root, or