@agent-native/core 0.84.10 → 0.84.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 +37 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/run-store.ts +29 -6
- package/corpus/core/src/client/require-session.tsx +20 -1
- package/corpus/core/src/client/sharing/ShareButton.tsx +9 -3
- package/corpus/core/src/collab/storage.ts +40 -5
- package/corpus/core/src/server/auth.ts +6 -0
- package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +7 -6
- package/corpus/templates/assets/AGENTS.md +15 -0
- package/corpus/templates/assets/actions/_helpers.ts +3 -1
- package/corpus/templates/assets/actions/create-generation-preset.ts +12 -1
- package/corpus/templates/assets/actions/generate-asset.ts +5 -1
- package/corpus/templates/assets/actions/generate-image-batch.ts +6 -1
- package/corpus/templates/assets/actions/generate-image.ts +18 -11
- package/corpus/templates/assets/actions/open-asset-picker.ts +4 -2
- package/corpus/templates/assets/actions/update-generation-preset.ts +16 -3
- package/corpus/templates/assets/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/assets/app/i18n-data.ts +30 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +42 -7
- package/corpus/templates/assets/app/routes/library.tsx +4 -2
- package/corpus/templates/assets/changelog/2026-07-01-generation-presets-can-now-composite-your-brand-s-canonical-.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-01-tagging-a-preset-now-briefs-the-agent-on-that-preset-s-aesth.md +6 -0
- package/corpus/templates/assets/server/lib/preset-chat-context.ts +118 -0
- package/corpus/templates/assets/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/assets/shared/api.ts +1 -0
- package/corpus/templates/design/actions/export-html.ts +4 -2
- package/corpus/templates/design/actions/export-pdf.ts +3 -1
- package/corpus/templates/design/actions/export-svg.ts +4 -2
- package/corpus/templates/design/actions/export-zip.ts +10 -3
- package/corpus/templates/design/actions/update-file.ts +68 -17
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +3 -2
- package/corpus/templates/design/app/components/design/EditPanel.tsx +35 -3
- package/corpus/templates/design/app/components/design/MotionDock.tsx +46 -11
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +80 -30
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +60 -10
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +2 -0
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +4 -0
- package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +2 -2
- package/corpus/templates/design/app/i18n-data.ts +1 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +491 -107
- package/corpus/templates/design/app/pages/VisualEdit.tsx +4 -16
- package/corpus/templates/design/e2e/helpers.ts +2 -2
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +28 -6
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/require-session.d.ts +14 -0
- package/dist/client/require-session.d.ts.map +1 -1
- package/dist/client/require-session.js +19 -1
- package/dist/client/require-session.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +4 -2
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +3 -1
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/storage.d.ts.map +1 -1
- package/dist/collab/storage.js +31 -5
- package/dist/collab/storage.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/observability/routes.d.ts +8 -8
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +7 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
package/dist/collab/routes.d.ts
CHANGED
|
@@ -41,9 +41,9 @@ export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import
|
|
|
41
41
|
* Body: { text: string, fieldName?: string, requestSource?: string }
|
|
42
42
|
*/
|
|
43
43
|
export declare const postCollabText: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
44
|
+
text?: undefined;
|
|
44
45
|
ok?: undefined;
|
|
45
46
|
error: string;
|
|
46
|
-
text?: undefined;
|
|
47
47
|
} | {
|
|
48
48
|
error?: undefined;
|
|
49
49
|
ok: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/collab/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/collab/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyFH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,0DAA0D;AAC1D,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAYjC;AAED,0DAA0D;AAC1D,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAG7E;AAED,gFAAgF;AAChF,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,UAAU,EACjB,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM,GAAG,IAAI,GAC7B,OAAO,CAAC,OAAO,CAAC,CAoBlB;AAED,6DAA6D;AAC7D,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,UAAU,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED,mDAAmD;AACnD,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQpE;AAED,iDAAiD;AACjD,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpE;AAID,iBAAS,kBAAkB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAUnD;AAED,iBAAS,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAUnD;AAED,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAC"}
|
package/dist/collab/storage.js
CHANGED
|
@@ -7,6 +7,32 @@
|
|
|
7
7
|
import { getDbExec, isPostgres } from "../db/client.js";
|
|
8
8
|
import { ensureTableExists, ensureColumnExists } from "../db/ddl-guard.js";
|
|
9
9
|
let _initPromise;
|
|
10
|
+
function isSqliteBusyError(error) {
|
|
11
|
+
if (isPostgres())
|
|
12
|
+
return false;
|
|
13
|
+
const candidate = error;
|
|
14
|
+
return (candidate?.code === "SQLITE_BUSY" ||
|
|
15
|
+
candidate?.code === "SQLITE_BUSY_RECOVERY" ||
|
|
16
|
+
(typeof candidate?.message === "string" &&
|
|
17
|
+
/database is locked|SQLITE_BUSY/i.test(candidate.message)));
|
|
18
|
+
}
|
|
19
|
+
function sleep(ms) {
|
|
20
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
21
|
+
}
|
|
22
|
+
async function executeWithSqliteRetry(client, statement) {
|
|
23
|
+
const delays = [25, 75, 150, 300, 600];
|
|
24
|
+
for (let attempt = 0;; attempt += 1) {
|
|
25
|
+
try {
|
|
26
|
+
return await client.execute(statement);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const delay = delays[attempt];
|
|
30
|
+
if (delay === undefined || !isSqliteBusyError(error))
|
|
31
|
+
throw error;
|
|
32
|
+
await sleep(delay);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
10
36
|
async function ensureTable() {
|
|
11
37
|
if (!_initPromise) {
|
|
12
38
|
_initPromise = (async () => {
|
|
@@ -73,7 +99,7 @@ export async function trySaveYDocState(docId, state, textSnapshot, expectedVersi
|
|
|
73
99
|
const b64 = uint8ArrayToBase64(state);
|
|
74
100
|
const nowExpr = isPostgres() ? "NOW()::text" : "datetime('now')";
|
|
75
101
|
if (expectedVersion === null) {
|
|
76
|
-
const result = await client
|
|
102
|
+
const result = await executeWithSqliteRetry(client, {
|
|
77
103
|
sql: isPostgres()
|
|
78
104
|
? `INSERT INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr}) ON CONFLICT (doc_id) DO NOTHING`
|
|
79
105
|
: `INSERT OR IGNORE INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr})`,
|
|
@@ -81,7 +107,7 @@ export async function trySaveYDocState(docId, state, textSnapshot, expectedVersi
|
|
|
81
107
|
});
|
|
82
108
|
return result.rowsAffected > 0;
|
|
83
109
|
}
|
|
84
|
-
const result = await client
|
|
110
|
+
const result = await executeWithSqliteRetry(client, {
|
|
85
111
|
sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ? AND version = ?`,
|
|
86
112
|
args: [b64, textSnapshot, docId, expectedVersion],
|
|
87
113
|
});
|
|
@@ -93,13 +119,13 @@ export async function saveYDocState(docId, state, textSnapshot) {
|
|
|
93
119
|
const client = getDbExec();
|
|
94
120
|
const b64 = uint8ArrayToBase64(state);
|
|
95
121
|
const nowExpr = isPostgres() ? "NOW()::text" : "datetime('now')";
|
|
96
|
-
const updated = await client
|
|
122
|
+
const updated = await executeWithSqliteRetry(client, {
|
|
97
123
|
sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ?`,
|
|
98
124
|
args: [b64, textSnapshot, docId],
|
|
99
125
|
});
|
|
100
126
|
if (updated.rowsAffected > 0)
|
|
101
127
|
return;
|
|
102
|
-
const inserted = await client
|
|
128
|
+
const inserted = await executeWithSqliteRetry(client, {
|
|
103
129
|
sql: isPostgres()
|
|
104
130
|
? `INSERT INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr}) ON CONFLICT (doc_id) DO NOTHING`
|
|
105
131
|
: `INSERT OR IGNORE INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr})`,
|
|
@@ -107,7 +133,7 @@ export async function saveYDocState(docId, state, textSnapshot) {
|
|
|
107
133
|
});
|
|
108
134
|
if (inserted.rowsAffected > 0)
|
|
109
135
|
return;
|
|
110
|
-
await client
|
|
136
|
+
await executeWithSqliteRetry(client, {
|
|
111
137
|
sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ?`,
|
|
112
138
|
args: [b64, textSnapshot, docId],
|
|
113
139
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/collab/storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SQL storage for Yjs collaborative document state.\n *\n * Uses a framework-level `_collab_docs` table (TEXT columns with base64\n * encoding for binary Yjs state) that works across SQLite and Postgres.\n */\n\nimport { getDbExec, isPostgres } from \"../db/client.js\";\nimport { ensureTableExists, ensureColumnExists } from \"../db/ddl-guard.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const nowDefault = isPostgres() ? \"NOW()::text\" : \"datetime('now')\";\n const createSql = `\n CREATE TABLE IF NOT EXISTS _collab_docs (\n doc_id TEXT PRIMARY KEY,\n yjs_state TEXT NOT NULL,\n text_snapshot TEXT NOT NULL DEFAULT '',\n version INTEGER NOT NULL DEFAULT 0,\n updated_at TEXT NOT NULL DEFAULT (${nowDefault})\n )\n `;\n\n if (isPostgres()) {\n // PG-guard: probe information_schema first (no lock) and only issue\n // DDL when the table/column is actually missing, wrapped in a\n // transaction-scoped lock_timeout so a contended lock fails fast.\n await ensureTableExists(\"_collab_docs\", createSql);\n await ensureColumnExists(\n \"_collab_docs\",\n \"version\",\n `ALTER TABLE _collab_docs ADD COLUMN IF NOT EXISTS version INTEGER NOT NULL DEFAULT 0`,\n );\n return;\n }\n\n // SQLite (local dev): no ACCESS EXCLUSIVE lock problem — keep existing\n // create-then-additive-alter behaviour.\n await client.execute(createSql);\n try {\n await client.execute(\n `ALTER TABLE _collab_docs ADD COLUMN version INTEGER NOT NULL DEFAULT 0`,\n );\n } catch {\n // Existing deployments already have the column after the first run.\n }\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport interface YDocStateRecord {\n state: Uint8Array;\n version: number;\n}\n\n/** Load Yjs state plus optimistic concurrency version. */\nexport async function loadYDocRecord(\n docId: string,\n): Promise<YDocStateRecord | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT yjs_state, version FROM _collab_docs WHERE doc_id = ?`,\n args: [docId],\n });\n if (rows.length === 0) return null;\n return {\n state: base64ToUint8Array(rows[0].yjs_state as string),\n version: Number(rows[0].version ?? 0),\n };\n}\n\n/** Load Yjs state as Uint8Array, or null if not found. */\nexport async function loadYDocState(docId: string): Promise<Uint8Array | null> {\n const record = await loadYDocRecord(docId);\n return record?.state ?? null;\n}\n\n/** Save only if the stored row still has the version the caller merged from. */\nexport async function trySaveYDocState(\n docId: string,\n state: Uint8Array,\n textSnapshot: string,\n expectedVersion: number | null,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const b64 = uint8ArrayToBase64(state);\n const nowExpr = isPostgres() ? \"NOW()::text\" : \"datetime('now')\";\n if (expectedVersion === null) {\n const result = await client.execute({\n sql: isPostgres()\n ? `INSERT INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr}) ON CONFLICT (doc_id) DO NOTHING`\n : `INSERT OR IGNORE INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr})`,\n args: [docId, b64, textSnapshot],\n });\n return result.rowsAffected > 0;\n }\n\n const result = await client.execute({\n sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ? AND version = ?`,\n args: [b64, textSnapshot, docId, expectedVersion],\n });\n return result.rowsAffected > 0;\n}\n\n/** Save Yjs state (Uint8Array) and a plain-text snapshot. */\nexport async function saveYDocState(\n docId: string,\n state: Uint8Array,\n textSnapshot: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const b64 = uint8ArrayToBase64(state);\n const nowExpr = isPostgres() ? \"NOW()::text\" : \"datetime('now')\";\n const updated = await client.execute({\n sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ?`,\n args: [b64, textSnapshot, docId],\n });\n if (updated.rowsAffected > 0) return;\n\n const inserted = await client.execute({\n sql: isPostgres()\n ? `INSERT INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr}) ON CONFLICT (doc_id) DO NOTHING`\n : `INSERT OR IGNORE INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr})`,\n args: [docId, b64, textSnapshot],\n });\n if (inserted.rowsAffected > 0) return;\n\n await client.execute({\n sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ?`,\n args: [b64, textSnapshot, docId],\n });\n}\n\n/** Check if a document has collaborative state. */\nexport async function hasCollabState(docId: string): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM _collab_docs WHERE doc_id = ?`,\n args: [docId],\n });\n return rows.length > 0;\n}\n\n/** Delete collaborative state for a document. */\nexport async function deleteCollabState(docId: string): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n await client.execute({\n sql: `DELETE FROM _collab_docs WHERE doc_id = ?`,\n args: [docId],\n });\n}\n\n// ─── Base64 helpers ──────────────────────────────────────────────────\n\nfunction uint8ArrayToBase64(arr: Uint8Array): string {\n // Works in both Node.js and edge runtimes\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(arr).toString(\"base64\");\n }\n let binary = \"\";\n for (let i = 0; i < arr.length; i++) {\n binary += String.fromCharCode(arr[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToUint8Array(b64: string): Uint8Array {\n if (typeof Buffer !== \"undefined\") {\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n }\n const binary = atob(b64);\n const arr = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n arr[i] = binary.charCodeAt(i);\n }\n return arr;\n}\n\nexport { uint8ArrayToBase64, base64ToUint8Array };\n"]}
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/collab/storage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SQL storage for Yjs collaborative document state.\n *\n * Uses a framework-level `_collab_docs` table (TEXT columns with base64\n * encoding for binary Yjs state) that works across SQLite and Postgres.\n */\n\nimport { getDbExec, isPostgres } from \"../db/client.js\";\nimport { ensureTableExists, ensureColumnExists } from \"../db/ddl-guard.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\ntype DbExecClient = ReturnType<typeof getDbExec>;\ntype DbExecuteArg = Parameters<DbExecClient[\"execute\"]>[0];\ntype DbExecuteResult = Awaited<ReturnType<DbExecClient[\"execute\"]>>;\n\nfunction isSqliteBusyError(error: unknown): boolean {\n if (isPostgres()) return false;\n const candidate = error as { code?: unknown; message?: unknown } | null;\n return (\n candidate?.code === \"SQLITE_BUSY\" ||\n candidate?.code === \"SQLITE_BUSY_RECOVERY\" ||\n (typeof candidate?.message === \"string\" &&\n /database is locked|SQLITE_BUSY/i.test(candidate.message))\n );\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function executeWithSqliteRetry(\n client: DbExecClient,\n statement: DbExecuteArg,\n): Promise<DbExecuteResult> {\n const delays = [25, 75, 150, 300, 600];\n for (let attempt = 0; ; attempt += 1) {\n try {\n return await client.execute(statement);\n } catch (error) {\n const delay = delays[attempt];\n if (delay === undefined || !isSqliteBusyError(error)) throw error;\n await sleep(delay);\n }\n }\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const nowDefault = isPostgres() ? \"NOW()::text\" : \"datetime('now')\";\n const createSql = `\n CREATE TABLE IF NOT EXISTS _collab_docs (\n doc_id TEXT PRIMARY KEY,\n yjs_state TEXT NOT NULL,\n text_snapshot TEXT NOT NULL DEFAULT '',\n version INTEGER NOT NULL DEFAULT 0,\n updated_at TEXT NOT NULL DEFAULT (${nowDefault})\n )\n `;\n\n if (isPostgres()) {\n // PG-guard: probe information_schema first (no lock) and only issue\n // DDL when the table/column is actually missing, wrapped in a\n // transaction-scoped lock_timeout so a contended lock fails fast.\n await ensureTableExists(\"_collab_docs\", createSql);\n await ensureColumnExists(\n \"_collab_docs\",\n \"version\",\n `ALTER TABLE _collab_docs ADD COLUMN IF NOT EXISTS version INTEGER NOT NULL DEFAULT 0`,\n );\n return;\n }\n\n // SQLite (local dev): no ACCESS EXCLUSIVE lock problem — keep existing\n // create-then-additive-alter behaviour.\n await client.execute(createSql);\n try {\n await client.execute(\n `ALTER TABLE _collab_docs ADD COLUMN version INTEGER NOT NULL DEFAULT 0`,\n );\n } catch {\n // Existing deployments already have the column after the first run.\n }\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport interface YDocStateRecord {\n state: Uint8Array;\n version: number;\n}\n\n/** Load Yjs state plus optimistic concurrency version. */\nexport async function loadYDocRecord(\n docId: string,\n): Promise<YDocStateRecord | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT yjs_state, version FROM _collab_docs WHERE doc_id = ?`,\n args: [docId],\n });\n if (rows.length === 0) return null;\n return {\n state: base64ToUint8Array(rows[0].yjs_state as string),\n version: Number(rows[0].version ?? 0),\n };\n}\n\n/** Load Yjs state as Uint8Array, or null if not found. */\nexport async function loadYDocState(docId: string): Promise<Uint8Array | null> {\n const record = await loadYDocRecord(docId);\n return record?.state ?? null;\n}\n\n/** Save only if the stored row still has the version the caller merged from. */\nexport async function trySaveYDocState(\n docId: string,\n state: Uint8Array,\n textSnapshot: string,\n expectedVersion: number | null,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const b64 = uint8ArrayToBase64(state);\n const nowExpr = isPostgres() ? \"NOW()::text\" : \"datetime('now')\";\n if (expectedVersion === null) {\n const result = await executeWithSqliteRetry(client, {\n sql: isPostgres()\n ? `INSERT INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr}) ON CONFLICT (doc_id) DO NOTHING`\n : `INSERT OR IGNORE INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr})`,\n args: [docId, b64, textSnapshot],\n });\n return result.rowsAffected > 0;\n }\n\n const result = await executeWithSqliteRetry(client, {\n sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ? AND version = ?`,\n args: [b64, textSnapshot, docId, expectedVersion],\n });\n return result.rowsAffected > 0;\n}\n\n/** Save Yjs state (Uint8Array) and a plain-text snapshot. */\nexport async function saveYDocState(\n docId: string,\n state: Uint8Array,\n textSnapshot: string,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const b64 = uint8ArrayToBase64(state);\n const nowExpr = isPostgres() ? \"NOW()::text\" : \"datetime('now')\";\n const updated = await executeWithSqliteRetry(client, {\n sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ?`,\n args: [b64, textSnapshot, docId],\n });\n if (updated.rowsAffected > 0) return;\n\n const inserted = await executeWithSqliteRetry(client, {\n sql: isPostgres()\n ? `INSERT INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr}) ON CONFLICT (doc_id) DO NOTHING`\n : `INSERT OR IGNORE INTO _collab_docs (doc_id, yjs_state, text_snapshot, version, updated_at) VALUES (?, ?, ?, 0, ${nowExpr})`,\n args: [docId, b64, textSnapshot],\n });\n if (inserted.rowsAffected > 0) return;\n\n await executeWithSqliteRetry(client, {\n sql: `UPDATE _collab_docs SET yjs_state = ?, text_snapshot = ?, version = version + 1, updated_at = ${nowExpr} WHERE doc_id = ?`,\n args: [b64, textSnapshot, docId],\n });\n}\n\n/** Check if a document has collaborative state. */\nexport async function hasCollabState(docId: string): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT 1 FROM _collab_docs WHERE doc_id = ?`,\n args: [docId],\n });\n return rows.length > 0;\n}\n\n/** Delete collaborative state for a document. */\nexport async function deleteCollabState(docId: string): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n await client.execute({\n sql: `DELETE FROM _collab_docs WHERE doc_id = ?`,\n args: [docId],\n });\n}\n\n// ─── Base64 helpers ──────────────────────────────────────────────────\n\nfunction uint8ArrayToBase64(arr: Uint8Array): string {\n // Works in both Node.js and edge runtimes\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(arr).toString(\"base64\");\n }\n let binary = \"\";\n for (let i = 0; i < arr.length; i++) {\n binary += String.fromCharCode(arr[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToUint8Array(b64: string): Uint8Array {\n if (typeof Buffer !== \"undefined\") {\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n }\n const binary = atob(b64);\n const arr = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n arr[i] = binary.charCodeAt(i);\n }\n return arr;\n}\n\nexport { uint8ArrayToBase64, base64ToUint8Array };\n"]}
|
|
@@ -3,18 +3,18 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
3
3
|
url?: string;
|
|
4
4
|
filename?: string;
|
|
5
5
|
}, {
|
|
6
|
-
id?: undefined;
|
|
7
6
|
error: string;
|
|
8
7
|
configured?: undefined;
|
|
9
8
|
connectPath?: undefined;
|
|
10
9
|
url?: undefined;
|
|
10
|
+
id?: undefined;
|
|
11
11
|
provider?: undefined;
|
|
12
12
|
} | {
|
|
13
|
-
id?: undefined;
|
|
14
13
|
error: string;
|
|
15
14
|
configured: boolean;
|
|
16
15
|
connectPath: string;
|
|
17
16
|
url?: undefined;
|
|
17
|
+
id?: undefined;
|
|
18
18
|
provider?: undefined;
|
|
19
19
|
} | {
|
|
20
20
|
error?: undefined;
|
|
@@ -41,28 +41,28 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
-
ok?: undefined;
|
|
45
|
-
error?: undefined;
|
|
46
44
|
summary: import("./types.js").TraceSummary;
|
|
47
45
|
spans: import("./types.js").TraceSpan[];
|
|
48
|
-
id?: undefined;
|
|
49
|
-
} | {
|
|
50
46
|
ok?: undefined;
|
|
51
47
|
error?: undefined;
|
|
48
|
+
id?: undefined;
|
|
49
|
+
} | {
|
|
52
50
|
summary?: undefined;
|
|
53
51
|
spans?: undefined;
|
|
54
52
|
id: string;
|
|
55
|
-
} | {
|
|
56
53
|
ok?: undefined;
|
|
54
|
+
error?: undefined;
|
|
55
|
+
} | {
|
|
57
56
|
summary?: undefined;
|
|
58
57
|
spans?: undefined;
|
|
59
|
-
id?: undefined;
|
|
60
58
|
error: any;
|
|
59
|
+
ok?: undefined;
|
|
60
|
+
id?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
error?: undefined;
|
|
63
62
|
summary?: undefined;
|
|
64
63
|
spans?: undefined;
|
|
65
|
-
id?: undefined;
|
|
66
64
|
ok: boolean;
|
|
65
|
+
error?: undefined;
|
|
66
|
+
id?: undefined;
|
|
67
67
|
}>>;
|
|
68
68
|
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -52,8 +52,8 @@ export declare function handleDeleteResource(event: any): Promise<{
|
|
|
52
52
|
error: string;
|
|
53
53
|
ok?: undefined;
|
|
54
54
|
} | {
|
|
55
|
-
ok: boolean;
|
|
56
55
|
error?: undefined;
|
|
56
|
+
ok: boolean;
|
|
57
57
|
}>;
|
|
58
58
|
/** POST /_agent-native/resources/upload — upload a file as a resource */
|
|
59
59
|
export declare function handleUploadResource(event: any): Promise<import("./store.js").Resource | {
|
|
@@ -73,9 +73,9 @@ export declare function handleUploadResource(event: any): Promise<import("./stor
|
|
|
73
73
|
runId: string | null;
|
|
74
74
|
expiresAt: number | null;
|
|
75
75
|
metadata: string | null;
|
|
76
|
+
error?: undefined;
|
|
76
77
|
url: string;
|
|
77
78
|
provider: string;
|
|
78
|
-
error?: undefined;
|
|
79
79
|
}>;
|
|
80
80
|
export {};
|
|
81
81
|
//# sourceMappingURL=handlers.d.ts.map
|
|
@@ -27,11 +27,11 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
-
error?: undefined;
|
|
31
30
|
ok: boolean;
|
|
32
31
|
key: string;
|
|
33
32
|
baseUrlKey?: string;
|
|
34
33
|
scope: AgentEngineApiKeyScope;
|
|
34
|
+
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
|
37
37
|
//# sourceMappingURL=agent-engine-api-key-route.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/server/auth.js
CHANGED
|
@@ -300,6 +300,13 @@ export function safeReturnPath(raw) {
|
|
|
300
300
|
const parsed = new URL(raw, "http://safe-base.invalid");
|
|
301
301
|
if (parsed.origin !== "http://safe-base.invalid")
|
|
302
302
|
return "/";
|
|
303
|
+
// Never return to the sign-in entry point itself. A `return` that resolves
|
|
304
|
+
// back to `…/_agent-native/sign-in` re-enters the redirect loop — each hop
|
|
305
|
+
// nests the prior sign-in URL as a fresh `?return=`. Collapse it to "/" so
|
|
306
|
+
// the post-sign-in 302 lands on the app, not another sign-in page. Matches
|
|
307
|
+
// with or without an app base-path prefix (`/<app>/_agent-native/sign-in`).
|
|
308
|
+
if (parsed.pathname.endsWith("/_agent-native/sign-in"))
|
|
309
|
+
return "/";
|
|
303
310
|
return parsed.pathname + parsed.search + parsed.hash;
|
|
304
311
|
}
|
|
305
312
|
catch {
|