@agent-native/core 0.114.0 → 0.114.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +9 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/build-compatibility.ts +77 -0
- package/corpus/core/src/client/route-chunk-recovery/index.ts +1 -0
- package/corpus/core/src/client/use-action.ts +32 -1
- package/corpus/core/src/deploy/build.ts +4 -1
- package/corpus/core/src/org/context.ts +56 -30
- package/corpus/core/src/server/action-routes.ts +52 -2
- package/corpus/core/src/server/auth-plugin.ts +9 -4
- package/corpus/core/src/server/better-auth-instance.ts +5 -1
- package/corpus/core/src/shared/mcp-embed-headers.ts +1 -1
- package/corpus/core/src/vite/client.ts +17 -0
- package/corpus/templates/content/AGENTS.md +2 -0
- package/corpus/templates/content/actions/_content-database-personal-view.ts +49 -5
- package/corpus/templates/content/actions/_content-document-access.ts +21 -0
- package/corpus/templates/content/actions/_content-favorites.ts +91 -0
- package/corpus/templates/content/actions/_content-files.ts +79 -23
- package/corpus/templates/content/actions/_content-sidebar-state.ts +24 -0
- package/corpus/templates/content/actions/_content-space-access.ts +15 -17
- package/corpus/templates/content/actions/_content-space-catalog-guards.ts +20 -0
- package/corpus/templates/content/actions/_content-spaces.ts +364 -167
- package/corpus/templates/content/actions/_database-source-utils.ts +8 -0
- package/corpus/templates/content/actions/_database-utils.ts +249 -15
- package/corpus/templates/content/actions/_delete-content-space.ts +84 -0
- package/corpus/templates/content/actions/_files-system-properties.ts +499 -0
- package/corpus/templates/content/actions/_local-file-documents.ts +0 -1
- package/corpus/templates/content/actions/_property-utils.ts +18 -9
- package/corpus/templates/content/actions/bind-content-database-source-field.ts +3 -0
- package/corpus/templates/content/actions/configure-document-property.ts +3 -0
- package/corpus/templates/content/actions/create-content-database.ts +2 -2
- package/corpus/templates/content/actions/create-content-space.ts +34 -0
- package/corpus/templates/content/actions/create-document.ts +4 -1
- package/corpus/templates/content/actions/delete-content-space.ts +19 -0
- package/corpus/templates/content/actions/delete-database-items.ts +28 -1
- package/corpus/templates/content/actions/delete-document-property.ts +3 -0
- package/corpus/templates/content/actions/delete-document.ts +39 -2
- package/corpus/templates/content/actions/duplicate-database-item.ts +6 -0
- package/corpus/templates/content/actions/duplicate-database-items.ts +6 -0
- package/corpus/templates/content/actions/duplicate-document-property.ts +3 -0
- package/corpus/templates/content/actions/get-content-database.ts +11 -2
- package/corpus/templates/content/actions/get-content-sidebar-state.ts +23 -0
- package/corpus/templates/content/actions/get-document.ts +32 -7
- package/corpus/templates/content/actions/import-content-source.ts +33 -1
- package/corpus/templates/content/actions/list-content-spaces.ts +34 -2
- package/corpus/templates/content/actions/list-documents.ts +39 -7
- package/corpus/templates/content/actions/reorder-document-property.ts +9 -0
- package/corpus/templates/content/actions/set-document-property.ts +41 -23
- package/corpus/templates/content/actions/share-local-file-document.ts +15 -0
- package/corpus/templates/content/actions/submit-content-database-form.ts +7 -0
- package/corpus/templates/content/actions/sync-local-folder-source.ts +14 -0
- package/corpus/templates/content/actions/update-content-database-view.ts +1 -0
- package/corpus/templates/content/actions/update-content-sidebar-state.ts +27 -0
- package/corpus/templates/content/actions/update-document.ts +158 -66
- package/corpus/templates/content/app/components/EmptyState.tsx +4 -57
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +8 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +519 -178
- package/corpus/templates/content/app/components/editor/DocumentInfoPanel.tsx +39 -0
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +1 -1
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +266 -7
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +430 -152
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +467 -66
- package/corpus/templates/content/app/components/editor/database/view-config.ts +9 -3
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +720 -196
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +173 -1
- package/corpus/templates/content/app/components/sidebar/document-sidebar-sections.ts +1 -1
- package/corpus/templates/content/app/components/sidebar/select-content-space.ts +91 -16
- package/corpus/templates/content/app/entry.client.tsx +5 -1
- package/corpus/templates/content/app/hooks/use-content-database.ts +84 -3
- package/corpus/templates/content/app/hooks/use-content-spaces.ts +60 -2
- package/corpus/templates/content/app/hooks/use-create-page.ts +30 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +270 -19
- package/corpus/templates/content/app/i18n-data.ts +243 -20
- package/corpus/templates/content/app/lib/local-folder-picker-safety.ts +54 -0
- package/corpus/templates/content/app/lib/local-folder-picker-support.ts +18 -0
- package/corpus/templates/content/app/routes/_app.favorites.tsx +30 -0
- package/corpus/templates/content/app/routes/_app.local-files.tsx +18 -6
- package/corpus/templates/content/changelog/2026-07-17-workspace-files-now-stay-in-a-loading-state-until-automatic-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-workspace-switching-now-opens-the-selected-files-database-pr.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-workspaces-now-expand-above-their-own-files-list-without-dup.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-create-named-workspaces-each-with-its-own-private-files-data.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-favorites-align-with-workspace-and-file-rows-in-the-sidebar.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-local-folder-selection-no-longer-invokes-the-unsafe-native-p.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-made-content-workspaces-independently-expandable-restored-or.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-page-details-now-live-in-a-shared-info-and-comments-rail-dat.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-workspace-database-views-now-control-sidebar-workspace-navig.md +6 -0
- package/corpus/templates/content/changelog/2026-07-18-workspace-navigation-now-aligns-favorites-with-workspace-pag.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-favorites-now-open-as-a-table-and-compact-breadcrumbs-make-w.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-files-databases-now-start-unfiltered-sidebar-rows-exactly-fo.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-sidebar-page-trees-now-open-only-when-expanded-remember-thei.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-workspace-file-databases-now-use-the-workspace-name-open-as-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-workspace-navigation-now-stays-stable-on-hover-uses-folder-i.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-workspace-rows-now-use-folder-icons-and-deleting-a-user-crea.md +6 -0
- package/corpus/templates/content/changelog/2026-07-19-workspaces-can-be-created-without-leaving-the-workspaces-dat.md +6 -0
- package/corpus/templates/content/e2e/global-setup.ts +3 -2
- package/corpus/templates/content/parity/matrix.md +28 -28
- package/corpus/templates/content/parity/matrix.ts +8 -2
- package/corpus/templates/content/server/db/index.ts +1 -0
- package/corpus/templates/content/server/db/schema.ts +10 -0
- package/corpus/templates/content/server/lib/documents.ts +12 -7
- package/corpus/templates/content/server/plugins/db.ts +17 -0
- package/corpus/templates/content/shared/api.ts +9 -0
- package/corpus/templates/content/shared/database-form.ts +11 -2
- package/corpus/templates/content/vite.config.ts +1 -0
- package/dist/client/build-compatibility.d.ts +11 -0
- package/dist/client/build-compatibility.d.ts.map +1 -0
- package/dist/client/build-compatibility.js +56 -0
- package/dist/client/build-compatibility.js.map +1 -0
- package/dist/client/route-chunk-recovery/index.d.ts +1 -0
- package/dist/client/route-chunk-recovery/index.d.ts.map +1 -1
- package/dist/client/route-chunk-recovery/index.js +1 -0
- package/dist/client/route-chunk-recovery/index.js.map +1 -1
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +19 -1
- package/dist/client/use-action.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +2 -1
- package/dist/deploy/build.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/org/context.d.ts +4 -2
- package/dist/org/context.d.ts.map +1 -1
- package/dist/org/context.js +44 -22
- package/dist/org/context.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
- package/dist/provider-api/actions/provider-api.d.ts +12 -12
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +32 -2
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth-plugin.d.ts.map +1 -1
- package/dist/server/auth-plugin.js +9 -5
- package/dist/server/auth-plugin.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +4 -1
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +1 -1
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +1 -1
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/vite/client.d.ts +6 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +8 -0
- package/dist/vite/client.js.map +1 -1
- package/package.json +1 -1
- package/src/client/build-compatibility.ts +77 -0
- package/src/client/route-chunk-recovery/index.ts +1 -0
- package/src/client/use-action.ts +32 -1
- package/src/deploy/build.ts +4 -1
- package/src/org/context.ts +56 -30
- package/src/server/action-routes.ts +52 -2
- package/src/server/auth-plugin.ts +9 -4
- package/src/server/better-auth-instance.ts +5 -1
- package/src/shared/mcp-embed-headers.ts +1 -1
- package/src/vite/client.ts +17 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
2
|
import { writeAppState } from "@agent-native/core/application-state";
|
|
3
3
|
import { assertAccess } from "@agent-native/core/sharing";
|
|
4
|
+
import { inArray } from "drizzle-orm";
|
|
4
5
|
|
|
5
|
-
import { getDb } from "../server/db/index.js";
|
|
6
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
7
|
+
import { assertNotWorkspaceCatalogDocuments } from "./_content-space-catalog-guards.js";
|
|
6
8
|
import {
|
|
7
9
|
databaseRowBatchSchema,
|
|
8
10
|
renumberDatabaseRows,
|
|
@@ -20,12 +22,37 @@ export default defineAction({
|
|
|
20
22
|
const { database, rows } = await resolveDatabaseRowsForBatch(args);
|
|
21
23
|
|
|
22
24
|
await assertAccess("document", database.documentId, "editor");
|
|
25
|
+
if (database.systemRole === "favorites") {
|
|
26
|
+
const removedItemIds = rows.map((row) => row.item.id);
|
|
27
|
+
const removedDocumentIds = rows.map((row) => row.document.id);
|
|
28
|
+
const now = new Date().toISOString();
|
|
29
|
+
await db.transaction(async (tx) => {
|
|
30
|
+
await tx
|
|
31
|
+
.delete(schema.contentDatabaseItems)
|
|
32
|
+
.where(inArray(schema.contentDatabaseItems.id, removedItemIds));
|
|
33
|
+
await renumberDatabaseRows(
|
|
34
|
+
tx as unknown as ReturnType<typeof getDb>,
|
|
35
|
+
database,
|
|
36
|
+
now,
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
40
|
+
return {
|
|
41
|
+
...(await getContentDatabaseResponse(database.id)),
|
|
42
|
+
deletedItemIds: removedItemIds,
|
|
43
|
+
deletedDocumentIds: [],
|
|
44
|
+
deletedCount: 0,
|
|
45
|
+
removedDocumentIds,
|
|
46
|
+
removedCount: removedItemIds.length,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
23
49
|
for (const row of rows) {
|
|
24
50
|
await assertAccess("document", row.document.id, "admin");
|
|
25
51
|
}
|
|
26
52
|
|
|
27
53
|
const deletedItemIds = rows.map((row) => row.item.id);
|
|
28
54
|
const deletedDocumentIds = rows.map((row) => row.document.id);
|
|
55
|
+
await assertNotWorkspaceCatalogDocuments(db, deletedDocumentIds, "deleted");
|
|
29
56
|
const now = new Date().toISOString();
|
|
30
57
|
|
|
31
58
|
await db.transaction(async (tx) => {
|
|
@@ -44,6 +44,9 @@ export default defineAction({
|
|
|
44
44
|
),
|
|
45
45
|
);
|
|
46
46
|
if (!definition) throw new Error(`Property "${propertyId}" not found`);
|
|
47
|
+
if (definition.systemRole) {
|
|
48
|
+
throw new Error("System properties cannot be deleted.");
|
|
49
|
+
}
|
|
47
50
|
|
|
48
51
|
const isBlocks = isBlocksPropertyType(
|
|
49
52
|
definition.type as DocumentPropertyType,
|
|
@@ -6,6 +6,7 @@ import { z } from "zod";
|
|
|
6
6
|
|
|
7
7
|
import { getDb, schema } from "../server/db/index.js";
|
|
8
8
|
import { chunks } from "./_batch-utils.js";
|
|
9
|
+
import { assertNotWorkspaceCatalogDocuments } from "./_content-space-catalog-guards.js";
|
|
9
10
|
|
|
10
11
|
const DELETE_BATCH_SIZE = 90;
|
|
11
12
|
|
|
@@ -170,6 +171,7 @@ export async function deleteDocumentRecursive(
|
|
|
170
171
|
): Promise<string[]> {
|
|
171
172
|
const { documentIds, ownedDatabaseIds } =
|
|
172
173
|
await collectDocumentSubtreeForDelete(db, id, ownerEmail);
|
|
174
|
+
await assertNotWorkspaceCatalogDocuments(db, documentIds, "deleted");
|
|
173
175
|
|
|
174
176
|
const propertyDefinitionIds: string[] = [];
|
|
175
177
|
await deleteWhereIn(ownedDatabaseIds, async (databaseIdBatch) => {
|
|
@@ -355,15 +357,50 @@ export default defineAction({
|
|
|
355
357
|
description: "Delete a document and all its children recursively.",
|
|
356
358
|
schema: z.object({
|
|
357
359
|
id: z.string().optional().describe("Document ID (required)"),
|
|
360
|
+
databaseDocumentId: z
|
|
361
|
+
.string()
|
|
362
|
+
.optional()
|
|
363
|
+
.describe("Database page the deletion was initiated from"),
|
|
358
364
|
}),
|
|
359
365
|
run: async (args) => {
|
|
360
366
|
const id = args.id;
|
|
361
367
|
if (!id) throw new Error("--id is required");
|
|
362
368
|
|
|
369
|
+
const db = getDb();
|
|
370
|
+
if (args.databaseDocumentId) {
|
|
371
|
+
const [contextDatabase] = await db
|
|
372
|
+
.select()
|
|
373
|
+
.from(schema.contentDatabases)
|
|
374
|
+
.where(
|
|
375
|
+
and(
|
|
376
|
+
eq(schema.contentDatabases.documentId, args.databaseDocumentId),
|
|
377
|
+
eq(schema.contentDatabases.systemRole, "favorites"),
|
|
378
|
+
),
|
|
379
|
+
);
|
|
380
|
+
if (contextDatabase) {
|
|
381
|
+
await assertAccess("document", contextDatabase.documentId, "editor");
|
|
382
|
+
const [membership] = await db
|
|
383
|
+
.select({ id: schema.contentDatabaseItems.id })
|
|
384
|
+
.from(schema.contentDatabaseItems)
|
|
385
|
+
.where(
|
|
386
|
+
and(
|
|
387
|
+
eq(schema.contentDatabaseItems.databaseId, contextDatabase.id),
|
|
388
|
+
eq(schema.contentDatabaseItems.documentId, id),
|
|
389
|
+
),
|
|
390
|
+
);
|
|
391
|
+
if (!membership) {
|
|
392
|
+
throw new Error("Document is not part of Favorites");
|
|
393
|
+
}
|
|
394
|
+
await db
|
|
395
|
+
.delete(schema.contentDatabaseItems)
|
|
396
|
+
.where(eq(schema.contentDatabaseItems.id, membership.id));
|
|
397
|
+
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
398
|
+
return { success: true, deleted: 0, removed: 1 };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
363
402
|
const access = await assertAccess("document", id, "admin");
|
|
364
403
|
const existing = access.resource;
|
|
365
|
-
|
|
366
|
-
const db = getDb();
|
|
367
404
|
const [systemDatabase] = await db
|
|
368
405
|
.select({ systemRole: schema.contentDatabases.systemRole })
|
|
369
406
|
.from(schema.contentDatabases)
|
|
@@ -7,6 +7,7 @@ import { z } from "zod";
|
|
|
7
7
|
|
|
8
8
|
import { getDb, schema } from "../server/db/index.js";
|
|
9
9
|
import { ensureDocumentFilesMembership } from "./_content-files.js";
|
|
10
|
+
import { assertNotWorkspaceCatalogDocuments } from "./_content-space-catalog-guards.js";
|
|
10
11
|
import { getContentDatabaseResponse } from "./_database-utils.js";
|
|
11
12
|
import { nanoid } from "./_property-utils.js";
|
|
12
13
|
|
|
@@ -58,6 +59,11 @@ export default defineAction({
|
|
|
58
59
|
|
|
59
60
|
await assertAccess("document", row.database.documentId, "editor");
|
|
60
61
|
await assertAccess("document", row.document.id, "viewer");
|
|
62
|
+
await assertNotWorkspaceCatalogDocuments(
|
|
63
|
+
db,
|
|
64
|
+
[row.document.id],
|
|
65
|
+
"duplicated",
|
|
66
|
+
);
|
|
61
67
|
|
|
62
68
|
const now = new Date().toISOString();
|
|
63
69
|
const nextDocumentId = nanoid();
|
|
@@ -6,6 +6,7 @@ import { and, eq, gte, inArray, sql } from "drizzle-orm";
|
|
|
6
6
|
|
|
7
7
|
import { getDb, schema } from "../server/db/index.js";
|
|
8
8
|
import { ensureDocumentsFilesMembership } from "./_content-files.js";
|
|
9
|
+
import { assertNotWorkspaceCatalogDocuments } from "./_content-space-catalog-guards.js";
|
|
9
10
|
import {
|
|
10
11
|
databaseRowBatchSchema,
|
|
11
12
|
resolveDatabaseRowsForBatch,
|
|
@@ -33,6 +34,11 @@ export default defineAction({
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
const sourceDocumentIds = rows.map((row) => row.document.id);
|
|
37
|
+
await assertNotWorkspaceCatalogDocuments(
|
|
38
|
+
db,
|
|
39
|
+
sourceDocumentIds,
|
|
40
|
+
"duplicated",
|
|
41
|
+
);
|
|
36
42
|
const sourceItemIds = rows.map((row) => row.item.id);
|
|
37
43
|
const now = new Date().toISOString();
|
|
38
44
|
const insertionPosition =
|
|
@@ -48,6 +48,9 @@ export default defineAction({
|
|
|
48
48
|
),
|
|
49
49
|
);
|
|
50
50
|
if (!definition) throw new Error(`Property "${propertyId}" not found`);
|
|
51
|
+
if (definition.systemRole) {
|
|
52
|
+
throw new Error("System properties cannot be duplicated.");
|
|
53
|
+
}
|
|
51
54
|
|
|
52
55
|
const now = new Date().toISOString();
|
|
53
56
|
const newPropertyId = nanoid();
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
ContentDatabaseResponse,
|
|
9
9
|
ContentDatabaseUnavailableResponse,
|
|
10
10
|
} from "../shared/api.js";
|
|
11
|
+
import { resolveContentSpaceAccess } from "./_content-space-access.js";
|
|
11
12
|
import {
|
|
12
13
|
CONTENT_DATABASE_MAX_READ_LIMIT,
|
|
13
14
|
getContentDatabaseResponse,
|
|
@@ -65,8 +66,16 @@ export default defineAction({
|
|
|
65
66
|
};
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
if (!
|
|
69
|
+
let canRead = Boolean(await resolveAccess("document", database.documentId));
|
|
70
|
+
if (!canRead && database.systemRole === "files" && database.spaceId) {
|
|
71
|
+
try {
|
|
72
|
+
await resolveContentSpaceAccess(database.spaceId);
|
|
73
|
+
canRead = true;
|
|
74
|
+
} catch {
|
|
75
|
+
canRead = false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (!canRead) throw new Error(`Database "${resolvedDatabaseId}" not found`);
|
|
70
79
|
|
|
71
80
|
if (database.deletedAt) {
|
|
72
81
|
return {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import { getUserSetting } from "@agent-native/core/settings";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
CONTENT_SIDEBAR_STATE_SETTING_KEY,
|
|
7
|
+
normalizeContentSidebarState,
|
|
8
|
+
} from "./_content-sidebar-state.js";
|
|
9
|
+
|
|
10
|
+
export default defineAction({
|
|
11
|
+
description: "Read the current user's Content sidebar expansion state.",
|
|
12
|
+
schema: z.object({}),
|
|
13
|
+
http: { method: "GET" },
|
|
14
|
+
agentTool: false,
|
|
15
|
+
run: async (_args, ctx) => {
|
|
16
|
+
if (!ctx?.userEmail) throw new Error("Not authenticated.");
|
|
17
|
+
const stored = await getUserSetting(
|
|
18
|
+
ctx.userEmail,
|
|
19
|
+
CONTENT_SIDEBAR_STATE_SETTING_KEY,
|
|
20
|
+
);
|
|
21
|
+
return { state: normalizeContentSidebarState(stored) };
|
|
22
|
+
},
|
|
23
|
+
});
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
2
|
import { buildDeepLink } from "@agent-native/core/server";
|
|
3
|
+
import { getRequestUserEmail } from "@agent-native/core/server/request-context";
|
|
3
4
|
import { resolveAccess } from "@agent-native/core/sharing";
|
|
5
|
+
import { eq } from "drizzle-orm";
|
|
4
6
|
import { z } from "zod";
|
|
5
7
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "
|
|
8
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
9
|
+
import { parseDocumentHideFromSearch } from "../server/lib/documents.js";
|
|
10
|
+
import { favoriteDocumentIds } from "./_content-favorites.js";
|
|
11
|
+
import { resolveContentSpaceAccess } from "./_content-space-access.js";
|
|
10
12
|
import {
|
|
11
13
|
getDatabaseByDocumentId,
|
|
12
14
|
getDocumentContextPath,
|
|
@@ -15,7 +17,6 @@ import {
|
|
|
15
17
|
serializeDatabaseMembership,
|
|
16
18
|
} from "./_database-utils.js";
|
|
17
19
|
import { serializeDocumentSource } from "./_document-source.js";
|
|
18
|
-
import "../server/db/index.js";
|
|
19
20
|
import {
|
|
20
21
|
listPropertiesForDocument,
|
|
21
22
|
serializeDatabase,
|
|
@@ -29,6 +30,26 @@ function canManageRole(role: string) {
|
|
|
29
30
|
return role === "owner" || role === "admin";
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
async function resolveDocumentAccess(id: string) {
|
|
34
|
+
const current = await resolveAccess("document", id);
|
|
35
|
+
if (current) return current;
|
|
36
|
+
const [reference] = await getDb()
|
|
37
|
+
.select({ spaceId: schema.documents.spaceId })
|
|
38
|
+
.from(schema.documents)
|
|
39
|
+
.where(eq(schema.documents.id, id))
|
|
40
|
+
.limit(1);
|
|
41
|
+
if (!reference?.spaceId) return null;
|
|
42
|
+
try {
|
|
43
|
+
const spaceAccess = await resolveContentSpaceAccess(reference.spaceId);
|
|
44
|
+
return resolveAccess("document", id, {
|
|
45
|
+
userEmail: spaceAccess.authority.userEmail,
|
|
46
|
+
orgId: spaceAccess.authority.orgId ?? undefined,
|
|
47
|
+
});
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
32
53
|
export default defineAction({
|
|
33
54
|
description: "Get a single document by ID with full content.",
|
|
34
55
|
schema: z.object({
|
|
@@ -40,7 +61,7 @@ export default defineAction({
|
|
|
40
61
|
run: async (args) => {
|
|
41
62
|
if (!args.id) throw new Error("--id is required");
|
|
42
63
|
|
|
43
|
-
const access = await
|
|
64
|
+
const access = await resolveDocumentAccess(args.id);
|
|
44
65
|
// Not-found is a deterministic client-state condition (deleted or
|
|
45
66
|
// inaccessible document still referenced by an open tab) — 404, not a
|
|
46
67
|
// 500 that floods the console as Internal Server Error.
|
|
@@ -57,6 +78,10 @@ export default defineAction({
|
|
|
57
78
|
const doc = access.resource;
|
|
58
79
|
const database = await getDatabaseByDocumentId(doc.id);
|
|
59
80
|
const databaseMembership = await getDatabaseItemByDocumentId(doc.id);
|
|
81
|
+
const userEmail = getRequestUserEmail();
|
|
82
|
+
const favoriteIds = userEmail
|
|
83
|
+
? await favoriteDocumentIds(getDb(), userEmail, [doc.id])
|
|
84
|
+
: new Set<string>();
|
|
60
85
|
|
|
61
86
|
return {
|
|
62
87
|
id: doc.id,
|
|
@@ -71,7 +96,7 @@ export default defineAction({
|
|
|
71
96
|
description: doc.description,
|
|
72
97
|
icon: doc.icon,
|
|
73
98
|
position: doc.position,
|
|
74
|
-
isFavorite:
|
|
99
|
+
isFavorite: favoriteIds.has(doc.id),
|
|
75
100
|
hideFromSearch: parseDocumentHideFromSearch(doc.hideFromSearch),
|
|
76
101
|
visibility: doc.visibility,
|
|
77
102
|
source: serializeDocumentSource(doc),
|
|
@@ -15,6 +15,10 @@ import {
|
|
|
15
15
|
parseContentSourceFile,
|
|
16
16
|
type ParsedContentSourceFile,
|
|
17
17
|
} from "../shared/content-source.js";
|
|
18
|
+
import {
|
|
19
|
+
favoriteDocumentIds,
|
|
20
|
+
setFavoriteMembership,
|
|
21
|
+
} from "./_content-favorites.js";
|
|
18
22
|
import { ensureDocumentsFilesMembership } from "./_content-files.js";
|
|
19
23
|
import { resolveContentSpaceAccess } from "./_content-space-access.js";
|
|
20
24
|
import {
|
|
@@ -231,6 +235,16 @@ export default defineAction({
|
|
|
231
235
|
.filter((document) => document.sourcePath)
|
|
232
236
|
.map((document) => [document.sourcePath!, document]),
|
|
233
237
|
);
|
|
238
|
+
const existingFavoriteIds = await favoriteDocumentIds(
|
|
239
|
+
db,
|
|
240
|
+
currentUserEmail,
|
|
241
|
+
[
|
|
242
|
+
...new Set([
|
|
243
|
+
...parsed.flatMap((file) => (file.id ? [file.id] : [])),
|
|
244
|
+
...existingLocalDocs.map((document) => document.id),
|
|
245
|
+
]),
|
|
246
|
+
],
|
|
247
|
+
);
|
|
234
248
|
|
|
235
249
|
const seenIds = new Set<string>();
|
|
236
250
|
const duplicateIds = new Set<string>();
|
|
@@ -311,7 +325,7 @@ export default defineAction({
|
|
|
311
325
|
file.icon !== undefined && file.icon !== existing.icon;
|
|
312
326
|
const favoriteChanged =
|
|
313
327
|
file.isFavorite !== undefined &&
|
|
314
|
-
|
|
328
|
+
file.isFavorite !== existingFavoriteIds.has(id);
|
|
315
329
|
const discoverabilityChanged =
|
|
316
330
|
file.hideFromSearch !== undefined &&
|
|
317
331
|
boolToInt(file.hideFromSearch) !== (existing.hideFromSearch ?? 0);
|
|
@@ -363,6 +377,15 @@ export default defineAction({
|
|
|
363
377
|
.update(schema.documents)
|
|
364
378
|
.set(updates)
|
|
365
379
|
.where(eq(schema.documents.id, id));
|
|
380
|
+
if (favoriteChanged) {
|
|
381
|
+
await setFavoriteMembership({
|
|
382
|
+
db,
|
|
383
|
+
userEmail: currentUserEmail,
|
|
384
|
+
documentId: id,
|
|
385
|
+
favorite: file.isFavorite === true,
|
|
386
|
+
now,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
366
389
|
}
|
|
367
390
|
|
|
368
391
|
updated.push({ id, path: file.path, title: file.title });
|
|
@@ -389,6 +412,15 @@ export default defineAction({
|
|
|
389
412
|
createdAt: now,
|
|
390
413
|
updatedAt: now,
|
|
391
414
|
});
|
|
415
|
+
if (file.isFavorite) {
|
|
416
|
+
await setFavoriteMembership({
|
|
417
|
+
db,
|
|
418
|
+
userEmail: currentUserEmail,
|
|
419
|
+
documentId: id,
|
|
420
|
+
favorite: true,
|
|
421
|
+
now,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
392
424
|
} catch (err) {
|
|
393
425
|
errors.push({
|
|
394
426
|
path: file.path,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
2
|
import { getRequestUserEmail } from "@agent-native/core/server/request-context";
|
|
3
|
-
import { and, eq, isNull } from "drizzle-orm";
|
|
3
|
+
import { and, eq, inArray, isNull } from "drizzle-orm";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
import { getDb, schema } from "../server/db/index.js";
|
|
@@ -26,6 +26,7 @@ export default defineAction({
|
|
|
26
26
|
const db = getDb();
|
|
27
27
|
const personalSpaceId = personalContentSpaceId(email);
|
|
28
28
|
const catalogIds = systemIdsForContentSpace(personalSpaceId, "workspaces");
|
|
29
|
+
const favoritesIds = systemIdsForContentSpace(personalSpaceId, "favorites");
|
|
29
30
|
const memberships = await listContentOrganizationMemberships(email);
|
|
30
31
|
const roleByOrgId = new Map(
|
|
31
32
|
memberships.map((membership) => [
|
|
@@ -65,11 +66,26 @@ export default defineAction({
|
|
|
65
66
|
isNull(schema.contentSpaces.archivedAt),
|
|
66
67
|
),
|
|
67
68
|
);
|
|
69
|
+
const filesDatabaseIds = rows.map((row) => row.space.filesDatabaseId);
|
|
70
|
+
const databaseIds = [...filesDatabaseIds, favoritesIds.databaseId];
|
|
71
|
+
const filesDatabases = databaseIds.length
|
|
72
|
+
? await db
|
|
73
|
+
.select({
|
|
74
|
+
id: schema.contentDatabases.id,
|
|
75
|
+
documentId: schema.contentDatabases.documentId,
|
|
76
|
+
})
|
|
77
|
+
.from(schema.contentDatabases)
|
|
78
|
+
.where(inArray(schema.contentDatabases.id, databaseIds))
|
|
79
|
+
: [];
|
|
80
|
+
const filesDocumentIdByDatabaseId = new Map(
|
|
81
|
+
filesDatabases.map((database) => [database.id, database.documentId]),
|
|
82
|
+
);
|
|
68
83
|
const spaces = [] as Array<{
|
|
69
84
|
id: string;
|
|
70
85
|
name: string;
|
|
71
86
|
kind: string;
|
|
72
87
|
filesDatabaseId: string;
|
|
88
|
+
filesDocumentId: string;
|
|
73
89
|
orgId: string | null;
|
|
74
90
|
role: string;
|
|
75
91
|
catalogItemId: string;
|
|
@@ -87,17 +103,33 @@ export default defineAction({
|
|
|
87
103
|
? "owner"
|
|
88
104
|
: undefined;
|
|
89
105
|
if (!role) continue;
|
|
106
|
+
const filesDocumentId = filesDocumentIdByDatabaseId.get(
|
|
107
|
+
row.space.filesDatabaseId,
|
|
108
|
+
);
|
|
109
|
+
if (!filesDocumentId) continue;
|
|
90
110
|
spaces.push({
|
|
91
111
|
id: row.space.id,
|
|
92
112
|
name: row.space.name,
|
|
93
113
|
kind: row.space.kind,
|
|
94
114
|
filesDatabaseId: row.space.filesDatabaseId,
|
|
115
|
+
filesDocumentId,
|
|
95
116
|
orgId: row.space.orgId,
|
|
96
117
|
role,
|
|
97
118
|
catalogItemId: row.mapping.databaseItemId,
|
|
98
119
|
catalogDocumentId: row.mapping.documentId,
|
|
99
120
|
});
|
|
100
121
|
}
|
|
101
|
-
return {
|
|
122
|
+
return {
|
|
123
|
+
catalogDatabaseId: catalogIds.databaseId,
|
|
124
|
+
catalogDocumentId: catalogIds.documentId,
|
|
125
|
+
favoritesDatabaseId: filesDocumentIdByDatabaseId.has(
|
|
126
|
+
favoritesIds.databaseId,
|
|
127
|
+
)
|
|
128
|
+
? favoritesIds.databaseId
|
|
129
|
+
: null,
|
|
130
|
+
favoritesDocumentId:
|
|
131
|
+
filesDocumentIdByDatabaseId.get(favoritesIds.databaseId) ?? null,
|
|
132
|
+
spaces,
|
|
133
|
+
};
|
|
102
134
|
},
|
|
103
135
|
});
|
|
@@ -14,9 +14,10 @@ import { z } from "zod";
|
|
|
14
14
|
import { getDb, schema } from "../server/db/index.js";
|
|
15
15
|
import {
|
|
16
16
|
documentDiscoveryFilter,
|
|
17
|
-
parseDocumentFavorite,
|
|
18
17
|
parseDocumentHideFromSearch,
|
|
19
18
|
} from "../server/lib/documents.js";
|
|
19
|
+
import { favoriteDocumentIds } from "./_content-favorites.js";
|
|
20
|
+
import { listContentOrganizationMemberships } from "./_content-space-access.js";
|
|
20
21
|
import { serializeDatabaseMembership } from "./_database-utils.js";
|
|
21
22
|
import { serializeDocumentSource } from "./_document-source.js";
|
|
22
23
|
import { parseDatabaseViewConfig } from "./_property-utils.js";
|
|
@@ -50,7 +51,23 @@ export default defineAction({
|
|
|
50
51
|
run: async () => {
|
|
51
52
|
const db = getDb();
|
|
52
53
|
const userEmail = getRequestUserEmail();
|
|
53
|
-
const
|
|
54
|
+
const activeOrgId = getRequestOrgId();
|
|
55
|
+
const memberships = userEmail
|
|
56
|
+
? await listContentOrganizationMemberships(userEmail)
|
|
57
|
+
: [];
|
|
58
|
+
const authorizedOrgIds = [
|
|
59
|
+
...new Set([
|
|
60
|
+
...memberships.map((membership) => membership.orgId),
|
|
61
|
+
...(!userEmail && activeOrgId ? [activeOrgId] : []),
|
|
62
|
+
]),
|
|
63
|
+
];
|
|
64
|
+
const accessContexts = [
|
|
65
|
+
{ userEmail: userEmail ?? undefined },
|
|
66
|
+
...authorizedOrgIds.map((orgId) => ({
|
|
67
|
+
userEmail: userEmail ?? undefined,
|
|
68
|
+
orgId,
|
|
69
|
+
})),
|
|
70
|
+
];
|
|
54
71
|
// Projection that deliberately avoids pulling the full `content` blob:
|
|
55
72
|
// document bodies can be multi-MB, and the list/tree path only needs a
|
|
56
73
|
// short preview plus the true length. `substr` truncates the transferred
|
|
@@ -84,8 +101,15 @@ export default defineAction({
|
|
|
84
101
|
.from(schema.documents)
|
|
85
102
|
.where(
|
|
86
103
|
and(
|
|
87
|
-
|
|
88
|
-
|
|
104
|
+
or(
|
|
105
|
+
...accessContexts.map((context) =>
|
|
106
|
+
accessFilter(schema.documents, schema.documentShares, context),
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
documentDiscoveryFilter({
|
|
110
|
+
userEmail,
|
|
111
|
+
orgIds: authorizedOrgIds,
|
|
112
|
+
}),
|
|
89
113
|
),
|
|
90
114
|
)
|
|
91
115
|
.orderBy(asc(schema.documents.position));
|
|
@@ -104,6 +128,13 @@ export default defineAction({
|
|
|
104
128
|
}
|
|
105
129
|
>();
|
|
106
130
|
const softDeletedDocumentIds = new Set<string>();
|
|
131
|
+
const favoriteIds = userEmail
|
|
132
|
+
? await favoriteDocumentIds(
|
|
133
|
+
db,
|
|
134
|
+
userEmail,
|
|
135
|
+
documents.map((document) => document.id),
|
|
136
|
+
)
|
|
137
|
+
: new Set<string>();
|
|
107
138
|
|
|
108
139
|
if (documents.length > 0) {
|
|
109
140
|
const visibleDocumentIds = documents.map((d) => d.id);
|
|
@@ -117,7 +148,7 @@ export default defineAction({
|
|
|
117
148
|
)!,
|
|
118
149
|
);
|
|
119
150
|
}
|
|
120
|
-
|
|
151
|
+
for (const orgId of authorizedOrgIds) {
|
|
121
152
|
principalClauses.push(
|
|
122
153
|
and(
|
|
123
154
|
eq(schema.documentShares.principalType, "org"),
|
|
@@ -170,6 +201,7 @@ export default defineAction({
|
|
|
170
201
|
)
|
|
171
202
|
.orderBy(
|
|
172
203
|
sql`CASE WHEN ${schema.contentDatabases.systemRole} IS NULL THEN 0 ELSE 1 END`,
|
|
204
|
+
sql`CASE WHEN ${schema.contentDatabases.systemRole} = 'files' THEN 0 ELSE 1 END`,
|
|
173
205
|
asc(schema.contentDatabases.id),
|
|
174
206
|
),
|
|
175
207
|
db
|
|
@@ -277,7 +309,7 @@ export default defineAction({
|
|
|
277
309
|
if (
|
|
278
310
|
userEmail &&
|
|
279
311
|
d.ownerEmail === userEmail &&
|
|
280
|
-
(
|
|
312
|
+
(!d.orgId || authorizedOrgIds.includes(d.orgId))
|
|
281
313
|
) {
|
|
282
314
|
accessRole = "owner";
|
|
283
315
|
}
|
|
@@ -291,7 +323,7 @@ export default defineAction({
|
|
|
291
323
|
contentLength: Number(d.contentLength) || 0,
|
|
292
324
|
icon: d.icon,
|
|
293
325
|
position: d.position,
|
|
294
|
-
isFavorite:
|
|
326
|
+
isFavorite: favoriteIds.has(d.id),
|
|
295
327
|
hideFromSearch: parseDocumentHideFromSearch(d.hideFromSearch),
|
|
296
328
|
notionPageId: notionPageIdByDocumentId.get(d.id) ?? null,
|
|
297
329
|
notionPageUrl: notionPageIdByDocumentId.has(d.id)
|
|
@@ -42,6 +42,15 @@ export default defineAction({
|
|
|
42
42
|
if (!ids.includes(targetPropertyId)) {
|
|
43
43
|
throw new Error(`Property "${targetPropertyId}" not found`);
|
|
44
44
|
}
|
|
45
|
+
const definitionById = new Map(
|
|
46
|
+
definitions.map((definition) => [definition.id, definition]),
|
|
47
|
+
);
|
|
48
|
+
if (
|
|
49
|
+
definitionById.get(propertyId)?.systemRole ||
|
|
50
|
+
definitionById.get(targetPropertyId)?.systemRole
|
|
51
|
+
) {
|
|
52
|
+
throw new Error("System properties cannot be reordered.");
|
|
53
|
+
}
|
|
45
54
|
if (propertyId === targetPropertyId) {
|
|
46
55
|
return {
|
|
47
56
|
documentId,
|