@agent-native/core 0.7.19 → 0.7.20
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/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +45 -2
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/loop-settings.d.ts +37 -0
- package/dist/agent/loop-settings.d.ts.map +1 -0
- package/dist/agent/loop-settings.js +127 -0
- package/dist/agent/loop-settings.js.map +1 -0
- package/dist/agent/production-agent.d.ts +8 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +268 -29
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +76 -3
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +65 -2
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +3 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +52 -10
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/agent/tool-search.d.ts +37 -0
- package/dist/agent/tool-search.d.ts.map +1 -0
- package/dist/agent/tool-search.js +201 -0
- package/dist/agent/tool-search.js.map +1 -0
- package/dist/agent/types.d.ts +8 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +44 -9
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/workspacify.d.ts +2 -0
- package/dist/cli/workspacify.d.ts.map +1 -1
- package/dist/cli/workspacify.js +34 -1
- package/dist/cli/workspacify.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +277 -18
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +1 -1
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +14 -6
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.d.ts +14 -0
- package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -0
- package/dist/client/NewWorkspaceAppFlow.js +200 -0
- package/dist/client/NewWorkspaceAppFlow.js.map +1 -0
- package/dist/client/PoweredByBadge.d.ts +10 -1
- package/dist/client/PoweredByBadge.d.ts.map +1 -1
- package/dist/client/PoweredByBadge.js +120 -8
- package/dist/client/PoweredByBadge.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts +3 -5
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +26 -19
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +15 -3
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/analytics.d.ts +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +141 -1
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/builder-frame.d.ts +10 -0
- package/dist/client/builder-frame.d.ts.map +1 -0
- package/dist/client/builder-frame.js +94 -0
- package/dist/client/builder-frame.js.map +1 -0
- package/dist/client/composer/MentionPopover.d.ts.map +1 -1
- package/dist/client/composer/MentionPopover.js +5 -1
- package/dist/client/composer/MentionPopover.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +11 -6
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/error-format.d.ts +20 -1
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +53 -5
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/index.d.ts +3 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/onboarding/OnboardingPanel.d.ts.map +1 -1
- package/dist/client/onboarding/OnboardingPanel.js +88 -6
- package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +145 -9
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +13 -0
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +50 -9
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +3 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +88 -7
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/tools/ToolsListPage.d.ts.map +1 -1
- package/dist/client/tools/ToolsListPage.js +16 -1
- package/dist/client/tools/ToolsListPage.js.map +1 -1
- package/dist/client/tools/ToolsSidebarSection.d.ts.map +1 -1
- package/dist/client/tools/ToolsSidebarSection.js +63 -8
- package/dist/client/tools/ToolsSidebarSection.js.map +1 -1
- package/dist/client/tools/tool-order.d.ts +7 -0
- package/dist/client/tools/tool-order.d.ts.map +1 -0
- package/dist/client/tools/tool-order.js +47 -0
- package/dist/client/tools/tool-order.js.map +1 -0
- package/dist/client/transcription/BuilderTranscriptionCta.d.ts.map +1 -1
- package/dist/client/transcription/BuilderTranscriptionCta.js +71 -6
- package/dist/client/transcription/BuilderTranscriptionCta.js.map +1 -1
- package/dist/client/use-send-to-agent-chat.d.ts.map +1 -1
- package/dist/client/use-send-to-agent-chat.js +11 -3
- package/dist/client/use-send-to-agent-chat.js.map +1 -1
- package/dist/client/useProductionAgent.d.ts.map +1 -1
- package/dist/client/useProductionAgent.js +1 -1
- package/dist/client/useProductionAgent.js.map +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +5 -1
- package/dist/db/client.js.map +1 -1
- package/dist/deploy/build.d.ts +1 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +4 -1
- package/dist/deploy/build.js.map +1 -1
- package/dist/oauth-tokens/index.d.ts +1 -1
- package/dist/oauth-tokens/index.d.ts.map +1 -1
- package/dist/oauth-tokens/index.js +1 -1
- package/dist/oauth-tokens/index.js.map +1 -1
- package/dist/oauth-tokens/store.d.ts.map +1 -1
- package/dist/oauth-tokens/store.js +6 -0
- package/dist/oauth-tokens/store.js.map +1 -1
- package/dist/observability/store.d.ts.map +1 -1
- package/dist/observability/store.js +19 -19
- package/dist/observability/store.js.map +1 -1
- package/dist/onboarding/default-steps.d.ts.map +1 -1
- package/dist/onboarding/default-steps.js +95 -61
- package/dist/onboarding/default-steps.js.map +1 -1
- package/dist/onboarding/plugin.d.ts.map +1 -1
- package/dist/onboarding/plugin.js +17 -8
- package/dist/onboarding/plugin.js.map +1 -1
- package/dist/org/migrations.js +2 -2
- package/dist/org/migrations.js.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +2 -3
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/scripts/db/exec.d.ts +2 -1
- package/dist/scripts/db/exec.d.ts.map +1 -1
- package/dist/scripts/db/exec.js +264 -61
- package/dist/scripts/db/exec.js.map +1 -1
- package/dist/scripts/db/schema.d.ts.map +1 -1
- package/dist/scripts/db/schema.js +16 -4
- package/dist/scripts/db/schema.js.map +1 -1
- package/dist/scripts/dev/index.d.ts.map +1 -1
- package/dist/scripts/dev/index.js +36 -11
- package/dist/scripts/dev/index.js.map +1 -1
- package/dist/scripts/manage-agent-loop-settings.d.ts +7 -0
- package/dist/scripts/manage-agent-loop-settings.d.ts.map +1 -0
- package/dist/scripts/manage-agent-loop-settings.js +63 -0
- package/dist/scripts/manage-agent-loop-settings.js.map +1 -0
- package/dist/scripts/runner.d.ts.map +1 -1
- package/dist/scripts/runner.js +11 -0
- package/dist/scripts/runner.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +60 -18
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/app-url.d.ts +5 -4
- package/dist/server/app-url.d.ts.map +1 -1
- package/dist/server/app-url.js +8 -4
- package/dist/server/app-url.js.map +1 -1
- package/dist/server/auth.d.ts +8 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +82 -29
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +16 -5
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/builder-browser.d.ts +12 -0
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +36 -4
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +350 -53
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts +21 -3
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +51 -21
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/google-oauth.d.ts +3 -0
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +27 -3
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/index.d.ts +4 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +4 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/schema-prompt.d.ts.map +1 -1
- package/dist/server/schema-prompt.js +2 -1
- package/dist/server/schema-prompt.js.map +1 -1
- package/dist/server/security-headers.d.ts +3 -0
- package/dist/server/security-headers.d.ts.map +1 -1
- package/dist/server/security-headers.js +7 -1
- package/dist/server/security-headers.js.map +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +24 -4
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/templates/default/_gitignore +5 -1
- package/dist/templates/default/app/root.tsx +1 -0
- package/dist/templates/default/public/favicon.svg +3 -3
- package/dist/templates/default/public/icon-180.svg +3 -3
- package/dist/templates/default/public/icon-192.svg +3 -3
- package/dist/templates/default/public/icon-512.svg +3 -3
- package/dist/templates/workspace-core/AGENTS.md +23 -7
- package/dist/templates/workspace-core/package.json +2 -1
- package/dist/templates/workspace-core/src/credentials.ts +22 -11
- package/dist/templates/workspace-root/.env.example +7 -0
- package/dist/templates/workspace-root/README.md +6 -3
- package/dist/templates/workspace-root/_gitignore +3 -0
- package/dist/templates/workspace-root/package.json +3 -1
- package/dist/templates/workspace-root/scripts/workspace-dev.ts +410 -0
- package/dist/tools/actions.d.ts.map +1 -1
- package/dist/tools/actions.js +2 -0
- package/dist/tools/actions.js.map +1 -1
- package/dist/tools/html-shell.d.ts.map +1 -1
- package/dist/tools/html-shell.js +13 -1
- package/dist/tools/html-shell.js.map +1 -1
- package/dist/tools/store.d.ts.map +1 -1
- package/dist/tools/store.js +10 -10
- package/dist/tools/store.js.map +1 -1
- package/dist/tracking/providers.d.ts +1 -0
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +72 -0
- package/dist/tracking/providers.js.map +1 -1
- package/dist/vite/action-types-plugin.d.ts.map +1 -1
- package/dist/vite/action-types-plugin.js +106 -9
- package/dist/vite/action-types-plugin.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +67 -2
- package/dist/vite/client.js.map +1 -1
- package/docs/content/authentication.md +17 -13
- package/docs/content/deployment.md +11 -11
- package/docs/content/mcp-clients.md +2 -2
- package/docs/content/onboarding.md +32 -30
- package/docs/content/security.md +1 -1
- package/docs/content/tools.md +4 -0
- package/package.json +2 -2
- package/src/templates/default/_gitignore +5 -1
- package/src/templates/default/app/root.tsx +1 -0
- package/src/templates/default/public/favicon.svg +3 -3
- package/src/templates/default/public/icon-180.svg +3 -3
- package/src/templates/default/public/icon-192.svg +3 -3
- package/src/templates/default/public/icon-512.svg +3 -3
- package/src/templates/workspace-core/AGENTS.md +23 -7
- package/src/templates/workspace-core/package.json +2 -1
- package/src/templates/workspace-core/src/credentials.ts +22 -11
- package/src/templates/workspace-root/.env.example +7 -0
- package/src/templates/workspace-root/README.md +6 -3
- package/src/templates/workspace-root/_gitignore +3 -0
- package/src/templates/workspace-root/package.json +3 -1
- package/src/templates/workspace-root/scripts/workspace-dev.ts +410 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="180" viewBox="0 0
|
|
2
|
-
<rect
|
|
3
|
-
<g transform="translate(
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="180" viewBox="0 0 600 600" fill="none">
|
|
2
|
+
<rect width="600" height="600" fill="#000000"/>
|
|
3
|
+
<g transform="translate(92 179) scale(3.6491228070175437)">
|
|
4
4
|
<path d="M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z" fill="white"/>
|
|
5
5
|
<path d="M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z" fill="url(#favicon_grad)"/>
|
|
6
6
|
<defs>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0
|
|
2
|
-
<rect
|
|
3
|
-
<g transform="translate(
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" viewBox="0 0 600 600" fill="none">
|
|
2
|
+
<rect width="600" height="600" fill="#000000"/>
|
|
3
|
+
<g transform="translate(92 179) scale(3.6491228070175437)">
|
|
4
4
|
<path d="M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z" fill="white"/>
|
|
5
5
|
<path d="M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z" fill="url(#favicon_grad)"/>
|
|
6
6
|
<defs>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0
|
|
2
|
-
<rect
|
|
3
|
-
<g transform="translate(
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 600 600" fill="none">
|
|
2
|
+
<rect width="600" height="600" fill="#000000"/>
|
|
3
|
+
<g transform="translate(92 179) scale(3.6491228070175437)">
|
|
4
4
|
<path d="M24.5537 65.7695H0L15.0859 39.4619L37.708 0L60.4912 39.4619H39.6396L24.5537 65.7695Z" fill="white"/>
|
|
5
5
|
<path d="M89.446 0H114L76.2921 65.7704H51.7383L89.446 0Z" fill="url(#favicon_grad)"/>
|
|
6
6
|
<defs>
|
|
@@ -17,11 +17,12 @@ business context without you having to repeat it per app.
|
|
|
17
17
|
workspace share `DATABASE_URL` by default, so a record created by one
|
|
18
18
|
app can be read by another as long as it respects the `owner_email` and
|
|
19
19
|
`org_id` scoping conventions.
|
|
20
|
-
- **All API secrets come from
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
- **All API secrets come from scoped credential storage.** Never hardcode a
|
|
21
|
+
token or read `process.env` for user/org credentials in production. Call
|
|
22
|
+
`resolveCompanyCredential("KEY", { userEmail, orgId })` from
|
|
23
|
+
`@{{APP_NAME}}/core-module/credentials`, or omit the context only when the
|
|
24
|
+
current request/action already has agent-native request context. The helper
|
|
25
|
+
reads per-user credentials first and org-shared credentials second.
|
|
25
26
|
- **UI chrome comes from the workspace core.** Wrap every screen in
|
|
26
27
|
`<AuthenticatedLayout>` from `@{{APP_NAME}}/core-module/client`. Don't
|
|
27
28
|
re-implement the brand header, sidebar, or org switcher per app.
|
|
@@ -46,10 +47,25 @@ Example rules:
|
|
|
46
47
|
## How to add a new app
|
|
47
48
|
|
|
48
49
|
```bash
|
|
49
|
-
|
|
50
|
-
pnpm exec agent-native create <app-name>
|
|
50
|
+
pnpm exec agent-native create <app-name> --template=starter
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
Run this from the workspace root. The CLI detects the workspace and creates
|
|
54
|
+
`apps/<app-name>` with the workspace core module already connected. Use a
|
|
55
|
+
different template when useful, for example `--template=analytics` or
|
|
56
|
+
`--template=forms`.
|
|
57
|
+
|
|
58
|
+
The workspace dev command is a gateway:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pnpm dev
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
It opens Dispatch at `/dispatch`, serves every app at `/<app-name>`, and
|
|
65
|
+
auto-detects newly-created app directories. After creating an app, do not
|
|
66
|
+
restart the dev server unless the gateway reports an error; wait for it to
|
|
67
|
+
start the new app process, then open `/<app-name>`.
|
|
68
|
+
|
|
53
69
|
The new app will automatically inherit:
|
|
54
70
|
|
|
55
71
|
1. The workspace auth plugin (Better Auth + company SSO)
|
|
@@ -12,17 +12,18 @@
|
|
|
12
12
|
* DATABASE_URL by default, so storing a credential once makes it
|
|
13
13
|
* available everywhere.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* versions — the published 1-arg signature ignores the second argument.
|
|
15
|
+
* A request/action context is required so credentials stay scoped to the
|
|
16
|
+
* correct user and organization. This helper can read that context
|
|
17
|
+
* automatically inside agent-native actions; otherwise pass it explicitly.
|
|
19
18
|
*/
|
|
20
19
|
import { resolveCredential } from "@agent-native/core/credentials";
|
|
20
|
+
import {
|
|
21
|
+
getRequestOrgId,
|
|
22
|
+
getRequestUserEmail,
|
|
23
|
+
} from "@agent-native/core/server";
|
|
21
24
|
|
|
22
25
|
/**
|
|
23
|
-
* Optional context for scoping a credential lookup to a specific user or
|
|
24
|
-
* org. Forward-compatible with the upcoming 2-arg @agent-native/core
|
|
25
|
-
* signature; ignored by the current 1-arg published one.
|
|
26
|
+
* Optional context for scoping a credential lookup to a specific user or org.
|
|
26
27
|
*/
|
|
27
28
|
export interface CompanyCredentialContext {
|
|
28
29
|
userEmail?: string;
|
|
@@ -31,7 +32,7 @@ export interface CompanyCredentialContext {
|
|
|
31
32
|
|
|
32
33
|
type ResolveCredentialFn = (
|
|
33
34
|
key: string,
|
|
34
|
-
ctx
|
|
35
|
+
ctx: CompanyCredentialContext,
|
|
35
36
|
) => Promise<string | undefined>;
|
|
36
37
|
|
|
37
38
|
/**
|
|
@@ -39,10 +40,10 @@ type ResolveCredentialFn = (
|
|
|
39
40
|
* directly — it keeps your keys organized under a workspace namespace and
|
|
40
41
|
* makes "where does this secret come from" greppable.
|
|
41
42
|
*
|
|
42
|
-
*
|
|
43
|
+
* Inside an agent-native action:
|
|
43
44
|
* const slackToken = await resolveCompanyCredential("SLACK_BOT_TOKEN");
|
|
44
45
|
*
|
|
45
|
-
*
|
|
46
|
+
* Outside request context:
|
|
46
47
|
* const slackToken = await resolveCompanyCredential("SLACK_BOT_TOKEN", {
|
|
47
48
|
* userEmail: session.email,
|
|
48
49
|
* orgId: session.orgId ?? null,
|
|
@@ -52,5 +53,15 @@ export async function resolveCompanyCredential(
|
|
|
52
53
|
key: string,
|
|
53
54
|
ctx?: CompanyCredentialContext,
|
|
54
55
|
): Promise<string | undefined> {
|
|
55
|
-
|
|
56
|
+
const effectiveCtx: CompanyCredentialContext = ctx?.userEmail
|
|
57
|
+
? ctx
|
|
58
|
+
: {
|
|
59
|
+
userEmail: getRequestUserEmail() ?? undefined,
|
|
60
|
+
orgId: getRequestOrgId(),
|
|
61
|
+
};
|
|
62
|
+
if (!effectiveCtx.userEmail) return undefined;
|
|
63
|
+
return await (resolveCredential as ResolveCredentialFn)(key, {
|
|
64
|
+
userEmail: effectiveCtx.userEmail,
|
|
65
|
+
orgId: effectiveCtx.orgId ?? null,
|
|
66
|
+
});
|
|
56
67
|
}
|
|
@@ -22,6 +22,13 @@ OPENAI_API_KEY=
|
|
|
22
22
|
BUILDER_PRIVATE_KEY=
|
|
23
23
|
BUILDER_PUBLIC_KEY=
|
|
24
24
|
|
|
25
|
+
# Builder app creation / branching. In local dev these can live in .env.
|
|
26
|
+
# In production, configure deploy env vars instead; production app creation
|
|
27
|
+
# never writes credentials or project IDs to the filesystem.
|
|
28
|
+
ENABLE_BUILDER=
|
|
29
|
+
DISPATCH_BUILDER_PROJECT_ID=
|
|
30
|
+
BUILDER_BRANCH_PROJECT_ID=
|
|
31
|
+
|
|
25
32
|
# A2A shared secret — required for cross-app JWT verification. Every app
|
|
26
33
|
# in the workspace should use the same value. Generate with:
|
|
27
34
|
# openssl rand -hex 32
|
|
@@ -40,14 +40,17 @@ the workspace core package (`@{{APP_NAME}}/core-module`).
|
|
|
40
40
|
```bash
|
|
41
41
|
pnpm install
|
|
42
42
|
cp .env.example .env # fill in DATABASE_URL, BETTER_AUTH_SECRET, ANTHROPIC_API_KEY
|
|
43
|
-
pnpm dev # starts the
|
|
43
|
+
pnpm dev # starts the workspace gateway and opens Dispatch
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
The dev gateway serves Dispatch at `/dispatch` and every app at its own path
|
|
47
|
+
such as `/starter`. It watches `apps/`, so newly-created apps are detected and
|
|
48
|
+
started without restarting `pnpm dev`.
|
|
49
|
+
|
|
46
50
|
## Adding a new app
|
|
47
51
|
|
|
48
52
|
```bash
|
|
49
|
-
|
|
50
|
-
pnpm exec agent-native create crm
|
|
53
|
+
pnpm exec agent-native create crm --template=starter
|
|
51
54
|
```
|
|
52
55
|
|
|
53
56
|
The CLI detects the workspace root and scaffolds a minimal app that already
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"private": true,
|
|
4
4
|
"version": "0.0.0",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"dev": "
|
|
6
|
+
"dev": "tsx scripts/workspace-dev.ts",
|
|
7
7
|
"build": "pnpm -r build",
|
|
8
8
|
"typecheck": "pnpm -r typecheck"
|
|
9
9
|
},
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"workspaceCore": "@{{APP_NAME}}/core-module"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
|
+
"@types/node": "^24.2.1",
|
|
14
15
|
"prettier": "^3.6.2",
|
|
16
|
+
"tsx": "catalog:",
|
|
15
17
|
"typescript": "^6.0.3"
|
|
16
18
|
},
|
|
17
19
|
"packageManager": "pnpm@10.14.0"
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
import { spawn, type ChildProcess } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import http from "node:http";
|
|
5
|
+
import net from "node:net";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
|
|
8
|
+
interface WorkspaceApp {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
dir: string;
|
|
12
|
+
port: number;
|
|
13
|
+
process?: ChildProcess;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const root = process.cwd();
|
|
17
|
+
const appsDir = path.join(root, "apps");
|
|
18
|
+
const gatewayHost = process.env.WORKSPACE_HOST || "127.0.0.1";
|
|
19
|
+
const requestedPort = Number(
|
|
20
|
+
process.env.WORKSPACE_PORT || process.env.PORT || 8080,
|
|
21
|
+
);
|
|
22
|
+
const appPortStart = Number(process.env.WORKSPACE_APP_PORT_START || 8100);
|
|
23
|
+
let gatewayUrl = `http://${gatewayHost}:${requestedPort}`;
|
|
24
|
+
|
|
25
|
+
function readJson(file: string): any {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function discoverApps(): WorkspaceApp[] {
|
|
34
|
+
if (!fs.existsSync(appsDir)) return [];
|
|
35
|
+
return fs
|
|
36
|
+
.readdirSync(appsDir, { withFileTypes: true })
|
|
37
|
+
.filter((entry) => entry.isDirectory())
|
|
38
|
+
.map((entry) => {
|
|
39
|
+
const dir = path.join(appsDir, entry.name);
|
|
40
|
+
const pkg = readJson(path.join(dir, "package.json"));
|
|
41
|
+
if (!pkg) return null;
|
|
42
|
+
return {
|
|
43
|
+
id: entry.name,
|
|
44
|
+
name: pkg.displayName || pkg.name || entry.name,
|
|
45
|
+
dir,
|
|
46
|
+
port: appPortStart,
|
|
47
|
+
} satisfies WorkspaceApp;
|
|
48
|
+
})
|
|
49
|
+
.filter((app): app is WorkspaceApp => !!app)
|
|
50
|
+
.sort((a, b) => {
|
|
51
|
+
if (a.id === "dispatch") return -1;
|
|
52
|
+
if (b.id === "dispatch") return 1;
|
|
53
|
+
return a.id.localeCompare(b.id);
|
|
54
|
+
})
|
|
55
|
+
.map((app, index) => ({ ...app, port: appPortStart + index }));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const apps = discoverApps();
|
|
59
|
+
if (apps.length === 0) {
|
|
60
|
+
console.error("[workspace] No apps found under ./apps");
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const appById = new Map(apps.map((app) => [app.id, app]));
|
|
65
|
+
const defaultApp =
|
|
66
|
+
process.env.WORKSPACE_DEFAULT_APP &&
|
|
67
|
+
appById.has(process.env.WORKSPACE_DEFAULT_APP)
|
|
68
|
+
? process.env.WORKSPACE_DEFAULT_APP
|
|
69
|
+
: appById.has("dispatch")
|
|
70
|
+
? "dispatch"
|
|
71
|
+
: apps[0].id;
|
|
72
|
+
|
|
73
|
+
function syncApps(): void {
|
|
74
|
+
const discovered = discoverApps();
|
|
75
|
+
for (const app of discovered) {
|
|
76
|
+
if (appById.has(app.id)) continue;
|
|
77
|
+
const usedPorts = new Set(apps.map((existing) => existing.port));
|
|
78
|
+
let port = appPortStart;
|
|
79
|
+
while (usedPorts.has(port)) port++;
|
|
80
|
+
const next = { ...app, port };
|
|
81
|
+
apps.push(next);
|
|
82
|
+
apps.sort((a, b) => {
|
|
83
|
+
if (a.id === "dispatch") return -1;
|
|
84
|
+
if (b.id === "dispatch") return 1;
|
|
85
|
+
return a.id.localeCompare(b.id);
|
|
86
|
+
});
|
|
87
|
+
appById.set(next.id, next);
|
|
88
|
+
console.log(`[workspace] Detected new app: /${next.id}`);
|
|
89
|
+
startApp(next);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let syncTimer: NodeJS.Timeout | undefined;
|
|
94
|
+
function scheduleSync(): void {
|
|
95
|
+
if (syncTimer) clearTimeout(syncTimer);
|
|
96
|
+
syncTimer = setTimeout(syncApps, 400);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function firstPathSegment(url: string | undefined): string | null {
|
|
100
|
+
if (!url) return null;
|
|
101
|
+
try {
|
|
102
|
+
const parsed = new URL(url, "http://workspace.local");
|
|
103
|
+
const [segment] = parsed.pathname.split("/").filter(Boolean);
|
|
104
|
+
return segment || null;
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function appForRequest(req: http.IncomingMessage): WorkspaceApp | null {
|
|
111
|
+
const direct = firstPathSegment(req.url);
|
|
112
|
+
if (direct && appById.has(direct)) return appById.get(direct) ?? null;
|
|
113
|
+
const referer = req.headers.referer;
|
|
114
|
+
const fromReferer =
|
|
115
|
+
typeof referer === "string" ? firstPathSegment(referer) : null;
|
|
116
|
+
return fromReferer && appById.has(fromReferer)
|
|
117
|
+
? (appById.get(fromReferer) ?? null)
|
|
118
|
+
: null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function stripAppPrefixForDevAsset(
|
|
122
|
+
app: WorkspaceApp,
|
|
123
|
+
requestUrl: string | undefined,
|
|
124
|
+
): string | undefined {
|
|
125
|
+
if (!requestUrl) return requestUrl;
|
|
126
|
+
const basePath = `/${app.id}`;
|
|
127
|
+
try {
|
|
128
|
+
const parsed = new URL(requestUrl, "http://workspace.local");
|
|
129
|
+
if (
|
|
130
|
+
parsed.pathname !== basePath &&
|
|
131
|
+
!parsed.pathname.startsWith(`${basePath}/`)
|
|
132
|
+
) {
|
|
133
|
+
return requestUrl;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const innerPath = parsed.pathname.slice(basePath.length) || "/";
|
|
137
|
+
const isDevAsset =
|
|
138
|
+
innerPath === "/@vite/client" ||
|
|
139
|
+
innerPath.startsWith("/@vite/") ||
|
|
140
|
+
innerPath.startsWith("/@id/") ||
|
|
141
|
+
innerPath.startsWith("/@fs/") ||
|
|
142
|
+
innerPath.startsWith("/@react-router/") ||
|
|
143
|
+
innerPath.startsWith("/.vite/") ||
|
|
144
|
+
innerPath.startsWith("/app/") ||
|
|
145
|
+
innerPath.startsWith("/node_modules/") ||
|
|
146
|
+
(/\.[a-z0-9]+$/i.test(innerPath) && !innerPath.endsWith(".data"));
|
|
147
|
+
if (!isDevAsset) return requestUrl;
|
|
148
|
+
|
|
149
|
+
parsed.pathname = innerPath;
|
|
150
|
+
return `${parsed.pathname}${parsed.search}`;
|
|
151
|
+
} catch {
|
|
152
|
+
return requestUrl;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function startApp(app: WorkspaceApp): void {
|
|
157
|
+
const basePath = `/${app.id}`;
|
|
158
|
+
const child = spawn(
|
|
159
|
+
"pnpm",
|
|
160
|
+
[
|
|
161
|
+
"--dir",
|
|
162
|
+
app.dir,
|
|
163
|
+
"exec",
|
|
164
|
+
"vite",
|
|
165
|
+
"--host",
|
|
166
|
+
"127.0.0.1",
|
|
167
|
+
"--port",
|
|
168
|
+
String(app.port),
|
|
169
|
+
"--strictPort",
|
|
170
|
+
],
|
|
171
|
+
{
|
|
172
|
+
cwd: root,
|
|
173
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
174
|
+
env: {
|
|
175
|
+
...process.env,
|
|
176
|
+
APP_NAME: app.id,
|
|
177
|
+
APP_BASE_PATH: basePath,
|
|
178
|
+
VITE_APP_BASE_PATH: basePath,
|
|
179
|
+
AGENT_NATIVE_VITE_BASE_PATH: "/",
|
|
180
|
+
PORT: String(app.port),
|
|
181
|
+
WORKSPACE_GATEWAY_URL: gatewayUrl,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
);
|
|
185
|
+
app.process = child;
|
|
186
|
+
|
|
187
|
+
const prefix = `[${app.id}]`;
|
|
188
|
+
child.stdout?.on("data", (chunk) => {
|
|
189
|
+
process.stdout.write(
|
|
190
|
+
String(chunk)
|
|
191
|
+
.split(/\r?\n/)
|
|
192
|
+
.filter(Boolean)
|
|
193
|
+
.map((line) => `${prefix} ${line}`)
|
|
194
|
+
.join("\n") + "\n",
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
child.stderr?.on("data", (chunk) => {
|
|
198
|
+
process.stderr.write(
|
|
199
|
+
String(chunk)
|
|
200
|
+
.split(/\r?\n/)
|
|
201
|
+
.filter(Boolean)
|
|
202
|
+
.map((line) => `${prefix} ${line}`)
|
|
203
|
+
.join("\n") + "\n",
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
child.on("exit", (code) => {
|
|
207
|
+
if (code === 0 || shuttingDown) return;
|
|
208
|
+
console.error(`${prefix} exited with code ${code}`);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function renderIndex(): string {
|
|
213
|
+
return `<!doctype html>
|
|
214
|
+
<html>
|
|
215
|
+
<head>
|
|
216
|
+
<meta charset="utf-8" />
|
|
217
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
218
|
+
<title>Agent-Native Workspace</title>
|
|
219
|
+
<style>
|
|
220
|
+
body { font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; margin: 0; padding: 32px; background: #fafafa; color: #171717; }
|
|
221
|
+
main { max-width: 760px; margin: 0 auto; }
|
|
222
|
+
a { color: inherit; text-decoration: none; }
|
|
223
|
+
.grid { display: grid; gap: 12px; margin-top: 20px; }
|
|
224
|
+
.card { display: flex; justify-content: space-between; border: 1px solid #d4d4d4; border-radius: 8px; padding: 14px 16px; background: white; }
|
|
225
|
+
.muted { color: #737373; }
|
|
226
|
+
</style>
|
|
227
|
+
</head>
|
|
228
|
+
<body>
|
|
229
|
+
<main>
|
|
230
|
+
<h1>Agent-Native Workspace</h1>
|
|
231
|
+
<p class="muted">Open an app below. Dispatch is the workspace control plane.</p>
|
|
232
|
+
<div class="grid">
|
|
233
|
+
${apps
|
|
234
|
+
.map(
|
|
235
|
+
(app) =>
|
|
236
|
+
`<a class="card" href="/${app.id}"><strong>${app.name}</strong><span class="muted">/${app.id}</span></a>`,
|
|
237
|
+
)
|
|
238
|
+
.join("")}
|
|
239
|
+
</div>
|
|
240
|
+
</main>
|
|
241
|
+
</body>
|
|
242
|
+
</html>`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function proxyHttp(
|
|
246
|
+
app: WorkspaceApp,
|
|
247
|
+
req: http.IncomingMessage,
|
|
248
|
+
res: http.ServerResponse,
|
|
249
|
+
): void {
|
|
250
|
+
const headers = { ...req.headers, host: `127.0.0.1:${app.port}` };
|
|
251
|
+
const proxyReq = http.request(
|
|
252
|
+
{
|
|
253
|
+
hostname: "127.0.0.1",
|
|
254
|
+
port: app.port,
|
|
255
|
+
method: req.method,
|
|
256
|
+
path: stripAppPrefixForDevAsset(app, req.url),
|
|
257
|
+
headers,
|
|
258
|
+
},
|
|
259
|
+
(proxyRes) => {
|
|
260
|
+
res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
|
|
261
|
+
proxyRes.pipe(res);
|
|
262
|
+
},
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
proxyReq.on("error", (err) => {
|
|
266
|
+
res.writeHead(502, { "content-type": "text/plain" });
|
|
267
|
+
res.end(`App "${app.id}" is not ready yet: ${err.message}`);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
req.pipe(proxyReq);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function proxyUpgrade(
|
|
274
|
+
app: WorkspaceApp,
|
|
275
|
+
req: http.IncomingMessage,
|
|
276
|
+
socket: net.Socket,
|
|
277
|
+
head: Buffer,
|
|
278
|
+
): void {
|
|
279
|
+
const target = net.connect(app.port, "127.0.0.1", () => {
|
|
280
|
+
const headers = Object.entries({
|
|
281
|
+
...req.headers,
|
|
282
|
+
host: `127.0.0.1:${app.port}`,
|
|
283
|
+
})
|
|
284
|
+
.flatMap(([key, value]) =>
|
|
285
|
+
Array.isArray(value)
|
|
286
|
+
? value.map((item) => `${key}: ${item}`)
|
|
287
|
+
: [`${key}: ${value ?? ""}`],
|
|
288
|
+
)
|
|
289
|
+
.join("\r\n");
|
|
290
|
+
target.write(
|
|
291
|
+
`${req.method} ${stripAppPrefixForDevAsset(app, req.url)} HTTP/${req.httpVersion}\r\n${headers}\r\n\r\n`,
|
|
292
|
+
);
|
|
293
|
+
if (head.length) target.write(head);
|
|
294
|
+
socket.pipe(target).pipe(socket);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
target.on("error", () => socket.destroy());
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
let shuttingDown = false;
|
|
301
|
+
let workspaceStarted = false;
|
|
302
|
+
|
|
303
|
+
function startWorkspaceProcesses(): void {
|
|
304
|
+
if (workspaceStarted) return;
|
|
305
|
+
workspaceStarted = true;
|
|
306
|
+
for (const app of apps) startApp(app);
|
|
307
|
+
try {
|
|
308
|
+
fs.watch(appsDir, { recursive: true }, scheduleSync);
|
|
309
|
+
} catch {
|
|
310
|
+
// Some platforms do not support recursive directory watches.
|
|
311
|
+
}
|
|
312
|
+
setInterval(syncApps, 2_000).unref();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function openBrowser(url: string): void {
|
|
316
|
+
if (process.env.WORKSPACE_NO_OPEN === "1") return;
|
|
317
|
+
const command =
|
|
318
|
+
process.platform === "darwin"
|
|
319
|
+
? "open"
|
|
320
|
+
: process.platform === "win32"
|
|
321
|
+
? "cmd"
|
|
322
|
+
: "xdg-open";
|
|
323
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
324
|
+
const child = spawn(command, args, {
|
|
325
|
+
stdio: "ignore",
|
|
326
|
+
detached: true,
|
|
327
|
+
});
|
|
328
|
+
child.unref();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const server = http.createServer((req, res) => {
|
|
332
|
+
if (req.url === "/" || req.url === "/index.html") {
|
|
333
|
+
res.writeHead(302, { location: `/${defaultApp}` });
|
|
334
|
+
res.end();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (req.url === "/_workspace/apps") {
|
|
339
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
340
|
+
res.end(
|
|
341
|
+
JSON.stringify(
|
|
342
|
+
apps.map((app) => ({
|
|
343
|
+
id: app.id,
|
|
344
|
+
name: app.name,
|
|
345
|
+
path: `/${app.id}`,
|
|
346
|
+
port: app.port,
|
|
347
|
+
})),
|
|
348
|
+
),
|
|
349
|
+
);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const app = appForRequest(req);
|
|
354
|
+
if (!app) {
|
|
355
|
+
res.writeHead(404, { "content-type": "text/html" });
|
|
356
|
+
res.end(renderIndex());
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
proxyHttp(app, req, res);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
server.on("upgrade", (req, socket, head) => {
|
|
363
|
+
const app = appForRequest(req);
|
|
364
|
+
if (!app) {
|
|
365
|
+
socket.destroy();
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
proxyUpgrade(app, req, socket, head);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
function listen(port: number, attempts = 20): void {
|
|
372
|
+
server.once("error", (err: NodeJS.ErrnoException) => {
|
|
373
|
+
if (err.code === "EADDRINUSE" && attempts > 0) {
|
|
374
|
+
listen(port + 1, attempts - 1);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
console.error(`[workspace] Could not start gateway: ${err.message}`);
|
|
378
|
+
process.exit(1);
|
|
379
|
+
});
|
|
380
|
+
server.listen(port, gatewayHost, () => {
|
|
381
|
+
const address = server.address();
|
|
382
|
+
const actualPort =
|
|
383
|
+
typeof address === "object" && address ? address.port : port;
|
|
384
|
+
gatewayUrl = `http://${gatewayHost}:${actualPort}`;
|
|
385
|
+
console.log(`[workspace] Gateway: http://${gatewayHost}:${actualPort}`);
|
|
386
|
+
console.log(
|
|
387
|
+
`[workspace] Default: http://${gatewayHost}:${actualPort}/${defaultApp}`,
|
|
388
|
+
);
|
|
389
|
+
for (const app of apps) {
|
|
390
|
+
console.log(`[workspace] ${app.id}: /${app.id} -> 127.0.0.1:${app.port}`);
|
|
391
|
+
}
|
|
392
|
+
startWorkspaceProcesses();
|
|
393
|
+
openBrowser(`http://${gatewayHost}:${actualPort}/${defaultApp}`);
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function shutdown(): void {
|
|
398
|
+
if (shuttingDown) return;
|
|
399
|
+
shuttingDown = true;
|
|
400
|
+
server.close();
|
|
401
|
+
for (const app of apps) {
|
|
402
|
+
app.process?.kill("SIGTERM");
|
|
403
|
+
}
|
|
404
|
+
setTimeout(() => process.exit(0), 300).unref();
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
process.on("SIGINT", shutdown);
|
|
408
|
+
process.on("SIGTERM", shutdown);
|
|
409
|
+
|
|
410
|
+
listen(requestedPort);
|