@agent-native/core 0.151.1 → 0.152.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/analytics/actions/bigquery-query.ts +21 -0
- package/corpus/templates/analytics/app/components/ConnectionTestStatus.tsx +65 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +3 -3
- package/corpus/templates/analytics/app/pages/DataSources.tsx +15 -37
- package/corpus/templates/analytics/app/root.tsx +0 -1
- package/corpus/templates/analytics/changelog/2026-08-11-analytics-custom-blocks-keep-loading-with-legacy-bigquery-action.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-11-analytics-routes-company-knowledge-and-slack-context-questio.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/analytics/public/manifest.json +0 -1
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +5 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/assets/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/assets/app/root.tsx +0 -1
- package/corpus/templates/assets/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/assets/public/manifest.json +0 -1
- package/corpus/templates/brain/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/brain/app/root.tsx +0 -1
- package/corpus/templates/brain/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/brain/public/manifest.json +0 -1
- package/corpus/templates/calendar/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +0 -53
- package/corpus/templates/calendar/app/i18n-data.ts +0 -80
- package/corpus/templates/calendar/app/root.tsx +0 -1
- package/corpus/templates/calendar/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-11-google-calendar-connection-buttons-now-open-the-sign-in-flow.md +6 -0
- package/corpus/templates/calendar/public/manifest.json +0 -1
- package/corpus/templates/calendar/server/plugins/auth.ts +0 -10
- package/corpus/templates/chat/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/chat/app/root.tsx +0 -1
- package/corpus/templates/chat/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/chat/public/manifest.json +0 -1
- package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +1 -1
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +6 -0
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +20 -3
- package/corpus/templates/clips/app/i18n/en-US.ts +4 -4
- package/corpus/templates/clips/app/root.tsx +0 -1
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +1 -3
- package/corpus/templates/clips/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-clips-desktop-settings-are-now-easier-to-scan-with-a-wider-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-google-calendar-connections-from-meetings-now-open-directly.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-recording-viewers-can-comment-and-react.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-system-audio-recording-now-lives-inside-the-microphone-menu-.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-11-timestamped-comments-stay-on-the-video-for-three-seconds-wh.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +773 -1001
- package/corpus/templates/clips/desktop/src/components/MediaDeviceRow.tsx +15 -27
- package/corpus/templates/clips/desktop/src/styles.css +364 -5
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +1 -1
- package/corpus/templates/clips/public/manifest.json +0 -1
- package/corpus/templates/content/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/content/AGENTS.md +3 -0
- package/corpus/templates/content/actions/resolve-content-landing.ts +120 -0
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +9 -0
- package/corpus/templates/content/app/i18n-data.ts +58 -0
- package/corpus/templates/content/app/lib/content-landing.ts +22 -0
- package/corpus/templates/content/app/root.tsx +0 -1
- package/corpus/templates/content/app/routes/_app._index.tsx +36 -26
- package/corpus/templates/content/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/content/changelog/2026-08-11-content-opens-your-last-page-on-return-and-gives-new-users-a.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.workspace.session-restore.md +7 -1
- package/corpus/templates/content/parity/matrix.md +1 -0
- package/corpus/templates/content/parity/matrix.ts +22 -0
- package/corpus/templates/content/public/manifest.json +0 -1
- package/corpus/templates/content/shared/content-landing.ts +17 -0
- package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/crm/app/root.tsx +0 -1
- package/corpus/templates/crm/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/crm/public/manifest.json +0 -1
- package/corpus/templates/design/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/design/app/i18n/en-US.ts +5 -0
- package/corpus/templates/design/app/root.tsx +0 -1
- package/corpus/templates/design/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/design/changelog/2026-08-11-design-sharing-now-identifies-viewers-who-can-add-review-com.md +6 -0
- package/corpus/templates/design/public/manifest.json +0 -1
- package/corpus/templates/dispatch/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/dispatch/AGENTS.md +9 -0
- package/corpus/templates/dispatch/actions/list-dispatch-usage-metrics.ts +5 -19
- package/corpus/templates/dispatch/actions/view-screen.ts +10 -3
- package/corpus/templates/dispatch/app/root.tsx +0 -1
- package/corpus/templates/dispatch/app/routes/settings.tsx +13 -1
- package/corpus/templates/dispatch/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-clicking-the-dispatch-logo-returns-to-the-overview-page.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-no-longer-links-to-itself-from-the-app-switcher.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-settings-now-link-directly-to-admin.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-dispatch-shows-usage-trends-recent-prompts-and-an-agent-revi.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-11-human-friendly-app-names.md +6 -0
- package/corpus/templates/dispatch/public/manifest.json +0 -1
- package/corpus/templates/dispatch/server/lib/identity-sso.ts +432 -158
- package/corpus/templates/dispatch/server/plugins/identity-sso.ts +204 -123
- package/corpus/templates/dispatch/server/plugins/setup-dispatch.ts +1 -0
- package/corpus/templates/factory/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/factory/app/root.tsx +0 -1
- package/corpus/templates/factory/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/factory/public/manifest.json +0 -1
- package/corpus/templates/forms/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/forms/app/root.tsx +0 -1
- package/corpus/templates/forms/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/forms/public/manifest.json +0 -1
- package/corpus/templates/macros/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/mail/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/mail/app/root.tsx +0 -1
- package/corpus/templates/mail/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/mail/changelog/2026-08-11-google-sign-in-now-opens-the-gmail-connection-flow-directly.md +6 -0
- package/corpus/templates/mail/public/manifest.json +0 -1
- package/corpus/templates/mail/server/plugins/auth.ts +0 -10
- package/corpus/templates/plan/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/plan/app/i18n/en-US.ts +2 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +6 -0
- package/corpus/templates/plan/app/root.tsx +0 -1
- package/corpus/templates/plan/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/plan/changelog/2026-08-11-plan-sharing-now-identifies-viewers-who-can-add-comments.md +6 -0
- package/corpus/templates/plan/public/manifest.json +0 -1
- package/corpus/templates/slides/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +13 -0
- package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +2 -0
- package/corpus/templates/slides/app/root.tsx +0 -1
- package/corpus/templates/slides/changelog/2026-08-10-asking-the-agent-for-several-slide-animations-at-once-now-ke.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-11-share-dialog-uses-commenter-terminology.md +6 -0
- package/corpus/templates/slides/public/manifest.json +0 -1
- package/corpus/templates/tasks/.agents/skills/sharing/SKILL.md +1 -1
- package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/tasks/app/root.tsx +0 -1
- package/corpus/templates/tasks/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/corpus/templates/tasks/public/manifest.json +0 -1
- package/dist/agent/production-agent.js +1 -22
- package/dist/agent/run-manager.d.ts +7 -0
- package/dist/agent/run-manager.js +46 -3
- package/dist/agent/tool-approval-migrations.d.ts +40 -0
- package/dist/agent/tool-approval-migrations.js +51 -0
- package/dist/agent/tool-approval-store.js +5 -20
- package/dist/cli/templates-meta.js +2 -2
- package/dist/client/AgentPanel.js +10 -1
- package/dist/client/AssistantChat.d.ts +3 -0
- package/dist/client/AssistantChat.js +148 -140
- package/dist/client/agent-page/AgentTabsPage.js +2 -2
- package/dist/client/chat/attachment-adapters.js +8 -2
- package/dist/client/chat/tool-call-display.d.ts +1 -0
- package/dist/client/chat/tool-call-display.js +3 -1
- package/dist/client/chat-first.js +11 -5
- package/dist/client/hooks/index.d.ts +1 -1
- package/dist/client/hooks/index.js +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +3 -1
- package/dist/client/org/OrgSwitcher.js +22 -16
- package/dist/client/org/WorkspaceNotice.js +3 -1
- package/dist/client/resources/index.d.ts +1 -1
- package/dist/client/resources/index.js +1 -1
- package/dist/client/resources/use-mcp-servers.d.ts +1 -0
- package/dist/client/resources/use-mcp-servers.js +14 -0
- package/dist/client/settings/AccountSettingsCard.js +8 -4
- package/dist/client/settings/SettingsPanel.js +46 -13
- package/dist/client/settings/SettingsTabsPage.js +8 -3
- package/dist/client/sharing/ShareButton.d.ts +7 -1
- package/dist/client/sharing/ShareButton.js +14 -7
- package/dist/client/sharing/ShareDialog.js +5 -1
- package/dist/client/sharing/useShareButtonController.d.ts +4 -0
- package/dist/client/sharing/useShareButtonController.js +15 -0
- package/dist/client/sharing/useShareDialogController.d.ts +7 -0
- package/dist/client/sharing/useShareDialogController.js +14 -0
- package/dist/client/sse-event-processor.js +37 -0
- package/dist/client/use-session.d.ts +2 -0
- package/dist/client/use-session.js +69 -5
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +1 -1
- package/dist/file-upload/pre-upload-attachments.d.ts +3 -4
- package/dist/file-upload/pre-upload-attachments.js +65 -17
- package/dist/ingestion/index.d.ts +1 -0
- package/dist/ingestion/index.js +1 -0
- package/dist/ingestion/office.d.ts +3 -3
- package/dist/ingestion/office.js +24 -2
- package/dist/ingestion/spreadsheet.d.ts +41 -0
- package/dist/ingestion/spreadsheet.js +172 -0
- package/dist/integrations/adapters/slack.js +7 -3
- package/dist/localization/default-messages.d.ts +3 -0
- package/dist/localization/default-messages.js +3 -0
- package/dist/mcp/embed-app.js +20 -9
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/notifications/routes.d.ts +6 -6
- package/dist/oauth-tokens/migrations.d.ts +8 -0
- package/dist/oauth-tokens/migrations.js +36 -0
- package/dist/observability/routes.d.ts +5 -5
- package/dist/org/workspace-url.d.ts +8 -0
- package/dist/org/workspace-url.js +26 -2
- package/dist/progress/routes.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-chat-plugin.js +16 -8
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.d.ts +0 -11
- package/dist/server/auth.js +37 -20
- package/dist/server/better-auth-migrations.js +23 -0
- package/dist/server/email-template.d.ts +5 -0
- package/dist/server/email-template.js +9 -0
- package/dist/server/embed-route.js +17 -5
- package/dist/server/identity-sso-store.d.ts +40 -66
- package/dist/server/identity-sso-store.js +187 -150
- package/dist/server/identity-sso.d.ts +17 -73
- package/dist/server/identity-sso.js +294 -297
- package/dist/server/onboarding-html.d.ts +0 -12
- package/dist/server/onboarding-html.js +46 -269
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/release-migrations.js +8 -0
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/workspace-app-id.d.ts +2 -0
- package/dist/shared/workspace-app-id.js +10 -0
- package/dist/sharing/actions/share-resource.d.ts +1 -0
- package/dist/sharing/actions/share-resource.js +20 -5
- package/dist/templates/chat/.agents/skills/sharing/SKILL.md +1 -1
- package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/dist/templates/chat/app/root.tsx +0 -1
- package/dist/templates/chat/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/dist/templates/chat/public/manifest.json +0 -1
- package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/dist/templates/default/app/root.tsx +0 -1
- package/dist/templates/default/public/manifest.json +0 -1
- package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +22 -11
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/docs/content/cross-app-sso.mdx +85 -27
- package/docs/content/template-mail.mdx +0 -6
- package/package.json +4 -3
- package/src/templates/chat/.agents/skills/sharing/SKILL.md +1 -1
- package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/src/templates/chat/app/root.tsx +0 -1
- package/src/templates/chat/changelog/2026-08-11-browser-no-longer-offers-desktop-install.md +6 -0
- package/src/templates/chat/public/manifest.json +0 -1
- package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/src/templates/default/app/root.tsx +0 -1
- package/src/templates/default/public/manifest.json +0 -1
- package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +22 -11
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +24 -0
- package/corpus/templates/calendar/changelog/2026-06-23-added-a-heads-up-explaining-google-s-app-not-verified-screen.md +0 -6
- package/corpus/templates/clips/app/components/meetings/google-oauth-identity-notice.tsx +0 -51
- package/corpus/templates/clips/changelog/2026-07-30-google-calendar-warning.md +0 -6
|
@@ -1,57 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* AUTHORITY ("Sign in with Agent-Native").
|
|
2
|
+
* Dispatch identity authority routes.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
4
|
+
* Browser flow:
|
|
5
|
+
* 1. Client -> /authorize?response_type=code&code_challenge=...
|
|
6
|
+
* 2. Dispatch authenticates the existing user and redirects with `code` and
|
|
7
|
+
* the caller's opaque `state`.
|
|
8
|
+
* 3. Client server -> /token with code + state + PKCE verifier.
|
|
9
|
+
* 4. Dispatch atomically consumes the code and returns a short-lived signed
|
|
10
|
+
* identity assertion server-to-server.
|
|
6
11
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* 2. We bind `app` and `redirect_uri` against the strict allowlist
|
|
12
|
-
* (`isAllowedIdentityRedirect`). An invalid/forbidden pair is rejected
|
|
13
|
-
* with 400 BEFORE any session work — an attacker-controlled
|
|
14
|
-
* `redirect_uri` must never receive a token. This is the single most
|
|
15
|
-
* important control on this endpoint.
|
|
16
|
-
*
|
|
17
|
-
* 3. We resolve the EXISTING Dispatch Better Auth session (`getSession`).
|
|
18
|
-
* - Not logged in -> 302 to the sign-in href minted by the framework's
|
|
19
|
-
* `signInJourney`, carrying this authorize path as an opaque
|
|
20
|
-
* continuation. The framework serves Dispatch's normal login form,
|
|
21
|
-
* and on success resumes the continuation, re-entering THIS handler
|
|
22
|
-
* authenticated. No new login UI; we reuse Dispatch's exact
|
|
23
|
-
* existing auth flow.
|
|
24
|
-
* - Logged in -> mint + redirect (step 4).
|
|
25
|
-
*
|
|
26
|
-
* 4. Mint a SHORT-LIVED signed identity JWT using the EXISTING A2A signer
|
|
27
|
-
* (`signA2AToken`, HS256 over the shared `A2A_SECRET`). Claims are
|
|
28
|
-
* exactly: sub=email, email, name?, org_domain?, scope:"identity",
|
|
29
|
-
* aud=redirect_uri, redirect_uri, jti, short exp (<= 5 min). 302 to
|
|
30
|
-
* `redirect_uri` with the token and the caller's UNTOUCHED `state`
|
|
31
|
-
* appended as query params.
|
|
32
|
-
*
|
|
33
|
-
* Why no `/token` code-exchange endpoint:
|
|
34
|
-
* The token is already (a) short-lived (<=2 min exp), (b) signature-
|
|
35
|
-
* verified against the shared A2A secret, and (c) only ever delivered to
|
|
36
|
-
* an allowlisted first-party https host. Adding a code + exchange
|
|
37
|
-
* endpoint would add surface (a second unauthenticated route, a code
|
|
38
|
-
* store) without changing the trust model, since the redirect target is
|
|
39
|
-
* already constrained. We therefore sign directly and return via the
|
|
40
|
-
* redirect query — the simplest secure flow.
|
|
41
|
-
*
|
|
42
|
-
* Replay protection:
|
|
43
|
-
* The short `exp` (<=2 min) + random `jti` + the caller's `state`
|
|
44
|
-
* echo-check (the client MUST verify `state` it generated) bound the
|
|
45
|
-
* replay window. We intentionally do NOT add a Dispatch-side jti store:
|
|
46
|
-
* the core MCP connect-store jti helpers are not importable from a public
|
|
47
|
-
* `@agent-native/core` subpath, and a bespoke store would be net-new
|
|
48
|
-
* surface for a token whose window is already <=2 min and single-origin.
|
|
49
|
-
* This is documented as the chosen trade-off.
|
|
50
|
-
*
|
|
51
|
-
* Auth-guard reachability:
|
|
52
|
-
* Dispatch's primary auth plugin receives the exact public paths from
|
|
53
|
-
* `setupDispatch`. These handlers still resolve the session themselves;
|
|
54
|
-
* public-path only means the guard does not pre-empt the logged-out bounce.
|
|
12
|
+
* The browser never receives a JWT, password, or reusable identity assertion.
|
|
13
|
+
* The default-off Desktop flag still gates only the packaged Canary path;
|
|
14
|
+
* ordinary browser federation remains controlled by the client's explicit
|
|
15
|
+
* identity-hub configuration.
|
|
55
16
|
*/
|
|
56
17
|
|
|
57
18
|
import { signA2AToken } from "@agent-native/core/a2a";
|
|
@@ -63,15 +24,22 @@ import {
|
|
|
63
24
|
import { getOrgDomain } from "@agent-native/core/org";
|
|
64
25
|
import { getH3App, getSession } from "@agent-native/core/server";
|
|
65
26
|
import { signInJourney } from "@agent-native/core/shared";
|
|
66
|
-
import { defineEventHandler, getHeader, getMethod } from "h3";
|
|
27
|
+
import { defineEventHandler, getHeader, getMethod, readBody } from "h3";
|
|
67
28
|
import type { H3Event } from "h3";
|
|
68
29
|
|
|
69
30
|
import { DESKTOP_WORKSPACE_SSO_FLAG } from "../../shared/feature-flags.js";
|
|
70
31
|
import {
|
|
32
|
+
IDENTITY_AUTHORIZATION_CODE_TTL_MS,
|
|
33
|
+
IDENTITY_SCOPE,
|
|
34
|
+
IDENTITY_SSO_TOKEN_PATH,
|
|
71
35
|
IDENTITY_TOKEN_TTL,
|
|
72
36
|
buildIdentityClaims,
|
|
73
37
|
buildRedirectLocation,
|
|
74
|
-
|
|
38
|
+
consumeIdentityAuthorizationCode,
|
|
39
|
+
createIdentityAuthorizationCode,
|
|
40
|
+
isValidSsoState,
|
|
41
|
+
normalizeIdentityAuthority,
|
|
42
|
+
resolveIdentitySsoApp,
|
|
75
43
|
} from "../lib/identity-sso.js";
|
|
76
44
|
|
|
77
45
|
const AVAILABILITY_PATH = "/_agent-native/identity/availability";
|
|
@@ -94,27 +62,42 @@ function jsonResponse(body: unknown, status: number): Response {
|
|
|
94
62
|
headers: {
|
|
95
63
|
"Cache-Control": "private, no-store",
|
|
96
64
|
"Content-Type": "application/json",
|
|
65
|
+
"Referrer-Policy": "no-referrer",
|
|
97
66
|
},
|
|
98
67
|
});
|
|
99
68
|
}
|
|
100
69
|
|
|
101
70
|
function redirect(location: string): Response {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
71
|
+
return new Response("", {
|
|
72
|
+
status: 302,
|
|
73
|
+
headers: {
|
|
74
|
+
Location: location,
|
|
75
|
+
"Cache-Control": "no-store",
|
|
76
|
+
"Referrer-Policy": "no-referrer",
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function resolveAuthority(): string | null {
|
|
82
|
+
return normalizeIdentityAuthority(
|
|
83
|
+
process.env.APP_URL || process.env.BETTER_AUTH_URL,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function bodyString(body: unknown, key: string): string | null {
|
|
88
|
+
if (!body || typeof body !== "object") return null;
|
|
89
|
+
const value = (body as Record<string, unknown>)[key];
|
|
90
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
105
91
|
}
|
|
106
92
|
|
|
107
|
-
/**
|
|
108
|
-
* Resolve the org domain for the active org, best-effort. A missing org
|
|
109
|
-
* just yields a token with no `org_domain` claim (still a valid identity).
|
|
110
|
-
*/
|
|
111
93
|
async function resolveOrgDomain(
|
|
112
94
|
orgId: string | undefined,
|
|
113
95
|
): Promise<string | undefined> {
|
|
114
96
|
if (!orgId) return undefined;
|
|
115
97
|
try {
|
|
116
98
|
return (await getOrgDomain(orgId)) ?? undefined;
|
|
117
|
-
} catch {
|
|
99
|
+
} catch (error) {
|
|
100
|
+
void error;
|
|
118
101
|
return undefined;
|
|
119
102
|
}
|
|
120
103
|
}
|
|
@@ -144,21 +127,29 @@ export async function isWorkspaceSsoEnabledForSession(
|
|
|
144
127
|
}).catch(() => false);
|
|
145
128
|
}
|
|
146
129
|
|
|
147
|
-
const availabilityHandler = defineEventHandler(
|
|
130
|
+
export const availabilityHandler = defineEventHandler(
|
|
148
131
|
async (event: H3Event): Promise<Response> => {
|
|
149
132
|
const method = getMethod(event);
|
|
150
133
|
if (method !== "GET" && method !== "HEAD") {
|
|
151
134
|
return jsonResponse({ error: "Method not allowed" }, 405);
|
|
152
135
|
}
|
|
153
136
|
const session = await getSession(event).catch(() => null);
|
|
137
|
+
const isDesktopRequest = isDesktopWorkspaceSsoRequest(
|
|
138
|
+
getHeader(event, "user-agent"),
|
|
139
|
+
);
|
|
140
|
+
// Anonymous availability is only a Canary hint used by an explicit
|
|
141
|
+
// Desktop settings action. Ordinary browser requests never get a positive
|
|
142
|
+
// answer here, so this endpoint cannot become an anonymous auto-login.
|
|
154
143
|
const available = session?.email
|
|
155
144
|
? await isWorkspaceSsoEnabledForSession(session)
|
|
156
|
-
:
|
|
145
|
+
: isDesktopRequest
|
|
146
|
+
? await canAttemptWorkspaceSso()
|
|
147
|
+
: false;
|
|
157
148
|
return jsonResponse({ available }, 200);
|
|
158
149
|
},
|
|
159
150
|
);
|
|
160
151
|
|
|
161
|
-
const authorizeHandler = defineEventHandler(
|
|
152
|
+
export const authorizeHandler = defineEventHandler(
|
|
162
153
|
async (event: H3Event): Promise<Response> => {
|
|
163
154
|
const method = getMethod(event);
|
|
164
155
|
if (method !== "GET" && method !== "HEAD") {
|
|
@@ -169,53 +160,66 @@ const authorizeHandler = defineEventHandler(
|
|
|
169
160
|
let search: URLSearchParams;
|
|
170
161
|
try {
|
|
171
162
|
search = new URL(rawUrl, "http://an.invalid").searchParams;
|
|
172
|
-
} catch {
|
|
163
|
+
} catch (error) {
|
|
164
|
+
void error;
|
|
173
165
|
search = new URLSearchParams();
|
|
174
166
|
}
|
|
175
167
|
|
|
176
168
|
const redirectUri = search.get("redirect_uri");
|
|
177
169
|
const appId = search.get("app");
|
|
170
|
+
const clientId = search.get("client_id");
|
|
178
171
|
const state = search.get("state");
|
|
172
|
+
const responseType = search.get("response_type");
|
|
173
|
+
const codeChallenge = search.get("code_challenge");
|
|
174
|
+
const codeChallengeMethod = search.get("code_challenge_method");
|
|
179
175
|
const isDesktopRequest = isDesktopWorkspaceSsoRequest(
|
|
180
176
|
getHeader(event, "user-agent"),
|
|
181
177
|
);
|
|
182
178
|
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
|
|
179
|
+
// Validate every browser-controlled protocol parameter before resolving a
|
|
180
|
+
// Dispatch session or constructing a continuation URL.
|
|
181
|
+
const registration = resolveIdentitySsoApp(appId, clientId, redirectUri);
|
|
182
|
+
if (
|
|
183
|
+
!registration ||
|
|
184
|
+
responseType !== "code" ||
|
|
185
|
+
!isValidSsoState(state) ||
|
|
186
|
+
!isValidSsoState(codeChallenge) ||
|
|
187
|
+
codeChallengeMethod !== "S256"
|
|
188
|
+
) {
|
|
186
189
|
return jsonResponse(
|
|
187
190
|
{
|
|
188
|
-
error: "
|
|
191
|
+
error: "invalid_authorization_request",
|
|
189
192
|
error_description:
|
|
190
|
-
"
|
|
191
|
-
"first-party host (a localhost http URL is allowed for " +
|
|
192
|
-
"local development).",
|
|
193
|
+
"The app, client, callback, state, or PKCE binding is not registered.",
|
|
193
194
|
},
|
|
194
195
|
400,
|
|
195
196
|
);
|
|
196
197
|
}
|
|
197
|
-
// Narrowed to string by isAllowedIdentityRedirect.
|
|
198
198
|
const safeRedirectUri = redirectUri as string;
|
|
199
|
+
const safeAppId = appId as string;
|
|
200
|
+
const safeClientId = clientId as string;
|
|
201
|
+
const safeState = state as string;
|
|
202
|
+
const safeCodeChallenge = codeChallenge as string;
|
|
203
|
+
const authority = resolveAuthority();
|
|
204
|
+
if (!authority) {
|
|
205
|
+
return jsonResponse(
|
|
206
|
+
{
|
|
207
|
+
error: "identity_unavailable",
|
|
208
|
+
error_description: "Dispatch identity authority is not configured.",
|
|
209
|
+
},
|
|
210
|
+
503,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
199
213
|
|
|
200
214
|
if (isDesktopRequest && !(await canAttemptWorkspaceSso())) {
|
|
201
215
|
return jsonResponse({ error: "not_found" }, 404);
|
|
202
216
|
}
|
|
203
217
|
|
|
204
|
-
// ---- Resolve the EXISTING Dispatch session --------------------------
|
|
205
218
|
const session = await getSession(event).catch(() => null);
|
|
206
|
-
|
|
207
219
|
if (!session?.email) {
|
|
208
|
-
// Logged out: bounce through the framework's one sign-in journey,
|
|
209
|
-
// preserving the FULL authorize path as the continuation so we
|
|
210
|
-
// re-enter here authenticated.
|
|
211
220
|
const queryStart = rawUrl.indexOf("?");
|
|
212
221
|
const authorizePathWithQuery =
|
|
213
222
|
AUTHORIZE_PATH + (queryStart >= 0 ? rawUrl.slice(queryStart) : "");
|
|
214
|
-
// No basePath: this route is registered at the bare, unprefixed path
|
|
215
|
-
// (see the getH3App().use below), so base-path containment would
|
|
216
|
-
// reject its own authorize URL on a base-path deploy. The pathname is
|
|
217
|
-
// a compile-time constant; only the query is request-derived, and
|
|
218
|
-
// signInJourney validates that.
|
|
219
223
|
const { signInHref } = signInJourney({ at: authorizePathWithQuery });
|
|
220
224
|
if (!signInHref) {
|
|
221
225
|
return jsonResponse(
|
|
@@ -233,67 +237,144 @@ const authorizeHandler = defineEventHandler(
|
|
|
233
237
|
if (isDesktopRequest && !(await isWorkspaceSsoEnabledForSession(session))) {
|
|
234
238
|
return jsonResponse({ error: "not_found" }, 404);
|
|
235
239
|
}
|
|
236
|
-
|
|
237
|
-
// ---- Mint the short-lived identity token ----------------------------
|
|
238
240
|
if (!process.env.A2A_SECRET) {
|
|
239
|
-
// Without a shared secret, no first-party app could verify the token.
|
|
240
241
|
return jsonResponse(
|
|
241
242
|
{
|
|
242
243
|
error: "identity_unavailable",
|
|
243
|
-
error_description:
|
|
244
|
-
"This Dispatch deployment has no A2A_SECRET configured, so " +
|
|
245
|
-
"identity tokens cannot be signed.",
|
|
244
|
+
error_description: "Dispatch identity signing is not configured.",
|
|
246
245
|
},
|
|
247
246
|
503,
|
|
248
247
|
);
|
|
249
248
|
}
|
|
250
249
|
|
|
251
|
-
|
|
252
|
-
const claims = buildIdentityClaims({
|
|
253
|
-
email: session.email,
|
|
254
|
-
name: session.name,
|
|
255
|
-
orgDomain,
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
let token: string;
|
|
250
|
+
let code: string;
|
|
259
251
|
try {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
email: claims.email,
|
|
271
|
-
...(claims.name ? { name: claims.name } : {}),
|
|
272
|
-
scope: claims.scope,
|
|
273
|
-
aud: safeRedirectUri,
|
|
274
|
-
redirect_uri: safeRedirectUri,
|
|
275
|
-
jti: claims.jti,
|
|
276
|
-
},
|
|
252
|
+
code = await createIdentityAuthorizationCode({
|
|
253
|
+
state: safeState,
|
|
254
|
+
appId: safeAppId,
|
|
255
|
+
clientId: safeClientId,
|
|
256
|
+
redirectUri: safeRedirectUri,
|
|
257
|
+
authority,
|
|
258
|
+
codeChallenge: safeCodeChallenge,
|
|
259
|
+
email: session.email,
|
|
260
|
+
name: session.name,
|
|
261
|
+
orgDomain: await resolveOrgDomain(session.orgId),
|
|
277
262
|
});
|
|
278
|
-
} catch {
|
|
263
|
+
} catch (error) {
|
|
264
|
+
void error;
|
|
279
265
|
return jsonResponse(
|
|
280
266
|
{
|
|
281
|
-
error: "
|
|
282
|
-
error_description: "
|
|
267
|
+
error: "identity_unavailable",
|
|
268
|
+
error_description: "Could not create a one-time sign-in code.",
|
|
283
269
|
},
|
|
284
|
-
|
|
270
|
+
503,
|
|
285
271
|
);
|
|
286
272
|
}
|
|
287
273
|
|
|
288
|
-
return redirect(buildRedirectLocation(safeRedirectUri,
|
|
274
|
+
return redirect(buildRedirectLocation(safeRedirectUri, code, safeState));
|
|
289
275
|
},
|
|
290
276
|
);
|
|
291
277
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
278
|
+
export const tokenHandler = defineEventHandler(
|
|
279
|
+
async (event: H3Event): Promise<Response> => {
|
|
280
|
+
if (getMethod(event) !== "POST") {
|
|
281
|
+
return jsonResponse({ error: "Method not allowed" }, 405);
|
|
282
|
+
}
|
|
283
|
+
const body = await readBody(event).catch((error) => {
|
|
284
|
+
// coercion-ok: unreadable request bodies are rejected as invalid requests below.
|
|
285
|
+
void error;
|
|
286
|
+
return null;
|
|
287
|
+
});
|
|
288
|
+
const grantType = bodyString(body, "grant_type");
|
|
289
|
+
const code = bodyString(body, "code");
|
|
290
|
+
const state = bodyString(body, "state");
|
|
291
|
+
const appId = bodyString(body, "app_id");
|
|
292
|
+
const clientId = bodyString(body, "client_id");
|
|
293
|
+
const redirectUri = bodyString(body, "redirect_uri");
|
|
294
|
+
const codeVerifier = bodyString(body, "code_verifier");
|
|
295
|
+
if (
|
|
296
|
+
grantType !== "authorization_code" ||
|
|
297
|
+
!code ||
|
|
298
|
+
!state ||
|
|
299
|
+
!appId ||
|
|
300
|
+
!clientId ||
|
|
301
|
+
!redirectUri ||
|
|
302
|
+
!codeVerifier
|
|
303
|
+
) {
|
|
304
|
+
return jsonResponse({ error: "invalid_token_request" }, 400);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const registration = resolveIdentitySsoApp(appId, clientId, redirectUri);
|
|
308
|
+
if (!registration) {
|
|
309
|
+
return jsonResponse({ error: "invalid_token_request" }, 400);
|
|
310
|
+
}
|
|
311
|
+
const authority = resolveAuthority();
|
|
312
|
+
if (!authority || !process.env.A2A_SECRET) {
|
|
313
|
+
return jsonResponse({ error: "identity_unavailable" }, 503);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const identity = await consumeIdentityAuthorizationCode({
|
|
317
|
+
code,
|
|
318
|
+
state,
|
|
319
|
+
appId,
|
|
320
|
+
clientId,
|
|
321
|
+
redirectUri,
|
|
322
|
+
authority,
|
|
323
|
+
codeVerifier,
|
|
324
|
+
}).catch((error) => {
|
|
325
|
+
// coercion-ok: an unreadable authorization code is handled as invalid_grant below.
|
|
326
|
+
void error;
|
|
327
|
+
return null;
|
|
328
|
+
});
|
|
329
|
+
if (!identity) return jsonResponse({ error: "invalid_grant" }, 400);
|
|
330
|
+
|
|
331
|
+
const claims = buildIdentityClaims({
|
|
332
|
+
email: identity.email,
|
|
333
|
+
name: identity.name,
|
|
334
|
+
orgDomain: identity.orgDomain,
|
|
335
|
+
});
|
|
336
|
+
let assertion: string;
|
|
337
|
+
try {
|
|
338
|
+
assertion = await signA2AToken(
|
|
339
|
+
identity.email,
|
|
340
|
+
identity.orgDomain,
|
|
341
|
+
undefined,
|
|
342
|
+
{
|
|
343
|
+
preferGlobalSecret: true,
|
|
344
|
+
expiresIn: IDENTITY_TOKEN_TTL,
|
|
345
|
+
audience: redirectUri,
|
|
346
|
+
extraClaims: {
|
|
347
|
+
email: claims.email,
|
|
348
|
+
...(claims.name ? { name: claims.name } : {}),
|
|
349
|
+
scope: IDENTITY_SCOPE,
|
|
350
|
+
jti: identity.jti,
|
|
351
|
+
redirect_uri: redirectUri,
|
|
352
|
+
identity_client_id: clientId,
|
|
353
|
+
identity_authority: authority,
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
void error;
|
|
359
|
+
return jsonResponse({ error: "sign_failed" }, 500);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// This response is server-to-server. It is never redirected through the
|
|
363
|
+
// browser and is intentionally not rendered or logged.
|
|
364
|
+
return jsonResponse(
|
|
365
|
+
{
|
|
366
|
+
assertion,
|
|
367
|
+
token_type: "identity-assertion",
|
|
368
|
+
expires_in: Math.floor(IDENTITY_AUTHORIZATION_CODE_TTL_MS / 1_000),
|
|
369
|
+
},
|
|
370
|
+
200,
|
|
371
|
+
);
|
|
372
|
+
},
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
/** Mount the authority and token endpoints. */
|
|
296
376
|
export default async (nitroApp: any) => {
|
|
297
377
|
getH3App(nitroApp).use(AVAILABILITY_PATH, availabilityHandler);
|
|
298
378
|
getH3App(nitroApp).use(AUTHORIZE_PATH, authorizeHandler);
|
|
379
|
+
getH3App(nitroApp).use(IDENTITY_SSO_TOKEN_PATH, tokenHandler);
|
|
299
380
|
};
|
|
@@ -189,7 +189,7 @@ The framework auto-mounts these actions in every template — no per-template bo
|
|
|
189
189
|
|
|
190
190
|
| Action | Args | Purpose |
|
|
191
191
|
| -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
|
|
192
|
-
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link
|
|
192
|
+
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
193
193
|
| `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
|
|
194
194
|
| `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
|
|
195
195
|
| `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
|
|
@@ -84,6 +84,30 @@ private web access, invent an importer, add fake OAuth, or scrape a logged-in
|
|
|
84
84
|
page. If the needed context is not visible, ask for an export, transcript, or
|
|
85
85
|
attachment and treat that artifact as imported source material.
|
|
86
86
|
|
|
87
|
+
### Spreadsheet sources
|
|
88
|
+
|
|
89
|
+
Spreadsheet attachments are valid source artifacts for this workflow:
|
|
90
|
+
|
|
91
|
+
- CSV files can be read as tabular text. XLS and XLSX uploads are parsed into
|
|
92
|
+
bounded worksheet metadata and representative rows when the host supports
|
|
93
|
+
workbook ingestion. Treat the preview as untrusted user data, preserve the
|
|
94
|
+
workbook name and worksheet names, and record row/column limits and any
|
|
95
|
+
truncation in the source brief.
|
|
96
|
+
- A Google Sheets URL is a live provider source, not proof that the sheet is
|
|
97
|
+
readable. Use an authenticated Google Sheets/Drive connection and the
|
|
98
|
+
provider API catalog/docs/request path to read only the required sheet or
|
|
99
|
+
range. If that connection is unavailable, ask for a CSV/XLSX export or the
|
|
100
|
+
required connection; do not use a public export URL to bypass access.
|
|
101
|
+
- Decide whether the generated app needs a one-time snapshot or a refreshable
|
|
102
|
+
source. For a snapshot, pass bounded sample context and provenance to the
|
|
103
|
+
Builder handoff. For a live source, preserve the provider/file identity,
|
|
104
|
+
worksheet or range, and refresh semantics, then implement a scoped action for
|
|
105
|
+
reads or refreshes. Never copy workbook bytes, base64 data, credentials, or a
|
|
106
|
+
full unbounded sheet into SQL, application state, or the handoff prompt.
|
|
107
|
+
- Do not claim the app imported the whole workbook when only a preview was
|
|
108
|
+
available. Keep unreadable, partial, and failed source states distinct from
|
|
109
|
+
an empty sheet.
|
|
110
|
+
|
|
87
111
|
For local code-agent runtimes, deliver a fresh app in a new directory,
|
|
88
112
|
implement the repeatable workflow with buttons and agent handoffs, start its dev
|
|
89
113
|
server, verify the main path, and continue through build/deployment handoff. Do
|
|
@@ -69,7 +69,6 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
69
69
|
suppressHydrationWarning
|
|
70
70
|
dangerouslySetInnerHTML={{ __html: LOCALE_INIT_SCRIPT }}
|
|
71
71
|
/>
|
|
72
|
-
<link rel="manifest" href={appPath("/manifest.json")} />
|
|
73
72
|
{/* guard:allow-raw-color - theme-color metadata requires a concrete browser-chrome color. */}
|
|
74
73
|
<meta name="theme-color" content="#18181B" />
|
|
75
74
|
<meta name="mobile-web-app-capable" content="yes" />
|
|
@@ -189,7 +189,7 @@ The framework auto-mounts these actions in every template — no per-template bo
|
|
|
189
189
|
|
|
190
190
|
| Action | Args | Purpose |
|
|
191
191
|
| -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
|
|
192
|
-
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link
|
|
192
|
+
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
193
193
|
| `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
|
|
194
194
|
| `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
|
|
195
195
|
| `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
|
|
@@ -84,6 +84,30 @@ private web access, invent an importer, add fake OAuth, or scrape a logged-in
|
|
|
84
84
|
page. If the needed context is not visible, ask for an export, transcript, or
|
|
85
85
|
attachment and treat that artifact as imported source material.
|
|
86
86
|
|
|
87
|
+
### Spreadsheet sources
|
|
88
|
+
|
|
89
|
+
Spreadsheet attachments are valid source artifacts for this workflow:
|
|
90
|
+
|
|
91
|
+
- CSV files can be read as tabular text. XLS and XLSX uploads are parsed into
|
|
92
|
+
bounded worksheet metadata and representative rows when the host supports
|
|
93
|
+
workbook ingestion. Treat the preview as untrusted user data, preserve the
|
|
94
|
+
workbook name and worksheet names, and record row/column limits and any
|
|
95
|
+
truncation in the source brief.
|
|
96
|
+
- A Google Sheets URL is a live provider source, not proof that the sheet is
|
|
97
|
+
readable. Use an authenticated Google Sheets/Drive connection and the
|
|
98
|
+
provider API catalog/docs/request path to read only the required sheet or
|
|
99
|
+
range. If that connection is unavailable, ask for a CSV/XLSX export or the
|
|
100
|
+
required connection; do not use a public export URL to bypass access.
|
|
101
|
+
- Decide whether the generated app needs a one-time snapshot or a refreshable
|
|
102
|
+
source. For a snapshot, pass bounded sample context and provenance to the
|
|
103
|
+
Builder handoff. For a live source, preserve the provider/file identity,
|
|
104
|
+
worksheet or range, and refresh semantics, then implement a scoped action for
|
|
105
|
+
reads or refreshes. Never copy workbook bytes, base64 data, credentials, or a
|
|
106
|
+
full unbounded sheet into SQL, application state, or the handoff prompt.
|
|
107
|
+
- Do not claim the app imported the whole workbook when only a preview was
|
|
108
|
+
available. Keep unreadable, partial, and failed source states distinct from
|
|
109
|
+
an empty sheet.
|
|
110
|
+
|
|
87
111
|
For local code-agent runtimes, deliver a fresh app in a new directory,
|
|
88
112
|
implement the repeatable workflow with buttons and agent handoffs, start its dev
|
|
89
113
|
server, verify the main path, and continue through build/deployment handoff. Do
|
|
@@ -95,7 +95,6 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
95
95
|
dangerouslySetInnerHTML={{ __html: LOCALE_INIT_SCRIPT }}
|
|
96
96
|
/>
|
|
97
97
|
<link rel="icon" type="image/svg+xml" href={appPath("/favicon.svg")} />
|
|
98
|
-
<link rel="manifest" href={appPath("/manifest.json")} />
|
|
99
98
|
<meta name="theme-color" content="#06B6D4" />
|
|
100
99
|
<meta name="mobile-web-app-capable" content="yes" />
|
|
101
100
|
<meta
|
|
@@ -189,7 +189,7 @@ The framework auto-mounts these actions in every template — no per-template bo
|
|
|
189
189
|
|
|
190
190
|
| Action | Args | Purpose |
|
|
191
191
|
| -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
|
|
192
|
-
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link
|
|
192
|
+
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
193
193
|
| `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
|
|
194
194
|
| `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
|
|
195
195
|
| `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
|
|
@@ -84,6 +84,30 @@ private web access, invent an importer, add fake OAuth, or scrape a logged-in
|
|
|
84
84
|
page. If the needed context is not visible, ask for an export, transcript, or
|
|
85
85
|
attachment and treat that artifact as imported source material.
|
|
86
86
|
|
|
87
|
+
### Spreadsheet sources
|
|
88
|
+
|
|
89
|
+
Spreadsheet attachments are valid source artifacts for this workflow:
|
|
90
|
+
|
|
91
|
+
- CSV files can be read as tabular text. XLS and XLSX uploads are parsed into
|
|
92
|
+
bounded worksheet metadata and representative rows when the host supports
|
|
93
|
+
workbook ingestion. Treat the preview as untrusted user data, preserve the
|
|
94
|
+
workbook name and worksheet names, and record row/column limits and any
|
|
95
|
+
truncation in the source brief.
|
|
96
|
+
- A Google Sheets URL is a live provider source, not proof that the sheet is
|
|
97
|
+
readable. Use an authenticated Google Sheets/Drive connection and the
|
|
98
|
+
provider API catalog/docs/request path to read only the required sheet or
|
|
99
|
+
range. If that connection is unavailable, ask for a CSV/XLSX export or the
|
|
100
|
+
required connection; do not use a public export URL to bypass access.
|
|
101
|
+
- Decide whether the generated app needs a one-time snapshot or a refreshable
|
|
102
|
+
source. For a snapshot, pass bounded sample context and provenance to the
|
|
103
|
+
Builder handoff. For a live source, preserve the provider/file identity,
|
|
104
|
+
worksheet or range, and refresh semantics, then implement a scoped action for
|
|
105
|
+
reads or refreshes. Never copy workbook bytes, base64 data, credentials, or a
|
|
106
|
+
full unbounded sheet into SQL, application state, or the handoff prompt.
|
|
107
|
+
- Do not claim the app imported the whole workbook when only a preview was
|
|
108
|
+
available. Keep unreadable, partial, and failed source states distinct from
|
|
109
|
+
an empty sheet.
|
|
110
|
+
|
|
87
111
|
For local code-agent runtimes, deliver a fresh app in a new directory,
|
|
88
112
|
implement the repeatable workflow with buttons and agent handoffs, start its dev
|
|
89
113
|
server, verify the main path, and continue through build/deployment handoff. Do
|
|
@@ -189,7 +189,7 @@ The framework auto-mounts these actions in every template — no per-template bo
|
|
|
189
189
|
|
|
190
190
|
| Action | Args | Purpose |
|
|
191
191
|
| -------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- |
|
|
192
|
-
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link
|
|
192
|
+
| `share-resource` | `resourceType, resourceId, principalType, principalId, role, notify?, resourceUrl?, message?` | Grant a user or org access. `notify` defaults to true for individual user shares; `resourceUrl` can provide the direct app link and `message` an optional short note for the notification email. |
|
|
193
193
|
| `unshare-resource` | `resourceType, resourceId, principalType, principalId` | Revoke access. |
|
|
194
194
|
| `list-resource-shares` | `resourceType, resourceId` | Current visibility + all share grants. |
|
|
195
195
|
| `set-resource-visibility` | `resourceType, resourceId, visibility` | Change to `private` / `org` / `public`. |
|