@agent-native/core 0.124.3 → 0.124.4
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/README.md +1 -1
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +10 -0
- package/corpus/core/docs/content/cloneable-saas.mdx +9 -9
- package/corpus/core/docs/content/creating-templates.mdx +2 -2
- package/corpus/core/docs/content/deployment.mdx +23 -0
- package/corpus/core/docs/content/faq.mdx +4 -4
- package/corpus/core/docs/content/key-concepts.mdx +1 -1
- package/corpus/core/docs/content/multi-app-workspace.mdx +1 -1
- package/corpus/core/docs/content/syncing-template-changes.mdx +1 -1
- package/corpus/core/docs/content/template-analytics-developers.mdx +2 -2
- package/corpus/core/docs/content/template-analytics.mdx +2 -2
- package/corpus/core/docs/content/template-assets-developers.mdx +3 -3
- package/corpus/core/docs/content/template-assets.mdx +2 -2
- package/corpus/core/docs/content/template-brain-developers.mdx +2 -2
- package/corpus/core/docs/content/template-brain.mdx +1 -1
- package/corpus/core/docs/content/template-calendar-developers.mdx +1 -1
- package/corpus/core/docs/content/template-calendar.mdx +1 -1
- package/corpus/core/docs/content/template-chat.mdx +1 -1
- package/corpus/core/docs/content/template-clips-developers.mdx +4 -4
- package/corpus/core/docs/content/template-clips.mdx +2 -2
- package/corpus/core/docs/content/template-content-developers.mdx +2 -2
- package/corpus/core/docs/content/template-content.mdx +1 -1
- package/corpus/core/docs/content/template-design-brand-and-figma.mdx +1 -1
- package/corpus/core/docs/content/template-design-developers.mdx +2 -2
- package/corpus/core/docs/content/template-design.mdx +2 -2
- package/corpus/core/docs/content/template-dispatch-developers.mdx +5 -5
- package/corpus/core/docs/content/template-forms-developers.mdx +1 -1
- package/corpus/core/docs/content/template-forms.mdx +1 -1
- package/corpus/core/docs/content/template-mail-developers.mdx +2 -2
- package/corpus/core/docs/content/template-mail.mdx +2 -2
- package/corpus/core/docs/content/template-plan-developers.mdx +3 -3
- package/corpus/core/docs/content/template-plan.mdx +3 -3
- package/corpus/core/docs/content/template-slides-developers.mdx +4 -4
- package/corpus/core/docs/content/template-slides.mdx +1 -1
- package/corpus/core/docs/content/what-is-agent-native.mdx +5 -5
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/auth-policy.ts +1 -1
- package/corpus/core/src/agent/production-agent.ts +14 -1
- package/corpus/core/src/agent/run-manager.ts +6 -13
- package/corpus/core/src/cli/index.ts +11 -1
- package/corpus/core/src/client/route-chunk-recovery.ts +18 -7
- package/corpus/core/src/db/client.ts +7 -6
- package/corpus/core/src/db/migrations.ts +2 -1
- package/corpus/core/src/deploy/build.ts +290 -4
- package/corpus/core/src/deploy/nitro-preset.ts +50 -0
- package/corpus/core/src/integrations/pending-tasks-retry-job.ts +2 -1
- package/corpus/core/src/scripts/call-agent.ts +4 -2
- package/corpus/core/src/server/agent-discovery.ts +1 -0
- package/corpus/core/src/server/agent-run-context.ts +13 -3
- package/corpus/core/src/server/request-context.ts +2 -0
- package/corpus/core/src/shared/cloudflare-globals.d.ts +2 -1
- package/corpus/core/src/shared/runtime.ts +2 -0
- package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +29 -0
- package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/de-DE.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/en-US.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/es-ES.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +6 -6
- package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +6 -6
- package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +29 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -0
- package/corpus/core/src/vite/client.ts +20 -1
- package/corpus/templates/analytics/actions/restore-dashboard-revision.ts +9 -2
- package/corpus/templates/analytics/app/components/dashboard/DashboardHistoryPanel.tsx +3 -0
- package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +1 -1
- package/corpus/templates/analytics/app/global.css +10 -9
- package/corpus/templates/analytics/app/hooks/use-chart-tooltip-portal.ts +59 -18
- package/corpus/templates/analytics/app/hooks/use-dashboard-revisions.ts +14 -2
- package/corpus/templates/analytics/app/i18n-data.ts +102 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +232 -2
- package/corpus/templates/analytics/changelog/2026-07-27-chart-tooltips-stay-beside-the-cursor-when-they-extend-beyon.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-27-chat-no-longer-claims-a-data-source-is-disconnected-when-it-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-27-dashboard-changes-can-be-undone-with-cmd-z-and-actions-menu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-27-dashboard-redo-with-cmd-shift-z-and-actions-menu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-27-dashboard-skeletons-are-subtler-and-smoother.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +61 -20
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +1 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +7 -3
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +7 -32
- package/corpus/templates/calendar/changelog/2026-07-27-calendar-s-sidebar-now-uses-a-cleaner-layout-with-fewer-util.md +6 -0
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +14 -6
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +32 -37
- package/corpus/templates/content/changelog/2026-07-27-content-sidebar-page-lists-now-load-with-quiet-skeletons-and.md +6 -0
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +16 -18
- package/corpus/templates/design/changelog/2026-07-27-collapsed-sidebar-keeps-the-agent-native-logo.md +6 -0
- package/corpus/templates/dispatch/AGENTS.md +7 -2
- package/corpus/templates/dispatch/actions/view-screen.ts +3 -2
- package/corpus/templates/dispatch/changelog/2026-07-17-curated-template-remixes.md +1 -1
- package/corpus/templates/dispatch/changelog/2026-07-27-creating-an-app-from-a-curated-template-now-uses-clearer-mor.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-07-27-thread-debug-accepts-request-run-ids.md +6 -0
- package/corpus/templates/slides/actions/create-design-system.ts +10 -2
- package/corpus/templates/slides/actions/import-file.ts +173 -1
- package/corpus/templates/slides/actions/list-slide-comments.ts +16 -1
- package/corpus/templates/slides/actions/update-design-system.ts +13 -2
- package/corpus/templates/slides/app/components/editor/AssetLibraryPanel.tsx +35 -12
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +36 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +3 -0
- package/corpus/templates/slides/app/lib/dynamic-import.ts +63 -0
- package/corpus/templates/slides/app/lib/export-pdf-client.ts +3 -2
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +5 -2
- package/corpus/templates/slides/app/pages/DesignSystems.tsx +9 -1
- package/corpus/templates/slides/server/db/schema.ts +11 -0
- package/corpus/templates/slides/server/handlers/assets.ts +64 -19
- package/corpus/templates/slides/server/handlers/import/html-converter.ts +38 -0
- package/corpus/templates/slides/server/plugins/db.ts +20 -0
- package/corpus/templates/slides/shared/design-system-validation.ts +62 -0
- package/dist/a2a/auth-policy.js +1 -1
- package/dist/a2a/auth-policy.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +12 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +2 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +4 -10
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/cli/index.js +8 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/client/route-chunk-recovery.d.ts.map +1 -1
- package/dist/client/route-chunk-recovery.js +16 -7
- package/dist/client/route-chunk-recovery.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +4 -2
- package/dist/db/client.js.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +2 -1
- package/dist/db/migrations.js.map +1 -1
- package/dist/deploy/build.d.ts +25 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +227 -4
- package/dist/deploy/build.js.map +1 -1
- package/dist/deploy/nitro-preset.d.ts +8 -0
- package/dist/deploy/nitro-preset.d.ts.map +1 -0
- package/dist/deploy/nitro-preset.js +36 -0
- package/dist/deploy/nitro-preset.js.map +1 -0
- package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.js +2 -1
- package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +4 -2
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-discovery.d.ts.map +1 -1
- package/dist/server/agent-discovery.js +1 -0
- package/dist/server/agent-discovery.js.map +1 -1
- package/dist/server/agent-run-context.d.ts.map +1 -1
- package/dist/server/agent-run-context.js +10 -3
- package/dist/server/agent-run-context.js.map +1 -1
- package/dist/server/request-context.d.ts +2 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/runtime.d.ts.map +1 -1
- package/dist/shared/runtime.js +2 -0
- package/dist/shared/runtime.js.map +1 -1
- package/dist/templates/default/.agents/skills/secrets/SKILL.md +29 -0
- package/dist/templates/default/app/i18n/ar-SA.ts +6 -6
- package/dist/templates/default/app/i18n/de-DE.ts +6 -6
- package/dist/templates/default/app/i18n/en-US.ts +6 -6
- package/dist/templates/default/app/i18n/es-ES.ts +6 -6
- package/dist/templates/default/app/i18n/fr-FR.ts +6 -6
- package/dist/templates/default/app/i18n/hi-IN.ts +6 -6
- package/dist/templates/default/app/i18n/ja-JP.ts +6 -6
- package/dist/templates/default/app/i18n/ko-KR.ts +6 -6
- package/dist/templates/default/app/i18n/pt-BR.ts +6 -6
- package/dist/templates/default/app/i18n/zh-CN.ts +6 -6
- package/dist/templates/default/app/i18n/zh-TW.ts +6 -6
- package/dist/templates/headless/.agents/skills/secrets/SKILL.md +29 -0
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -0
- package/dist/vite/client.d.ts +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +16 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/cloneable-saas.mdx +9 -9
- package/docs/content/creating-templates.mdx +2 -2
- package/docs/content/deployment.mdx +23 -0
- package/docs/content/faq.mdx +4 -4
- package/docs/content/key-concepts.mdx +1 -1
- package/docs/content/multi-app-workspace.mdx +1 -1
- package/docs/content/syncing-template-changes.mdx +1 -1
- package/docs/content/template-analytics-developers.mdx +2 -2
- package/docs/content/template-analytics.mdx +2 -2
- package/docs/content/template-assets-developers.mdx +3 -3
- package/docs/content/template-assets.mdx +2 -2
- package/docs/content/template-brain-developers.mdx +2 -2
- package/docs/content/template-brain.mdx +1 -1
- package/docs/content/template-calendar-developers.mdx +1 -1
- package/docs/content/template-calendar.mdx +1 -1
- package/docs/content/template-chat.mdx +1 -1
- package/docs/content/template-clips-developers.mdx +4 -4
- package/docs/content/template-clips.mdx +2 -2
- package/docs/content/template-content-developers.mdx +2 -2
- package/docs/content/template-content.mdx +1 -1
- package/docs/content/template-design-brand-and-figma.mdx +1 -1
- package/docs/content/template-design-developers.mdx +2 -2
- package/docs/content/template-design.mdx +2 -2
- package/docs/content/template-dispatch-developers.mdx +5 -5
- package/docs/content/template-forms-developers.mdx +1 -1
- package/docs/content/template-forms.mdx +1 -1
- package/docs/content/template-mail-developers.mdx +2 -2
- package/docs/content/template-mail.mdx +2 -2
- package/docs/content/template-plan-developers.mdx +3 -3
- package/docs/content/template-plan.mdx +3 -3
- package/docs/content/template-slides-developers.mdx +4 -4
- package/docs/content/template-slides.mdx +1 -1
- package/docs/content/what-is-agent-native.mdx +5 -5
- package/package.json +1 -1
- package/src/a2a/auth-policy.ts +1 -1
- package/src/agent/production-agent.ts +14 -1
- package/src/agent/run-manager.ts +6 -13
- package/src/cli/index.ts +11 -1
- package/src/client/route-chunk-recovery.ts +18 -7
- package/src/db/client.ts +7 -6
- package/src/db/migrations.ts +2 -1
- package/src/deploy/build.ts +290 -4
- package/src/deploy/nitro-preset.ts +50 -0
- package/src/integrations/pending-tasks-retry-job.ts +2 -1
- package/src/scripts/call-agent.ts +4 -2
- package/src/server/agent-discovery.ts +1 -0
- package/src/server/agent-run-context.ts +13 -3
- package/src/server/request-context.ts +2 -0
- package/src/shared/cloudflare-globals.d.ts +2 -1
- package/src/shared/runtime.ts +2 -0
- package/src/templates/default/.agents/skills/secrets/SKILL.md +29 -0
- package/src/templates/default/app/i18n/ar-SA.ts +6 -6
- package/src/templates/default/app/i18n/de-DE.ts +6 -6
- package/src/templates/default/app/i18n/en-US.ts +6 -6
- package/src/templates/default/app/i18n/es-ES.ts +6 -6
- package/src/templates/default/app/i18n/fr-FR.ts +6 -6
- package/src/templates/default/app/i18n/hi-IN.ts +6 -6
- package/src/templates/default/app/i18n/ja-JP.ts +6 -6
- package/src/templates/default/app/i18n/ko-KR.ts +6 -6
- package/src/templates/default/app/i18n/pt-BR.ts +6 -6
- package/src/templates/default/app/i18n/zh-CN.ts +6 -6
- package/src/templates/default/app/i18n/zh-TW.ts +6 -6
- package/src/templates/headless/.agents/skills/secrets/SKILL.md +29 -0
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +29 -0
- package/src/vite/client.ts +20 -1
- /package/corpus/templates/slides/server/routes/api/assets/{[filename].delete.ts → [id].delete.ts} +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
export const AGENT_NATIVE_NITRO_PRESET_MARKER = path.join(
|
|
5
|
+
".agent-native",
|
|
6
|
+
"nitro-preset",
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
function markerPath(cwd: string): string {
|
|
10
|
+
return path.join(cwd, AGENT_NATIVE_NITRO_PRESET_MARKER);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function clearAgentNativeNitroPresetMarker(cwd = process.cwd()): void {
|
|
14
|
+
fs.rmSync(markerPath(cwd), { force: true });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function writeAgentNativeNitroPresetMarker(
|
|
18
|
+
preset: string,
|
|
19
|
+
cwd = process.cwd(),
|
|
20
|
+
): void {
|
|
21
|
+
const normalizedPreset = preset.trim();
|
|
22
|
+
if (!normalizedPreset) return;
|
|
23
|
+
|
|
24
|
+
const filePath = markerPath(cwd);
|
|
25
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
26
|
+
fs.writeFileSync(filePath, `${normalizedPreset}\n`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function resolveAgentNativeNitroPreset({
|
|
30
|
+
cwd = process.cwd(),
|
|
31
|
+
env = process.env,
|
|
32
|
+
}: {
|
|
33
|
+
cwd?: string;
|
|
34
|
+
env?: NodeJS.ProcessEnv;
|
|
35
|
+
} = {}): string | undefined {
|
|
36
|
+
const environmentPreset = env.NITRO_PRESET?.trim();
|
|
37
|
+
if (environmentPreset) return environmentPreset;
|
|
38
|
+
|
|
39
|
+
const filePath = markerPath(cwd);
|
|
40
|
+
try {
|
|
41
|
+
const marker = fs.readFileSync(filePath, "utf8").trim();
|
|
42
|
+
if (!marker) {
|
|
43
|
+
throw new Error(`Agent-Native Nitro preset marker is empty: ${filePath}`);
|
|
44
|
+
}
|
|
45
|
+
return marker;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -323,7 +323,8 @@ function getProcessingStuckAfterMs(): number {
|
|
|
323
323
|
process.env.NETLIFY ||
|
|
324
324
|
process.env.AWS_LAMBDA_FUNCTION_NAME ||
|
|
325
325
|
process.env.VERCEL ||
|
|
326
|
-
"__cf_env" in globalThis
|
|
326
|
+
"__cf_env" in globalThis ||
|
|
327
|
+
"__env__" in globalThis
|
|
327
328
|
) {
|
|
328
329
|
return SERVERLESS_PROCESSING_STUCK_AFTER_MS;
|
|
329
330
|
}
|
|
@@ -77,12 +77,14 @@ function parseTimeoutMs(value: string | undefined): number | undefined {
|
|
|
77
77
|
function isServerlessHost(): boolean {
|
|
78
78
|
// Detection mirrors db/migrations.ts:297-301. On Cloudflare Workers/Pages,
|
|
79
79
|
// `process.env` is shimmed and CF_PAGES isn't reliably populated at runtime —
|
|
80
|
-
// the canonical signal is the `__cf_env` global injected by
|
|
80
|
+
// the canonical signal is the `__cf_env`/`__env__` global injected by the
|
|
81
|
+
// Cloudflare runtime adapter.
|
|
81
82
|
return (
|
|
82
83
|
!!process.env.NETLIFY ||
|
|
83
84
|
!!process.env.AWS_LAMBDA_FUNCTION_NAME ||
|
|
84
85
|
!!process.env.VERCEL ||
|
|
85
|
-
"__cf_env" in globalThis
|
|
86
|
+
"__cf_env" in globalThis ||
|
|
87
|
+
"__env__" in globalThis
|
|
86
88
|
);
|
|
87
89
|
}
|
|
88
90
|
|
|
@@ -34,6 +34,13 @@ function eventContext(
|
|
|
34
34
|
return event.context;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function requestWaitUntil(
|
|
38
|
+
event: H3Event,
|
|
39
|
+
): ((promise: Promise<unknown>) => void) | undefined {
|
|
40
|
+
const waitUntil = event.req?.waitUntil;
|
|
41
|
+
return typeof waitUntil === "function" ? waitUntil : undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
function normalizeId(value: string | null | undefined): string | undefined {
|
|
38
45
|
return typeof value === "string" && value.trim().length > 0
|
|
39
46
|
? value.trim()
|
|
@@ -179,14 +186,17 @@ export async function resolveAgentRunRequestContext(options: {
|
|
|
179
186
|
}): Promise<RequestContext> {
|
|
180
187
|
const orgId = await resolveAgentRunOrgId(options);
|
|
181
188
|
const timezone = readAgentRunTimezone(options.event);
|
|
189
|
+
const waitUntil = requestWaitUntil(options.event);
|
|
190
|
+
const run = {
|
|
191
|
+
...(options.isBackgroundWorker ? { isBackgroundWorker: true } : {}),
|
|
192
|
+
...(waitUntil ? { waitUntil } : {}),
|
|
193
|
+
};
|
|
182
194
|
return {
|
|
183
195
|
userEmail: options.ownerContext.owner,
|
|
184
196
|
userName: options.ownerContext.name,
|
|
185
197
|
orgId,
|
|
186
198
|
timezone,
|
|
187
|
-
...(
|
|
188
|
-
? { run: { isBackgroundWorker: true } }
|
|
189
|
-
: {}),
|
|
199
|
+
...(Object.keys(run).length > 0 ? { run } : {}),
|
|
190
200
|
};
|
|
191
201
|
}
|
|
192
202
|
|
|
@@ -84,6 +84,8 @@ function processEnv(name: string): string | undefined {
|
|
|
84
84
|
* the live per-request value via `getRequestRunContext()`.
|
|
85
85
|
*/
|
|
86
86
|
export interface RequestRunContext {
|
|
87
|
+
/** Request-scoped serverless continuation hook, when the runtime provides one. */
|
|
88
|
+
waitUntil?: (promise: Promise<unknown>) => void;
|
|
87
89
|
/** Origin of the current request (used by the builder-browser tool). */
|
|
88
90
|
requestOrigin?: string;
|
|
89
91
|
/** Stable browser tab id for tab-scoped app-state reads/writes. */
|
|
@@ -31,4 +31,5 @@ interface CfEnv {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
declare var __cf_env: CfEnv | undefined;
|
|
34
|
-
|
|
34
|
+
/** Nitro's native Cloudflare preset exposes the request environment here. */
|
|
35
|
+
declare var __env__: CfEnv | undefined;
|
|
@@ -12,6 +12,7 @@ export function isNodeRuntime(): boolean {
|
|
|
12
12
|
typeof process !== "undefined" &&
|
|
13
13
|
typeof process.versions?.node === "string" &&
|
|
14
14
|
!("__cf_env" in globalThis) &&
|
|
15
|
+
!("__env__" in globalThis) &&
|
|
15
16
|
!("Deno" in globalThis)
|
|
16
17
|
);
|
|
17
18
|
}
|
|
@@ -20,6 +21,7 @@ export function isNodeRuntime(): boolean {
|
|
|
20
21
|
export function isCloudflareRuntime(): boolean {
|
|
21
22
|
return (
|
|
22
23
|
"__cf_env" in globalThis ||
|
|
24
|
+
"__env__" in globalThis ||
|
|
23
25
|
(typeof navigator !== "undefined" &&
|
|
24
26
|
navigator.userAgent === "Cloudflare-Workers")
|
|
25
27
|
);
|
|
@@ -151,6 +151,35 @@ Rules:
|
|
|
151
151
|
- **Scope matches the registration.** `scope: "user"` → pass the user email.
|
|
152
152
|
`scope: "workspace"` → pass the active `orgId` from
|
|
153
153
|
`getOrgContext(event).orgId`.
|
|
154
|
+
- **One resolver per key, and every runtime path goes through it.** Before
|
|
155
|
+
reading a credential, grep the app for the key name. If a resolver already
|
|
156
|
+
exists (`resolveXConfig`, a connector, a client factory), call it — do not
|
|
157
|
+
read `process.env` for that key a second time somewhere else.
|
|
158
|
+
- **Identity comes from the caller, not the module.** A shared helper under
|
|
159
|
+
`server/lib/` takes the email as a parameter; only an entrypoint (action,
|
|
160
|
+
route, cron) decides whose identity it is. A library that resolves its own
|
|
161
|
+
identity from `process.env.AGENT_USER_EMAIL` authorizes the deployment rather
|
|
162
|
+
than the caller.
|
|
163
|
+
|
|
164
|
+
The failure this prevents is not a leak — it is a split brain. When the config
|
|
165
|
+
check reads the vault and the feature reads `process.env`, the settings UI and
|
|
166
|
+
onboarding checklist report the integration as **configured** while every actual
|
|
167
|
+
call fails with "env var is required". The credential is right there in the
|
|
168
|
+
vault, so the error names the wrong cause and sends everyone hunting for a sync
|
|
169
|
+
or redeploy problem that does not exist. This has now shipped four times in one
|
|
170
|
+
app (BigQuery, Jira, Pylon, Academy).
|
|
171
|
+
|
|
172
|
+
Before finishing any change that touches a credential, run the guard from the
|
|
173
|
+
app directory — it finds this whether the app lives in `templates/` or in a
|
|
174
|
+
workspace repo:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npx agent-native doctor --only no-env-credentials
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Findings in files that already call `resolveCredential` are usually the
|
|
181
|
+
sanctioned `resolveCredential(key, ctx) ?? process.env.KEY` fallback. The bugs
|
|
182
|
+
are the files with **no** resolver at all.
|
|
154
183
|
|
|
155
184
|
## HTTP routes
|
|
156
185
|
|
|
@@ -92,19 +92,19 @@ const messages = {
|
|
|
92
92
|
curatedTemplates: "قوالب منسقة",
|
|
93
93
|
curatedTemplatesDescription:
|
|
94
94
|
"ابدأ من شكل تطبيق تمت مراجعته، ثم اجعله خاصًا بك.",
|
|
95
|
-
remix: "
|
|
96
|
-
remixTemplate: "
|
|
95
|
+
remix: "إنشاء من قالب",
|
|
96
|
+
remixTemplate: "إنشاء تطبيق {{name}}",
|
|
97
97
|
remixAppIdLabel: "معرّف التطبيق الجديد",
|
|
98
|
-
remixStarted: "
|
|
98
|
+
remixStarted: "بدأ إنشاء {{name}}",
|
|
99
99
|
viewLiveApp: "عرض التطبيق المباشر",
|
|
100
100
|
alreadyInWorkspace: "موجود بالفعل في مساحة العمل",
|
|
101
101
|
remixAppIdDescription:
|
|
102
102
|
"اختر معرّفًا آمنًا لعنوان URL لتطبيق مساحة العمل الجديد.",
|
|
103
103
|
integrationSetup: "إعداد التكامل",
|
|
104
104
|
source: "المصدر",
|
|
105
|
-
remixing: "جارٍ
|
|
106
|
-
remixSuccess: "
|
|
107
|
-
remixError: "
|
|
105
|
+
remixing: "جارٍ إنشاء التطبيق…",
|
|
106
|
+
remixSuccess: "بدأ إنشاء التطبيق من القالب.",
|
|
107
|
+
remixError: "تعذر إنشاء تطبيق من هذا القالب",
|
|
108
108
|
appIdRequired: "معرّف التطبيق مطلوب.",
|
|
109
109
|
hide: "إخفاء",
|
|
110
110
|
show: "Show",
|
|
@@ -91,19 +91,19 @@ const messages = {
|
|
|
91
91
|
curatedTemplates: "Kuratierte Vorlagen",
|
|
92
92
|
curatedTemplatesDescription:
|
|
93
93
|
"Starte mit einer geprüften App-Struktur und passe sie an.",
|
|
94
|
-
remix: "
|
|
95
|
-
remixTemplate: "{{name}}
|
|
94
|
+
remix: "Aus Vorlage erstellen",
|
|
95
|
+
remixTemplate: "{{name}}-App erstellen",
|
|
96
96
|
remixAppIdLabel: "Neue App-ID",
|
|
97
|
-
remixStarted: "
|
|
97
|
+
remixStarted: "Erstellung von {{name}} gestartet",
|
|
98
98
|
viewLiveApp: "Live-App öffnen",
|
|
99
99
|
alreadyInWorkspace: "Bereits im Workspace",
|
|
100
100
|
remixAppIdDescription:
|
|
101
101
|
"Wähle eine URL-sichere ID für die neue Workspace-App.",
|
|
102
102
|
integrationSetup: "Integration einrichten",
|
|
103
103
|
source: "Quelle",
|
|
104
|
-
remixing: "
|
|
105
|
-
remixSuccess: "
|
|
106
|
-
remixError: "
|
|
104
|
+
remixing: "App wird erstellt…",
|
|
105
|
+
remixSuccess: "Erstellung der App aus der Vorlage gestartet.",
|
|
106
|
+
remixError: "App konnte nicht aus dieser Vorlage erstellt werden",
|
|
107
107
|
appIdRequired: "App-ID ist erforderlich.",
|
|
108
108
|
hide: "Ausblenden",
|
|
109
109
|
show: "Show",
|
|
@@ -89,19 +89,19 @@ const messages = {
|
|
|
89
89
|
curatedTemplates: "Curated templates",
|
|
90
90
|
curatedTemplatesDescription:
|
|
91
91
|
"Start from a reviewed app shape, then make it your own.",
|
|
92
|
-
remix: "
|
|
93
|
-
remixTemplate: "
|
|
92
|
+
remix: "Create from template",
|
|
93
|
+
remixTemplate: "Create {{name}} app",
|
|
94
94
|
remixAppIdLabel: "New app id",
|
|
95
|
-
remixStarted: "Started {{name}}
|
|
95
|
+
remixStarted: "Started creating {{name}}",
|
|
96
96
|
viewLiveApp: "View live app",
|
|
97
97
|
alreadyInWorkspace: "Already in workspace",
|
|
98
98
|
remixAppIdDescription:
|
|
99
99
|
"Choose the URL-safe id for the new workspace app.",
|
|
100
100
|
integrationSetup: "Integration setup",
|
|
101
101
|
source: "Source",
|
|
102
|
-
remixing: "
|
|
103
|
-
remixSuccess: "Template
|
|
104
|
-
remixError: "Could not
|
|
102
|
+
remixing: "Creating app…",
|
|
103
|
+
remixSuccess: "Template app creation started.",
|
|
104
|
+
remixError: "Could not create an app from this template",
|
|
105
105
|
appIdRequired: "App ID is required.",
|
|
106
106
|
hide: "Hide",
|
|
107
107
|
show: "Show",
|
|
@@ -91,19 +91,19 @@ const messages = {
|
|
|
91
91
|
curatedTemplates: "Plantillas seleccionadas",
|
|
92
92
|
curatedTemplatesDescription:
|
|
93
93
|
"Empieza con una estructura de aplicación revisada y hazla tuya.",
|
|
94
|
-
remix: "
|
|
95
|
-
remixTemplate: "
|
|
94
|
+
remix: "Crear desde plantilla",
|
|
95
|
+
remixTemplate: "Crear app de {{name}}",
|
|
96
96
|
remixAppIdLabel: "Nuevo ID de aplicación",
|
|
97
|
-
remixStarted: "Se inició la
|
|
97
|
+
remixStarted: "Se inició la creación de {{name}}",
|
|
98
98
|
viewLiveApp: "Ver aplicación en vivo",
|
|
99
99
|
alreadyInWorkspace: "Ya está en el espacio de trabajo",
|
|
100
100
|
remixAppIdDescription:
|
|
101
101
|
"Elige un ID seguro para URL para la nueva aplicación.",
|
|
102
102
|
integrationSetup: "Configuración de integración",
|
|
103
103
|
source: "Origen",
|
|
104
|
-
remixing: "
|
|
105
|
-
remixSuccess: "Se inició la
|
|
106
|
-
remixError: "No se pudo
|
|
104
|
+
remixing: "Creando app…",
|
|
105
|
+
remixSuccess: "Se inició la creación de la app desde la plantilla.",
|
|
106
|
+
remixError: "No se pudo crear una app desde esta plantilla",
|
|
107
107
|
appIdRequired: "El ID de aplicación es obligatorio.",
|
|
108
108
|
hide: "Ocultar",
|
|
109
109
|
show: "Show",
|
|
@@ -93,19 +93,19 @@ const messages = {
|
|
|
93
93
|
curatedTemplates: "Modèles sélectionnés",
|
|
94
94
|
curatedTemplatesDescription:
|
|
95
95
|
"Partez d’une structure d’application vérifiée et personnalisez-la.",
|
|
96
|
-
remix: "
|
|
97
|
-
remixTemplate: "
|
|
96
|
+
remix: "Créer à partir du modèle",
|
|
97
|
+
remixTemplate: "Créer l’app {{name}}",
|
|
98
98
|
remixAppIdLabel: "Nouvel identifiant d’application",
|
|
99
|
-
remixStarted: "
|
|
99
|
+
remixStarted: "Création de {{name}} démarrée",
|
|
100
100
|
viewLiveApp: "Voir l’application en direct",
|
|
101
101
|
alreadyInWorkspace: "Déjà dans l’espace de travail",
|
|
102
102
|
remixAppIdDescription:
|
|
103
103
|
"Choisissez un identifiant compatible avec l’URL pour la nouvelle application.",
|
|
104
104
|
integrationSetup: "Configuration de l’intégration",
|
|
105
105
|
source: "Source",
|
|
106
|
-
remixing: "
|
|
107
|
-
remixSuccess: "
|
|
108
|
-
remixError: "Impossible de
|
|
106
|
+
remixing: "Création de l’app…",
|
|
107
|
+
remixSuccess: "Création de l’app à partir du modèle démarrée.",
|
|
108
|
+
remixError: "Impossible de créer une app à partir de ce modèle",
|
|
109
109
|
appIdRequired: "L’identifiant de l’application est requis.",
|
|
110
110
|
hide: "Masquer",
|
|
111
111
|
show: "Show",
|
|
@@ -87,18 +87,18 @@ const messages = {
|
|
|
87
87
|
curatedTemplates: "चयनित टेम्पलेट",
|
|
88
88
|
curatedTemplatesDescription:
|
|
89
89
|
"समीक्षित ऐप संरचना से शुरू करें और इसे अपने अनुसार बनाएं।",
|
|
90
|
-
remix: "
|
|
91
|
-
remixTemplate: "{{name}}
|
|
90
|
+
remix: "टेम्पलेट से बनाएं",
|
|
91
|
+
remixTemplate: "{{name}} ऐप बनाएं",
|
|
92
92
|
remixAppIdLabel: "नया ऐप आईडी",
|
|
93
|
-
remixStarted: "{{name}}
|
|
93
|
+
remixStarted: "{{name}} बनाना शुरू किया",
|
|
94
94
|
viewLiveApp: "लाइव ऐप देखें",
|
|
95
95
|
alreadyInWorkspace: "वर्कस्पेस में पहले से मौजूद",
|
|
96
96
|
remixAppIdDescription: "नए वर्कस्पेस ऐप के लिए URL-सुरक्षित आईडी चुनें।",
|
|
97
97
|
integrationSetup: "इंटीग्रेशन सेटअप",
|
|
98
98
|
source: "स्रोत",
|
|
99
|
-
remixing: "
|
|
100
|
-
remixSuccess: "टेम्पलेट
|
|
101
|
-
remixError: "
|
|
99
|
+
remixing: "ऐप बनाया जा रहा है…",
|
|
100
|
+
remixSuccess: "टेम्पलेट से ऐप बनाना शुरू किया गया।",
|
|
101
|
+
remixError: "इस टेम्पलेट से ऐप नहीं बना सके",
|
|
102
102
|
appIdRequired: "ऐप आईडी आवश्यक है।",
|
|
103
103
|
hide: "छिपाएं",
|
|
104
104
|
show: "Show",
|
|
@@ -89,19 +89,19 @@ const messages = {
|
|
|
89
89
|
curatedTemplates: "厳選テンプレート",
|
|
90
90
|
curatedTemplatesDescription:
|
|
91
91
|
"レビュー済みのアプリ構成をベースに、自分用にカスタマイズできます。",
|
|
92
|
-
remix: "
|
|
93
|
-
remixTemplate: "{{name}}
|
|
92
|
+
remix: "テンプレートから作成",
|
|
93
|
+
remixTemplate: "{{name}} アプリを作成",
|
|
94
94
|
remixAppIdLabel: "新しいアプリ ID",
|
|
95
|
-
remixStarted: "{{name}}
|
|
95
|
+
remixStarted: "{{name}} の作成を開始しました",
|
|
96
96
|
viewLiveApp: "ライブアプリを見る",
|
|
97
97
|
alreadyInWorkspace: "ワークスペースに追加済み",
|
|
98
98
|
remixAppIdDescription:
|
|
99
99
|
"新しいワークスペースアプリの URL セーフな ID を選択してください。",
|
|
100
100
|
integrationSetup: "連携の設定",
|
|
101
101
|
source: "提供元",
|
|
102
|
-
remixing: "
|
|
103
|
-
remixSuccess: "
|
|
104
|
-
remixError: "
|
|
102
|
+
remixing: "アプリを作成中…",
|
|
103
|
+
remixSuccess: "テンプレートからのアプリ作成を開始しました。",
|
|
104
|
+
remixError: "このテンプレートからアプリを作成できませんでした",
|
|
105
105
|
appIdRequired: "アプリ ID は必須です。",
|
|
106
106
|
hide: "非表示",
|
|
107
107
|
show: "Show",
|
|
@@ -88,18 +88,18 @@ const messages = {
|
|
|
88
88
|
curatedTemplates: "엄선된 템플릿",
|
|
89
89
|
curatedTemplatesDescription:
|
|
90
90
|
"검토된 앱 구조에서 시작해 나만의 앱으로 만들어 보세요.",
|
|
91
|
-
remix: "
|
|
92
|
-
remixTemplate: "{{name}}
|
|
91
|
+
remix: "템플릿에서 만들기",
|
|
92
|
+
remixTemplate: "{{name}} 앱 만들기",
|
|
93
93
|
remixAppIdLabel: "새 앱 ID",
|
|
94
|
-
remixStarted: "{{name}}
|
|
94
|
+
remixStarted: "{{name}} 생성을 시작했습니다",
|
|
95
95
|
viewLiveApp: "라이브 앱 보기",
|
|
96
96
|
alreadyInWorkspace: "워크스페이스에 이미 있음",
|
|
97
97
|
remixAppIdDescription: "새 워크스페이스 앱의 URL 안전 ID를 선택하세요.",
|
|
98
98
|
integrationSetup: "연동 설정",
|
|
99
99
|
source: "소스",
|
|
100
|
-
remixing: "
|
|
101
|
-
remixSuccess: "
|
|
102
|
-
remixError: "이
|
|
100
|
+
remixing: "앱 만드는 중…",
|
|
101
|
+
remixSuccess: "템플릿에서 앱 만들기를 시작했습니다.",
|
|
102
|
+
remixError: "이 템플릿에서 앱을 만들 수 없습니다",
|
|
103
103
|
appIdRequired: "앱 ID가 필요합니다.",
|
|
104
104
|
hide: "숨기기",
|
|
105
105
|
show: "Show",
|
|
@@ -92,19 +92,19 @@ const messages = {
|
|
|
92
92
|
curatedTemplates: "Modelos selecionados",
|
|
93
93
|
curatedTemplatesDescription:
|
|
94
94
|
"Comece com uma estrutura de app revisada e personalize-a.",
|
|
95
|
-
remix: "
|
|
96
|
-
remixTemplate: "
|
|
95
|
+
remix: "Criar a partir do modelo",
|
|
96
|
+
remixTemplate: "Criar app {{name}}",
|
|
97
97
|
remixAppIdLabel: "Novo ID do app",
|
|
98
|
-
remixStarted: "
|
|
98
|
+
remixStarted: "Criação de {{name}} iniciada",
|
|
99
99
|
viewLiveApp: "Ver app ao vivo",
|
|
100
100
|
alreadyInWorkspace: "Já está no workspace",
|
|
101
101
|
remixAppIdDescription:
|
|
102
102
|
"Escolha um ID seguro para URL para o novo app do workspace.",
|
|
103
103
|
integrationSetup: "Configuração da integração",
|
|
104
104
|
source: "Origem",
|
|
105
|
-
remixing: "
|
|
106
|
-
remixSuccess: "
|
|
107
|
-
remixError: "Não foi possível
|
|
105
|
+
remixing: "Criando app…",
|
|
106
|
+
remixSuccess: "Criação do app a partir do modelo iniciada.",
|
|
107
|
+
remixError: "Não foi possível criar um app a partir deste modelo",
|
|
108
108
|
appIdRequired: "O ID do app é obrigatório.",
|
|
109
109
|
hide: "Ocultar",
|
|
110
110
|
show: "Show",
|
|
@@ -85,18 +85,18 @@ const messages = {
|
|
|
85
85
|
curatedTemplates: "精选模板",
|
|
86
86
|
curatedTemplatesDescription:
|
|
87
87
|
"从经过审核的应用结构开始,然后打造属于你的应用。",
|
|
88
|
-
remix: "
|
|
89
|
-
remixTemplate: "
|
|
88
|
+
remix: "从模板创建",
|
|
89
|
+
remixTemplate: "创建 {{name}} 应用",
|
|
90
90
|
remixAppIdLabel: "新应用 ID",
|
|
91
|
-
remixStarted: "
|
|
91
|
+
remixStarted: "已开始创建 {{name}}",
|
|
92
92
|
viewLiveApp: "查看在线应用",
|
|
93
93
|
alreadyInWorkspace: "已在工作区中",
|
|
94
94
|
remixAppIdDescription: "为新的工作区应用选择一个 URL 安全的 ID。",
|
|
95
95
|
integrationSetup: "集成设置",
|
|
96
96
|
source: "来源",
|
|
97
|
-
remixing: "
|
|
98
|
-
remixSuccess: "
|
|
99
|
-
remixError: "
|
|
97
|
+
remixing: "正在创建应用…",
|
|
98
|
+
remixSuccess: "已开始从模板创建应用。",
|
|
99
|
+
remixError: "无法从此模板创建应用",
|
|
100
100
|
appIdRequired: "应用 ID 是必填项。",
|
|
101
101
|
hide: "隐藏",
|
|
102
102
|
show: "Show",
|
|
@@ -85,18 +85,18 @@ const messages = {
|
|
|
85
85
|
curatedTemplates: "精選範本",
|
|
86
86
|
curatedTemplatesDescription:
|
|
87
87
|
"從經過審核的應用程式結構開始,打造屬於你的應用程式。",
|
|
88
|
-
remix: "
|
|
89
|
-
remixTemplate: "
|
|
88
|
+
remix: "從範本建立",
|
|
89
|
+
remixTemplate: "建立 {{name}} 應用程式",
|
|
90
90
|
remixAppIdLabel: "新應用程式 ID",
|
|
91
|
-
remixStarted: "
|
|
91
|
+
remixStarted: "已開始建立 {{name}}",
|
|
92
92
|
viewLiveApp: "查看線上應用程式",
|
|
93
93
|
alreadyInWorkspace: "已在工作區中",
|
|
94
94
|
remixAppIdDescription: "為新的工作區應用程式選擇 URL 安全的 ID。",
|
|
95
95
|
integrationSetup: "整合設定",
|
|
96
96
|
source: "來源",
|
|
97
|
-
remixing: "
|
|
98
|
-
remixSuccess: "
|
|
99
|
-
remixError: "
|
|
97
|
+
remixing: "正在建立應用程式…",
|
|
98
|
+
remixSuccess: "已開始從範本建立應用程式。",
|
|
99
|
+
remixError: "無法從此範本建立應用程式",
|
|
100
100
|
appIdRequired: "應用程式 ID 為必填項目。",
|
|
101
101
|
hide: "隱藏",
|
|
102
102
|
show: "Show",
|
|
@@ -151,6 +151,35 @@ Rules:
|
|
|
151
151
|
- **Scope matches the registration.** `scope: "user"` → pass the user email.
|
|
152
152
|
`scope: "workspace"` → pass the active `orgId` from
|
|
153
153
|
`getOrgContext(event).orgId`.
|
|
154
|
+
- **One resolver per key, and every runtime path goes through it.** Before
|
|
155
|
+
reading a credential, grep the app for the key name. If a resolver already
|
|
156
|
+
exists (`resolveXConfig`, a connector, a client factory), call it — do not
|
|
157
|
+
read `process.env` for that key a second time somewhere else.
|
|
158
|
+
- **Identity comes from the caller, not the module.** A shared helper under
|
|
159
|
+
`server/lib/` takes the email as a parameter; only an entrypoint (action,
|
|
160
|
+
route, cron) decides whose identity it is. A library that resolves its own
|
|
161
|
+
identity from `process.env.AGENT_USER_EMAIL` authorizes the deployment rather
|
|
162
|
+
than the caller.
|
|
163
|
+
|
|
164
|
+
The failure this prevents is not a leak — it is a split brain. When the config
|
|
165
|
+
check reads the vault and the feature reads `process.env`, the settings UI and
|
|
166
|
+
onboarding checklist report the integration as **configured** while every actual
|
|
167
|
+
call fails with "env var is required". The credential is right there in the
|
|
168
|
+
vault, so the error names the wrong cause and sends everyone hunting for a sync
|
|
169
|
+
or redeploy problem that does not exist. This has now shipped four times in one
|
|
170
|
+
app (BigQuery, Jira, Pylon, Academy).
|
|
171
|
+
|
|
172
|
+
Before finishing any change that touches a credential, run the guard from the
|
|
173
|
+
app directory — it finds this whether the app lives in `templates/` or in a
|
|
174
|
+
workspace repo:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npx agent-native doctor --only no-env-credentials
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Findings in files that already call `resolveCredential` are usually the
|
|
181
|
+
sanctioned `resolveCredential(key, ctx) ?? process.env.KEY` fallback. The bugs
|
|
182
|
+
are the files with **no** resolver at all.
|
|
154
183
|
|
|
155
184
|
## HTTP routes
|
|
156
185
|
|
|
@@ -151,6 +151,35 @@ Rules:
|
|
|
151
151
|
- **Scope matches the registration.** `scope: "user"` → pass the user email.
|
|
152
152
|
`scope: "workspace"` → pass the active `orgId` from
|
|
153
153
|
`getOrgContext(event).orgId`.
|
|
154
|
+
- **One resolver per key, and every runtime path goes through it.** Before
|
|
155
|
+
reading a credential, grep the app for the key name. If a resolver already
|
|
156
|
+
exists (`resolveXConfig`, a connector, a client factory), call it — do not
|
|
157
|
+
read `process.env` for that key a second time somewhere else.
|
|
158
|
+
- **Identity comes from the caller, not the module.** A shared helper under
|
|
159
|
+
`server/lib/` takes the email as a parameter; only an entrypoint (action,
|
|
160
|
+
route, cron) decides whose identity it is. A library that resolves its own
|
|
161
|
+
identity from `process.env.AGENT_USER_EMAIL` authorizes the deployment rather
|
|
162
|
+
than the caller.
|
|
163
|
+
|
|
164
|
+
The failure this prevents is not a leak — it is a split brain. When the config
|
|
165
|
+
check reads the vault and the feature reads `process.env`, the settings UI and
|
|
166
|
+
onboarding checklist report the integration as **configured** while every actual
|
|
167
|
+
call fails with "env var is required". The credential is right there in the
|
|
168
|
+
vault, so the error names the wrong cause and sends everyone hunting for a sync
|
|
169
|
+
or redeploy problem that does not exist. This has now shipped four times in one
|
|
170
|
+
app (BigQuery, Jira, Pylon, Academy).
|
|
171
|
+
|
|
172
|
+
Before finishing any change that touches a credential, run the guard from the
|
|
173
|
+
app directory — it finds this whether the app lives in `templates/` or in a
|
|
174
|
+
workspace repo:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npx agent-native doctor --only no-env-credentials
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Findings in files that already call `resolveCredential` are usually the
|
|
181
|
+
sanctioned `resolveCredential(key, ctx) ?? process.env.KEY` fallback. The bugs
|
|
182
|
+
are the files with **no** resolver at all.
|
|
154
183
|
|
|
155
184
|
## HTTP routes
|
|
156
185
|
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
parsePendingEntry,
|
|
23
23
|
} from "../changelog/parse.js";
|
|
24
24
|
import { getViteDevRecoveryScript } from "../client/vite-dev-recovery-script.js";
|
|
25
|
+
import { writeAgentNativeNitroPresetMarker } from "../deploy/nitro-preset.js";
|
|
25
26
|
import { findWorkspaceRoot } from "../scripts/utils.js";
|
|
26
27
|
import { verifyEmbedSessionToken } from "../server/embed-session.js";
|
|
27
28
|
import {
|
|
@@ -1387,7 +1388,7 @@ function getCoreSourceAliases(
|
|
|
1387
1388
|
}
|
|
1388
1389
|
|
|
1389
1390
|
export interface NitroOptions {
|
|
1390
|
-
/** Nitro deployment preset (e.g. "node", "vercel", "netlify", "cloudflare_pages"). Default: "node" */
|
|
1391
|
+
/** Nitro deployment preset (e.g. "node", "vercel", "netlify", "cloudflare_pages", "cloudflare_module"). Default: "node" */
|
|
1391
1392
|
preset?: string;
|
|
1392
1393
|
/** Source directory for server files. Default: "./server" */
|
|
1393
1394
|
srcDir?: string;
|
|
@@ -2964,6 +2965,22 @@ function forceServeOnly(pluginOrPreset: any): any {
|
|
|
2964
2965
|
return { ...pluginOrPreset, apply: "serve" };
|
|
2965
2966
|
}
|
|
2966
2967
|
|
|
2968
|
+
function nitroPresetMarkerPlugin(
|
|
2969
|
+
options: ClientConfigOptions | AgentNativeVitePluginOptions,
|
|
2970
|
+
): Plugin | null {
|
|
2971
|
+
const preset = options.nitro?.preset;
|
|
2972
|
+
if (typeof preset !== "string" || !preset.trim()) return null;
|
|
2973
|
+
|
|
2974
|
+
return {
|
|
2975
|
+
name: "agent-native-nitro-preset-marker",
|
|
2976
|
+
configResolved(config) {
|
|
2977
|
+
if (config.command === "build") {
|
|
2978
|
+
writeAgentNativeNitroPresetMarker(preset);
|
|
2979
|
+
}
|
|
2980
|
+
},
|
|
2981
|
+
};
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2967
2984
|
function createAgentNativePlugins(
|
|
2968
2985
|
options: ClientConfigOptions | AgentNativeVitePluginOptions,
|
|
2969
2986
|
{
|
|
@@ -2981,8 +2998,10 @@ function createAgentNativePlugins(
|
|
|
2981
2998
|
const { appBasePath } = getConfiguredAppBasePath();
|
|
2982
2999
|
const nitroPlugin = createNitroDevPlugin(options, appBasePath);
|
|
2983
3000
|
const includeNitro = !isBuildCommand(command);
|
|
3001
|
+
const presetMarkerPlugin = nitroPresetMarkerPlugin(options);
|
|
2984
3002
|
|
|
2985
3003
|
return [
|
|
3004
|
+
presetMarkerPlugin,
|
|
2986
3005
|
// Stub packages from `options.ssrStubs` in the SSR bundle so they
|
|
2987
3006
|
// don't bloat the edge worker. Opt-in per template — the framework
|
|
2988
3007
|
// hardcodes nothing (e.g. docs sites legitimately import `shiki` on
|