@agent-native/core 0.77.10 → 0.77.12
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 +12 -0
- package/corpus/core/package.json +3 -3
- package/corpus/core/src/cli/create.ts +65 -15
- package/corpus/core/src/cli/sync-builder-starter-manifest.ts +261 -65
- package/corpus/core/src/collab/awareness.ts +12 -9
- package/corpus/core/src/collab/client.ts +4 -2
- package/corpus/templates/clips/actions/finalize-recording.ts +1 -1
- package/corpus/templates/clips/chrome-extension/package.json +1 -1
- package/corpus/templates/clips/desktop/src/app.tsx +35 -19
- package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +38 -1
- package/corpus/templates/clips/desktop/src/styles.css +35 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +343 -14
- package/corpus/templates/content/actions/list-trashed-content-databases.ts +36 -2
- package/corpus/templates/content/actions/move-document.ts +79 -33
- package/corpus/templates/content/actions/restore-content-database.ts +40 -3
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +84 -2
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +177 -49
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +393 -219
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +632 -467
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +114 -14
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +56 -15
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +6 -5
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +12 -4
- package/corpus/templates/content/app/global.css +9 -0
- package/corpus/templates/content/app/hooks/use-create-page.ts +36 -7
- package/corpus/templates/content/app/i18n-data.ts +140 -10
- package/corpus/templates/content/changelog/2026-06-25-comment-cards-now-track-their-highlighted-text-while-scrolli.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-comments-now-scroll-with-the-document-and-stay-below-the-pag.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-creating-a-database-from-the-slash-menu-no-longer-leaves-sta.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-sidebar-hover-controls-no-longer-leave-a-lingering-fade-when.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-sidebar-page-actions-are-now-easier-to-see-when-hovering-ina.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-text-selections-in-the-editor-no-longer-show-white-gaps-when.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-the-editor-toolbar-now-has-a-one-click-page-link-copy-button.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-the-editor-toolbar-now-shows-page-breadcrumbs-and-the-latest.md +6 -0
- package/corpus/templates/content/changelog/2026-06-25-the-page-command-now-leaves-a-notion-style-page-reference-an.md +6 -0
- package/corpus/templates/content/shared/nfm.ts +16 -0
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +53 -15
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/sync-builder-starter-manifest.d.ts +36 -2
- package/dist/cli/sync-builder-starter-manifest.d.ts.map +1 -1
- package/dist/cli/sync-builder-starter-manifest.js +180 -37
- package/dist/cli/sync-builder-starter-manifest.js.map +1 -1
- package/dist/collab/awareness.d.ts +1 -1
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/awareness.js +12 -8
- package/dist/collab/awareness.js.map +1 -1
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +4 -3
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +2 -2
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +3 -3
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.77.12
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 67dba9b: Sync builder-agent-native-starter toolchain files (React Router config, Vite config, server plugins, etc.) alongside the manifest so dependency bumps from templates/chat do not leave the starter in a broken state. Standalone UI scaffolds re-declare tsconfig `paths` and `baseUrl` for `@/*` resolution; headless scaffolds omit `baseUrl` for TS 6 tsgo compatibility. Netlify post-process now rewrites unindented template build commands.
|
|
8
|
+
|
|
9
|
+
## 0.77.11
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 7560a7c: Clear collaborative awareness state immediately when an editor leaves a document.
|
|
14
|
+
|
|
3
15
|
## 0.77.10
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.77.
|
|
3
|
+
"version": "0.77.12",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -211,8 +211,8 @@
|
|
|
211
211
|
"@react-router/dev": "^8.0.1",
|
|
212
212
|
"@react-router/fs-routes": "^8.0.1",
|
|
213
213
|
"@resvg/resvg-js": "^2.6.2",
|
|
214
|
-
"@sentry/browser": "
|
|
215
|
-
"@sentry/node": "
|
|
214
|
+
"@sentry/browser": "10.60.0",
|
|
215
|
+
"@sentry/node": "10.60.0",
|
|
216
216
|
"@standard-schema/spec": "^1.1.0",
|
|
217
217
|
"@tanstack/react-table": "^8.21.3",
|
|
218
218
|
"@tiptap/core": "3.27.1",
|
|
@@ -27,19 +27,7 @@ const STANDALONE_EXACT_DEPENDENCY_OVERRIDES: Record<string, string> = {
|
|
|
27
27
|
"@react-router/fs-routes": "8.0.1",
|
|
28
28
|
"react-router": "8.0.1",
|
|
29
29
|
};
|
|
30
|
-
const SENTRY_MINIMUM_RELEASE_AGE_EXCLUDES = [
|
|
31
|
-
'"@sentry/browser"',
|
|
32
|
-
'"@sentry/browser-utils"',
|
|
33
|
-
'"@sentry/conventions"',
|
|
34
|
-
'"@sentry/core"',
|
|
35
|
-
'"@sentry/feedback"',
|
|
36
|
-
'"@sentry/node"',
|
|
37
|
-
'"@sentry/node-core"',
|
|
38
|
-
'"@sentry/opentelemetry"',
|
|
39
|
-
'"@sentry/replay"',
|
|
40
|
-
'"@sentry/replay-canvas"',
|
|
41
|
-
'"@sentry/server-utils"',
|
|
42
|
-
];
|
|
30
|
+
const SENTRY_MINIMUM_RELEASE_AGE_EXCLUDES = ['"@sentry/*"'];
|
|
43
31
|
const FIRST_PARTY_TARBALL_SYMLINK_EXCLUDES = [
|
|
44
32
|
"*/CLAUDE.md",
|
|
45
33
|
"*/.claude/skills",
|
|
@@ -978,6 +966,7 @@ function postProcessStandalone(
|
|
|
978
966
|
const appTitle = appTitleForScaffold(name);
|
|
979
967
|
replacePlaceholders(targetDir, name, appTitle);
|
|
980
968
|
rewriteTrackingAppId(targetDir, name, templateName);
|
|
969
|
+
rewriteAgentChatAppId(targetDir, name, templateName);
|
|
981
970
|
fixPackageJsonName(targetDir, name, templateName);
|
|
982
971
|
fixWebManifestName(targetDir, name, templateName);
|
|
983
972
|
rewriteNetlifyToml(targetDir, name, "standalone");
|
|
@@ -1078,9 +1067,40 @@ function postProcessStandalone(
|
|
|
1078
1067
|
}
|
|
1079
1068
|
} catch {}
|
|
1080
1069
|
|
|
1070
|
+
fixStandaloneTsconfig(targetDir, templateName);
|
|
1071
|
+
|
|
1081
1072
|
setupAgentSymlinks(targetDir);
|
|
1082
1073
|
}
|
|
1083
1074
|
|
|
1075
|
+
function fixStandaloneTsconfig(targetDir: string, templateName?: string): void {
|
|
1076
|
+
const tsconfigPath = path.join(targetDir, "tsconfig.json");
|
|
1077
|
+
if (!fs.existsSync(tsconfigPath)) return;
|
|
1078
|
+
try {
|
|
1079
|
+
const tsconfig = JSON.parse(fs.readFileSync(tsconfigPath, "utf-8")) as {
|
|
1080
|
+
compilerOptions?: Record<string, unknown>;
|
|
1081
|
+
};
|
|
1082
|
+
tsconfig.compilerOptions ??= {};
|
|
1083
|
+
const hasUiApp =
|
|
1084
|
+
templateName !== "headless" && fs.existsSync(path.join(targetDir, "app"));
|
|
1085
|
+
const paths = {
|
|
1086
|
+
...((tsconfig.compilerOptions.paths as Record<string, string[]>) ?? {}),
|
|
1087
|
+
};
|
|
1088
|
+
paths["*"] ??= ["./*"];
|
|
1089
|
+
if (hasUiApp) {
|
|
1090
|
+
paths["@/*"] ??= ["./app/*"];
|
|
1091
|
+
paths["@shared/*"] ??= ["./shared/*"];
|
|
1092
|
+
// Child baseUrl anchors @/* for apps extending legacy core tsconfig bases
|
|
1093
|
+
// that still ship baseUrl. Headless scaffolds omit it so raw tsgo --noEmit
|
|
1094
|
+
// keeps working under TS 6.
|
|
1095
|
+
tsconfig.compilerOptions.baseUrl = ".";
|
|
1096
|
+
} else {
|
|
1097
|
+
delete tsconfig.compilerOptions.baseUrl;
|
|
1098
|
+
}
|
|
1099
|
+
tsconfig.compilerOptions.paths = paths;
|
|
1100
|
+
fs.writeFileSync(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\n`);
|
|
1101
|
+
} catch {}
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1084
1104
|
/* ─────────────────────────────────────────────────────────────────────────
|
|
1085
1105
|
* Prompting helpers
|
|
1086
1106
|
* ───────────────────────────────────────────────────────────────────────── */
|
|
@@ -1734,7 +1754,7 @@ function rewriteNetlifyToml(
|
|
|
1734
1754
|
|
|
1735
1755
|
try {
|
|
1736
1756
|
let content = fs.readFileSync(netlifyPath, "utf-8");
|
|
1737
|
-
const originalCommand = content.match(
|
|
1757
|
+
const originalCommand = content.match(/^\s*command = "([^"]*)"$/m)?.[1];
|
|
1738
1758
|
const usesUnpooledDatabase =
|
|
1739
1759
|
originalCommand?.includes("NETLIFY_DATABASE_URL_UNPOOLED") ?? false;
|
|
1740
1760
|
const buildCommand =
|
|
@@ -1754,7 +1774,7 @@ function rewriteNetlifyToml(
|
|
|
1754
1774
|
: ".netlify/functions-internal";
|
|
1755
1775
|
|
|
1756
1776
|
content = content
|
|
1757
|
-
.replace(/^
|
|
1777
|
+
.replace(/^(\s*)command = ".*"$/m, `$1command = "${command}"`)
|
|
1758
1778
|
.replace(
|
|
1759
1779
|
/publish = "templates\/[^"]+\/dist"/g,
|
|
1760
1780
|
`publish = "${publishPath}"`,
|
|
@@ -1776,6 +1796,36 @@ function rewriteNetlifyToml(
|
|
|
1776
1796
|
} catch {}
|
|
1777
1797
|
}
|
|
1778
1798
|
|
|
1799
|
+
function rewriteAgentChatAppId(
|
|
1800
|
+
appDir: string,
|
|
1801
|
+
appName: string,
|
|
1802
|
+
templateName?: string,
|
|
1803
|
+
): void {
|
|
1804
|
+
const pluginPath = path.join(appDir, "server", "plugins", "agent-chat.ts");
|
|
1805
|
+
if (!fs.existsSync(pluginPath)) return;
|
|
1806
|
+
|
|
1807
|
+
try {
|
|
1808
|
+
const content = fs.readFileSync(pluginPath, "utf-8");
|
|
1809
|
+
const sourceAppIds = ["chat", "starter"];
|
|
1810
|
+
if (templateName && templateName !== appName) {
|
|
1811
|
+
sourceAppIds.push(templateName);
|
|
1812
|
+
}
|
|
1813
|
+
const pattern = new RegExp(
|
|
1814
|
+
`(appId:\\s*)(["'])(${sourceAppIds.map(escapeRegExp).join("|")})\\2`,
|
|
1815
|
+
);
|
|
1816
|
+
if (!pattern.test(content)) return;
|
|
1817
|
+
|
|
1818
|
+
const next = content.replace(
|
|
1819
|
+
pattern,
|
|
1820
|
+
(_match, prefix: string, quote: string) =>
|
|
1821
|
+
`${prefix}${quote}${appName}${quote}`,
|
|
1822
|
+
);
|
|
1823
|
+
if (next !== content) {
|
|
1824
|
+
fs.writeFileSync(pluginPath, next);
|
|
1825
|
+
}
|
|
1826
|
+
} catch {}
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1779
1829
|
function rewriteTrackingAppId(
|
|
1780
1830
|
appDir: string,
|
|
1781
1831
|
appName: string,
|
|
@@ -8,8 +8,45 @@ import { _postProcessStandalone } from "./create.js";
|
|
|
8
8
|
export const STARTER_APP_NAME = "builder-agent-native-starter";
|
|
9
9
|
export const CHAT_TEMPLATE = "chat";
|
|
10
10
|
|
|
11
|
+
/** Toolchain files that must track templates/chat or typecheck/build drift. */
|
|
12
|
+
export const STARTER_TOOLCHAIN_SYNC_PATHS = [
|
|
13
|
+
"react-router.config.ts",
|
|
14
|
+
"vite.config.ts",
|
|
15
|
+
"tsconfig.json",
|
|
16
|
+
"ssr-entry.ts",
|
|
17
|
+
"app/vite-env.d.ts",
|
|
18
|
+
"app/routes.ts",
|
|
19
|
+
"server/routes/[...page].get.ts",
|
|
20
|
+
// server/plugins/* are intentionally excluded: starter keeps its own
|
|
21
|
+
// systemPrompt and auth marketing copy; post-process only rewrites appId/title.
|
|
22
|
+
"server/middleware/auth.ts",
|
|
23
|
+
"components.json",
|
|
24
|
+
".oxfmtrc.json",
|
|
25
|
+
"netlify.toml",
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
export type StarterToolchainSyncPath =
|
|
29
|
+
(typeof STARTER_TOOLCHAIN_SYNC_PATHS)[number];
|
|
30
|
+
|
|
11
31
|
type PackageJson = Record<string, unknown>;
|
|
12
32
|
|
|
33
|
+
export type StandaloneChatSnapshot = {
|
|
34
|
+
cleanup: () => void;
|
|
35
|
+
dir: string;
|
|
36
|
+
packageJson: PackageJson;
|
|
37
|
+
pnpmWorkspaceYaml: string | null;
|
|
38
|
+
toolchainFiles: Map<StarterToolchainSyncPath, string>;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export function listStarterSyncPaths(): string[] {
|
|
42
|
+
return [
|
|
43
|
+
"package.json",
|
|
44
|
+
"pnpm-lock.yaml",
|
|
45
|
+
"pnpm-workspace.yaml",
|
|
46
|
+
...STARTER_TOOLCHAIN_SYNC_PATHS,
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
|
|
13
50
|
export function findAgentNativeRoot(startDir = process.cwd()): string {
|
|
14
51
|
let dir = path.resolve(startDir);
|
|
15
52
|
for (let i = 0; i < 12; i++) {
|
|
@@ -31,32 +68,62 @@ export function findAgentNativeRoot(startDir = process.cwd()): string {
|
|
|
31
68
|
);
|
|
32
69
|
}
|
|
33
70
|
|
|
34
|
-
export function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
71
|
+
export function collectStarterToolchainFiles(
|
|
72
|
+
canonicalDir: string,
|
|
73
|
+
): Map<StarterToolchainSyncPath, string> {
|
|
74
|
+
const files = new Map<StarterToolchainSyncPath, string>();
|
|
75
|
+
for (const relativePath of STARTER_TOOLCHAIN_SYNC_PATHS) {
|
|
76
|
+
const absolutePath = path.join(canonicalDir, relativePath);
|
|
77
|
+
if (!fs.existsSync(absolutePath)) continue;
|
|
78
|
+
files.set(relativePath, fs.readFileSync(absolutePath, "utf-8"));
|
|
79
|
+
}
|
|
80
|
+
return files;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function createStandaloneChatSnapshot(
|
|
84
|
+
repoRoot?: string,
|
|
85
|
+
): StandaloneChatSnapshot {
|
|
38
86
|
const root = repoRoot ?? findAgentNativeRoot();
|
|
39
87
|
const chatTemplateDir = path.join(root, "templates", CHAT_TEMPLATE);
|
|
40
88
|
const tempDir = fs.mkdtempSync(
|
|
41
89
|
path.join(os.tmpdir(), "an-builder-starter-sync-"),
|
|
42
90
|
);
|
|
43
91
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
_postProcessStandalone(STARTER_APP_NAME, tempDir, CHAT_TEMPLATE);
|
|
92
|
+
fs.cpSync(chatTemplateDir, tempDir, { recursive: true });
|
|
93
|
+
_postProcessStandalone(STARTER_APP_NAME, tempDir, CHAT_TEMPLATE);
|
|
47
94
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
95
|
+
const packageJson = JSON.parse(
|
|
96
|
+
fs.readFileSync(path.join(tempDir, "package.json"), "utf-8"),
|
|
97
|
+
) as PackageJson;
|
|
51
98
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
99
|
+
const workspacePath = path.join(tempDir, "pnpm-workspace.yaml");
|
|
100
|
+
const pnpmWorkspaceYaml = fs.existsSync(workspacePath)
|
|
101
|
+
? fs.readFileSync(workspacePath, "utf-8")
|
|
102
|
+
: null;
|
|
56
103
|
|
|
57
|
-
|
|
104
|
+
return {
|
|
105
|
+
cleanup: () => {
|
|
106
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
107
|
+
},
|
|
108
|
+
dir: tempDir,
|
|
109
|
+
packageJson,
|
|
110
|
+
pnpmWorkspaceYaml,
|
|
111
|
+
toolchainFiles: collectStarterToolchainFiles(tempDir),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function generateStandaloneChatManifest(repoRoot?: string): {
|
|
116
|
+
packageJson: PackageJson;
|
|
117
|
+
pnpmWorkspaceYaml: string | null;
|
|
118
|
+
} {
|
|
119
|
+
const snapshot = createStandaloneChatSnapshot(repoRoot);
|
|
120
|
+
try {
|
|
121
|
+
return {
|
|
122
|
+
packageJson: snapshot.packageJson,
|
|
123
|
+
pnpmWorkspaceYaml: snapshot.pnpmWorkspaceYaml,
|
|
124
|
+
};
|
|
58
125
|
} finally {
|
|
59
|
-
|
|
126
|
+
snapshot.cleanup();
|
|
60
127
|
}
|
|
61
128
|
}
|
|
62
129
|
|
|
@@ -144,73 +211,185 @@ function stableJson(value: unknown): string {
|
|
|
144
211
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
145
212
|
}
|
|
146
213
|
|
|
214
|
+
export type StarterToolchainSyncChange = {
|
|
215
|
+
relativePath: StarterToolchainSyncPath;
|
|
216
|
+
changed: boolean;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export function diffStarterToolchainFiles(
|
|
220
|
+
starterDir: string,
|
|
221
|
+
canonicalFiles: Map<StarterToolchainSyncPath, string>,
|
|
222
|
+
): StarterToolchainSyncChange[] {
|
|
223
|
+
return STARTER_TOOLCHAIN_SYNC_PATHS.map((relativePath) => {
|
|
224
|
+
const canonicalContent = canonicalFiles.get(relativePath) ?? null;
|
|
225
|
+
const targetPath = path.join(starterDir, relativePath);
|
|
226
|
+
const existingContent = fs.existsSync(targetPath)
|
|
227
|
+
? fs.readFileSync(targetPath, "utf-8")
|
|
228
|
+
: null;
|
|
229
|
+
return {
|
|
230
|
+
relativePath,
|
|
231
|
+
changed: existingContent !== canonicalContent,
|
|
232
|
+
};
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function applyStarterToolchainSync(
|
|
237
|
+
starterDir: string,
|
|
238
|
+
canonicalFiles: Map<StarterToolchainSyncPath, string>,
|
|
239
|
+
): StarterToolchainSyncChange[] {
|
|
240
|
+
const changes: StarterToolchainSyncChange[] = [];
|
|
241
|
+
for (const relativePath of STARTER_TOOLCHAIN_SYNC_PATHS) {
|
|
242
|
+
const canonicalContent = canonicalFiles.get(relativePath) ?? null;
|
|
243
|
+
const targetPath = path.join(starterDir, relativePath);
|
|
244
|
+
const existingContent = fs.existsSync(targetPath)
|
|
245
|
+
? fs.readFileSync(targetPath, "utf-8")
|
|
246
|
+
: null;
|
|
247
|
+
const changed = existingContent !== canonicalContent;
|
|
248
|
+
if (changed) {
|
|
249
|
+
if (canonicalContent === null) {
|
|
250
|
+
if (fs.existsSync(targetPath)) {
|
|
251
|
+
fs.unlinkSync(targetPath);
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
255
|
+
fs.writeFileSync(targetPath, canonicalContent);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
changes.push({ relativePath, changed });
|
|
259
|
+
}
|
|
260
|
+
return changes;
|
|
261
|
+
}
|
|
262
|
+
|
|
147
263
|
export type SyncStarterManifestResult = {
|
|
148
264
|
changed: boolean;
|
|
265
|
+
packageChanged: boolean;
|
|
266
|
+
workspaceChanged: boolean;
|
|
149
267
|
packageJson: PackageJson;
|
|
150
268
|
pnpmWorkspaceYaml: string | null;
|
|
269
|
+
toolchainChanges: StarterToolchainSyncChange[];
|
|
270
|
+
changedToolchainPaths: StarterToolchainSyncPath[];
|
|
151
271
|
};
|
|
152
272
|
|
|
153
|
-
export function
|
|
273
|
+
export function resolveStarterPaths(options: {
|
|
274
|
+
starterDir?: string;
|
|
275
|
+
starterPackageJsonPath?: string;
|
|
276
|
+
starterPnpmWorkspacePath?: string;
|
|
277
|
+
}): {
|
|
278
|
+
starterDir: string;
|
|
154
279
|
starterPackageJsonPath: string;
|
|
280
|
+
starterPnpmWorkspacePath: string;
|
|
281
|
+
} {
|
|
282
|
+
if (options.starterDir) {
|
|
283
|
+
return {
|
|
284
|
+
starterDir: path.resolve(options.starterDir),
|
|
285
|
+
starterPackageJsonPath: path.join(
|
|
286
|
+
path.resolve(options.starterDir),
|
|
287
|
+
"package.json",
|
|
288
|
+
),
|
|
289
|
+
starterPnpmWorkspacePath: path.join(
|
|
290
|
+
path.resolve(options.starterDir),
|
|
291
|
+
"pnpm-workspace.yaml",
|
|
292
|
+
),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
if (!options.starterPackageJsonPath) {
|
|
296
|
+
throw new Error("Provide --starter-dir or --starter-package-json.");
|
|
297
|
+
}
|
|
298
|
+
const starterPackageJsonPath = path.resolve(options.starterPackageJsonPath);
|
|
299
|
+
return {
|
|
300
|
+
starterDir: path.dirname(starterPackageJsonPath),
|
|
301
|
+
starterPackageJsonPath,
|
|
302
|
+
starterPnpmWorkspacePath:
|
|
303
|
+
options.starterPnpmWorkspacePath ??
|
|
304
|
+
path.join(path.dirname(starterPackageJsonPath), "pnpm-workspace.yaml"),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function syncStarterManifestFiles(options: {
|
|
309
|
+
starterDir?: string;
|
|
310
|
+
starterPackageJsonPath?: string;
|
|
155
311
|
starterPnpmWorkspacePath?: string;
|
|
156
312
|
repoRoot?: string;
|
|
157
313
|
write?: boolean;
|
|
158
314
|
}): SyncStarterManifestResult {
|
|
159
|
-
const {
|
|
160
|
-
|
|
315
|
+
const { starterDir, starterPackageJsonPath, starterPnpmWorkspacePath } =
|
|
316
|
+
resolveStarterPaths(options);
|
|
317
|
+
const snapshot = createStandaloneChatSnapshot(options.repoRoot);
|
|
161
318
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
319
|
+
try {
|
|
320
|
+
const starterPackageJson = JSON.parse(
|
|
321
|
+
fs.readFileSync(starterPackageJsonPath, "utf-8"),
|
|
322
|
+
) as PackageJson;
|
|
323
|
+
const mergedPackageJson = mergeStarterManifest(
|
|
324
|
+
starterPackageJson,
|
|
325
|
+
snapshot.packageJson,
|
|
326
|
+
);
|
|
166
327
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
options.starterPnpmWorkspacePath &&
|
|
170
|
-
fs.existsSync(options.starterPnpmWorkspacePath)
|
|
171
|
-
? fs.readFileSync(options.starterPnpmWorkspacePath, "utf-8")
|
|
328
|
+
const existingWorkspace = fs.existsSync(starterPnpmWorkspacePath)
|
|
329
|
+
? fs.readFileSync(starterPnpmWorkspacePath, "utf-8")
|
|
172
330
|
: null;
|
|
173
331
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
)
|
|
332
|
+
const workspaceChanged = workspaceFileSyncChanged(
|
|
333
|
+
existingWorkspace,
|
|
334
|
+
snapshot.pnpmWorkspaceYaml,
|
|
335
|
+
);
|
|
336
|
+
const packageChanged =
|
|
337
|
+
stableJson(starterPackageJson) !== stableJson(mergedPackageJson);
|
|
338
|
+
const toolchainChanges = diffStarterToolchainFiles(
|
|
339
|
+
starterDir,
|
|
340
|
+
snapshot.toolchainFiles,
|
|
341
|
+
);
|
|
342
|
+
const changedToolchainPaths = toolchainChanges
|
|
343
|
+
.filter((change) => change.changed)
|
|
344
|
+
.map((change) => change.relativePath);
|
|
345
|
+
const changed =
|
|
346
|
+
packageChanged || workspaceChanged || changedToolchainPaths.length > 0;
|
|
347
|
+
|
|
348
|
+
if (options.write && changed) {
|
|
349
|
+
if (packageChanged) {
|
|
350
|
+
fs.writeFileSync(starterPackageJsonPath, stableJson(mergedPackageJson));
|
|
351
|
+
}
|
|
352
|
+
if (workspaceChanged) {
|
|
353
|
+
applyWorkspaceFileSync(
|
|
354
|
+
starterPnpmWorkspacePath,
|
|
355
|
+
snapshot.pnpmWorkspaceYaml,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
if (changedToolchainPaths.length > 0) {
|
|
359
|
+
applyStarterToolchainSync(starterDir, snapshot.toolchainFiles);
|
|
360
|
+
}
|
|
195
361
|
}
|
|
196
|
-
}
|
|
197
362
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
363
|
+
return {
|
|
364
|
+
changed,
|
|
365
|
+
packageChanged,
|
|
366
|
+
workspaceChanged,
|
|
367
|
+
packageJson: mergedPackageJson,
|
|
368
|
+
pnpmWorkspaceYaml: snapshot.pnpmWorkspaceYaml,
|
|
369
|
+
toolchainChanges,
|
|
370
|
+
changedToolchainPaths,
|
|
371
|
+
};
|
|
372
|
+
} finally {
|
|
373
|
+
snapshot.cleanup();
|
|
374
|
+
}
|
|
203
375
|
}
|
|
204
376
|
|
|
205
377
|
export function parseSyncStarterManifestArgs(argv: string[]): {
|
|
206
|
-
command: "merge" | "generate";
|
|
378
|
+
command: "merge" | "generate" | "paths";
|
|
379
|
+
starterDir?: string;
|
|
207
380
|
starterPackageJsonPath?: string;
|
|
208
381
|
starterPnpmWorkspacePath?: string;
|
|
209
382
|
write: boolean;
|
|
210
383
|
repoRoot?: string;
|
|
211
384
|
} {
|
|
212
385
|
const [commandRaw, ...rest] = argv;
|
|
213
|
-
const command =
|
|
386
|
+
const command =
|
|
387
|
+
commandRaw === "generate"
|
|
388
|
+
? "generate"
|
|
389
|
+
: commandRaw === "paths"
|
|
390
|
+
? "paths"
|
|
391
|
+
: "merge";
|
|
392
|
+
let starterDir: string | undefined;
|
|
214
393
|
let starterPackageJsonPath: string | undefined;
|
|
215
394
|
let starterPnpmWorkspacePath: string | undefined;
|
|
216
395
|
let write = false;
|
|
@@ -222,6 +401,10 @@ export function parseSyncStarterManifestArgs(argv: string[]): {
|
|
|
222
401
|
write = true;
|
|
223
402
|
continue;
|
|
224
403
|
}
|
|
404
|
+
if (arg === "--starter-dir") {
|
|
405
|
+
starterDir = rest[++i];
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
225
408
|
if (arg === "--starter-package-json") {
|
|
226
409
|
starterPackageJsonPath = rest[++i];
|
|
227
410
|
continue;
|
|
@@ -237,14 +420,15 @@ export function parseSyncStarterManifestArgs(argv: string[]): {
|
|
|
237
420
|
throw new Error(`Unknown argument: ${arg}`);
|
|
238
421
|
}
|
|
239
422
|
|
|
240
|
-
if (command === "merge" && !starterPackageJsonPath) {
|
|
423
|
+
if (command === "merge" && !starterDir && !starterPackageJsonPath) {
|
|
241
424
|
throw new Error(
|
|
242
|
-
"merge requires --starter-package-json <path> [--starter-pnpm-workspace <path>] [--write] [--repo-root <path>]",
|
|
425
|
+
"merge requires --starter-dir <path> or --starter-package-json <path> [--starter-pnpm-workspace <path>] [--write] [--repo-root <path>]",
|
|
243
426
|
);
|
|
244
427
|
}
|
|
245
428
|
|
|
246
429
|
return {
|
|
247
430
|
command,
|
|
431
|
+
starterDir,
|
|
248
432
|
starterPackageJsonPath,
|
|
249
433
|
starterPnpmWorkspacePath,
|
|
250
434
|
write,
|
|
@@ -267,25 +451,37 @@ export function runSyncStarterManifestCli(argv: string[]): number {
|
|
|
267
451
|
return 0;
|
|
268
452
|
}
|
|
269
453
|
|
|
454
|
+
if (args.command === "paths") {
|
|
455
|
+
for (const syncPath of listStarterSyncPaths()) {
|
|
456
|
+
process.stdout.write(`${syncPath}\n`);
|
|
457
|
+
}
|
|
458
|
+
return 0;
|
|
459
|
+
}
|
|
460
|
+
|
|
270
461
|
const result = syncStarterManifestFiles({
|
|
271
|
-
|
|
462
|
+
starterDir: args.starterDir,
|
|
463
|
+
starterPackageJsonPath: args.starterPackageJsonPath,
|
|
272
464
|
starterPnpmWorkspacePath: args.starterPnpmWorkspacePath,
|
|
273
465
|
repoRoot: args.repoRoot,
|
|
274
466
|
write: args.write,
|
|
275
467
|
});
|
|
276
468
|
|
|
277
469
|
if (result.changed) {
|
|
470
|
+
const updatedPaths = [
|
|
471
|
+
...(result.packageChanged ? ["package.json"] : []),
|
|
472
|
+
...(result.workspaceChanged ? ["pnpm-workspace.yaml"] : []),
|
|
473
|
+
...result.changedToolchainPaths,
|
|
474
|
+
];
|
|
475
|
+
const summary = updatedPaths.length ? ` (${updatedPaths.join(", ")})` : "";
|
|
278
476
|
console.log(
|
|
279
477
|
args.write
|
|
280
|
-
?
|
|
281
|
-
:
|
|
478
|
+
? `Updated builder-agent-native-starter from templates/chat${summary}.`
|
|
479
|
+
: `builder-agent-native-starter is out of date with templates/chat${summary}.`,
|
|
282
480
|
);
|
|
283
481
|
return args.write ? 0 : 1;
|
|
284
482
|
}
|
|
285
483
|
|
|
286
|
-
console.log(
|
|
287
|
-
"builder-agent-native-starter manifest already matches templates/chat.",
|
|
288
|
-
);
|
|
484
|
+
console.log("builder-agent-native-starter already matches templates/chat.");
|
|
289
485
|
return 0;
|
|
290
486
|
}
|
|
291
487
|
|
|
@@ -102,7 +102,7 @@ function pruneIfEmpty(docId: string, map: Map<number, AwarenessEntry>): void {
|
|
|
102
102
|
*
|
|
103
103
|
* Client sends its awareness state and receives other clients' states.
|
|
104
104
|
*
|
|
105
|
-
* Body: { clientId: number, state: string (
|
|
105
|
+
* Body: { clientId: number, state: string | null (JSON-encoded awareness state, or null to clear) }
|
|
106
106
|
* Response: { states: Array<{ clientId: number, state: string }> }
|
|
107
107
|
*/
|
|
108
108
|
export const postAwareness = defineEventHandler(async (event: H3Event) => {
|
|
@@ -115,28 +115,31 @@ export const postAwareness = defineEventHandler(async (event: H3Event) => {
|
|
|
115
115
|
const body = await readBody(event);
|
|
116
116
|
const { clientId, state } = body as {
|
|
117
117
|
clientId?: number;
|
|
118
|
-
state?: string;
|
|
118
|
+
state?: string | null;
|
|
119
119
|
};
|
|
120
120
|
|
|
121
|
-
if (clientId == null ||
|
|
121
|
+
if (clientId == null || state === undefined) {
|
|
122
122
|
// `!clientId` would wrongly reject clientId === 0, which is a valid
|
|
123
|
-
// (if rare) Yjs client id.
|
|
123
|
+
// (if rare) Yjs client id. A null state is valid: it clears this client.
|
|
124
124
|
setResponseStatus(event, 400);
|
|
125
125
|
return { error: "clientId and state required" };
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
const map = getDocAwareness(docId);
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
if (state === null) {
|
|
131
|
+
map.delete(clientId);
|
|
132
|
+
} else {
|
|
133
|
+
// Store this client's state
|
|
134
|
+
map.set(clientId, { clientId, state, lastSeen: Date.now() });
|
|
135
|
+
}
|
|
132
136
|
|
|
133
137
|
// Clean expired entries, then prune the outer-map entry if it becomes empty.
|
|
134
138
|
// Without pruning, a deployment with many transient docIds (e.g. one per
|
|
135
139
|
// session) would grow _awarenessMap without bound.
|
|
136
140
|
cleanExpired(map);
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
// that was immediately evicted by a concurrent cleanExpired run).
|
|
141
|
+
// Null-state clears and expiry can empty the map; prune the outer entry so
|
|
142
|
+
// transient document ids do not accumulate.
|
|
140
143
|
pruneIfEmpty(docId, map);
|
|
141
144
|
|
|
142
145
|
// Build the full list of current states (all clients including sender).
|
|
@@ -276,11 +276,13 @@ function scheduleAwarenessPush(
|
|
|
276
276
|
const timer = setTimeout(() => {
|
|
277
277
|
_awarenessThrottleTimers.delete(key);
|
|
278
278
|
const state = getState();
|
|
279
|
-
if (!state) return;
|
|
280
279
|
fetch(`${baseUrl}/${docId}/awareness`, {
|
|
281
280
|
method: "POST",
|
|
282
281
|
headers: { "Content-Type": "application/json" },
|
|
283
|
-
body: JSON.stringify({
|
|
282
|
+
body: JSON.stringify({
|
|
283
|
+
clientId,
|
|
284
|
+
state: state ? JSON.stringify(state) : null,
|
|
285
|
+
}),
|
|
284
286
|
}).catch(() => {}); // best-effort; poll cycle is the baseline fallback
|
|
285
287
|
}, 150);
|
|
286
288
|
|
|
@@ -349,7 +349,7 @@ export default defineAction({
|
|
|
349
349
|
|
|
350
350
|
if (!hasPlayableMp4Metadata(uploadData)) {
|
|
351
351
|
const err = new Error(
|
|
352
|
-
"Recorded MP4 is
|
|
352
|
+
"Recorded MP4 is corrupted or incomplete and cannot be recovered. Please record again.",
|
|
353
353
|
);
|
|
354
354
|
try {
|
|
355
355
|
captureRouteError(err, {
|