create-flowdular 0.2.6 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/agent-template/.agents/skills/agent-tool-design/SKILL.md +1 -1
  2. package/agent-template/.agents/skills/auth-security-review/SKILL.md +1 -1
  3. package/agent-template/.agents/skills/cli-extension/SKILL.md +1 -1
  4. package/agent-template/.agents/skills/deploy-operate/SKILL.md +114 -0
  5. package/agent-template/.agents/skills/module-new/SKILL.md +29 -0
  6. package/agent-template/.agents/skills/module-update/SKILL.md +9 -1
  7. package/agent-template/.agents/skills/spec-interview/SKILL.md +114 -0
  8. package/agent-template/.agents/skills/ux-design/SKILL.md +34 -3
  9. package/agent-template/.ai/README.md +2 -1
  10. package/agent-template/.ai/agents/sandbox/business-manager.md +5 -1
  11. package/agent-template/.ai/blueprints/author-spec/README.md +1 -1
  12. package/agent-template/.ai/blueprints/author-spec/spec-requirements.yaml +44 -0
  13. package/agent-template/.ai/blueprints/author-spec/steps.yaml +5 -5
  14. package/agent-template/.ai/blueprints/author-spec/templates/module.yaml +99 -3
  15. package/agent-template/.ai/blueprints/edit-module/gates.yaml +4 -0
  16. package/agent-template/.ai/blueprints/edit-module/required-files.yaml +9 -0
  17. package/agent-template/.ai/blueprints/new-module/gates.yaml +4 -0
  18. package/agent-template/.ai/blueprints/new-module/spec-requirements.yaml +2 -2
  19. package/agent-template/.ai/blueprints/release/gates.yaml +4 -0
  20. package/agent-template/.ai/platform-capabilities.md +128 -0
  21. package/agent-template/.ai/policies/capabilities.yaml +130 -3
  22. package/agent-template/.ai/policies/path-ownership.yaml +5 -2
  23. package/agent-template/.ai/policies/task-budgets.yaml +5 -3
  24. package/agent-template/.ai/references/catalog/module.json +4 -4
  25. package/agent-template/.ai/references/catalog/package.json +2 -2
  26. package/agent-template/.ai/references/catalog/spec/module.yaml +5 -3
  27. package/agent-template/.ai/references/catalog/src/platform.ts +2 -0
  28. package/agent-template/.ai/references/catalog/src/services/catalog-service.ts +89 -1
  29. package/agent-template/.ai/references/catalog/src/services/data-classes.ts +47 -0
  30. package/agent-template/.ai/references/catalog/src/services/database-repository.ts +98 -1
  31. package/agent-template/.ai/references/catalog/src/services/repository.ts +22 -1
  32. package/agent-template/.ai/references/catalog/tests/data-classes.test.ts +157 -0
  33. package/agent-template/.ai/references/catalog.provenance.json +12 -10
  34. package/agent-template/.ai/rules/flowdular.md +4 -0
  35. package/agent-template/.ai/skills/README.md +10 -0
  36. package/agent-template/.ai/skills/agent-tool-design/SKILL.md +1 -2
  37. package/agent-template/.ai/skills/auth-security-review/SKILL.md +1 -1
  38. package/agent-template/.ai/skills/business-agent-design/SKILL.md +0 -1
  39. package/agent-template/.ai/skills/cli-extension/SKILL.md +1 -1
  40. package/agent-template/.ai/skills/deploy-operate/SKILL.md +119 -0
  41. package/agent-template/.ai/skills/module-new/SKILL.md +29 -3
  42. package/agent-template/.ai/skills/module-update/SKILL.md +9 -3
  43. package/agent-template/.ai/skills/perf-audit/SKILL.md +0 -1
  44. package/agent-template/.ai/skills/release-eject-pr/SKILL.md +0 -1
  45. package/agent-template/.ai/skills/spec-interview/SKILL.md +120 -0
  46. package/agent-template/.ai/skills/test-hardening/SKILL.md +1 -0
  47. package/agent-template/.ai/skills/ux-design/SKILL.md +34 -3
  48. package/agent-template/.ai/skills/variables/SKILL.md +0 -2
  49. package/agent-template/.ai/skills/workflow-development/SKILL.md +0 -1
  50. package/agent-template/.claude/skills/agent-tool-design/SKILL.md +1 -1
  51. package/agent-template/.claude/skills/auth-security-review/SKILL.md +1 -1
  52. package/agent-template/.claude/skills/cli-extension/SKILL.md +1 -1
  53. package/agent-template/.claude/skills/deploy-operate/SKILL.md +114 -0
  54. package/agent-template/.claude/skills/module-new/SKILL.md +29 -0
  55. package/agent-template/.claude/skills/module-update/SKILL.md +9 -1
  56. package/agent-template/.claude/skills/spec-interview/SKILL.md +114 -0
  57. package/agent-template/.claude/skills/ux-design/SKILL.md +34 -3
  58. package/agent-template/AGENTS.md +4 -0
  59. package/agent-template/CLAUDE.md +4 -0
  60. package/agent-template/docs/adr/0003-module-settings.md +1 -1
  61. package/agent-template/docs/adr/0006-agentic-workflows.md +24 -21
  62. package/agent-template/docs/agent-contract.md +2 -2
  63. package/agent-template/docs/cli-extensions.md +82 -0
  64. package/agent-template/docs/cli.md +195 -0
  65. package/agent-template/docs/configuration.md +593 -36
  66. package/agent-template/docs/design-system.md +185 -31
  67. package/agent-template/docs/getting-started.md +118 -0
  68. package/agent-template/docs/module-distribution.md +96 -0
  69. package/agent-template/docs/module-web-surfaces.md +221 -0
  70. package/agent-template/docs/modules.md +216 -0
  71. package/agent-template/docs/operations.md +545 -0
  72. package/agent-template/docs/sandbox.md +212 -0
  73. package/agent-template/platform/scripts/build.mjs +11 -0
  74. package/dist/bin.js +29 -0
  75. package/package.json +1 -1
  76. package/template/default/.dockerignore +14 -0
  77. package/template/default/.env.example +96 -0
  78. package/template/default/README.md +37 -1
  79. package/template/default/flowdular.json +15 -4
  80. package/template/default/infra/README.md +116 -0
  81. package/template/default/infra/docker/Dockerfile +37 -0
  82. package/template/default/infra/docker/compose.yaml +158 -0
  83. package/template/default/infra/docker/postgres/10-roles.sh +31 -0
  84. package/template/default/infra/docker/postgres/tls-init.sh +28 -0
  85. package/template/default/infra/kubernetes/database-secret.example.yaml +15 -0
  86. package/template/default/infra/kubernetes/deployment.yaml +211 -0
  87. package/template/default/infra/kubernetes/kustomization.yaml +9 -0
  88. package/template/default/infra/kubernetes/secrets.example.yaml +52 -0
  89. package/template/default/infra/kubernetes/service.yaml +13 -0
  90. package/template/default/modules/example/module.json +2 -1
  91. package/template/default/modules/example/package.json +1 -1
  92. package/template/default/modules/example/spec/module.yaml +1 -1
  93. package/template/default/modules/example/src/services/database-repository.ts +2 -12
  94. package/template/default/package.json +3 -2
  95. package/template/default/platform/octane.config.ts +99 -9
  96. package/template/default/platform/package.json +1 -1
  97. package/template/default/platform/src/generated/modules.client.ts +26 -2
  98. package/template/default/platform/src/generated/modules.server.ts +241 -10
  99. package/template/default/platform/src/server/health.ts +47 -0
  100. package/template/default/platform/src/server/metrics.ts +100 -0
  101. package/template/default/platform/src/server/storage.ts +172 -0
  102. package/template/default/platform/src/server/tracing.ts +85 -0
  103. package/template/default/specs/application.yaml +15 -0
@@ -1,19 +1,30 @@
1
1
  import {
2
2
  assertRouteConflicts,
3
+ createModuleMetrics,
3
4
  createModuleWebRoutes,
4
5
  createApplicationRoutes,
6
+ serverTracer,
5
7
  validateApplicationPath,
8
+ createMailPort,
9
+ mailConfigFromEnvironment,
6
10
  } from '@flowdular/sdk/server';
11
+ import {
12
+ createDataClassRegistry,
13
+ PLATFORM_SETTINGS_TENANT,
14
+ } from '@flowdular/sdk/kernel';
7
15
  import { resolve } from 'node:path';
8
16
  import { defineConfig, RenderRoute } from '@octanejs/vite-plugin';
9
17
  import {
10
18
  authRuntimeOptionsFromEnvironment,
11
19
  createAuthRoutes,
20
+ isTokenPrincipal,
21
+ mfaEnrolmentSatisfied,
12
22
  principalFromContext,
13
23
  createAuthRuntime,
14
24
  createPlatformAgentRegistry,
15
25
  createPlatformCapabilityRegistry,
16
26
  createPlatformToolRegistry,
27
+ nodemailerSmtpTransport,
17
28
  } from '@flowdular/sdk/modules/auth/server';
18
29
  import {
19
30
  composeModuleServer,
@@ -24,6 +35,18 @@ import {
24
35
  createPlatformDatabaseProvider,
25
36
  databaseProviderConfigFromEnvironment,
26
37
  } from './src/server/database.ts';
38
+ import {
39
+ createReadinessEndpoint,
40
+ healthEndpoint,
41
+ } from './src/server/health.ts';
42
+ import { createMetricsRoutes } from './src/server/metrics.ts';
43
+ import { createPlatformObservability } from './src/server/tracing.ts';
44
+ import {
45
+ createStorageKeyring,
46
+ createStoragePort,
47
+ createStorageRoutes,
48
+ storageConfigFromEnvironment,
49
+ } from './src/server/storage.ts';
27
50
 
28
51
  function checkedRoutes<T extends Parameters<typeof assertRouteConflicts>[0]>(
29
52
  routes: T,
@@ -36,19 +59,53 @@ const SHELL = ['App', '/src/App.tsrx'] as const;
36
59
  const workspaceRoot = resolve(import.meta.dirname, '..');
37
60
  const building = process.env.FD_INTERNAL_BUILD === 'true';
38
61
 
62
+ /* Composed first and drained last: a trace or an error report is evidence about
63
+ the boot that follows it, and both egresses refuse a misconfigured endpoint
64
+ here rather than at the first request that needed them. */
65
+ const observability = createPlatformObservability({
66
+ environment: building
67
+ ? { ...process.env, NODE_ENV: 'development' }
68
+ : process.env,
69
+ });
39
70
  const databases = createPlatformDatabaseProvider(
40
71
  databaseProviderConfigFromEnvironment(
41
72
  building ? { ...process.env, NODE_ENV: 'development' } : process.env,
42
73
  workspaceRoot,
43
74
  ),
44
75
  );
76
+ const storageKeyring = createStorageKeyring(process.env, workspaceRoot);
77
+ const storage = createStoragePort(
78
+ storageConfigFromEnvironment(
79
+ building ? { ...process.env, NODE_ENV: 'development' } : process.env,
80
+ workspaceRoot,
81
+ ),
82
+ { keyring: storageKeyring },
83
+ );
45
84
  const configuredApplicationPath = validateApplicationPath(
46
85
  process.env.FD_APPLICATION_PATH ?? applicationBasePath,
47
86
  );
87
+ /* One outbound transport for the whole deployment. auth.core is only its
88
+ first sender; the SMTP client comes from that module because it is the one
89
+ that declares the dependency. */
90
+ const mail = createMailPort(
91
+ mailConfigFromEnvironment(
92
+ building ? { ...process.env, NODE_ENV: 'development' } : process.env,
93
+ ),
94
+ { createSmtpTransport: nodemailerSmtpTransport },
95
+ );
96
+ /* Created before the auth runtime so auth.core, which composes outside the
97
+ generated module list, declares its data classes into the same registry. */
98
+ const dataClasses = createDataClassRegistry();
48
99
  const authRuntime = createAuthRuntime({
49
- ...authRuntimeOptionsFromEnvironment(process.env, workspaceRoot),
100
+ ...authRuntimeOptionsFromEnvironment(
101
+ building ? { ...process.env, NODE_ENV: 'development' } : process.env,
102
+ workspaceRoot,
103
+ ),
50
104
  applicationPath: configuredApplicationPath,
51
105
  databases,
106
+ dataClasses,
107
+ mail,
108
+ metrics: createModuleMetrics('auth.core'),
52
109
  });
53
110
 
54
111
  /* Module APIs come from the generated composition. Enable or disable modules
@@ -63,17 +120,30 @@ const moduleCompositions = composeModuleServer({
63
120
  agentTools: createPlatformToolRegistry(),
64
121
  agentDefinitions,
65
122
  capabilities: createPlatformCapabilityRegistry(),
123
+ dataClasses,
66
124
  databases,
125
+ storage,
126
+ mail,
127
+ /* Rebound to the composing module by the generated composition; this binding
128
+ is what a series the platform itself records would carry. */
129
+ metrics: createModuleMetrics('platform'),
130
+ tracer: serverTracer(),
67
131
  });
68
132
  for (const composition of moduleCompositions) {
69
133
  if (composition.settings) settings.declare(composition.settings);
70
134
  }
71
135
  agentDefinitions.seal();
136
+ /* Sealed once every composition has run and before any start hook reads the
137
+ catalogue, so every reader sees the declarations the modules agreed on. */
138
+ dataClasses.seal();
72
139
 
73
140
  /* check() proves the runtime role holds neither SUPERUSER nor BYPASSRLS before
74
141
  any module reads a row. */
75
142
  if (!building) {
76
143
  await databases.check();
144
+ /* Platform-scoped settings are read by background work before any request
145
+ could prime them; a workspace is primed by the authentication middleware. */
146
+ await settings.prime(PLATFORM_SETTINGS_TENANT);
77
147
  for (const composition of moduleCompositions) await composition.prepare?.();
78
148
  for (const composition of moduleCompositions) composition.start?.();
79
149
  }
@@ -87,7 +157,11 @@ const shutdown = async () => {
87
157
  await composition.dispose?.();
88
158
  }
89
159
  await authRuntime.dispose();
160
+ await storage.dispose();
90
161
  await databases.dispose();
162
+ /* Last, so the spans and error reports this process queued while it stopped
163
+ still leave with it. */
164
+ await observability.dispose();
91
165
  };
92
166
  // Bundling needs route declarations without background work or retained leases.
93
167
  if (building) {
@@ -106,21 +180,37 @@ export default defineConfig({
106
180
  entry: SHELL,
107
181
  publicRoot: moduleWebMounts.some((site) => site.path === '/'),
108
182
  }),
183
+ healthEndpoint.serverRoute,
184
+ createReadinessEndpoint(databases).serverRoute,
185
+ ...createMetricsRoutes({ environment: process.env }),
186
+ ...createStorageRoutes({
187
+ storage,
188
+ keyring: storageKeyring,
189
+ environment: process.env,
190
+ }),
109
191
  ...createAuthRoutes(authRuntime),
110
192
  ...moduleCompositions.flatMap((composition) => composition.routes),
111
193
  ...createModuleWebRoutes({
112
194
  modules: moduleCompositions,
113
195
  mounts: moduleWebMounts,
114
196
  applicationPath: configuredApplicationPath,
115
- resolveIdentity: (context) => {
197
+ resolveIdentity: async (context) => {
116
198
  const principal = principalFromContext(context);
117
- return principal
118
- ? {
119
- subjectId: principal.accountId,
120
- tenantId: principal.tenantId,
121
- permissions: new Set(principal.scopes),
122
- }
123
- : null;
199
+ if (!principal) return null;
200
+ /* The /api gate does not reach these pages, and a member who
201
+ still owes enrolment must not read workspace data from one.
202
+ Machine credentials cannot enrol and stay exempt there too. */
203
+ if (
204
+ !isTokenPrincipal(context) &&
205
+ !(await mfaEnrolmentSatisfied(authRuntime, principal))
206
+ ) {
207
+ return null;
208
+ }
209
+ return {
210
+ subjectId: principal.accountId,
211
+ tenantId: principal.tenantId,
212
+ permissions: new Set(principal.scopes),
213
+ };
124
214
  },
125
215
  }),
126
216
  ]),
@@ -15,7 +15,7 @@
15
15
  "@octanejs/vite-plugin": "0.1.51",
16
16
  "octane": "0.1.51",
17
17
  "pg": "8.23.0",
18
- "@flowdular/sdk": "0.2.4"
18
+ "@flowdular/sdk": "0.3.1"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@octanejs/app-core": "0.0.47",
@@ -7,12 +7,24 @@ import type {
7
7
  } from '@flowdular/sdk/client';
8
8
  import { createClientContribution as system_core } from '@flowdular/sdk/modules/system/client';
9
9
  import { createClientContribution as auth_core } from '@flowdular/sdk/modules/auth/client';
10
+ import { createClientContribution as access_core } from '@flowdular/sdk/modules/access/client';
11
+ import { createClientContribution as reports_core } from '@flowdular/sdk/modules/reports/client';
12
+ import { createClientContribution as metering_core } from '@flowdular/sdk/modules/metering/client';
10
13
  import { createClientContribution as agents_core } from '@flowdular/sdk/modules/agents/client';
11
- import { createClientContribution as automations_core } from '@flowdular/sdk/modules/automations/client';
14
+ import { createClientContribution as approvals_core } from '@flowdular/sdk/modules/approvals/client';
15
+ import { createClientContribution as audit_core } from '@flowdular/sdk/modules/audit/client';
12
16
  import { createClientContribution as workflows_core } from '@flowdular/sdk/modules/workflows/client';
17
+ import { createClientContribution as automations_core } from '@flowdular/sdk/modules/automations/client';
18
+ import { createClientContribution as connectors_core } from '@flowdular/sdk/modules/connectors/client';
19
+ import { createClientContribution as directory_core } from '@flowdular/sdk/modules/directory/client';
20
+ import { createClientContribution as documents_core } from '@flowdular/sdk/modules/documents/client';
13
21
  import { createClientContribution as example_core } from '@app/module-example/client';
22
+ import { createClientContribution as exports_core } from '@flowdular/sdk/modules/exports/client';
23
+ import { createClientContribution as import_core } from '@flowdular/sdk/modules/import/client';
24
+ import { createClientContribution as notifications_core } from '@flowdular/sdk/modules/notifications/client';
14
25
  import { createClientContribution as profile_core } from '@flowdular/sdk/modules/profile/client';
15
26
  import { createClientContribution as sandbox_core } from '@flowdular/sdk/modules/sandbox/client';
27
+ import { createClientContribution as search_core } from '@flowdular/sdk/modules/search/client';
16
28
  import { createClientContribution as users_core } from '@flowdular/sdk/modules/users/client';
17
29
 
18
30
  export function createModuleClientContributions(
@@ -21,12 +33,24 @@ export function createModuleClientContributions(
21
33
  return [
22
34
  system_core(context),
23
35
  auth_core(context),
36
+ access_core(context),
37
+ reports_core(context),
38
+ metering_core(context),
24
39
  agents_core(context),
25
- automations_core(context),
40
+ approvals_core(context),
41
+ audit_core(context),
26
42
  workflows_core(context),
43
+ automations_core(context),
44
+ connectors_core(context),
45
+ directory_core(context),
46
+ documents_core(context),
27
47
  example_core(context),
48
+ exports_core(context),
49
+ import_core(context),
50
+ notifications_core(context),
28
51
  profile_core(context),
29
52
  sandbox_core(context),
53
+ search_core(context),
30
54
  users_core(context),
31
55
  ];
32
56
  }
@@ -5,14 +5,26 @@ import type {
5
5
  PlatformServerContext,
6
6
  } from '@flowdular/sdk/modules/auth/server';
7
7
  import type { WebMount } from '@flowdular/sdk/server';
8
+ import { createModuleMetrics } from '@flowdular/sdk/server';
8
9
  import { createServerComposition as system_core } from '@flowdular/sdk/modules/system/platform';
10
+ import { createServerComposition as access_core } from '@flowdular/sdk/modules/access/platform';
11
+ import { createServerComposition as reports_core } from '@flowdular/sdk/modules/reports/platform';
12
+ import { createServerComposition as metering_core } from '@flowdular/sdk/modules/metering/platform';
9
13
  import { createServerComposition as agents_core } from '@flowdular/sdk/modules/agents/platform';
10
- import { createServerComposition as automations_core } from '@flowdular/sdk/modules/automations/platform';
14
+ import { createServerComposition as approvals_core } from '@flowdular/sdk/modules/approvals/platform';
15
+ import { createServerComposition as audit_core } from '@flowdular/sdk/modules/audit/platform';
11
16
  import { createServerComposition as workflows_core } from '@flowdular/sdk/modules/workflows/platform';
12
- import { createServerComposition as automations_workflows_integration } from '@flowdular/sdk/modules/automations-workflows-integration/platform';
17
+ import { createServerComposition as automations_core } from '@flowdular/sdk/modules/automations/platform';
18
+ import { createServerComposition as connectors_core } from '@flowdular/sdk/modules/connectors/platform';
19
+ import { createServerComposition as directory_core } from '@flowdular/sdk/modules/directory/platform';
20
+ import { createServerComposition as documents_core } from '@flowdular/sdk/modules/documents/platform';
13
21
  import { createServerComposition as example_core } from '@app/module-example/platform';
22
+ import { createServerComposition as exports_core } from '@flowdular/sdk/modules/exports/platform';
23
+ import { createServerComposition as import_core } from '@flowdular/sdk/modules/import/platform';
24
+ import { createServerComposition as notifications_core } from '@flowdular/sdk/modules/notifications/platform';
14
25
  import { createServerComposition as profile_core } from '@flowdular/sdk/modules/profile/platform';
15
26
  import { createServerComposition as sandbox_core } from '@flowdular/sdk/modules/sandbox/platform';
27
+ import { createServerComposition as search_core } from '@flowdular/sdk/modules/search/platform';
16
28
  import { createServerComposition as users_core } from '@flowdular/sdk/modules/users/platform';
17
29
 
18
30
  export function composeModuleServer(
@@ -23,51 +35,241 @@ export function composeModuleServer(
23
35
  ...system_core({
24
36
  ...context,
25
37
  agentDefinitions: context.agentDefinitions.forModule('system.core'),
38
+ dataClasses: context.dataClasses.forModule('system.core'),
39
+ capabilities: context.capabilities.forModule('system.core', {
40
+ provides: [],
41
+ requires: [],
42
+ }),
43
+ metrics: createModuleMetrics('system.core'),
26
44
  }),
27
45
  moduleId: 'system.core',
28
46
  },
47
+ {
48
+ ...access_core({
49
+ ...context,
50
+ agentDefinitions: context.agentDefinitions.forModule('access.core'),
51
+ dataClasses: context.dataClasses.forModule('access.core'),
52
+ capabilities: context.capabilities.forModule('access.core', {
53
+ provides: [],
54
+ requires: [{ id: 'exports.lists.v1', optional: true }],
55
+ }),
56
+ metrics: createModuleMetrics('access.core'),
57
+ }),
58
+ moduleId: 'access.core',
59
+ },
60
+ {
61
+ ...reports_core({
62
+ ...context,
63
+ agentDefinitions: context.agentDefinitions.forModule('reports.core'),
64
+ dataClasses: context.dataClasses.forModule('reports.core'),
65
+ capabilities: context.capabilities.forModule('reports.core', {
66
+ provides: ['reports.v1'],
67
+ requires: [],
68
+ }),
69
+ metrics: createModuleMetrics('reports.core'),
70
+ }),
71
+ moduleId: 'reports.core',
72
+ },
73
+ {
74
+ ...metering_core({
75
+ ...context,
76
+ agentDefinitions: context.agentDefinitions.forModule('metering.core'),
77
+ dataClasses: context.dataClasses.forModule('metering.core'),
78
+ capabilities: context.capabilities.forModule('metering.core', {
79
+ provides: ['metering.meters.v1'],
80
+ requires: [
81
+ { id: 'notifications.publish.v1', optional: true },
82
+ { id: 'reports.v1', optional: true },
83
+ ],
84
+ }),
85
+ metrics: createModuleMetrics('metering.core'),
86
+ }),
87
+ moduleId: 'metering.core',
88
+ },
29
89
  {
30
90
  ...agents_core({
31
91
  ...context,
32
92
  agentDefinitions: context.agentDefinitions.forModule('agents.core'),
93
+ dataClasses: context.dataClasses.forModule('agents.core'),
94
+ capabilities: context.capabilities.forModule('agents.core', {
95
+ provides: [
96
+ 'agents.run-queue',
97
+ 'agents.run-execution.v2',
98
+ 'agents.actions.v1',
99
+ ],
100
+ requires: [
101
+ { id: 'notifications.publish.v1', optional: true },
102
+ { id: 'metering.meters.v1' },
103
+ { id: 'reports.v1', optional: true },
104
+ ],
105
+ }),
106
+ metrics: createModuleMetrics('agents.core'),
33
107
  }),
34
108
  moduleId: 'agents.core',
35
109
  },
110
+ {
111
+ ...approvals_core({
112
+ ...context,
113
+ agentDefinitions: context.agentDefinitions.forModule('approvals.core'),
114
+ dataClasses: context.dataClasses.forModule('approvals.core'),
115
+ capabilities: context.capabilities.forModule('approvals.core', {
116
+ provides: ['approvals.requests.v1'],
117
+ requires: [{ id: 'notifications.publish.v1', optional: true }],
118
+ }),
119
+ metrics: createModuleMetrics('approvals.core'),
120
+ }),
121
+ moduleId: 'approvals.core',
122
+ },
123
+ {
124
+ ...audit_core({
125
+ ...context,
126
+ agentDefinitions: context.agentDefinitions.forModule('audit.core'),
127
+ dataClasses: context.dataClasses.forModule('audit.core'),
128
+ capabilities: context.capabilities.forModule('audit.core', {
129
+ provides: ['audit.erasure.v1'],
130
+ requires: [],
131
+ }),
132
+ metrics: createModuleMetrics('audit.core'),
133
+ }),
134
+ moduleId: 'audit.core',
135
+ },
136
+ {
137
+ ...workflows_core({
138
+ ...context,
139
+ agentDefinitions: context.agentDefinitions.forModule('workflows.core'),
140
+ dataClasses: context.dataClasses.forModule('workflows.core'),
141
+ capabilities: context.capabilities.forModule('workflows.core', {
142
+ provides: ['workflows.execution.v1'],
143
+ requires: [
144
+ { id: 'agents.run-execution.v2' },
145
+ { id: 'agents.actions.v1' },
146
+ { id: 'notifications.publish.v1', optional: true },
147
+ { id: 'approvals.requests.v1', optional: true },
148
+ ],
149
+ }),
150
+ metrics: createModuleMetrics('workflows.core'),
151
+ }),
152
+ moduleId: 'workflows.core',
153
+ },
36
154
  {
37
155
  ...automations_core({
38
156
  ...context,
39
157
  agentDefinitions:
40
158
  context.agentDefinitions.forModule('automations.core'),
159
+ dataClasses: context.dataClasses.forModule('automations.core'),
160
+ capabilities: context.capabilities.forModule('automations.core', {
161
+ provides: ['automations.targets.v1', 'automations.execution.v1'],
162
+ requires: [
163
+ { id: 'agents.run-queue' },
164
+ { id: 'workflows.execution.v1', optional: true },
165
+ ],
166
+ }),
167
+ metrics: createModuleMetrics('automations.core'),
41
168
  }),
42
169
  moduleId: 'automations.core',
43
170
  },
44
171
  {
45
- ...workflows_core({
172
+ ...connectors_core({
46
173
  ...context,
47
- agentDefinitions: context.agentDefinitions.forModule('workflows.core'),
174
+ agentDefinitions: context.agentDefinitions.forModule('connectors.core'),
175
+ dataClasses: context.dataClasses.forModule('connectors.core'),
176
+ capabilities: context.capabilities.forModule('connectors.core', {
177
+ provides: ['connectors.definitions.v1', 'connectors.calls.v1'],
178
+ requires: [],
179
+ }),
180
+ metrics: createModuleMetrics('connectors.core'),
48
181
  }),
49
- moduleId: 'workflows.core',
182
+ moduleId: 'connectors.core',
50
183
  },
51
184
  {
52
- ...automations_workflows_integration({
185
+ ...directory_core({
53
186
  ...context,
54
- agentDefinitions: context.agentDefinitions.forModule(
55
- 'automations-workflows.integration',
56
- ),
187
+ agentDefinitions: context.agentDefinitions.forModule('directory.core'),
188
+ dataClasses: context.dataClasses.forModule('directory.core'),
189
+ capabilities: context.capabilities.forModule('directory.core', {
190
+ provides: [],
191
+ requires: [],
192
+ }),
193
+ metrics: createModuleMetrics('directory.core'),
57
194
  }),
58
- moduleId: 'automations-workflows.integration',
195
+ moduleId: 'directory.core',
196
+ },
197
+ {
198
+ ...documents_core({
199
+ ...context,
200
+ agentDefinitions: context.agentDefinitions.forModule('documents.core'),
201
+ dataClasses: context.dataClasses.forModule('documents.core'),
202
+ capabilities: context.capabilities.forModule('documents.core', {
203
+ provides: ['documents.attachments.v1'],
204
+ requires: [],
205
+ }),
206
+ metrics: createModuleMetrics('documents.core'),
207
+ }),
208
+ moduleId: 'documents.core',
59
209
  },
60
210
  {
61
211
  ...example_core({
62
212
  ...context,
63
213
  agentDefinitions: context.agentDefinitions.forModule('example.core'),
214
+ dataClasses: context.dataClasses.forModule('example.core'),
215
+ capabilities: context.capabilities.forModule('example.core', {
216
+ provides: [],
217
+ requires: [],
218
+ }),
219
+ metrics: createModuleMetrics('example.core'),
64
220
  }),
65
221
  moduleId: 'example.core',
66
222
  },
223
+ {
224
+ ...exports_core({
225
+ ...context,
226
+ agentDefinitions: context.agentDefinitions.forModule('exports.core'),
227
+ dataClasses: context.dataClasses.forModule('exports.core'),
228
+ capabilities: context.capabilities.forModule('exports.core', {
229
+ provides: ['exports.lists.v1'],
230
+ requires: [],
231
+ }),
232
+ metrics: createModuleMetrics('exports.core'),
233
+ }),
234
+ moduleId: 'exports.core',
235
+ },
236
+ {
237
+ ...import_core({
238
+ ...context,
239
+ agentDefinitions: context.agentDefinitions.forModule('import.core'),
240
+ dataClasses: context.dataClasses.forModule('import.core'),
241
+ capabilities: context.capabilities.forModule('import.core', {
242
+ provides: ['import.ports.v1'],
243
+ requires: [{ id: 'documents.attachments.v1' }],
244
+ }),
245
+ metrics: createModuleMetrics('import.core'),
246
+ }),
247
+ moduleId: 'import.core',
248
+ },
249
+ {
250
+ ...notifications_core({
251
+ ...context,
252
+ agentDefinitions:
253
+ context.agentDefinitions.forModule('notifications.core'),
254
+ dataClasses: context.dataClasses.forModule('notifications.core'),
255
+ capabilities: context.capabilities.forModule('notifications.core', {
256
+ provides: ['notifications.publish.v1'],
257
+ requires: [],
258
+ }),
259
+ metrics: createModuleMetrics('notifications.core'),
260
+ }),
261
+ moduleId: 'notifications.core',
262
+ },
67
263
  {
68
264
  ...profile_core({
69
265
  ...context,
70
266
  agentDefinitions: context.agentDefinitions.forModule('profile.core'),
267
+ dataClasses: context.dataClasses.forModule('profile.core'),
268
+ capabilities: context.capabilities.forModule('profile.core', {
269
+ provides: [],
270
+ requires: [],
271
+ }),
272
+ metrics: createModuleMetrics('profile.core'),
71
273
  }),
72
274
  moduleId: 'profile.core',
73
275
  },
@@ -75,13 +277,42 @@ export function composeModuleServer(
75
277
  ...sandbox_core({
76
278
  ...context,
77
279
  agentDefinitions: context.agentDefinitions.forModule('sandbox.core'),
280
+ dataClasses: context.dataClasses.forModule('sandbox.core'),
281
+ capabilities: context.capabilities.forModule('sandbox.core', {
282
+ provides: [],
283
+ requires: [],
284
+ }),
285
+ metrics: createModuleMetrics('sandbox.core'),
78
286
  }),
79
287
  moduleId: 'sandbox.core',
80
288
  },
289
+ {
290
+ ...search_core({
291
+ ...context,
292
+ agentDefinitions: context.agentDefinitions.forModule('search.core'),
293
+ dataClasses: context.dataClasses.forModule('search.core'),
294
+ capabilities: context.capabilities.forModule('search.core', {
295
+ provides: ['search.providers.v1'],
296
+ requires: [],
297
+ }),
298
+ metrics: createModuleMetrics('search.core'),
299
+ }),
300
+ moduleId: 'search.core',
301
+ },
81
302
  {
82
303
  ...users_core({
83
304
  ...context,
84
305
  agentDefinitions: context.agentDefinitions.forModule('users.core'),
306
+ dataClasses: context.dataClasses.forModule('users.core'),
307
+ capabilities: context.capabilities.forModule('users.core', {
308
+ provides: [],
309
+ requires: [
310
+ { id: 'search.providers.v1', optional: true },
311
+ { id: 'import.ports.v1', optional: true },
312
+ { id: 'exports.lists.v1', optional: true },
313
+ ],
314
+ }),
315
+ metrics: createModuleMetrics('users.core'),
85
316
  }),
86
317
  moduleId: 'users.core',
87
318
  },
@@ -0,0 +1,47 @@
1
+ import { defineEndpoint } from '@flowdular/sdk/server';
2
+ import type { ConfiguredDatabaseProvider } from '@flowdular/sdk/database';
3
+
4
+ /* Liveness only: it proves the process answers and touches nothing else, so a
5
+ container probe never fails because the database is busy. */
6
+ export const healthEndpoint = defineEndpoint({
7
+ id: 'system.health',
8
+ path: '/api/health',
9
+ methods: ['GET'],
10
+ access: { kind: 'public' },
11
+ handler: ({ requestId }) =>
12
+ Response.json(
13
+ { status: 'ok', service: 'flowdular', requestId },
14
+ { headers: { 'cache-control': 'no-store' } },
15
+ ),
16
+ });
17
+
18
+ export function createReadinessEndpoint(databases: ConfiguredDatabaseProvider) {
19
+ return defineEndpoint({
20
+ id: 'system.ready',
21
+ path: '/api/ready',
22
+ methods: ['GET'],
23
+ access: { kind: 'public' },
24
+ handler: async ({ requestId }) => {
25
+ try {
26
+ const database = await databases.check();
27
+ return Response.json(
28
+ { status: 'ready', service: 'flowdular', requestId, database },
29
+ { headers: { 'cache-control': 'no-store' } },
30
+ );
31
+ } catch {
32
+ return Response.json(
33
+ {
34
+ status: 'unavailable',
35
+ service: 'flowdular',
36
+ requestId,
37
+ database: { adapter: databases.adapter, status: 'unavailable' },
38
+ },
39
+ {
40
+ status: 503,
41
+ headers: { 'cache-control': 'no-store', 'retry-after': '1' },
42
+ },
43
+ );
44
+ }
45
+ },
46
+ });
47
+ }