@agent-native/core 0.102.2 → 0.103.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +6 -0
- package/corpus/core/docs/content/local-file-mode.mdx +99 -489
- package/corpus/core/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
- package/corpus/core/docs/content/locales/ar-SA/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
- package/corpus/core/docs/content/locales/de-DE/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
- package/corpus/core/docs/content/locales/es-ES/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
- package/corpus/core/docs/content/locales/fr-FR/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
- package/corpus/core/docs/content/locales/hi-IN/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
- package/corpus/core/docs/content/locales/ja-JP/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
- package/corpus/core/docs/content/locales/ko-KR/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
- package/corpus/core/docs/content/locales/pt-BR/template-content.mdx +10 -24
- package/corpus/core/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
- package/corpus/core/docs/content/locales/zh-CN/template-content.mdx +9 -24
- package/corpus/core/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
- package/corpus/core/docs/content/locales/zh-TW/template-content.mdx +9 -24
- package/corpus/core/docs/content/template-content.mdx +26 -42
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/content-local.ts +86 -18
- package/corpus/core/src/cli/index.ts +2 -2
- package/corpus/core/src/cli/skills-content/content-skill.ts +36 -25
- package/corpus/core/src/cli/skills.ts +47 -13
- package/corpus/core/src/local-artifacts/index.ts +65 -0
- package/corpus/templates/assets/app/components/library/GenerationPresetsPanel.tsx +325 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +12 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +21 -0
- package/corpus/templates/assets/app/i18n-data.ts +215 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +19 -479
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id_.settings.tsx +446 -0
- package/corpus/templates/clips/app/components/editor/editor-layout.tsx +12 -2
- package/corpus/templates/clips/app/components/player/settings-panel.tsx +30 -12
- package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
- package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +22 -3
- package/corpus/templates/clips/app/i18n/ar-SA.ts +3 -2
- package/corpus/templates/clips/app/i18n/de-DE.ts +4 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -2
- package/corpus/templates/clips/app/i18n/es-ES.ts +4 -2
- package/corpus/templates/clips/app/i18n/fr-FR.ts +3 -2
- package/corpus/templates/clips/app/i18n/hi-IN.ts +3 -2
- package/corpus/templates/clips/app/i18n/ja-JP.ts +4 -1
- package/corpus/templates/clips/app/i18n/ko-KR.ts +3 -2
- package/corpus/templates/clips/app/i18n/pt-BR.ts +4 -2
- package/corpus/templates/clips/app/i18n/zh-CN.ts +3 -2
- package/corpus/templates/clips/app/i18n/zh-TW.ts +3 -2
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +27 -1
- package/corpus/templates/clips/server/lib/share-password.ts +6 -3
- package/corpus/templates/content/.agents/skills/content/SKILL.md +26 -39
- package/corpus/templates/content/AGENTS.md +25 -30
- package/corpus/templates/content/actions/_builder-docs-client.ts +23 -0
- package/corpus/templates/content/actions/_content-database-source-adapters.ts +21 -3
- package/corpus/templates/content/actions/_content-files.ts +338 -0
- package/corpus/templates/content/actions/_content-space-access.ts +135 -0
- package/corpus/templates/content/actions/_content-spaces.ts +640 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +90 -7
- package/corpus/templates/content/actions/_database-utils.ts +7 -1
- package/corpus/templates/content/actions/_local-file-documents.ts +0 -9
- package/corpus/templates/content/actions/_local-folder-source.ts +53 -0
- package/corpus/templates/content/actions/_property-utils.ts +9 -4
- package/corpus/templates/content/actions/add-database-item.ts +108 -65
- package/corpus/templates/content/actions/backfill-content-files.ts +22 -0
- package/corpus/templates/content/actions/connect-local-folder-source.ts +221 -0
- package/corpus/templates/content/actions/create-content-database.ts +129 -3
- package/corpus/templates/content/actions/create-document.ts +65 -55
- package/corpus/templates/content/actions/create-inline-content-database.ts +6 -1
- package/corpus/templates/content/actions/delete-content-database.ts +3 -0
- package/corpus/templates/content/actions/delete-document.ts +7 -11
- package/corpus/templates/content/actions/disconnect-local-folder-source.ts +175 -0
- package/corpus/templates/content/actions/duplicate-database-item.ts +10 -0
- package/corpus/templates/content/actions/duplicate-database-items.ts +13 -0
- package/corpus/templates/content/actions/edit-document.ts +2 -28
- package/corpus/templates/content/actions/ensure-content-spaces.ts +26 -0
- package/corpus/templates/content/actions/export-content-source.ts +47 -9
- package/corpus/templates/content/actions/get-document.ts +0 -14
- package/corpus/templates/content/actions/import-content-source.ts +43 -3
- package/corpus/templates/content/actions/list-content-spaces.ts +103 -0
- package/corpus/templates/content/actions/list-documents.ts +12 -9
- package/corpus/templates/content/actions/move-document.ts +3 -14
- package/corpus/templates/content/actions/pull-document.ts +0 -23
- package/corpus/templates/content/actions/remove-local-file-source.ts +50 -29
- package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +442 -0
- package/corpus/templates/content/actions/reveal-local-source-file.ts +6 -17
- package/corpus/templates/content/actions/search-documents.ts +0 -34
- package/corpus/templates/content/actions/share-local-file-document.ts +25 -1
- package/corpus/templates/content/actions/submit-content-database-form.ts +12 -0
- package/corpus/templates/content/actions/sync-local-folder-source.ts +731 -0
- package/corpus/templates/content/actions/sync-manifest-local-folder-source.ts +123 -0
- package/corpus/templates/content/actions/update-content-database-view.ts +10 -1
- package/corpus/templates/content/actions/update-document.ts +0 -15
- package/corpus/templates/content/actions/view-screen.ts +2 -50
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +76 -62
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +70 -22
- package/corpus/templates/content/app/components/editor/database/grouping.ts +2 -1
- package/corpus/templates/content/app/components/editor/database/settings.tsx +23 -20
- package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +330 -0
- package/corpus/templates/content/app/components/editor/database/types.ts +1 -0
- package/corpus/templates/content/app/components/editor/database/view-config.ts +3 -1
- package/corpus/templates/content/app/components/layout/Layout.tsx +25 -1
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +209 -113
- package/corpus/templates/content/app/components/sidebar/select-content-space.ts +31 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +16 -0
- package/corpus/templates/content/app/hooks/use-content-spaces.ts +39 -0
- package/corpus/templates/content/app/i18n/zh-TW.ts +11 -0
- package/corpus/templates/content/app/i18n-data.ts +75 -0
- package/corpus/templates/content/app/routes/_app.local-files.tsx +174 -65
- package/corpus/templates/content/changelog/2026-07-16-content-callouts-can-now-be-reviewed-and-published-to-builder.md +6 -0
- package/corpus/templates/content/changelog/2026-07-16-heading-5-and-heading-6-are-available-from-the-editor-slash-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-16-organize-every-workspace-through-a-customizable-files-databa.md +6 -0
- package/corpus/templates/content/parity/matrix.md +2 -1
- package/corpus/templates/content/parity/matrix.ts +37 -1
- package/corpus/templates/content/scripts/dev-database.mjs +7 -5
- package/corpus/templates/content/server/db/schema.ts +113 -40
- package/corpus/templates/content/server/lib/document-context.ts +15 -1
- package/corpus/templates/content/server/lib/notion-sync.ts +6 -0
- package/corpus/templates/content/server/plugins/db.ts +153 -0
- package/corpus/templates/content/shared/api.ts +17 -3
- package/corpus/templates/content/shared/builder-mdx.ts +163 -16
- package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +59 -0
- package/corpus/templates/design/AGENTS.md +6 -1
- package/corpus/templates/design/actions/create-design-from-template.ts +19 -7
- package/corpus/templates/design/actions/list-design-templates.ts +69 -21
- package/corpus/templates/design/actions/navigate.ts +2 -2
- package/corpus/templates/design/actions/view-screen.ts +15 -2
- package/corpus/templates/design/agent-native.app-skill.json +5 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +281 -56
- package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +14 -2
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +3 -3
- package/corpus/templates/design/app/i18n-data.ts +21 -2
- package/corpus/templates/design/app/i18n-template-feature.ts +218 -10
- package/corpus/templates/design/app/pages/Index.tsx +195 -24
- package/corpus/templates/design/app/pages/Templates.tsx +144 -55
- package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +12 -1
- package/corpus/templates/design/changelog/2026-07-14-templates-now-appear-in-the-new-design-picker-with-previews-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-15-templates-now-open-immediately-after-copying-even-while-the-.md +6 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/mail/AGENTS.md +23 -18
- package/corpus/templates/mail/actions/bulk-archive.ts +32 -28
- package/corpus/templates/mail/actions/mark-read.ts +131 -20
- package/corpus/templates/mail/actions/mark-thread-read.ts +1 -1
- package/corpus/templates/mail/actions/move-email.ts +30 -24
- package/corpus/templates/mail/actions/send-email.ts +73 -68
- package/corpus/templates/mail/changelog/2026-07-15-unread-inbox-cleanup-now-completes-reliably.md +6 -0
- package/corpus/templates/mail/server/handlers/emails.ts +218 -209
- package/corpus/templates/mail/server/lib/bulk-mark-read.ts +30 -0
- package/corpus/templates/mail/server/lib/email-state.ts +198 -113
- package/corpus/templates/mail/server/lib/google-auth.ts +218 -25
- package/corpus/templates/mail/server/lib/jobs.ts +69 -74
- package/corpus/templates/mail/server/lib/local-email-store.ts +265 -0
- package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
- package/dist/cli/content-local.d.ts +1 -0
- package/dist/cli/content-local.d.ts.map +1 -1
- package/dist/cli/content-local.js +75 -17
- package/dist/cli/content-local.js.map +1 -1
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/skills-content/content-skill.d.ts +1 -1
- package/dist/cli/skills-content/content-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/content-skill.js +36 -25
- package/dist/cli/skills-content/content-skill.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +47 -14
- package/dist/cli/skills.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/local-artifacts/index.d.ts +10 -0
- package/dist/local-artifacts/index.d.ts.map +1 -1
- package/dist/local-artifacts/index.js +39 -0
- package/dist/local-artifacts/index.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/docs/content/local-file-mode.mdx +99 -489
- package/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
- package/docs/content/locales/ar-SA/template-content.mdx +10 -24
- package/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
- package/docs/content/locales/de-DE/template-content.mdx +10 -24
- package/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
- package/docs/content/locales/es-ES/template-content.mdx +10 -24
- package/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
- package/docs/content/locales/fr-FR/template-content.mdx +10 -24
- package/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
- package/docs/content/locales/hi-IN/template-content.mdx +10 -24
- package/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
- package/docs/content/locales/ja-JP/template-content.mdx +10 -24
- package/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
- package/docs/content/locales/ko-KR/template-content.mdx +10 -24
- package/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
- package/docs/content/locales/pt-BR/template-content.mdx +10 -24
- package/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
- package/docs/content/locales/zh-CN/template-content.mdx +9 -24
- package/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
- package/docs/content/locales/zh-TW/template-content.mdx +9 -24
- package/docs/content/template-content.mdx +26 -42
- package/package.json +1 -1
- 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.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-local.d.ts","sourceRoot":"","sources":["../../src/cli/content-local.ts"],"names":[],"mappings":"
|
|
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
|
|
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
|
|
24
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
189
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
314
|
+
if (parsed.help) {
|
|
315
|
+
printHelp();
|
|
316
|
+
return 0;
|
|
317
|
+
}
|
|
318
|
+
if (!parsed.target) {
|
|
261
319
|
printHelp();
|
|
262
|
-
return
|
|
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
|
|
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":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,MAAM,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,cAAc,GAAG,SAAS,CAAC;AACjC,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAC1C,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAC9C,MAAM,8BAA8B,GAAG,eAAe,CAAC;AAyBvD,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;IAClC,OAAO,CACL,IAAI;SACD,QAAQ,CAAC,KAAK,CAAC;SACf,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC;SAClC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE;SACN,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,SAAS,CAC/D,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAAC,gBAAwB;IACvD,IAAI,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACvD,IAAI,CAAC;QACH,iBAAiB,GAAG,MAAM,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,KAAK,CAAC;IACtE,CAAC;IACD,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC;SAChC,MAAM,CAAC,iBAAiB,CAAC;SACzB,MAAM,CAAC,WAAW,CAAC;SACnB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChB,OAAO,GAAG,8BAA8B,GAAG,MAAM,EAAE,CAAC;AACtD,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,CAAC,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,eAAe,CAAC,IAAc,EAAE,KAAa,EAAE,IAAY;IAClE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,mBAAmB,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAc;IAClD,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,MAAM,MAAM,GAAqB;QAC/B,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,YAAY;QAClB,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,KAAK;QACX,IAAI,EAAE,KAAK;KACZ,CAAC;IAEF,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAE,CAAC;QACzB,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACvD,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;QACrB,CAAC;aAAM,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;QACrB,CAAC;aAAM,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC;QACtB,CAAC;aAAM,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YAC/B,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;QACvB,CAAC;aAAM,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;QACrB,CAAC;aAAM,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;YACrE,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;aAAM,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;QACjE,CAAC;aAAM,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YAC/B,MAAM,CAAC,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;YACnD,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;aAAM,IAAI,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACxC,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAClD,CAAC;aAAM,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,EAAE,CAAC,CAAC;QAC5C,CAAC;aAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAC1B,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,MAAc,EAAE,GAAW;IAChD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IAC7C,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE;QACxC,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IACjC,IAAI,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC,CAAC;IAE7E,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAC9C,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE;QAC5B,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC;QAC1E,CAAC,CAAC,SAAS,CAAC;IAEd,OAAO;QACL,cAAc;QACd,gBAAgB,EAAE,eAAe;QACjC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;QACrB,aAAa;QACb,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC;QAClC,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,YAAY,CACzB,YAAoB;IAEpB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;QAC1C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,GAAG,aAAa,6BAA6B,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QACxB,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,qBAAqB,CAC5B,QAAiC,EACjC,aAAqB,EACrB,QAAgB,EAChB,OAIC;IAED,MAAM,IAAI,GAA4B;QACpC,GAAG,QAAQ;QACX,OAAO,EAAE,QAAQ,CAAC,OAAO,IAAI,CAAC;KAC/B,CAAC;IACF,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,MAAM,eAAe,GAAG,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACpD,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,cAAc,CAAC,EAAE;QAC7B,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC;QACxD,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;QACrE,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAClE,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IAC9E,MAAM,YAAY,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAC7E,MAAM,OAAO,GACX,OAAO,CAAC,OAAO,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1E,MAAM,IAAI,GAAG;QACX,GAAG,YAAY;QACf,IAAI,EACF,OAAO,YAAY,EAAE,IAAI,KAAK,QAAQ;YACpC,CAAC,CAAC,YAAY,CAAC,IAAI;YACnB,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC;QAC7B,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,OAAO,YAAY,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;QACzE,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,UAAU,CAAC;YACjD,CAAC,CAAC,YAAY,CAAC,UAAU;YACzB,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC;QACnB,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,GAAG,CAAC,OAAO,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YACvE,CAAC,CAAC;gBACE,OAAO,EAAE,KAAK,CAAC,IAAI,CACjB,IAAI,GAAG,CAAC;oBACN,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC;wBACtC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CACzB,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CACtD;wBACH,CAAC,CAAC,EAAE,CAAC;oBACP,GAAG,OAAO;iBACX,CAAC,CACH;aACF;YACH,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,EAAE;YACN,GAAG,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,IAAI,EAAE,cAAc;YACpB,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,WAAW,EAAE,gBAAgB;SAC9B;KACF,CAAC;IACF,IAAI,KAAK,GACP,SAAS,KAAK,CAAC,CAAC;QACd,CAAC,CAAC,CAAC,GAAG,aAAa,EAAE,IAAI,CAAC;QAC1B,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CACjC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CACpD,CAAC;IAER,IAAI,eAAe,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAC3C,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YAC1B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,KAAK,CAAC;YACnE,MAAM,cAAc,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,OAAO;gBACL,GAAG,KAAK;gBACR,MAAM,EAAE;oBACN,GAAG,cAAc;oBACjB,IAAI,EAAE,cAAc;oBACpB,YAAY,EACV,OAAO,cAAc,CAAC,YAAY,KAAK,QAAQ;wBAC/C,cAAc,CAAC,YAAY;wBACzB,CAAC,CAAC,cAAc,CAAC,YAAY;wBAC7B,CAAC,CAAC,uBAAuB,CACrB,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,CACxC;oBACP,WAAW,EACT,OAAO,cAAc,CAAC,WAAW,KAAK,QAAQ;wBAC5C,CAAC,CAAC,cAAc,CAAC,WAAW;wBAC5B,CAAC,CAAC,gBAAgB;iBACvB;aACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,eAAe,CAAC,IAAI,CAAC;IAC5B,IAAI,CAAC,cAAc,CAAC,GAAG;QACrB,GAAG,eAAe;QAClB,KAAK;QACL,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC;YACvC,CAAC,CAAC,eAAe,CAAC,IAAI;YACtB,CAAC,CAAC,CAAC,UAAU,EAAE,WAAW,CAAC;KAC9B,CAAC;IACF,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACjB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,SAAS,CAAC;QACR,UAAU,CAAC,IAAI,CACb,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,CAAC,EAC1C,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CACtC,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,MAAM,KAAK,OAAO;YAAE,MAAM;QAC9B,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;IACD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAC1E,OAAO,CACL,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAC5B,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CACxD,IAAI,IAAI,CACV,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,YAAoB,EAAE,QAAiB;IACtE,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC;IACrD,IAAI,QAAQ;QAAE,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC3C,OAAO,oBAAoB,IAAI,gBAAgB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AACrE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,OAM/C;IACC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IACpE,MAAM,YAAY,GAAG,uBAAuB,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAG,qBAAqB,CACpC,MAAM,YAAY,CAAC,YAAY,CAAC,EAChC,MAAM,CAAC,aAAa,EACpB,MAAM,CAAC,QAAQ,EACf;QACE,YAAY;QACZ,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,OAAO,EAAE,OAAO,CAAC,OAAO;KACzB,CACF,CAAC;IACF,MAAM,WAAW,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,+HAA+H,CAChI,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO;QACL,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,YAAY;QACZ,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,YAAY;QACZ,GAAG,EAAE,SAAS,CAAC,OAAO,CAAC,IAAI,IAAI,YAAY,EAAE,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC;QAC3E,OAAO,EAAE,UAAU;QACnB,IAAI,EAAE;YACJ,MAAM;YACN,OAAO;YACP,WAAW;YACX,KAAK;YACL,QAAQ;YACR,WAAW;YACX,QAAQ;YACR,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,YAAY,CAAC;YACpC,cAAc;SACf;QACD,GAAG,EAAE;YACH,0BAA0B,EAAE,YAAY;SACzC;QACD,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,MAAM,OAAO,GACX,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAC3B,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC5B,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,UAAU,CAAC;IACnB,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7E,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;QACjC,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,QAAQ;QACf,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;IACH,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC;;;;;;gDAMkC,YAAY;;;6DAGC,aAAa;mDACvB,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAc;IAClD,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,yBAAyB,CAAC;QAC3C,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,eAAe;QAC1C,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,OAAO,CAAC,CAAC;IACX,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAEpC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,UAAU,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;IACxD,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE;QAC3C,GAAG,EAAE,IAAI,CAAC,aAAa;QACvB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO;QACnC,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE;KACrC,CAAC,CAAC;IAEH,OAAO,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QAC3C,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/C,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC1B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7B,OAAO,CAAC,CAAC,CAAC,CAAC;QACb,CAAC,CAAC,CAAC;QACH,KAAK,MAAM,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAU,EAAE,CAAC;YAC9D,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;gBACtB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACnB,OAAO,CAAC,CAAC,CAAC,CAAC;YACb,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC","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
|
-
|
|
979
|
-
|
|
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
|