@agent-native/core 0.164.4 → 0.164.11
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/templates/calendar/app/components/calendar/CreateEventDialog.tsx +36 -47
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +3 -0
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +18 -1
- package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +256 -38
- package/corpus/templates/calendar/app/hooks/use-google-auth.ts +43 -3
- package/corpus/templates/calendar/app/i18n-data.ts +120 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +113 -0
- package/corpus/templates/calendar/server/handlers/google-auth.ts +119 -3
- package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
- package/corpus/templates/calendar/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/calendar/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
- package/corpus/templates/calendar/server/routes/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/clips/app/components/player/video-player.tsx +12 -0
- package/corpus/templates/clips/app/routes/_app.dictate.tsx +18 -4
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +114 -20
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +83 -78
- package/corpus/templates/clips/changelog/2026-08-19-clips-nightly-builds-are-available-for-trying-the-latest-upd.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-19-social-preview-video-frames.md +1 -0
- package/corpus/templates/clips/desktop/README.md +19 -4
- package/corpus/templates/clips/desktop/src/app.tsx +78 -16
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +3 -2
- package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -0
- package/corpus/templates/clips/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +41 -3
- package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/clips/server/routes/_agent-native/google/callback.get.ts +22 -1
- package/corpus/templates/clips/server/routes/api/clips-updater.json.get.ts +41 -22
- package/corpus/templates/factory/AGENTS.md +1 -1
- package/corpus/templates/factory/actions/list-factory-automations.ts +4 -0
- package/corpus/templates/factory/actions/start-builder-for-item.ts +19 -17
- package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +100 -36
- package/corpus/templates/factory/app/i18n/en-US.ts +4 -0
- package/corpus/templates/factory/app/routes/factory.tsx +209 -46
- package/corpus/templates/factory/server/connectors/slack.ts +44 -2
- package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -7
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +13 -0
- package/corpus/templates/factory/server/triage/slack-client.ts +5 -0
- package/corpus/templates/factory/server/triage/slack-poller.ts +72 -4
- package/corpus/templates/mail/actions/view-screen.ts +1 -1
- package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -18
- package/corpus/templates/mail/server/handlers/google-auth.ts +125 -24
- package/corpus/templates/mail/server/lib/gmail-query.ts +5 -2
- package/corpus/templates/mail/server/routes/[base]/_agent-native/google/add-account/auth-url.post.ts +1 -0
- package/corpus/templates/mail/server/routes/[base]/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/mail/server/routes/_agent-native/google/add-account/auth-url.post.ts +1 -0
- package/corpus/templates/mail/server/routes/_agent-native/google/auth-url.post.ts +1 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +0 -12
- package/corpus/templates/plan/changelog/2026-08-19-plan-prompts-now-guide-you-to-connect-ai-before-sending-and-.md +6 -0
- package/corpus/templates/slides/actions/approve-deck-access-request.ts +234 -0
- package/corpus/templates/slides/actions/get-deck-access-status.ts +35 -2
- package/corpus/templates/slides/actions/request-deck-access.ts +263 -64
- package/corpus/templates/slides/app/components/editor/MissingDeckAccessPane.tsx +196 -0
- package/corpus/templates/slides/app/components/editor/RequestDeckAccessDialog.tsx +101 -0
- package/corpus/templates/slides/app/hooks/use-deck-access.ts +7 -4
- package/corpus/templates/slides/app/i18n/en-US.ts +29 -6
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +147 -169
- package/corpus/templates/slides/app/routes/access-request.approve.tsx +250 -0
- package/corpus/templates/slides/server/db/schema.ts +8 -0
- package/corpus/templates/slides/server/lib/access-request-email.ts +36 -0
- package/corpus/templates/slides/server/lib/emails.ts +26 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +7 -0
- package/corpus/templates/slides/server/plugins/db.ts +50 -0
- package/corpus/templates/slides/shared/deck-access.ts +31 -0
- package/dist/a2a/task-store.d.ts +1 -0
- package/dist/a2a/task-store.js +1 -1
- package/dist/agent/observational-memory/store.d.ts +1 -0
- package/dist/agent/observational-memory/store.js +1 -1
- package/dist/agent/run-store.d.ts +1 -0
- package/dist/agent/run-store.js +1 -1
- package/dist/agent/tool-approval-store.d.ts +1 -0
- package/dist/agent/tool-approval-store.js +1 -1
- package/dist/application-state/store.d.ts +1 -0
- package/dist/application-state/store.js +1 -1
- package/dist/browser-sessions/store.d.ts +1 -0
- package/dist/browser-sessions/store.js +1 -1
- package/dist/checkpoints/store.d.ts +1 -0
- package/dist/checkpoints/store.js +1 -1
- package/dist/cli/code-agent-schedules.d.ts +11 -0
- package/dist/cli/code-agent-schedules.js +44 -5
- package/dist/client/AgentPanel.d.ts +5 -1
- package/dist/client/AgentPanel.js +2 -2
- package/dist/client/AssistantChat.d.ts +5 -1
- package/dist/client/AssistantChat.js +40 -6
- package/dist/client/EnvironmentBadge.d.ts +7 -13
- package/dist/client/EnvironmentBadge.js +43 -92
- package/dist/client/agent-chat/index.d.ts +1 -0
- package/dist/client/agent-chat/index.js +1 -0
- package/dist/client/agent-chat-adapter.js +76 -50
- package/dist/client/app-providers.js +4 -4
- package/dist/client/chat/message-components.d.ts +4 -0
- package/dist/client/chat/message-components.js +28 -1
- package/dist/client/chat/run-recovery.js +26 -0
- package/dist/client/chat-first/apps-rail.d.ts +1 -0
- package/dist/client/chat-first/apps-rail.js +28 -2
- package/dist/client/sse-event-processor.js +13 -8
- package/dist/client/use-active-agent-chat-run.d.ts +2 -0
- package/dist/client/use-active-agent-chat-run.js +22 -0
- package/dist/coding-tools/sandbox/executions-store.d.ts +1 -0
- package/dist/coding-tools/sandbox/executions-store.js +1 -1
- package/dist/collab/awareness-store.d.ts +1 -0
- package/dist/collab/awareness-store.js +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/storage.d.ts +1 -0
- package/dist/collab/storage.js +1 -1
- package/dist/db/client.js +3 -3
- package/dist/db/ddl-guard.js +8 -0
- package/dist/db/migration-runtime.d.ts +23 -0
- package/dist/db/migration-runtime.js +41 -0
- package/dist/db/migrations.d.ts +1 -2
- package/dist/db/migrations.js +2 -24
- package/dist/email-catalog/log.d.ts +1 -0
- package/dist/email-catalog/log.js +1 -1
- package/dist/guards/index.d.ts +1 -0
- package/dist/guards/index.js +1 -0
- package/dist/guards/release-schema-complete.d.ts +22 -0
- package/dist/guards/release-schema-complete.js +144 -0
- package/dist/integrations/a2a-continuations-store.d.ts +1 -0
- package/dist/integrations/a2a-continuations-store.js +1 -1
- package/dist/integrations/awaiting-input-store.d.ts +1 -0
- package/dist/integrations/awaiting-input-store.js +1 -1
- package/dist/integrations/config-store.d.ts +1 -0
- package/dist/integrations/config-store.js +1 -1
- package/dist/integrations/controls-store.d.ts +1 -0
- package/dist/integrations/controls-store.js +1 -1
- package/dist/integrations/identity-links-store.d.ts +1 -0
- package/dist/integrations/identity-links-store.js +1 -1
- package/dist/integrations/installations-store.d.ts +1 -0
- package/dist/integrations/installations-store.js +1 -1
- package/dist/integrations/remote-commands-store.d.ts +1 -0
- package/dist/integrations/remote-commands-store.js +1 -1
- package/dist/integrations/remote-devices-store.d.ts +1 -0
- package/dist/integrations/remote-devices-store.js +1 -1
- package/dist/integrations/remote-push-store.d.ts +1 -0
- package/dist/integrations/remote-push-store.js +1 -1
- package/dist/integrations/remote-run-events-store.d.ts +1 -0
- package/dist/integrations/remote-run-events-store.js +1 -1
- package/dist/integrations/scope-store.d.ts +1 -0
- package/dist/integrations/scope-store.js +1 -1
- package/dist/integrations/thread-mapping-store.d.ts +1 -0
- package/dist/integrations/thread-mapping-store.js +1 -1
- package/dist/integrations/usage-budget-store.d.ts +1 -0
- package/dist/integrations/usage-budget-store.js +1 -1
- package/dist/jobs/background-automation-runner.js +111 -9
- package/dist/jobs/run-history.d.ts +1 -0
- package/dist/jobs/run-history.js +1 -1
- package/dist/jobs/scheduler-health.d.ts +1 -0
- package/dist/jobs/scheduler-health.js +1 -1
- package/dist/mcp/approval-store.d.ts +1 -0
- package/dist/mcp/approval-store.js +1 -1
- package/dist/mcp/connect-store.d.ts +1 -0
- package/dist/mcp/connect-store.js +1 -1
- package/dist/mcp/oauth-store.d.ts +1 -0
- package/dist/mcp/oauth-store.js +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/notifications/store.d.ts +1 -0
- package/dist/notifications/store.js +1 -1
- package/dist/oauth-tokens/store.d.ts +1 -0
- package/dist/oauth-tokens/store.js +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/store.d.ts +1 -0
- package/dist/progress/store.js +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/provider-api/corpus-jobs-store.d.ts +1 -0
- package/dist/provider-api/corpus-jobs-store.js +1 -1
- package/dist/provider-api/custom-registry.d.ts +1 -0
- package/dist/provider-api/custom-registry.js +1 -1
- package/dist/provider-api/quota-governor.d.ts +1 -0
- package/dist/provider-api/quota-governor.js +1 -1
- package/dist/provider-api/staged-datasets-store.d.ts +1 -0
- package/dist/provider-api/staged-datasets-store.js +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/resources/store.d.ts +1 -0
- package/dist/resources/store.js +1 -1
- package/dist/scripts/dev/index.js +15 -1
- package/dist/search/index.js +3 -0
- package/dist/secrets/storage.d.ts +1 -0
- package/dist/secrets/storage.js +1 -1
- package/dist/server/agent-chat-plugin.js +38 -9
- package/dist/server/agent-teams-run-queue.d.ts +1 -0
- package/dist/server/agent-teams-run-queue.js +1 -1
- package/dist/server/auth-marketing.js +3 -1
- package/dist/server/auth.d.ts +18 -1
- package/dist/server/auth.js +370 -154
- package/dist/server/embed-session.d.ts +1 -0
- package/dist/server/embed-session.js +1 -1
- package/dist/server/google-auth-plugin.js +3 -1
- package/dist/server/google-oauth.d.ts +10 -0
- package/dist/server/google-oauth.js +25 -1
- package/dist/server/identity-sso-store.d.ts +1 -0
- package/dist/server/identity-sso-store.js +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/onboarding-html.js +200 -31
- package/dist/server/poll.d.ts +2 -0
- package/dist/server/poll.js +19 -1
- package/dist/server/release-migrations.js +5 -0
- package/dist/server/release-schema.d.ts +46 -0
- package/dist/server/release-schema.js +307 -0
- package/dist/settings/store.d.ts +1 -0
- package/dist/settings/store.js +1 -1
- package/dist/shared/environment-lanes.d.ts +28 -0
- package/dist/shared/environment-lanes.js +61 -0
- package/dist/sharing/actions/share-resource.js +22 -2
- package/dist/styles/agent-native.css +17 -15
- package/dist/triggers/routes.d.ts +11 -0
- package/dist/triggers/routes.js +10 -0
- package/dist/usage/alerts-store.d.ts +1 -0
- package/dist/usage/alerts-store.js +1 -1
- package/dist/usage/store.d.ts +1 -0
- package/dist/usage/store.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import { defineAction } from "@agent-native/core";
|
|
4
|
+
import { verifyScopedAgentAccessToken } from "@agent-native/core/server";
|
|
5
|
+
import { invalidateCollabAccessCache } from "@agent-native/core/server/poll";
|
|
6
|
+
import {
|
|
7
|
+
getRequestOrgId,
|
|
8
|
+
getRequestUserEmail,
|
|
9
|
+
} from "@agent-native/core/server/request-context";
|
|
10
|
+
import { resolveAccess } from "@agent-native/core/sharing";
|
|
11
|
+
import { and, eq, sql } from "drizzle-orm";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
|
|
14
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
15
|
+
import { SLIDES_ACCESS_APPROVAL_TOKEN_PREFIX } from "../shared/deck-access.js";
|
|
16
|
+
|
|
17
|
+
function httpError(message: string, statusCode: number): Error {
|
|
18
|
+
return Object.assign(new Error(message), { statusCode });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalizeEmail(email: string): string {
|
|
22
|
+
return email.trim().toLowerCase();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function approvalTokenHash(token: string): string {
|
|
26
|
+
return createHash("sha256").update(token).digest("hex");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type AccessRequestPayload = {
|
|
30
|
+
requesterEmail?: string;
|
|
31
|
+
approvalTokenHash?: string;
|
|
32
|
+
accessGrantedAt?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function deckViewerShareId(deckId: string, requesterEmail: string): string {
|
|
36
|
+
return (
|
|
37
|
+
"deck-share-" +
|
|
38
|
+
createHash("sha256")
|
|
39
|
+
.update(deckId)
|
|
40
|
+
.update("\0")
|
|
41
|
+
.update(requesterEmail)
|
|
42
|
+
.digest("hex")
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default defineAction({
|
|
47
|
+
description:
|
|
48
|
+
"Approve a private Slides deck access request and add the requester as a viewer in the deck's standard sharing list.",
|
|
49
|
+
schema: z.object({
|
|
50
|
+
deckId: z.string().min(1).describe("Deck ID to share."),
|
|
51
|
+
approvalToken: z
|
|
52
|
+
.string()
|
|
53
|
+
.trim()
|
|
54
|
+
.min(1)
|
|
55
|
+
.describe("Signed approval capability from the deck owner email."),
|
|
56
|
+
}),
|
|
57
|
+
agentTool: false,
|
|
58
|
+
run: async ({ deckId, approvalToken }) => {
|
|
59
|
+
const approverEmail = getRequestUserEmail();
|
|
60
|
+
if (!approverEmail) {
|
|
61
|
+
throw httpError("Sign in as the deck owner to allow access.", 401);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const token = verifyScopedAgentAccessToken(approvalToken, {
|
|
65
|
+
resourceKind: SLIDES_ACCESS_APPROVAL_TOKEN_PREFIX,
|
|
66
|
+
resourceId: deckId,
|
|
67
|
+
});
|
|
68
|
+
if (!token.ok || !token.viewerEmail) {
|
|
69
|
+
throw httpError("This access request is invalid or expired.", 404);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const normalizedApproverEmail = normalizeEmail(approverEmail);
|
|
73
|
+
const access = await resolveAccess("deck", deckId, {
|
|
74
|
+
userEmail: normalizedApproverEmail,
|
|
75
|
+
orgId: getRequestOrgId() ?? undefined,
|
|
76
|
+
});
|
|
77
|
+
if (!access || !["owner", "admin"].includes(access.role)) {
|
|
78
|
+
throw httpError("Only a deck owner or admin can allow access.", 403);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const db = getDb();
|
|
82
|
+
const [deck] = await db
|
|
83
|
+
.select({
|
|
84
|
+
id: schema.decks.id,
|
|
85
|
+
title: schema.decks.title,
|
|
86
|
+
visibility: schema.decks.visibility,
|
|
87
|
+
})
|
|
88
|
+
.from(schema.decks)
|
|
89
|
+
.where(eq(schema.decks.id, deckId))
|
|
90
|
+
.limit(1);
|
|
91
|
+
if (!deck || deck.visibility !== "private") {
|
|
92
|
+
throw httpError(`Deck ${deckId} not found`, 404);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const requesterEmail = normalizeEmail(token.viewerEmail);
|
|
96
|
+
const accessRequests = await db
|
|
97
|
+
.select({
|
|
98
|
+
id: schema.deckEvents.id,
|
|
99
|
+
payload: schema.deckEvents.payload,
|
|
100
|
+
})
|
|
101
|
+
.from(schema.deckEvents)
|
|
102
|
+
.where(
|
|
103
|
+
and(
|
|
104
|
+
eq(schema.deckEvents.deckId, deckId),
|
|
105
|
+
eq(schema.deckEvents.type, "deck.access_requested"),
|
|
106
|
+
),
|
|
107
|
+
);
|
|
108
|
+
const request = accessRequests.find((event) => {
|
|
109
|
+
try {
|
|
110
|
+
const payload = JSON.parse(event.payload ?? "") as AccessRequestPayload;
|
|
111
|
+
return (
|
|
112
|
+
typeof payload.requesterEmail === "string" &&
|
|
113
|
+
normalizeEmail(payload.requesterEmail) === requesterEmail &&
|
|
114
|
+
payload.approvalTokenHash === approvalTokenHash(approvalToken)
|
|
115
|
+
);
|
|
116
|
+
} catch {
|
|
117
|
+
// coercion-ok: malformed historical event payload cannot authorize a share.
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
if (!request) {
|
|
122
|
+
throw httpError("This access request is invalid or expired.", 404);
|
|
123
|
+
}
|
|
124
|
+
let requestPayload: AccessRequestPayload;
|
|
125
|
+
try {
|
|
126
|
+
requestPayload = JSON.parse(
|
|
127
|
+
request.payload ?? "",
|
|
128
|
+
) as AccessRequestPayload;
|
|
129
|
+
} catch {
|
|
130
|
+
throw httpError("This access request is invalid or expired.", 404);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const [existingShare] = await db
|
|
134
|
+
.select({ id: schema.deckShares.id })
|
|
135
|
+
.from(schema.deckShares)
|
|
136
|
+
.where(
|
|
137
|
+
and(
|
|
138
|
+
eq(schema.deckShares.resourceId, deckId),
|
|
139
|
+
eq(schema.deckShares.principalType, "user"),
|
|
140
|
+
// Share email principals are normalized on write, but this keeps
|
|
141
|
+
// approval idempotent for rows created before that convention.
|
|
142
|
+
sql`lower(${schema.deckShares.principalId}) = ${requesterEmail}`,
|
|
143
|
+
),
|
|
144
|
+
)
|
|
145
|
+
.limit(1);
|
|
146
|
+
if (existingShare) {
|
|
147
|
+
return {
|
|
148
|
+
ok: true as const,
|
|
149
|
+
alreadyAllowed: true,
|
|
150
|
+
requesterEmail,
|
|
151
|
+
deckId,
|
|
152
|
+
deckTitle: deck.title,
|
|
153
|
+
shareId: existingShare.id,
|
|
154
|
+
message: "Access was already granted to this requester.",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (requestPayload.accessGrantedAt) {
|
|
158
|
+
throw httpError("This access request is invalid or expired.", 404);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Use a deterministic primary key as the idempotency key. The generic
|
|
162
|
+
// shares table predates a composite unique constraint, but concurrent
|
|
163
|
+
// approvals for this flow still collide atomically on this key.
|
|
164
|
+
const shareId = deckViewerShareId(deckId, requesterEmail);
|
|
165
|
+
const [insertedShare] = await db
|
|
166
|
+
.insert(schema.deckShares)
|
|
167
|
+
.values({
|
|
168
|
+
id: shareId,
|
|
169
|
+
resourceId: deckId,
|
|
170
|
+
principalType: "user",
|
|
171
|
+
principalId: requesterEmail,
|
|
172
|
+
role: "viewer",
|
|
173
|
+
createdBy: normalizedApproverEmail,
|
|
174
|
+
createdAt: new Date().toISOString(),
|
|
175
|
+
})
|
|
176
|
+
.onConflictDoNothing()
|
|
177
|
+
.returning({ id: schema.deckShares.id });
|
|
178
|
+
if (!insertedShare) {
|
|
179
|
+
const [existingShareAfterConflict] = await db
|
|
180
|
+
.select({ id: schema.deckShares.id })
|
|
181
|
+
.from(schema.deckShares)
|
|
182
|
+
.where(
|
|
183
|
+
and(
|
|
184
|
+
eq(schema.deckShares.resourceId, deckId),
|
|
185
|
+
eq(schema.deckShares.principalType, "user"),
|
|
186
|
+
sql`lower(${schema.deckShares.principalId}) = ${requesterEmail}`,
|
|
187
|
+
),
|
|
188
|
+
)
|
|
189
|
+
.limit(1);
|
|
190
|
+
if (!existingShareAfterConflict) {
|
|
191
|
+
throw new Error("Deck access share conflict could not be resolved.");
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
ok: true as const,
|
|
195
|
+
alreadyAllowed: true,
|
|
196
|
+
requesterEmail,
|
|
197
|
+
deckId,
|
|
198
|
+
deckTitle: deck.title,
|
|
199
|
+
shareId: existingShareAfterConflict.id,
|
|
200
|
+
message: "Access was already granted to this requester.",
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const [markedRequest] = await db
|
|
204
|
+
.update(schema.deckEvents)
|
|
205
|
+
.set({
|
|
206
|
+
payload: JSON.stringify({
|
|
207
|
+
...requestPayload,
|
|
208
|
+
accessGrantedAt: new Date().toISOString(),
|
|
209
|
+
accessShareId: insertedShare.id,
|
|
210
|
+
}),
|
|
211
|
+
})
|
|
212
|
+
.where(
|
|
213
|
+
and(
|
|
214
|
+
eq(schema.deckEvents.id, request.id),
|
|
215
|
+
eq(schema.deckEvents.payload, request.payload ?? ""),
|
|
216
|
+
),
|
|
217
|
+
)
|
|
218
|
+
.returning({ id: schema.deckEvents.id });
|
|
219
|
+
if (!markedRequest) {
|
|
220
|
+
throw new Error("Deck access request approval could not be recorded.");
|
|
221
|
+
}
|
|
222
|
+
invalidateCollabAccessCache("deck", deckId);
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
ok: true as const,
|
|
226
|
+
alreadyAllowed: false,
|
|
227
|
+
requesterEmail,
|
|
228
|
+
deckId,
|
|
229
|
+
deckTitle: deck.title,
|
|
230
|
+
shareId: insertedShare.id,
|
|
231
|
+
message: "Access granted. This requester can now open the deck.",
|
|
232
|
+
};
|
|
233
|
+
},
|
|
234
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { signScopedAgentAccessToken } from "@agent-native/core/server";
|
|
2
3
|
import {
|
|
3
4
|
getRequestUserEmail,
|
|
4
5
|
getRequestUserName,
|
|
@@ -8,6 +9,11 @@ import { eq } from "drizzle-orm";
|
|
|
8
9
|
import { z } from "zod";
|
|
9
10
|
|
|
10
11
|
import { getDb, schema } from "../server/db/index.js";
|
|
12
|
+
import {
|
|
13
|
+
SLIDES_ACCESS_REQUEST_FALLBACK_TOKEN_PREFIX,
|
|
14
|
+
SLIDES_ACCESS_REQUEST_TOKEN_PREFIX,
|
|
15
|
+
SLIDES_ACCESS_REQUEST_TOKEN_TTL_SECONDS,
|
|
16
|
+
} from "../shared/deck-access.js";
|
|
11
17
|
|
|
12
18
|
export type DeckAccessStatus = {
|
|
13
19
|
exists: boolean;
|
|
@@ -17,6 +23,7 @@ export type DeckAccessStatus = {
|
|
|
17
23
|
viewerName: string | null;
|
|
18
24
|
role: "owner" | "viewer" | "commenter" | "editor" | "admin" | null;
|
|
19
25
|
visibility: "private" | "org" | "public" | null;
|
|
26
|
+
accessRequestToken?: string;
|
|
20
27
|
};
|
|
21
28
|
|
|
22
29
|
export default defineAction({
|
|
@@ -53,7 +60,32 @@ export default defineAction({
|
|
|
53
60
|
};
|
|
54
61
|
}
|
|
55
62
|
|
|
56
|
-
|
|
63
|
+
let access: Awaited<ReturnType<typeof resolveAccess>> = null;
|
|
64
|
+
let accessProbeFailed = false;
|
|
65
|
+
try {
|
|
66
|
+
access = await resolveAccess("deck", deckId, currentAccess());
|
|
67
|
+
} catch (error) {
|
|
68
|
+
// coercion-ok: an access probe failure must fail closed as no access so
|
|
69
|
+
// the page stays gated while a separate fallback request capability
|
|
70
|
+
// keeps the owner-notification path available.
|
|
71
|
+
accessProbeFailed = true;
|
|
72
|
+
console.warn(
|
|
73
|
+
"[slides] deck access probe failed; treating the deck as private:",
|
|
74
|
+
error,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
const visibility = deck.visibility ?? "private";
|
|
78
|
+
const accessRequestToken =
|
|
79
|
+
!access && visibility === "private"
|
|
80
|
+
? signScopedAgentAccessToken({
|
|
81
|
+
resourceKind: accessProbeFailed
|
|
82
|
+
? SLIDES_ACCESS_REQUEST_FALLBACK_TOKEN_PREFIX
|
|
83
|
+
: SLIDES_ACCESS_REQUEST_TOKEN_PREFIX,
|
|
84
|
+
resourceId: deckId,
|
|
85
|
+
...(viewerEmail ? { viewerEmail } : {}),
|
|
86
|
+
ttlSeconds: SLIDES_ACCESS_REQUEST_TOKEN_TTL_SECONDS,
|
|
87
|
+
})
|
|
88
|
+
: undefined;
|
|
57
89
|
return {
|
|
58
90
|
exists: true,
|
|
59
91
|
hasAccess: Boolean(access),
|
|
@@ -61,7 +93,8 @@ export default defineAction({
|
|
|
61
93
|
viewerEmail,
|
|
62
94
|
viewerName,
|
|
63
95
|
role: access?.role ?? null,
|
|
64
|
-
visibility
|
|
96
|
+
visibility,
|
|
97
|
+
...(accessRequestToken ? { accessRequestToken } : {}),
|
|
65
98
|
};
|
|
66
99
|
},
|
|
67
100
|
});
|