create-flowdular 0.4.1 → 0.5.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.
- package/README.md +9 -6
- package/agent-template/.agents/skills/auth-security-review/SKILL.md +1 -1
- package/agent-template/.agents/skills/bug-hunt/SKILL.md +1 -1
- package/agent-template/.agents/skills/module-new/SKILL.md +20 -19
- package/agent-template/.agents/skills/module-update/SKILL.md +4 -2
- package/agent-template/.agents/skills/spec-interview/SKILL.md +21 -20
- package/agent-template/.agents/skills/ux-design/SKILL.md +24 -2
- package/agent-template/.ai/README.md +5 -3
- package/agent-template/.ai/agents/README.md +1 -1
- package/agent-template/.ai/agents/sandbox/agentic-engineer.md +1 -0
- package/agent-template/.ai/agents/sandbox/backend-engineer.md +1 -0
- package/agent-template/.ai/agents/sandbox/frontend-engineer.md +1 -0
- package/agent-template/.ai/blueprints/add-migration/README.md +1 -1
- package/agent-template/.ai/examples/bad/client-imports-server/README.md +1 -1
- package/agent-template/.ai/examples/bad/missing-acl/README.md +1 -1
- package/agent-template/.ai/examples/bad/tenant-from-body/README.md +1 -1
- package/agent-template/.ai/guides/application-development.md +7 -5
- package/agent-template/.ai/platform-capabilities.md +10 -4
- package/agent-template/.ai/policies/capabilities.yaml +1 -0
- package/agent-template/.ai/policies/task-budgets.yaml +1 -1
- package/agent-template/.ai/rules/flowdular.md +3 -2
- package/agent-template/.ai/skills/auth-security-review/SKILL.md +1 -1
- package/agent-template/.ai/skills/bug-hunt/SKILL.md +1 -1
- package/agent-template/.ai/skills/module-new/SKILL.md +20 -19
- package/agent-template/.ai/skills/module-update/SKILL.md +4 -2
- package/agent-template/.ai/skills/spec-interview/SKILL.md +21 -20
- package/agent-template/.ai/skills/ux-design/SKILL.md +24 -2
- package/agent-template/.ai/subagents/module-executor.md +25 -0
- package/agent-template/.ai/subagents/reviewer.md +23 -0
- package/agent-template/.ai/subagents/spec-author.md +23 -0
- package/agent-template/.claude/agents/module-executor.md +22 -0
- package/agent-template/.claude/agents/reviewer.md +24 -0
- package/agent-template/.claude/agents/spec-author.md +20 -0
- package/agent-template/.claude/skills/auth-security-review/SKILL.md +1 -1
- package/agent-template/.claude/skills/bug-hunt/SKILL.md +1 -1
- package/agent-template/.claude/skills/module-new/SKILL.md +20 -19
- package/agent-template/.claude/skills/module-update/SKILL.md +4 -2
- package/agent-template/.claude/skills/spec-interview/SKILL.md +21 -20
- package/agent-template/.claude/skills/ux-design/SKILL.md +24 -2
- package/agent-template/.codex/agents/module-executor.toml +17 -0
- package/agent-template/.codex/agents/reviewer.toml +14 -0
- package/agent-template/.codex/agents/spec-author.toml +15 -0
- package/agent-template/AGENTS.md +3 -2
- package/agent-template/CLAUDE.md +3 -2
- package/agent-template/docs/cli.md +3 -0
- package/agent-template/docs/configuration.md +44 -0
- package/agent-template/docs/design-system.md +10 -3
- package/agent-template/docs/module-web-surfaces.md +31 -3
- package/agent-template/docs/modules.md +6 -0
- package/agent-template/docs/sandbox.md +117 -6
- package/agent-template/rulesync.jsonc +1 -1
- package/dist/bin.js +9 -0
- package/package.json +2 -2
- package/template/default/.env.example +7 -0
- package/template/default/.prettierignore +2 -0
- package/template/default/README.md +6 -4
- package/template/default/modules/example/module.json +1 -1
- package/template/default/modules/example/package.json +1 -1
- package/template/default/modules/example/spec/module.yaml +1 -1
- package/template/default/package.json +3 -2
- package/template/default/platform/index.html +7 -19
- package/template/default/platform/octane.config.ts +26 -2
- package/template/default/platform/package.json +1 -1
- package/template/default/platform/public/favicon.svg +1 -1
- package/template/default/platform/src/App.tsrx +25 -1
- package/template/default/platform/src/generated/modules.server.ts +2 -0
|
@@ -81,7 +81,7 @@ Read-only master-detail (runs, playground) keeps `ui-two-col` (+ `--wide-aside`)
|
|
|
81
81
|
- `Alert`: `tone` danger (default), warning, info.
|
|
82
82
|
- `Avatar`: `name`, `square` (organizations), `large`.
|
|
83
83
|
- `Icon`: `name`, `size` (18 default, 16 in controls, 14 in `Button size="sm"`), `strokeWidth`.
|
|
84
|
-
- `BrandMark`: `size`, `
|
|
84
|
+
- `BrandMark`: `size`, `tone`; three bars with a copper accent bar, brand moments only.
|
|
85
85
|
|
|
86
86
|
Icon keys (`ICON_PATHS`, `packages/ui/src/icons/Icon.tsrx`): `dashboard`, `parties`, `catalog`, `user`, `users`, `shield`, `code`, `modules`, `file-text`, `play`, `bot`, `flask`, `activity`, `plug`, `search`, `chevron-down`, `chevron-left`, `chevron-right`, `chevrons-up-down`, `sort`, `calendar`, `plus`, `panel-left`, `check`, `filter`, `download`, `more`, `external`, `alert`, `x`, `sign-out`, `refresh`, `help`, `info`, `key`, `settings`, `braces`, `copy`. An unknown name renders `modules` silently, so check the list.
|
|
87
87
|
|
|
@@ -93,7 +93,28 @@ Layout `ui-view`, `ui-two-col` (+`--wide-aside`), `ui-grid-2`, `ui-kpi-grid`, `u
|
|
|
93
93
|
|
|
94
94
|
User-facing copy lives in every declared `translations/*.json` bundle and is read with fully qualified `t()` keys. Eyebrow names the domain, title names the records, and description is one sentence. Table headers say what the value is. Buttons start with a verb. Loading text ends with `…`. Drawer footer states the constraint the user cannot see. Write natural copy in each locale, with no exclamation marks or database jargon.
|
|
95
95
|
|
|
96
|
-
## 7.
|
|
96
|
+
## 7. Design the screen as a preview first
|
|
97
|
+
|
|
98
|
+
A design is a file, not a description: `modules/<dir>/design/<screen>.html`, beside the spec. It is markup only, dressed by the platform's own stylesheets, so it shows what the screen will look like before a component exists and long before the application boots. An implementation phase reads it the way it reads the spec.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pnpm ui:preview modules/<dir>/design/<screen>.html --scaffold # the record recipe in all five states
|
|
102
|
+
pnpm ui:preview modules/<dir>/design/<screen>.html # renders it, prints a file:// address
|
|
103
|
+
pnpm ui:preview modules/<dir>/design/<screen>.html --shot .flowdular/ui-preview/<screen>.png
|
|
104
|
+
pnpm ui:preview modules/<dir>/design/<screen>.html --open # shows it to the person asking
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Rules that keep a preview honest:
|
|
108
|
+
|
|
109
|
+
- Markup only. No `<style>`, no CSS rule, no `<html>` or `<body>`; the command refuses a fragment that carries one. Every visual decision comes from `packages/ui`, which is what stops a preview from becoming a second source of truth.
|
|
110
|
+
- One `<section class="ui-view" data-state="...">` per state: `populated`, `loading`, `empty`, `error`, `denied`. The command labels each one, so a single page answers for all five.
|
|
111
|
+
- Real content. The longest realistic name, a real identifier, the copy the screen will actually carry. A preview of `Lorem ipsum` proves nothing about overflow or alignment.
|
|
112
|
+
- Only classes a stylesheet declares. `pnpm ui-classes:check` fails on a class nothing defines, in a preview and in a `.tsrx` alike, which is the one mistake that compiles, passes its tests and renders unstyled. A class a module needs and the design system lacks is declared in the module's own CSS, as rule 4 says.
|
|
113
|
+
- `--shot` writes a PNG, which is how an agent with no browser looks at its own work. `--open` opens the preview in the person's browser, which is how a design is shown for approval; a correction loop renders without it rather than throwing a window at whoever is at the keyboard.
|
|
114
|
+
|
|
115
|
+
Hand off the path, not a description. An implementation phase opens the preview, mirrors its structure with the components in section 4, and keeps the copy.
|
|
116
|
+
|
|
117
|
+
## 8. Inspect the rendered screen
|
|
97
118
|
|
|
98
119
|
A screen is not finished until it has been looked at. Typecheck and tests say nothing about overflow, alignment, a duplicate label or a column that collapses.
|
|
99
120
|
|
|
@@ -117,6 +138,7 @@ Check the drawer form in the same pass: one label per field, fields top-aligned,
|
|
|
117
138
|
|
|
118
139
|
## Pitfalls
|
|
119
140
|
|
|
141
|
+
- A preview that renders unstyled is a class nothing declares, not a broken harness; run `pnpm ui-classes:check`.
|
|
120
142
|
- `Kpi value={items.length}` does not typecheck; use `String(items.length)`.
|
|
121
143
|
- A `Tag` for a lifecycle state uses `success` for active and `neutral` for archived, with `dot`.
|
|
122
144
|
- An `Icon` inside `Button size="sm"` is 14, not 18.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name = "module-executor"
|
|
2
|
+
description = "Implement one Flowdular blueprint end to end at the repository root: scaffold from an approved spec, write the module, run the gates and join it to the platform through the CLI. Use once a spec is approved and the change is ready to be built."
|
|
3
|
+
developer_instructions = '''
|
|
4
|
+
Your role prompt is `.ai/agents/module-executor.md`. Read it with the blueprint
|
|
5
|
+
under `.ai/blueprints/<id>/` and the one matching skill in
|
|
6
|
+
`.ai/skills/<name>/SKILL.md` before changing anything.
|
|
7
|
+
|
|
8
|
+
Implement only what the approved spec's acceptance scenarios describe, inside the
|
|
9
|
+
blueprint's `allowed-paths.yaml`. Join the platform through
|
|
10
|
+
`pnpm flowdular module enable <id> --apply` and `auth sync-scopes`; never edit
|
|
11
|
+
`flowdular.json`, `platform/package.json`, `platform/src/generated/**` or
|
|
12
|
+
`platform/octane.config.ts` by hand.
|
|
13
|
+
|
|
14
|
+
Run the module gates and `pnpm verify` yourself and report their exact commands
|
|
15
|
+
and results. Never waive a gate. End with `HANDOFF: reviewer - <what to review>`
|
|
16
|
+
or `HANDOFF: none - <blocker>`.
|
|
17
|
+
'''
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name = "reviewer"
|
|
2
|
+
description = "Review a finished Flowdular change against its approved spec, the blueprint, the invariants and the executable evidence, and report findings by severity. Use as a separate phase before delivery or a pull request. Reports defects and fixes nothing."
|
|
3
|
+
developer_instructions = '''
|
|
4
|
+
Your role prompt is `.ai/agents/reviewer.md` and the one task skill for this
|
|
5
|
+
phase is `.ai/skills/auto-review/SKILL.md`. Read both before reviewing.
|
|
6
|
+
|
|
7
|
+
You write no production code. Review the complete requested change, its
|
|
8
|
+
requirements, callers and tests; report concrete findings by severity with file,
|
|
9
|
+
line and failure scenario. Never waive missing or failing verification, and never
|
|
10
|
+
approve a change whose evidence you have not seen run.
|
|
11
|
+
|
|
12
|
+
End with `HANDOFF: module-executor - <findings to fix>` or
|
|
13
|
+
`HANDOFF: none - <review result and remaining verification>`.
|
|
14
|
+
'''
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name = "spec-author"
|
|
2
|
+
description = "Turn a business request into a schema-valid Flowdular module specification at the repository root. Use before any implementation, for a new module spec or a change to an existing one. Writes only modules/<dir>/spec/module.yaml and never approves it."
|
|
3
|
+
developer_instructions = '''
|
|
4
|
+
Your role prompt is `.ai/agents/spec-author.md` and the one task skill for this
|
|
5
|
+
phase is `.ai/skills/spec-interview/SKILL.md`. Read both before writing.
|
|
6
|
+
|
|
7
|
+
You write only `modules/<dir>/spec/module.yaml`. Propose a platform default for
|
|
8
|
+
every decision and ask the user for what cannot be inferred; never guess a
|
|
9
|
+
business fact. Never set `status: approved`: approval is the user's, recorded by
|
|
10
|
+
the `spec-approval` skill.
|
|
11
|
+
|
|
12
|
+
`pnpm flowdular spec validate --all --json` must report the file valid before you
|
|
13
|
+
report. End with `HANDOFF: reviewer - spec ready for owner approval` or
|
|
14
|
+
`HANDOFF: none - <open question>`.
|
|
15
|
+
'''
|
package/agent-template/AGENTS.md
CHANGED
|
@@ -78,5 +78,6 @@ made in the Flowdular repository and released before this application uses it.
|
|
|
78
78
|
The skills and examples use @flowdular/sdk subpath imports. For pnpm --filter,
|
|
79
79
|
read the actual module package name from its package.json. Use pnpm verify and
|
|
80
80
|
pnpm build for this application. Root .ai files are editable project guidance;
|
|
81
|
-
run pnpm rules:generate after changing rules or
|
|
82
|
-
AGENTS.md, CLAUDE.md, .agents/skills
|
|
81
|
+
run pnpm rules:generate after changing rules, skills or subagents, then pnpm
|
|
82
|
+
rules:check. AGENTS.md, CLAUDE.md, .agents/skills, .claude/skills, .claude/agents
|
|
83
|
+
and .codex/agents are generated copies.
|
package/agent-template/CLAUDE.md
CHANGED
|
@@ -78,5 +78,6 @@ made in the Flowdular repository and released before this application uses it.
|
|
|
78
78
|
The skills and examples use @flowdular/sdk subpath imports. For pnpm --filter,
|
|
79
79
|
read the actual module package name from its package.json. Use pnpm verify and
|
|
80
80
|
pnpm build for this application. Root .ai files are editable project guidance;
|
|
81
|
-
run pnpm rules:generate after changing rules or
|
|
82
|
-
AGENTS.md, CLAUDE.md, .agents/skills
|
|
81
|
+
run pnpm rules:generate after changing rules, skills or subagents, then pnpm
|
|
82
|
+
rules:check. AGENTS.md, CLAUDE.md, .agents/skills, .claude/skills, .claude/agents
|
|
83
|
+
and .codex/agents are generated copies.
|
|
@@ -33,6 +33,9 @@ flowdular module version <id> # version, platformApi range,
|
|
|
33
33
|
flowdular module version bump <id> <level> [--apply] # patch|minor|major across module.json, package.json, specVersion and dependent ranges
|
|
34
34
|
flowdular module new <id> --spec <path> [--apply] # scaffold from an approved spec
|
|
35
35
|
flowdular module enable|disable <id> [--apply] # composition and scope grants
|
|
36
|
+
flowdular web list # the addresses this workspace serves module pages at
|
|
37
|
+
flowdular web mount <module id> <surface id> --path <path> --tenant <id> [--id <mount id>] [--apply]
|
|
38
|
+
flowdular web unmount <mount id> [--apply] # stop serving that site
|
|
36
39
|
flowdular migration status [--module <id>] # migration ledger
|
|
37
40
|
flowdular migration apply --module <id> [--apply]
|
|
38
41
|
flowdular migration verify # checksum drift, row security, file and constant parity
|
|
@@ -18,6 +18,33 @@ deployments must set the secret keys.
|
|
|
18
18
|
| `FD_METRICS` | `false` | Expose `GET /api/metrics`; see [operations.md](operations.md) |
|
|
19
19
|
| `FD_METRICS_TOKEN` | none | Bearer token a metrics scrape must present |
|
|
20
20
|
|
|
21
|
+
## Branding
|
|
22
|
+
|
|
23
|
+
The name, the document title, the description, the link preview image, the
|
|
24
|
+
browser icon, the theme colour and the logo are not environment variables: they
|
|
25
|
+
are `system.core` settings an owner with `system.settings.manage` changes under
|
|
26
|
+
Administration, Branding, and every change is audited. One value serves the
|
|
27
|
+
whole deployment, so the sign-in screen and a shared link carry it too, and a
|
|
28
|
+
setting nobody changed renders the product's own.
|
|
29
|
+
|
|
30
|
+
An address is stored only as a path on this deployment (`/brand/logo.svg`) or an
|
|
31
|
+
https URL; `javascript:`, `data:` and protocol-relative values are refused when
|
|
32
|
+
they are written. Every https branding image origin is added to `img-src` of the
|
|
33
|
+
policy this deployment serves, including an `FD_CSP` of your own, so the browser
|
|
34
|
+
loads it; an `FD_CSP` without an `img-src` directive is left alone and then has
|
|
35
|
+
to name the origin itself. The label an authenticator lists an enrolled account
|
|
36
|
+
under is the same name, decided by the server, so a member who enrols after a
|
|
37
|
+
rename sees the new one.
|
|
38
|
+
|
|
39
|
+
An application scaffolded before this release owns its own `platform/src/App.tsrx`
|
|
40
|
+
and `platform/index.html`, so its head does not follow the settings until it
|
|
41
|
+
adopts two changes the template now carries: `configureBrandingFromPage(props)`
|
|
42
|
+
plus the `Seo`, `Link` and `Meta` block in the entry, and the removal of the
|
|
43
|
+
static `<link rel="icon">` and `<meta name="theme-color">` from the page, which
|
|
44
|
+
would otherwise compete with the rendered ones. `pnpm flowdular doctor` reports
|
|
45
|
+
both as the `platform.branding` check. The navigation, the mobile header and the
|
|
46
|
+
sign-in screen follow the settings without any change.
|
|
47
|
+
|
|
21
48
|
## Observability
|
|
22
49
|
|
|
23
50
|
Spans are always recorded into a bounded in-process buffer and the logger always
|
|
@@ -583,6 +610,23 @@ deployment on the old names keeps working and the server logs one
|
|
|
583
610
|
replacement. The platform name wins when both are set, and every refusal names
|
|
584
611
|
the variable the deployment actually set.
|
|
585
612
|
|
|
613
|
+
The relay is also five platform-scoped `auth.core` settings, edited under
|
|
614
|
+
Administration, Modules: `mailTransport` (`environment`, `none` or `smtp`),
|
|
615
|
+
`mailSmtpUrl` (secret, write only), `mailFrom`, `mailRequireTls` and
|
|
616
|
+
`mailRejectUnauthorized`. `mailTransport` decides which source wins. It is
|
|
617
|
+
`environment` by default, and while it stays there the `FD_MAIL_*`
|
|
618
|
+
configuration above is in effect exactly as described, deprecation warnings
|
|
619
|
+
included; storing `none` or `smtp` overrides the environment for every sender of
|
|
620
|
+
the installation. `development` is reachable only through the environment and is
|
|
621
|
+
never settable from the UI. A stored relay is resolved when a message is sent,
|
|
622
|
+
so a change carries the next message without a restart, and the built transport
|
|
623
|
+
is cached by a digest of the effective configuration. Storing `smtp` without a
|
|
624
|
+
relay URL or without a sender is refused, as is a URL that is not `smtp://` or
|
|
625
|
+
`smtps://` and a sender the sender rule rejects; the refusal names the setting
|
|
626
|
+
and never carries its value. Administration, Settings states which source and
|
|
627
|
+
which transport are in effect and sends a test message to the signed-in
|
|
628
|
+
address.
|
|
629
|
+
|
|
586
630
|
`none` refuses every message with `MAIL_NOT_CONFIGURED` and sends nothing.
|
|
587
631
|
`development` keeps the last 100 messages in memory for a local run and a test
|
|
588
632
|
and is refused at boot in production, where it would be silent data loss.
|
|
@@ -17,9 +17,15 @@ shared primitives, tokens, and the rules for using them.
|
|
|
17
17
|
- Modules: compose screens from `@flowdular/sdk/ui`. Module CSS may only add
|
|
18
18
|
module-specific composites built on the tokens (example:
|
|
19
19
|
`modules/agents/src/client/agents.css`).
|
|
20
|
+
- `modules/<dir>/design/*.html`: a screen as markup, rendered with the
|
|
21
|
+
stylesheets above by `pnpm ui:preview <path>` (`--scaffold` writes the record
|
|
22
|
+
recipe in all five states, `--shot <file.png>` captures it). It is where a
|
|
23
|
+
screen is designed and agreed before it is a component, and it carries no CSS
|
|
24
|
+
of its own, so it cannot drift from the platform. `pnpm ui-classes:check`
|
|
25
|
+
refuses a class no stylesheet declares, there and in every `.tsrx`.
|
|
20
26
|
- `platform/public`: `favicon.svg`, `og.png` (1200x630 Open Graph image).
|
|
21
27
|
- Brand mark geometry is generated: `node packages/ui/scripts/gen-mark.mjs`
|
|
22
|
-
rewrites `packages/ui/src/brand/mark.ts` from the
|
|
28
|
+
rewrites `packages/ui/src/brand/mark.ts` from the bar parameters.
|
|
23
29
|
|
|
24
30
|
## Rules
|
|
25
31
|
|
|
@@ -276,7 +282,7 @@ them; outside the shell the English defaults and the host locale apply.
|
|
|
276
282
|
| `Switch` | Boolean setting that applies on its own (no form submit): `checked`, `label` as the accessible name, `disabled`, `onChange` |
|
|
277
283
|
| `ConfirmDialog` | One question before an irreversible action: `open`, `title`, children, `confirmLabel`, `tone` danger (default) or primary, `busy`, `onConfirm`, `onCancel`; traps Tab and restores focus to the opener |
|
|
278
284
|
| `Icon` | Stroke icon by `name` from `ICON_PATHS`; `size` 18 default, 16 in controls, 14 in `Button size="sm"`; `strokeWidth` 1.75 default |
|
|
279
|
-
| `BrandMark` |
|
|
285
|
+
| `BrandMark` | Three bars, the short one copper: `size`, `tone` brand, current, inverse (`signature` is accepted and changes nothing) |
|
|
280
286
|
|
|
281
287
|
`Drawer` takes one child, a `ui-drawer__form` (fields in `ui-drawer__body`, actions in `ui-drawer__foot`) or a plain `ui-drawer__body`; it closes on Escape and on the scrim. `SearchField` carries no visible label, so pass `label` as its accessible name. `FormField` renders `error` in place of `help` and marks it `role="alert"`. `SettingRow` is presentation only: the caller owns the draft value, the save call, and passes the result back as `status`. `ScopeSummary` is the read side of `CheckGrid`; both keep the first-seen module order, and `summarizeScopes` is exported for callers that need the grouping without the markup.
|
|
282
288
|
|
|
@@ -407,7 +413,8 @@ Icon names (`packages/ui/src/icons/Icon.tsrx`): `dashboard`, `parties`, `catalog
|
|
|
407
413
|
- Drawer: `ui-drawer__form` (scrolling body plus pinned footer),
|
|
408
414
|
`ui-drawer__body`, `ui-drawer__foot`
|
|
409
415
|
- Settings rows inside a `ui-card` or a drawer `ui-form__section`, rendered
|
|
410
|
-
by `SettingRow`: `ui-setting`
|
|
416
|
+
by `SettingRow`: `ui-setting`, grouped in a card by `ui-setting-group` with
|
|
417
|
+
`ui-setting-group__head` (the group's name on the rows' own inset)
|
|
411
418
|
(+`__text` for title, scope tag, and help, `__control` for the one-line
|
|
412
419
|
control cluster, `__status` (+`--error`) for the inline result)
|
|
413
420
|
- Buttons: `ui-btn` with `--primary`, `--secondary`, `--ghost`, `--danger`,
|
|
@@ -63,7 +63,27 @@ runtime and the module's existing bundles when localization is needed.
|
|
|
63
63
|
|
|
64
64
|
## Operator configuration
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
One command writes the mount and regenerates the composition:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pnpm flowdular web mount example.core public --path /blog --tenant <tenant id> # preview
|
|
70
|
+
pnpm flowdular web mount example.core public --path /blog --tenant <tenant id> --apply
|
|
71
|
+
pnpm flowdular web list
|
|
72
|
+
pnpm flowdular web unmount acme-public --apply
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
It refuses before it writes what the platform would refuse at boot: a module
|
|
76
|
+
this workspace has not enabled, a reserved address, the configured backoffice
|
|
77
|
+
path, an address overlapping a site already mounted, and a mount naming no
|
|
78
|
+
workspace. The mount id defaults to the module's first segment; `--id` names it
|
|
79
|
+
when one module serves several addresses, and mounting the same id again moves
|
|
80
|
+
that site rather than adding a second one.
|
|
81
|
+
|
|
82
|
+
The tenant is the workspace whose pages are served, so it exists before the
|
|
83
|
+
mount does: run `flowdular setup` first and use the id it reports.
|
|
84
|
+
|
|
85
|
+
The same section can be written by hand in the installation's
|
|
86
|
+
`flowdular.json`:
|
|
67
87
|
|
|
68
88
|
```json
|
|
69
89
|
{
|
|
@@ -82,7 +102,8 @@ Add the optional `web` section in the installation's `flowdular.json`:
|
|
|
82
102
|
}
|
|
83
103
|
```
|
|
84
104
|
|
|
85
|
-
|
|
105
|
+
A hand-written section needs `pnpm flowdular module sync --apply` afterwards;
|
|
106
|
+
the `web` commands run it themselves. Then rebuild or redeploy production, or
|
|
86
107
|
restart development. Configuration is generated into the server composition;
|
|
87
108
|
changing it requires regeneration. It is not read from a process-local settings
|
|
88
109
|
cache or an anonymous query parameter. Verify the tenant ID before publishing.
|
|
@@ -91,7 +112,14 @@ paths. `/records/:slug` above becomes `/blog/records/:slug`.
|
|
|
91
112
|
|
|
92
113
|
The root `/` can host a public storefront, alongside more specific mounts such as
|
|
93
114
|
`/blog`. Reserved platform prefixes such as `/app`, `/auth`, `/api`, `/setup` and
|
|
94
|
-
the configured backoffice path cannot be claimed by modules
|
|
115
|
+
the configured backoffice path cannot be claimed by modules; the list is
|
|
116
|
+
`RESERVED_WEB_SEGMENTS` in `@flowdular/sdk/contracts`, which the composition and the
|
|
117
|
+
CLI both read. A segment may carry dots inside it, so a page answers at
|
|
118
|
+
`/rss.xml`, `/sitemap.xml` or `/robots.txt` as a reader or a crawler expects;
|
|
119
|
+
each dot separates two non-empty groups, which keeps `..`, a leading dot and a
|
|
120
|
+
trailing dot out of every address. A mount naming a surface its module does not declare stops the
|
|
121
|
+
composition with that sentence, rather than answering 404 at the address for the
|
|
122
|
+
life of the deployment. Other overlapping
|
|
95
123
|
mounts and equivalent route patterns are rejected. Custom paths such as `/blog`, `/portal` and `/forms/contact` work without a
|
|
96
124
|
tenant ID in the URL. `/sites/` is an optional convention for multiple sites;
|
|
97
125
|
unknown sites under that prefix return 404. A disabled binding
|
|
@@ -65,6 +65,12 @@ at least one entity (`SPEC_ACTION_PERMISSION_UNKNOWN`, `SPEC_ENTITY_UNKNOWN`,
|
|
|
65
65
|
`SPEC_DUPLICATE_ID`). A client without a list screen, or a stored entity with no
|
|
66
66
|
tenant-unique field, is a warning.
|
|
67
67
|
|
|
68
|
+
An action may not declare `risk: external`. A module never calls another system
|
|
69
|
+
directly: the harness and the CLI runner both refuse an external action, so the
|
|
70
|
+
specification catches it here instead of at delivery
|
|
71
|
+
(`SPEC_ACTION_RISK_UNSUPPORTED`). Model the effect as a connector definition plus
|
|
72
|
+
an adapter, or lower the risk.
|
|
73
|
+
|
|
68
74
|
The three optional sections have checks of their own. `research.evidenceOwner`
|
|
69
75
|
and `templates[].inputEntity` must name an entity (`SPEC_ENTITY_UNKNOWN`). An
|
|
70
76
|
adapter id must start with the module id (`SPEC_ADAPTER_ID_NAMESPACE`); a source
|
|
@@ -12,12 +12,85 @@ Full documentation lives with the package:
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
pnpm sandbox # from this repository
|
|
15
|
-
npx @flowdular/sandbox # from any Flowdular workspace
|
|
15
|
+
npx @flowdular/sandbox # from any Flowdular workspace, or from an empty one
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
the
|
|
18
|
+
In an empty directory, the command creates a standalone Flowdular application,
|
|
19
|
+
installs its dependencies, starts it with a local embedded PostgreSQL database,
|
|
20
|
+
prepares the sandbox credential and serves the dashboard on
|
|
21
|
+
`http://127.0.0.1:4320`. You can then describe the module in the dashboard.
|
|
22
|
+
|
|
23
|
+
## Start with nothing installed
|
|
24
|
+
|
|
25
|
+
There is no checkout step.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
mkdir acme-erp && cd acme-erp
|
|
29
|
+
npx @flowdular/sandbox
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The launcher uses `create-flowdular` at the sandbox package's exact version,
|
|
33
|
+
installs the generated application and makes its first local Git commit. It
|
|
34
|
+
reuses that workspace on later runs. The application lands in `./flowdular`
|
|
35
|
+
unless `--workspace <path>` names another directory. No remote is created until
|
|
36
|
+
you choose one.
|
|
37
|
+
|
|
38
|
+
The repository dialog can create a private GitHub repository or connect an
|
|
39
|
+
empty one after showing the initial push plan. If creation is interrupted, the
|
|
40
|
+
same dialog shows the pending attempt. You can resume it or discard its local
|
|
41
|
+
record after GitHub returns an authenticated 404. A private repository hidden
|
|
42
|
+
from the current account may also return 404, so check GitHub if creation may
|
|
43
|
+
have succeeded.
|
|
44
|
+
|
|
45
|
+
To work on an existing platform repository, run
|
|
46
|
+
`npx @flowdular/sandbox --connect <git-url>` and optionally `--branch <name>`.
|
|
47
|
+
The launcher clones into a new directory, checks `flowdular.json` and the
|
|
48
|
+
committed pnpm lockfile, then installs dependencies. Run from an existing
|
|
49
|
+
Flowdular checkout to reuse it without cloning. `--no-bootstrap` refuses to
|
|
50
|
+
create an application. The older `--repository` and pinned `--ref` options
|
|
51
|
+
remain available when you deliberately want a checkout of the Flowdular core
|
|
52
|
+
repository.
|
|
53
|
+
|
|
54
|
+
The launcher refuses to create or clone into a directory containing unrelated
|
|
55
|
+
files. For application generation, Git and pnpm are checked before files are
|
|
56
|
+
written.
|
|
57
|
+
|
|
58
|
+
An application already serving on the platform port is left alone, and no
|
|
59
|
+
credential is prepared for it. `--platform` and `--platform-port` say otherwise
|
|
60
|
+
explicitly; `--no-platform` never starts one.
|
|
61
|
+
|
|
62
|
+
## The credential is prepared, not pasted
|
|
63
|
+
|
|
64
|
+
A business user used to sign in to the application, create an API token with
|
|
65
|
+
three scopes, paste it into the sandbox and grant that account sandbox access
|
|
66
|
+
before describing anything. None of those is a business decision, so the
|
|
67
|
+
application does them during its own boot when the launcher asks, and the
|
|
68
|
+
launcher collects the result.
|
|
69
|
+
|
|
70
|
+
- It runs only when the launcher started the application, so an application
|
|
71
|
+
someone else owns is never asked to create an account.
|
|
72
|
+
- It reuses an existing workspace and account rather than replacing them, and
|
|
73
|
+
mints exactly one token for the life of the deployment.
|
|
74
|
+
- The token carries the four sandbox scopes and nothing else.
|
|
75
|
+
- It is written to a `0600` file that the launcher seals into its own
|
|
76
|
+
configuration and deletes. It is never printed, so it reaches no terminal, log
|
|
77
|
+
or transcript.
|
|
78
|
+
- Token management over HTTP is unchanged: `POST /api/auth/api-tokens` still
|
|
79
|
+
refuses a machine credential, and no route was added for this.
|
|
80
|
+
|
|
81
|
+
The account is `sandbox-operator@example.com` in a `sandbox` workspace, with a
|
|
82
|
+
password nobody has. It exists to hold the grant.
|
|
83
|
+
|
|
84
|
+
**A failed provision never stops the application.** It is a convenience for a
|
|
85
|
+
local operator, and an application that will not serve because a sandbox account
|
|
86
|
+
could not be created is worse than one that serves and reports the problem.
|
|
87
|
+
|
|
88
|
+
For a remote deployment, or a sandbox started some other way, the dashboard says
|
|
89
|
+
what is missing. One command fixes it:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pnpm flowdular sandbox provision --apply
|
|
93
|
+
```
|
|
21
94
|
|
|
22
95
|
## Connect it to a running application
|
|
23
96
|
|
|
@@ -29,6 +102,8 @@ security a deployment has, and it is thrown away with the session.
|
|
|
29
102
|
1. In the application, open Administration, API tokens, and issue a token with
|
|
30
103
|
`sandbox.access.use` plus the read scopes the preview should see. Add
|
|
31
104
|
`sandbox.preview.data` for live data and `sandbox.modules.eject` for eject.
|
|
105
|
+
Enable token writes so the sandbox can record sessions, eject modules and
|
|
106
|
+
publish the application repository when you request it.
|
|
32
107
|
2. Grant sandbox access to the account, in the app under Development, Sandbox,
|
|
33
108
|
or from the CLI:
|
|
34
109
|
|
|
@@ -38,9 +113,11 @@ pnpm flowdular sandbox access --tenant operations-demo
|
|
|
38
113
|
```
|
|
39
114
|
|
|
40
115
|
3. Paste the token and the application address into the sandbox connect screen.
|
|
116
|
+
The same screen asks for a model provider and key, unless the workspace
|
|
117
|
+
already carries one, in which case it names the variable it adopted.
|
|
41
118
|
|
|
42
|
-
The token
|
|
43
|
-
returned to the browser. The application may run anywhere: locally on
|
|
119
|
+
The token and the model key are encrypted at rest under
|
|
120
|
+
`.flowdular/sandbox/secret.key` and are never returned to the browser. The application may run anywhere: locally on
|
|
44
121
|
`http://127.0.0.1:4310` or a deployment.
|
|
45
122
|
|
|
46
123
|
## Modes
|
|
@@ -54,6 +131,25 @@ A non-loopback `--host` forces `self-hosted`. A sandbox that cannot prove it is
|
|
|
54
131
|
loopback never offers a local binary, because a local binary carries the
|
|
55
132
|
operator's own login.
|
|
56
133
|
|
|
134
|
+
The bring-your-own-key driver takes its credential from the sandbox model
|
|
135
|
+
settings, or, when none is saved there, from `ANTHROPIC_API_KEY`,
|
|
136
|
+
`OPENAI_API_KEY`, `AZURE_API_KEY` or `AI_GATEWAY_API_KEY` in the environment or
|
|
137
|
+
the workspace `.env`. A scaffolded application ships the first of those as an
|
|
138
|
+
empty placeholder, so pasting a key is the whole setup.
|
|
139
|
+
|
|
140
|
+
## Typed decisions
|
|
141
|
+
|
|
142
|
+
The brief classification the planner performs (which module, spans several,
|
|
143
|
+
which specialist starts) can be answered by a decision provider instead of a
|
|
144
|
+
coding-agent turn. It is off by default, turned on per sandbox with
|
|
145
|
+
`decisionsEnabled` through `POST /sandbox/api/config`, and takes its credential
|
|
146
|
+
from the sandbox configuration or `TYPESAFE_API_KEY` in the environment or the
|
|
147
|
+
workspace `.env`. Answers below their confidence thresholds, a brief that spans
|
|
148
|
+
modules, and any provider failure all fall back to the workspace rules and the
|
|
149
|
+
planner turn. Implementation writing stays with the coding agent: a decision
|
|
150
|
+
provider generates no text and drives no tools. See
|
|
151
|
+
[`packages/sandbox/README.md`](https://github.com/flowdular/flowdular/blob/main/packages/sandbox/README.md) for the settings.
|
|
152
|
+
|
|
57
153
|
## How a session works
|
|
58
154
|
|
|
59
155
|
A planner names the modules the brief touches and the first specialist role.
|
|
@@ -147,6 +243,21 @@ commits the same change on a branch and opens a pull request; the operator's
|
|
|
147
243
|
working tree and index stay untouched because the work happens in a detached
|
|
148
244
|
worktree under `.flowdular/sandbox/worktrees/<session id>`, removed afterwards.
|
|
149
245
|
|
|
246
|
+
For an application generated by the sandbox, open **GitHub settings** and then
|
|
247
|
+
**Set up repository**. Choose an existing empty GitHub repository or
|
|
248
|
+
create a new private one. The sandbox shows the repository, local commit and
|
|
249
|
+
target branch for review before the operator confirms the first push. It then
|
|
250
|
+
adds a local `app` remote, pushes `main` without rewriting remote history and
|
|
251
|
+
configures GitHub delivery to use that remote. A repository that already has
|
|
252
|
+
commits should be opened with `--connect` when launching the sandbox. GitHub
|
|
253
|
+
CLI authentication or a token saved in GitHub settings is needed for creating
|
|
254
|
+
and pushing; the setup action also requires `sandbox.modules.eject`.
|
|
255
|
+
|
|
256
|
+
After the module specification is approved and the gates pass, choose the
|
|
257
|
+
`git-pr` delivery target to push a branch and open its review. The sandbox does
|
|
258
|
+
not approve a specification or push module code merely because a repository
|
|
259
|
+
was connected.
|
|
260
|
+
|
|
150
261
|
### Configuration
|
|
151
262
|
|
|
152
263
|
Project settings live in `flowdular.json` under `sandbox.delivery`, read at
|
package/dist/bin.js
CHANGED
|
@@ -386,6 +386,15 @@ function renderEnvironmentFile(secrets) {
|
|
|
386
386
|
"",
|
|
387
387
|
"# 32 byte keys, generated once for this app; MFA uses base64url.",
|
|
388
388
|
...SECRET_KEYS.map((key) => `${key}=${secrets[key]}`),
|
|
389
|
+
"",
|
|
390
|
+
"# Model key for the chat-first sandbox (pnpm sandbox). Paste one here and",
|
|
391
|
+
"# the sandbox offers that model on the next start, with no further setup.",
|
|
392
|
+
"# OPENAI_API_KEY, AZURE_API_KEY and AI_GATEWAY_API_KEY are read the same",
|
|
393
|
+
"# way, with the model chosen in the sandbox model settings. A variable",
|
|
394
|
+
"# exported in the shell wins over this file. The application's own agents",
|
|
395
|
+
"# keep their credentials in the workspace vault instead: open",
|
|
396
|
+
"# Administration, AI providers.",
|
|
397
|
+
"ANTHROPIC_API_KEY=",
|
|
389
398
|
""
|
|
390
399
|
].join("\n");
|
|
391
400
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-flowdular",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Scaffold a Flowdular application: the platform, one example module and the secrets a fresh install needs.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/
|
|
9
|
+
"url": "git+https://github.com/Flowdular/flowdular.git",
|
|
10
10
|
"directory": "packages/create-flowdular"
|
|
11
11
|
},
|
|
12
12
|
"homepage": "https://flowdular.com",
|
|
@@ -91,6 +91,13 @@ FD_DATABASE_MIGRATOR_PASSWORD=
|
|
|
91
91
|
FD_DATABASE_RUNTIME_PASSWORD=
|
|
92
92
|
FD_DATABASE_BACKGROUND_PASSWORD=
|
|
93
93
|
|
|
94
|
+
# Model key for a self-hosted sandbox. The application itself never reads it:
|
|
95
|
+
# agents.core keeps every provider credential sealed in the workspace vault,
|
|
96
|
+
# configured in Administration, AI providers. The sandbox reads this name,
|
|
97
|
+
# OPENAI_API_KEY, AZURE_API_KEY and AI_GATEWAY_API_KEY from the environment or
|
|
98
|
+
# from the workspace .env, and an exported variable wins over the file.
|
|
99
|
+
ANTHROPIC_API_KEY=
|
|
100
|
+
|
|
94
101
|
# Prometheus exposition on GET /api/metrics. Leave FD_METRICS unset to keep it off.
|
|
95
102
|
FD_METRICS=false
|
|
96
103
|
FD_METRICS_TOKEN=
|
|
@@ -33,11 +33,13 @@ install. Data lives under `.flowdular/data`.
|
|
|
33
33
|
## Work with coding agents
|
|
34
34
|
|
|
35
35
|
`AGENTS.md` and `CLAUDE.md` introduce the application contract. `.ai` contains the
|
|
36
|
-
editable rules, skills, role prompts, policies, blueprints and
|
|
37
|
-
Codex and Claude Code discover generated skills in
|
|
38
|
-
`.claude/skills
|
|
36
|
+
editable rules, skills, role prompts, subagents, policies, blueprints and
|
|
37
|
+
reference module. Codex and Claude Code discover generated skills in
|
|
38
|
+
`.agents/skills` and `.claude/skills`, and the spec author, module executor and
|
|
39
|
+
reviewer as subagents in `.codex/agents` and `.claude/agents`. Supporting guides
|
|
40
|
+
are in `docs`.
|
|
39
41
|
|
|
40
|
-
After editing `.ai/rules` or `.ai/
|
|
42
|
+
After editing `.ai/rules`, `.ai/skills` or `.ai/subagents`, run `pnpm rules:generate`.
|
|
41
43
|
`pnpm rules:check` detects drift and also runs as part of `pnpm verify`.
|
|
42
44
|
Extend local modules using the published `@flowdular/sdk` imports. Installed
|
|
43
45
|
SDK source is reference material and must not be edited in `node_modules`.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"dev": "node --env-file-if-exists=.env platform/scripts/dev.mjs",
|
|
12
|
-
"sandbox": "
|
|
12
|
+
"sandbox": "flowdular-sandbox",
|
|
13
13
|
"typecheck": "pnpm -r --if-present typecheck",
|
|
14
14
|
"test": "pnpm -r --if-present test",
|
|
15
15
|
"format": "prettier --write .",
|
|
@@ -23,9 +23,10 @@
|
|
|
23
23
|
"build": "flowdular module sync --apply && pnpm --filter @app/platform build"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
+
"@flowdular/sandbox": "0.5.1",
|
|
26
27
|
"@tsrx/prettier-plugin": "0.3.120",
|
|
27
28
|
"prettier": "3.6.2",
|
|
28
|
-
"flowdular": "0.
|
|
29
|
+
"flowdular": "0.5.1",
|
|
29
30
|
"rulesync": "16.21.0"
|
|
30
31
|
}
|
|
31
32
|
}
|