@rubytech/create-maxy-code 0.1.169 → 0.1.171
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/dist/__tests__/premium-mcp-discover.test.js +42 -0
- package/dist/lib/premium-mcp-discover.js +9 -2
- package/package.json +1 -1
- package/payload/platform/plugins/.claude-plugin/marketplace.json +0 -15
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/dist/index.d.ts +51 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/dist/index.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/dist/index.js +196 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/dist/index.js.map +1 -0
- package/payload/premium-plugins/writer-craft/lib/mcp-stderr-tee/package.json +7 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/index.d.ts +3 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/index.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js +257 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/neo4j.d.ts +14 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/neo4j.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/neo4j.js +47 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/neo4j.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.d.ts +42 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.d.ts.map +1 -0
- package/payload/{platform/plugins/writer-craft/mcp/src/lib/voice-corpus.ts → premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.js} +5 -5
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts +36 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js +225 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-distil-profile.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.d.ts +26 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.js +74 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-record-feedback.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts +22 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js +102 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.d.ts +14 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.d.ts.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.js +84 -0
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-tag-content.js.map +1 -0
- package/payload/premium-plugins/writer-craft/mcp/package-lock.json +1327 -0
- package/payload/server/{chunk-3TRIXQSJ.js → chunk-L2YK2VK3.js} +17 -29
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/server.js +1 -1
- package/payload/platform/plugins/teaching/.claude-plugin/plugin.json +0 -8
- package/payload/platform/plugins/teaching/PLUGIN.md +0 -58
- package/payload/platform/plugins/teaching/skills/interactive-tutor/SKILL.md +0 -59
- package/payload/platform/plugins/teaching/skills/interactive-tutor/references/assessment.md +0 -70
- package/payload/platform/plugins/teaching/skills/interactive-tutor/references/classroom-conduct.md +0 -43
- package/payload/platform/plugins/teaching/skills/interactive-tutor/references/teaching-modes.md +0 -83
- package/payload/platform/plugins/teaching/skills/lesson-planner/SKILL.md +0 -48
- package/payload/platform/plugins/teaching/skills/lesson-planner/references/context-gathering.md +0 -41
- package/payload/platform/plugins/teaching/skills/lesson-planner/references/plan-structure.md +0 -94
- package/payload/platform/plugins/teaching/skills/study-pack-builder/SKILL.md +0 -52
- package/payload/platform/plugins/teaching/skills/study-pack-builder/references/disaggregation.md +0 -49
- package/payload/platform/plugins/teaching/skills/study-pack-builder/references/materials.md +0 -116
- package/payload/platform/plugins/venture-studio/.claude-plugin/plugin.json +0 -8
- package/payload/platform/plugins/venture-studio/PLUGIN.md +0 -119
- package/payload/platform/plugins/venture-studio/bin/scaffold.sh +0 -116
- package/payload/platform/plugins/venture-studio/skills/brand-pack/SKILL.md +0 -256
- package/payload/platform/plugins/venture-studio/skills/brand-pack/references/color-psychology.md +0 -118
- package/payload/platform/plugins/venture-studio/skills/investor-data-room/SKILL.md +0 -376
- package/payload/platform/plugins/venture-studio/skills/investor-data-room/references/business-plan-template.md +0 -64
- package/payload/platform/plugins/venture-studio/skills/investor-data-room/references/compliance-research-checklist.md +0 -53
- package/payload/platform/plugins/venture-studio/skills/investor-data-room/references/data-room-structure.md +0 -88
- package/payload/platform/plugins/venture-studio/skills/investor-data-room/references/deck-blueprint-template.md +0 -39
- package/payload/platform/plugins/venture-studio/skills/investor-data-room/references/design-tokens-application.md +0 -79
- package/payload/platform/plugins/venture-studio/skills/investor-data-room/references/html-pdf-pipeline.md +0 -236
- package/payload/platform/plugins/venture-studio/skills/investor-data-room/references/internal-workings-scrub.md +0 -33
- package/payload/platform/plugins/venture-studio/skills/investor-data-room/references/termsheet-template.md +0 -88
- package/payload/platform/plugins/venture-studio/skills/investor-data-room/templates/prospectus/index.html +0 -1565
- package/payload/platform/plugins/venture-studio/skills/investor-data-room/templates/prospectus/render-pdf.mjs +0 -91
- package/payload/platform/plugins/venture-studio/skills/investor-data-room/templates/prospectus/term_sheet.html +0 -715
- package/payload/platform/plugins/venture-studio/skills/office-hours/SKILL.md +0 -587
- package/payload/platform/plugins/venture-studio/skills/prototype-host/SKILL.md +0 -179
- package/payload/platform/plugins/venture-studio/skills/prototype-host/references/cloudflared-ingress-edit.md +0 -81
- package/payload/platform/plugins/venture-studio/skills/prototype-host/references/scaffold-frameworks.md +0 -60
- package/payload/platform/plugins/venture-studio/skills/prototype-host/references/systemd-user-service.md +0 -104
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/SKILL.md +0 -336
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/aarrr-metrics.md +0 -275
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/assumption-testing.md +0 -93
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/boolean-search.md +0 -308
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/build-measure-learn.md +0 -262
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/business-model-canvas.md +0 -171
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/commitment-signals.md +0 -246
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/design-thinking.md +0 -183
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/earlyvangelist.md +0 -190
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/first-principles.md +0 -58
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/fishbone.md +0 -114
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/five-whys.md +0 -43
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/ice-scoring.md +0 -237
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/innovation-accounting.md +0 -290
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/jtbd.md +0 -105
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/landing-page.md +0 -361
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/market-type.md +0 -167
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/mom-test.md +0 -193
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/mvp-types.md +0 -200
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/og-images.md +0 -239
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/pareto.md +0 -103
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/persona-development.md +0 -291
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/pivot-types.md +0 -225
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/positioning-statement.md +0 -179
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/prd.md +0 -363
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/pre-mortem.md +0 -74
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/problem-validation.md +0 -253
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/product-market-fit.md +0 -256
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/research-synthesis.md +0 -276
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/three-engines-of-growth.md +0 -248
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/validation-tests.md +0 -89
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/value-proposition-canvas.md +0 -121
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/win-loss-analysis.md +0 -242
- package/payload/platform/plugins/venture-studio/skills/zero-to-prototype/references/workflow-mapping.md +0 -271
- package/payload/platform/plugins/writer-craft/.claude-plugin/plugin.json +0 -17
- package/payload/platform/plugins/writer-craft/PLUGIN.md +0 -130
- package/payload/platform/plugins/writer-craft/agents/writer-craft--manuscript-reviewer.md +0 -96
- package/payload/platform/plugins/writer-craft/mcp/package.json +0 -19
- package/payload/platform/plugins/writer-craft/mcp/scripts/smoke.mjs +0 -152
- package/payload/platform/plugins/writer-craft/mcp/src/index.ts +0 -289
- package/payload/platform/plugins/writer-craft/mcp/src/lib/neo4j.ts +0 -56
- package/payload/platform/plugins/writer-craft/mcp/src/tools/voice-distil-profile.ts +0 -303
- package/payload/platform/plugins/writer-craft/mcp/src/tools/voice-record-feedback.ts +0 -114
- package/payload/platform/plugins/writer-craft/mcp/src/tools/voice-retrieve-conditioning.ts +0 -145
- package/payload/platform/plugins/writer-craft/mcp/src/tools/voice-tag-content.ts +0 -117
- package/payload/platform/plugins/writer-craft/mcp/tsconfig.json +0 -8
- package/payload/platform/plugins/writer-craft/skills/citation-style/SKILL.md +0 -94
- package/payload/platform/plugins/writer-craft/skills/citation-style/references/book-and-chapter-models.md +0 -77
- package/payload/platform/plugins/writer-craft/skills/citation-style/references/citation-rules.md +0 -103
- package/payload/platform/plugins/writer-craft/skills/citation-style/references/journal-article-models.md +0 -74
- package/payload/platform/plugins/writer-craft/skills/citation-style/references/other-source-models.md +0 -146
- package/payload/platform/plugins/writer-craft/skills/citation-style/references/reference-list-rules.md +0 -70
- package/payload/platform/plugins/writer-craft/skills/editorial-practice/SKILL.md +0 -108
- package/payload/platform/plugins/writer-craft/skills/editorial-practice/references/copyediting.md +0 -73
- package/payload/platform/plugins/writer-craft/skills/editorial-practice/references/developmental-editing.md +0 -85
- package/payload/platform/plugins/writer-craft/skills/editorial-practice/references/genre-specific-editing.md +0 -78
- package/payload/platform/plugins/writer-craft/skills/editorial-practice/references/line-editing.md +0 -55
- package/payload/platform/plugins/writer-craft/skills/editorial-practice/references/self-editing.md +0 -89
- package/payload/platform/plugins/writer-craft/skills/persuasive-storytelling/SKILL.md +0 -114
- package/payload/platform/plugins/writer-craft/skills/persuasive-storytelling/references/audience-analysis.md +0 -73
- package/payload/platform/plugins/writer-craft/skills/persuasive-storytelling/references/crafting-persuasive-story.md +0 -76
- package/payload/platform/plugins/writer-craft/skills/persuasive-storytelling/references/persuasion-case-studies.md +0 -67
- package/payload/platform/plugins/writer-craft/skills/persuasive-storytelling/references/transformation-framework.md +0 -86
- package/payload/platform/plugins/writer-craft/skills/point-of-view/SKILL.md +0 -97
- package/payload/platform/plugins/writer-craft/skills/point-of-view/references/indirect-narration.md +0 -72
- package/payload/platform/plugins/writer-craft/skills/point-of-view/references/pov-types-and-voice.md +0 -91
- package/payload/platform/plugins/writer-craft/skills/point-of-view/references/protagonist-filter.md +0 -71
- package/payload/platform/plugins/writer-craft/skills/point-of-view/references/tense-and-person.md +0 -85
- package/payload/platform/plugins/writer-craft/skills/prose-craft/SKILL.md +0 -100
- package/payload/platform/plugins/writer-craft/skills/prose-craft/references/punctuation-and-grammar.md +0 -72
- package/payload/platform/plugins/writer-craft/skills/prose-craft/references/repetition.md +0 -71
- package/payload/platform/plugins/writer-craft/skills/prose-craft/references/sound-and-rhythm.md +0 -64
- package/payload/platform/plugins/writer-craft/skills/prose-craft/references/word-economy.md +0 -93
- package/payload/platform/plugins/writer-craft/skills/reader-engagement/SKILL.md +0 -100
- package/payload/platform/plugins/writer-craft/skills/reader-engagement/references/cause-effect-setup-payoff.md +0 -79
- package/payload/platform/plugins/writer-craft/skills/reader-engagement/references/conflict-escalation.md +0 -81
- package/payload/platform/plugins/writer-craft/skills/reader-engagement/references/hooking-readers.md +0 -67
- package/payload/platform/plugins/writer-craft/skills/reader-engagement/references/neurochemistry-of-engagement.md +0 -94
- package/payload/platform/plugins/writer-craft/skills/review-manuscript/SKILL.md +0 -111
- package/payload/platform/plugins/writer-craft/skills/review-manuscript/references/review-manuscript-checklist.md +0 -119
- package/payload/platform/plugins/writer-craft/skills/review-prose/SKILL.md +0 -99
- package/payload/platform/plugins/writer-craft/skills/review-prose/references/prose-review-checklist.md +0 -112
- package/payload/platform/plugins/writer-craft/skills/review-scene/SKILL.md +0 -99
- package/payload/platform/plugins/writer-craft/skills/review-scene/references/scene-analysis-framework.md +0 -95
- package/payload/platform/plugins/writer-craft/skills/story-architecture/SKILL.md +0 -106
- package/payload/platform/plugins/writer-craft/skills/story-architecture/references/blueprinting-and-scene-cards.md +0 -118
- package/payload/platform/plugins/writer-craft/skills/story-architecture/references/inner-issue-and-protagonist-goal.md +0 -66
- package/payload/platform/plugins/writer-craft/skills/story-architecture/references/misbelief-desire-worldview.md +0 -87
- package/payload/platform/plugins/writer-craft/skills/story-architecture/references/origin-scenes-and-escalation.md +0 -82
- package/payload/platform/plugins/writer-craft/skills/story-blueprint/SKILL.md +0 -133
- package/payload/platform/plugins/writer-craft/skills/story-blueprint/references/blueprinting-exercises.md +0 -118
- package/payload/platform/plugins/writer-craft/skills/story-blueprint/references/blueprinting-process.md +0 -128
- package/payload/platform/plugins/writer-craft/skills/voice-mirror/SKILL.md +0 -166
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: prototype-host
|
|
3
|
-
description: Use when the operator asks to "host the landing", "host the prototype", "deploy this", "give me a public URL", or any variant that means "make the landing page or wedge prototype reachable on a real URL the founder can send to a customer". Scaffolds a source tree, allocates a local port, edits the brand's cloudflared ingress, creates a DNS row, runs the dev server (or static file-server) under a systemd-user unit, and verifies HTTP 200 from outside the box. Idempotent: re-run for the same surface restarts cleanly without duplicating ports, ingress rows, or units.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# prototype-host
|
|
7
|
-
|
|
8
|
-
## Outcome
|
|
9
|
-
|
|
10
|
-
A founder asks "host the landing" or "host the prototype" and ends the turn with a working public URL — `https://<surface>.<brand-hostname>` — that returns HTTP 200, backed by a dev server (or static file server) supervised by systemd so it survives reboot. No CI, no Vercel, no Pages — the install device itself serves the prototype through the brand's existing cloudflared tunnel.
|
|
11
|
-
|
|
12
|
-
The skill is a composition of primitives that already exist on every Maxy Code install: cloudflared multi-hostname ingress (see [`cloudflared/references/manual-setup.md`](../../../../platform/plugins/cloudflare/references/manual-setup.md)), `systemctl --user` unit files for per-process supervision, and the agent's Bash + Read + Write + Edit tools for source scaffolding. The skill's job is to compose them deterministically so the founder never has to.
|
|
13
|
-
|
|
14
|
-
## Inputs
|
|
15
|
-
|
|
16
|
-
The agent gathers these before invoking any of the six phases. Defaults below; use `AskUserQuestion` only when the operator's intent is ambiguous about surface name or framework.
|
|
17
|
-
|
|
18
|
-
| Input | Default | Notes |
|
|
19
|
-
|---|---|---|
|
|
20
|
-
| `surface` | `landing` for "host the landing"; `prototype` for "host the prototype"; otherwise ask | The DNS label; becomes `<surface>.<brand-hostname>`. Lowercase, kebab-case, must be a valid DNS label (`^[a-z0-9-]+$`, max 63 chars). |
|
|
21
|
-
| `framework` | `static` for `landing`; `vite` for `prototype` | One of `static`, `vite`, `next`, `sveltekit`, `astro`. See [`references/scaffold-frameworks.md`](references/scaffold-frameworks.md). |
|
|
22
|
-
| `surface-dir` | `${PROJECT_ROOT}/.docs/data-room/prototype/<surface>/` | Source tree root. |
|
|
23
|
-
|
|
24
|
-
Two environment values the skill derives, never asks for:
|
|
25
|
-
|
|
26
|
-
- `BRAND` — from `jq -r '.hostname' <PROJECT_ROOT>/platform/config/brand.json` (same idiom every other skill uses).
|
|
27
|
-
- `BRAND_HOSTNAME` — the cloudflared root domain for this install: `jq -r '.cloudflareRootDomain // .hostname' <PROJECT_ROOT>/platform/config/brand.json`. If the field is empty, abort with literal stderr `[prototype-host] FATAL brand-hostname-unresolved — set cloudflareRootDomain in brand.json`.
|
|
28
|
-
|
|
29
|
-
## Hard prerequisite — brand-pack must have run
|
|
30
|
-
|
|
31
|
-
Before phase 1, check `[ -d "${PROJECT_ROOT}/.docs/data-room/06-product-ip/brand" ]` and `[ -n "$(ls -A "${PROJECT_ROOT}/.docs/data-room/06-product-ip/brand" 2>/dev/null)" ]`. If either fails, abort:
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
[prototype-host] FATAL brand-pack-missing — run brand-pack first; hosted surfaces consume 06-product-ip/brand/{color-palette,typography-system,tone-of-voice}.md
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
This is doctrinal — hosting a brandless landing ships a surface that either confounds conversion data or forces a re-render after brand-pack. The check is exit-code, not LLM judgement.
|
|
38
|
-
|
|
39
|
-
## Phases
|
|
40
|
-
|
|
41
|
-
Every phase emits exactly one structured stdout line on success:
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
[prototype-host] phase=<n> surface=<surface> framework=<framework> port=<port> ingress=<state> dns=<state> service=<unit> status=<systemd-state> http=<code>
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Any phase that cannot produce its key/value pair fails by emitting literal stderr (no narration, no retry, no menu-phrasings — [[feedback_no_stdout_parsing_for_control_flow]]) and exits non-zero. The agent surfaces the stderr verbatim to the operator.
|
|
48
|
-
|
|
49
|
-
### Phase 1 — Surface intake (idempotency check)
|
|
50
|
-
|
|
51
|
-
If `<surface-dir>/.port` exists, this is a re-run. Read the port from `.port`, skip directly to phase 5 (systemd restart) then phase 6 (verify). Log:
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
[prototype-host] phase=1 surface=<surface> idempotent=true port=<port>
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Otherwise proceed to phase 2.
|
|
58
|
-
|
|
59
|
-
### Phase 2 — Scaffold source
|
|
60
|
-
|
|
61
|
-
Run the framework's canonical scaffolder into `<surface-dir>/`. The exact commands per framework live in [`references/scaffold-frameworks.md`](references/scaffold-frameworks.md). Static `landing` is a special case: write `<surface-dir>/index.html` directly from `01-narrative/LANDING.md` using the brand tokens.
|
|
62
|
-
|
|
63
|
-
Brand application (every framework):
|
|
64
|
-
|
|
65
|
-
1. Read `06-product-ip/brand/color-palette.md` — extract the CSS custom properties (the file ships a `:root { --color-*: ... }` block).
|
|
66
|
-
2. Read `06-product-ip/brand/typography-system.md` — extract the font-family + scale tokens.
|
|
67
|
-
3. For `static`: inline both into the `<head>` of `<surface-dir>/index.html`.
|
|
68
|
-
4. For framework scaffolds: write the tokens into the framework's global stylesheet entry point (`src/index.css` for Vite/Astro, `app/globals.css` for Next, `src/app.css` for SvelteKit).
|
|
69
|
-
5. For the landing's hero + CTA copy: read `06-product-ip/brand/tone-of-voice.md` and apply it when rewriting `01-narrative/LANDING.md` into HTML.
|
|
70
|
-
|
|
71
|
-
### Phase 3 — Port allocation
|
|
72
|
-
|
|
73
|
-
First unbound port in 4100–4199:
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
for p in $(seq 4100 4199); do
|
|
77
|
-
ss -tln 2>/dev/null | awk '{print $4}' | grep -qE ":${p}$" || { echo "$p"; break; }
|
|
78
|
-
done
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Write to `<surface-dir>/.port`. If the range is exhausted, abort:
|
|
82
|
-
|
|
83
|
-
```
|
|
84
|
-
[prototype-host] FATAL port-range-exhausted range=4100-4199 — free a port or extend the range
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
The range is a skill constant (not a `brand.json` field) because it sits below every documented admin/chat/VNC band and above the OS ephemeral range default. Extending the range is a follow-up task, not a per-install knob.
|
|
88
|
-
|
|
89
|
-
### Phase 4 — Cloudflared ingress edit
|
|
90
|
-
|
|
91
|
-
Per [`references/cloudflared-ingress-edit.md`](references/cloudflared-ingress-edit.md):
|
|
92
|
-
|
|
93
|
-
1. Append a hostname rule for `<surface>.<BRAND_HOSTNAME>` → `http://localhost:<port>` immediately before the final `http_status:404` catch-all in `${CFG_DIR}/config.yml`. The reference doc carries the exact `awk` insert pattern (sed cannot safely insert before the last line of a multi-line YAML rule).
|
|
94
|
-
2. Validate: `cloudflared --origincert "${CFG_DIR}/cert.pem" --config "${CFG_DIR}/config.yml" tunnel ingress validate`. Non-zero exit aborts with the literal cloudflared error.
|
|
95
|
-
3. Create DNS row: `cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel route dns --overwrite-dns "${TUNNEL_ID}" <surface>.<BRAND_HOSTNAME>`. `TUNNEL_ID` comes from `${CFG_DIR}/tunnel.state` (the Step 5b file every install writes).
|
|
96
|
-
4. Reload cloudflared: `systemctl --user restart ${BRAND}-cloudflared.service`.
|
|
97
|
-
|
|
98
|
-
Idempotency: before appending, grep `${CFG_DIR}/config.yml` for `<surface>.<BRAND_HOSTNAME>`. If present, skip the append + DNS route + reload — the row already exists; this is a re-run after a failed phase 5 or 6.
|
|
99
|
-
|
|
100
|
-
### Phase 5 — Systemd user unit
|
|
101
|
-
|
|
102
|
-
Per [`references/systemd-user-service.md`](references/systemd-user-service.md), write `${HOME}/.config/systemd/user/${BRAND}-prototype-<surface>.service`. Two modes:
|
|
103
|
-
|
|
104
|
-
- **Dev-server mode** (frameworks with hot-reload — Vite, Next dev, SvelteKit dev, Astro dev): `ExecStart=` runs the framework's dev command bound to `<port>` via `PORT=` or `--port`.
|
|
105
|
-
- **Static file-server mode** (the `static` framework): run `npm run build` first (no-op for raw HTML), then `ExecStart=cloudflared file-server --port <port> <surface-dir>` (or `<surface-dir>/dist` for built output). No Node process for raw static landings.
|
|
106
|
-
|
|
107
|
-
Then:
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
systemctl --user daemon-reload
|
|
111
|
-
systemctl --user enable --now "${BRAND}-prototype-<surface>.service"
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Check `systemctl --user is-active "${BRAND}-prototype-<surface>.service"` returns `active`. If not, dump the last 50 lines of `journalctl --user -u ${BRAND}-prototype-<surface>.service -n 50 --no-pager` to stderr and abort.
|
|
115
|
-
|
|
116
|
-
### Phase 6 — External verification
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
curl -I -m 15 "https://<surface>.<BRAND_HOSTNAME>"
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
The skill emits the full HTTP response line (e.g. `HTTP/2 200`) to chat. Anything other than `200` aborts with the full curl output as literal stderr — same setup-done contract Task 288 established for cloudflare setup. This is end-to-end proof because the chat itself runs through the same tunnel: if the cloudflared connector were dead, the agent would never have answered the operator.
|
|
123
|
-
|
|
124
|
-
Final success line:
|
|
125
|
-
|
|
126
|
-
```
|
|
127
|
-
[prototype-host] DONE surface=<surface> url=https://<surface>.<BRAND_HOSTNAME> port=<port> service=${BRAND}-prototype-<surface>.service http=200
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## Idempotency contract
|
|
131
|
-
|
|
132
|
-
Re-running the skill with the same `<surface>` is a first-class flow, not an error:
|
|
133
|
-
|
|
134
|
-
| State on re-run | Behaviour |
|
|
135
|
-
|---|---|
|
|
136
|
-
| `<surface-dir>/.port` exists, ingress row exists, service active | Restart the systemd unit; re-verify; emit `idempotent=true` |
|
|
137
|
-
| `.port` exists, ingress row missing | Re-run phase 4 (append, validate, route, reload); restart unit; verify |
|
|
138
|
-
| `.port` exists, service failed | Restart unit; if still failing, dump journal and abort |
|
|
139
|
-
| Nothing exists | Fresh six-phase run |
|
|
140
|
-
|
|
141
|
-
The skill never allocates a second port for the same surface, never appends a duplicate ingress row, never creates a second systemd unit with a different name.
|
|
142
|
-
|
|
143
|
-
## Failure surfaces
|
|
144
|
-
|
|
145
|
-
Likely failure points and the literal stderr the agent should expect:
|
|
146
|
-
|
|
147
|
-
- **Port range exhausted** — `[prototype-host] FATAL port-range-exhausted range=4100-4199`
|
|
148
|
-
- **Ingress validate fails** — cloudflared's own multi-line YAML error, verbatim
|
|
149
|
-
- **DNS route fails** — cloudflared's stderr (typically a 1xxx error code from the API)
|
|
150
|
-
- **Systemd unit fails to start** — last 50 lines of journalctl, verbatim
|
|
151
|
-
- **Curl returns non-200** — full `curl -I` output, verbatim
|
|
152
|
-
|
|
153
|
-
Diagnostic commands the agent should suggest on failure:
|
|
154
|
-
|
|
155
|
-
```bash
|
|
156
|
-
cat ${CFG_DIR}/config.yml
|
|
157
|
-
cloudflared --origincert "${CFG_DIR}/cert.pem" --config ${CFG_DIR}/config.yml tunnel ingress validate
|
|
158
|
-
systemctl --user status ${BRAND}-prototype-<surface>.service
|
|
159
|
-
journalctl --user -u ${BRAND}-prototype-<surface>.service -n 200 --no-pager
|
|
160
|
-
ss -tln | grep :<port>
|
|
161
|
-
dig <surface>.<BRAND_HOSTNAME>
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
## Out of scope
|
|
165
|
-
|
|
166
|
-
- Production hosting (Cloudflare Pages, Vercel, Netlify, Workers). The dev server on the install device covers the founder workflow; production deploy is a separate, later task.
|
|
167
|
-
- TLS / cert handling beyond cloudflared's own.
|
|
168
|
-
- Per-prototype auth gating — the public landing is public; access control is a separate skill.
|
|
169
|
-
- `brand.json` schema changes. Port range is a skill constant.
|
|
170
|
-
- `paths.ts` reads.
|
|
171
|
-
- Multi-tunnel — one tunnel per install with multiple ingress rules is the existing pattern.
|
|
172
|
-
- CI/CD or git-push deploy.
|
|
173
|
-
- Pre-built framework templates beyond the framework's official `create-*` scaffolders.
|
|
174
|
-
- Telemetry on the public URL — the founder wires their own (Plausible, GA, server logs).
|
|
175
|
-
- A `prototype-host-remove` companion skill. For now: `systemctl --user disable --now ${BRAND}-prototype-<surface>.service` + manually strip the ingress row + delete the DNS record. Removal is a follow-up task if it becomes friction.
|
|
176
|
-
|
|
177
|
-
## Multi-prototype is supported by construction
|
|
178
|
-
|
|
179
|
-
Port allocation scans first-free; ingress rules are additive; systemd unit names are namespaced by `<surface>`. Running `landing` + `prototype` + `prototype-v2` simultaneously on the same install requires no special handling — invoke the skill three times with three different surface names.
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
# Cloudflared ingress edit — append a hostname rule before the catch-all
|
|
2
|
-
|
|
3
|
-
This is the canonical pattern for adding a new hostname → local port mapping to an existing brand cloudflared `config.yml`. Sourced from the multi-hostname ingress block documented at [`platform/plugins/cloudflare/references/manual-setup.md`](../../../../../platform/plugins/cloudflare/references/manual-setup.md) under the "Multi-ingress: adding hostnames for prototype services" section.
|
|
4
|
-
|
|
5
|
-
## The config.yml shape
|
|
6
|
-
|
|
7
|
-
A working brand `config.yml` always looks like:
|
|
8
|
-
|
|
9
|
-
```yaml
|
|
10
|
-
tunnel: <TUNNEL_ID>
|
|
11
|
-
credentials-file: <CFG_DIR>/<TUNNEL_ID>.json
|
|
12
|
-
ingress:
|
|
13
|
-
- hostname: admin.<BRAND_HOSTNAME>
|
|
14
|
-
service: http://localhost:<PORT>
|
|
15
|
-
- hostname: public.<BRAND_HOSTNAME>
|
|
16
|
-
service: http://localhost:<PORT>
|
|
17
|
-
- service: http_status:404
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
A new prototype rule must land **before** the `http_status:404` catch-all; otherwise cloudflared matches the catch-all first and the prototype hostname returns 404.
|
|
21
|
-
|
|
22
|
-
## The append pattern
|
|
23
|
-
|
|
24
|
-
`sed` cannot safely insert before the last block of a multi-line YAML rule (`- service: http_status:404` is one logical entry but the line above it might be a comment or blank). `awk` is the right primitive: scan for the literal `- service: http_status:404` line and insert two lines before it.
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
SURFACE="<surface>" # e.g. landing
|
|
28
|
-
PORT="<port>" # from <surface-dir>/.port
|
|
29
|
-
HOSTNAME="${SURFACE}.${BRAND_HOSTNAME}"
|
|
30
|
-
|
|
31
|
-
# Idempotency guard — skip if the hostname already appears as an ingress rule.
|
|
32
|
-
if grep -qE "^[[:space:]]*-[[:space:]]+hostname:[[:space:]]+${HOSTNAME}\b" "${CFG_DIR}/config.yml"; then
|
|
33
|
-
echo "[prototype-host] ingress-row-exists hostname=${HOSTNAME} — skipping append"
|
|
34
|
-
else
|
|
35
|
-
awk -v host="${HOSTNAME}" -v port="${PORT}" '
|
|
36
|
-
/^[[:space:]]*-[[:space:]]+service:[[:space:]]+http_status:404[[:space:]]*$/ {
|
|
37
|
-
print " - hostname: " host
|
|
38
|
-
print " service: http://localhost:" port
|
|
39
|
-
}
|
|
40
|
-
{ print }
|
|
41
|
-
' "${CFG_DIR}/config.yml" > "${CFG_DIR}/config.yml.new"
|
|
42
|
-
mv "${CFG_DIR}/config.yml.new" "${CFG_DIR}/config.yml"
|
|
43
|
-
fi
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Validate, route, reload
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
# 1. Validate the YAML and ingress semantics.
|
|
50
|
-
cloudflared --origincert "${CFG_DIR}/cert.pem" \
|
|
51
|
-
--config "${CFG_DIR}/config.yml" \
|
|
52
|
-
tunnel ingress validate
|
|
53
|
-
|
|
54
|
-
# 2. Create the DNS row (CNAME from <hostname> to <TUNNEL_ID>.cfargotunnel.com).
|
|
55
|
-
TUNNEL_ID=$(jq -r '.id // .tunnelId' "${CFG_DIR}/tunnel.state")
|
|
56
|
-
cloudflared --origincert "${CFG_DIR}/cert.pem" \
|
|
57
|
-
tunnel route dns --overwrite-dns "${TUNNEL_ID}" "${HOSTNAME}"
|
|
58
|
-
|
|
59
|
-
# 3. Reload the connector so it picks up the new ingress rule.
|
|
60
|
-
systemctl --user restart "${BRAND}-cloudflared.service"
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Each step is its own command, runs sequentially, exits non-zero on failure. The skill propagates the failing command's stderr verbatim — no parsing, no narration ([[feedback_no_stdout_parsing_for_control_flow]]).
|
|
64
|
-
|
|
65
|
-
## What `--overwrite-dns` does
|
|
66
|
-
|
|
67
|
-
If a CNAME for `<hostname>` already exists in the account's DNS (e.g. from a prior install), `tunnel route dns` errors with `record already exists`. `--overwrite-dns` replaces the existing record with one pointing at this tunnel. Use it unconditionally — the alternative (probe-then-create) doubles the API surface and races with concurrent DNS edits.
|
|
68
|
-
|
|
69
|
-
## Why restart, not reload
|
|
70
|
-
|
|
71
|
-
`cloudflared` watches `config.yml` only when launched with `--config-watch` (off by default in the brand service unit). `systemctl --user restart` is the deterministic way to pick up the new rule. Brief connection blip (~1s) is acceptable because the brand's admin + public hostnames already tolerate the same blip on every cloudflared upgrade.
|
|
72
|
-
|
|
73
|
-
## Failure points
|
|
74
|
-
|
|
75
|
-
| Symptom | Root cause | Fix |
|
|
76
|
-
|---|---|---|
|
|
77
|
-
| `validate: ingress rule N has no service` | `awk` inserted only the hostname line, not the service line | Re-run; the snippet emits both lines atomically per match |
|
|
78
|
-
| `validate: failed to parse YAML` | A previous `awk` run left a partial line | `cat ${CFG_DIR}/config.yml` and hand-fix; the skill cannot recover from corrupt YAML |
|
|
79
|
-
| `route dns: 1004 invalid hostname` | `<surface>` violates DNS label rules (uppercase, underscore, leading dash) | Re-run with a valid `<surface>`; the skill validates `^[a-z0-9-]+$` before phase 4 |
|
|
80
|
-
| `route dns: 9103 unauthorized` | The cert.pem in `${CFG_DIR}` doesn't grant DNS-edit on the zone | Re-run Step 1 of the cloudflare runbook to refresh `cert.pem` |
|
|
81
|
-
| Restart succeeds but `curl -I` returns 502 | The dev server isn't actually listening on `<port>` | Phase 5 (systemd) failed earlier; the skill should have caught it. Read `journalctl --user -u ${BRAND}-prototype-<surface>.service -n 50` |
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# Framework scaffolders, dev-server commands, and port overrides
|
|
2
|
-
|
|
3
|
-
Five frameworks supported. Each row gives the exact command set the prototype-host skill runs in phases 2 and 5.
|
|
4
|
-
|
|
5
|
-
## Selection defaults
|
|
6
|
-
|
|
7
|
-
| Surface intent | Default framework | Reason |
|
|
8
|
-
|---|---|---|
|
|
9
|
-
| `landing` | `static` | One HTML file rendered from `01-narrative/LANDING.md` is faster to ship and faster to load than a Vite bundle. Lighthouse-clean by construction. |
|
|
10
|
-
| `prototype` | `vite` | Vue/React/Svelte all supported; HMR is the founder's iteration loop. |
|
|
11
|
-
|
|
12
|
-
If the operator names a framework explicitly, use that. Otherwise these defaults stand.
|
|
13
|
-
|
|
14
|
-
## `static`
|
|
15
|
-
|
|
16
|
-
No scaffolder. The skill writes a single `index.html` directly into `<surface-dir>/` from `01-narrative/LANDING.md`, with brand tokens inlined into `<style>` in the `<head>`.
|
|
17
|
-
|
|
18
|
-
- **Build:** none (raw HTML); for a `dist/` build (if the operator adds Tailwind etc later) the skill respects `<surface-dir>/dist/` if present.
|
|
19
|
-
- **Serve:** `cloudflared file-server --port <port> <surface-dir>` (or `<surface-dir>/dist` if a build directory exists).
|
|
20
|
-
- **Port override:** `--port <port>` on `cloudflared file-server`.
|
|
21
|
-
|
|
22
|
-
## `vite`
|
|
23
|
-
|
|
24
|
-
- **Scaffold:** `cd <surface-dir-parent> && npm create vite@latest <surface-name> -- --template vanilla-ts -- --yes`. The agent prompts the operator before this if they want a non-vanilla template (`react-ts`, `svelte-ts`, `vue-ts`); otherwise `vanilla-ts` is the default — the founder can rewrite `index.html` + `src/main.ts` to anything from there.
|
|
25
|
-
- **Install:** `cd <surface-dir> && npm install`
|
|
26
|
-
- **Build:** `npm run build` (outputs to `<surface-dir>/dist/`)
|
|
27
|
-
- **Dev server:** `npm run dev -- --port <port> --host 127.0.0.1`
|
|
28
|
-
- **Port override:** `--port <port>` (Vite respects the flag; `PORT=` is ignored).
|
|
29
|
-
|
|
30
|
-
## `next`
|
|
31
|
-
|
|
32
|
-
- **Scaffold:** `cd <surface-dir-parent> && npx -y create-next-app@latest <surface-name> --typescript --app --tailwind --eslint --src-dir --import-alias "@/*" --use-npm --yes`
|
|
33
|
-
- **Install:** scaffolder runs `npm install` automatically.
|
|
34
|
-
- **Build:** `npm run build`
|
|
35
|
-
- **Dev server:** `npm run dev -- -p <port> -H 127.0.0.1`
|
|
36
|
-
- **Port override:** `-p <port>` (Next.js dev/start respect the flag; `PORT=` also works but `-p` is explicit).
|
|
37
|
-
|
|
38
|
-
## `sveltekit`
|
|
39
|
-
|
|
40
|
-
- **Scaffold:** `cd <surface-dir-parent> && npx -y sv create <surface-name> --template minimal --types ts --no-add-ons --install npm`
|
|
41
|
-
- **Install:** scaffolder runs `npm install` automatically when `--install npm` is passed.
|
|
42
|
-
- **Build:** `npm run build`
|
|
43
|
-
- **Dev server:** `npm run dev -- --port <port> --host 127.0.0.1`
|
|
44
|
-
- **Port override:** `--port <port>`.
|
|
45
|
-
|
|
46
|
-
## `astro`
|
|
47
|
-
|
|
48
|
-
- **Scaffold:** `cd <surface-dir-parent> && npm create astro@latest <surface-name> -- --template minimal --typescript strict --install --no-git --yes`
|
|
49
|
-
- **Install:** scaffolder runs `npm install` when `--install` is passed.
|
|
50
|
-
- **Build:** `npm run build` (outputs to `<surface-dir>/dist/`)
|
|
51
|
-
- **Dev server:** `npm run dev -- --port <port> --host 127.0.0.1`
|
|
52
|
-
- **Port override:** `--port <port>`.
|
|
53
|
-
|
|
54
|
-
## Why `--host 127.0.0.1`
|
|
55
|
-
|
|
56
|
-
Every dev server above binds to `127.0.0.1`, not `0.0.0.0`. Cloudflared connects to the loopback address; binding to `0.0.0.0` would expose the dev server on the install device's LAN address, which is unwanted (LAN clients should reach the prototype through the tunnel, not directly).
|
|
57
|
-
|
|
58
|
-
## Why no `--yes` on `create-next-app` interactive prompts in older versions
|
|
59
|
-
|
|
60
|
-
`create-next-app@latest` (≥14.2) treats all listed flags as the answer set and runs non-interactively. If a flag is omitted it falls back to a TTY prompt — fatal under the agent's non-TTY Bash invocation. Always pass every flag the table lists.
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
# Systemd user unit template for a hosted prototype surface
|
|
2
|
-
|
|
3
|
-
One unit per hosted surface: `${HOME}/.config/systemd/user/${BRAND}-prototype-<surface>.service`.
|
|
4
|
-
|
|
5
|
-
Two flavours: dev-server mode (frameworks with hot-reload) and static file-server mode (raw HTML or built `dist/`).
|
|
6
|
-
|
|
7
|
-
## Dev-server mode (vite, next, sveltekit, astro)
|
|
8
|
-
|
|
9
|
-
```ini
|
|
10
|
-
[Unit]
|
|
11
|
-
Description=${BRAND} prototype surface <surface> — <framework> dev server
|
|
12
|
-
After=network.target
|
|
13
|
-
|
|
14
|
-
[Service]
|
|
15
|
-
Type=simple
|
|
16
|
-
WorkingDirectory=<surface-dir>
|
|
17
|
-
Environment="PORT=<port>"
|
|
18
|
-
Environment="NODE_ENV=development"
|
|
19
|
-
ExecStart=/usr/bin/env npm run dev -- <framework-port-flag> <port> --host 127.0.0.1
|
|
20
|
-
Restart=on-failure
|
|
21
|
-
RestartSec=3
|
|
22
|
-
StandardOutput=journal
|
|
23
|
-
StandardError=journal
|
|
24
|
-
|
|
25
|
-
[Install]
|
|
26
|
-
WantedBy=default.target
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
`<framework-port-flag>` is the per-framework override from [`scaffold-frameworks.md`](scaffold-frameworks.md): `--port` for Vite/SvelteKit/Astro, `-p` for Next.
|
|
30
|
-
|
|
31
|
-
## Static file-server mode
|
|
32
|
-
|
|
33
|
-
For the `static` framework (raw HTML rendered from `LANDING.md`):
|
|
34
|
-
|
|
35
|
-
```ini
|
|
36
|
-
[Unit]
|
|
37
|
-
Description=${BRAND} prototype surface <surface> — static file server
|
|
38
|
-
After=network.target
|
|
39
|
-
|
|
40
|
-
[Service]
|
|
41
|
-
Type=simple
|
|
42
|
-
WorkingDirectory=<surface-dir>
|
|
43
|
-
ExecStart=/usr/local/bin/cloudflared file-server --port <port> --address 127.0.0.1 <surface-dir>
|
|
44
|
-
Restart=on-failure
|
|
45
|
-
RestartSec=3
|
|
46
|
-
StandardOutput=journal
|
|
47
|
-
StandardError=journal
|
|
48
|
-
|
|
49
|
-
[Install]
|
|
50
|
-
WantedBy=default.target
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
If `<surface-dir>/dist/` exists (the founder added a build step later), point `ExecStart`'s last arg at `<surface-dir>/dist` instead of `<surface-dir>`. The skill detects this automatically with `[ -d <surface-dir>/dist ] && SERVE_DIR=<surface-dir>/dist || SERVE_DIR=<surface-dir>`.
|
|
54
|
-
|
|
55
|
-
## Why a user unit, not a root unit
|
|
56
|
-
|
|
57
|
-
The brand cloudflared connector already runs as a `systemctl --user` unit ([`platform/plugins/cloudflare/references/reset-guide.md`](../../../../../platform/plugins/cloudflare/references/reset-guide.md)). Keeping prototype units in the same scope means:
|
|
58
|
-
|
|
59
|
-
- Same `journalctl --user` namespace — the operator reads one log namespace, not two.
|
|
60
|
-
- No `sudo` required to install, enable, or restart.
|
|
61
|
-
- `loginctl enable-linger <user>` (already set by the installer) survives reboot — units come back without an SSH session.
|
|
62
|
-
|
|
63
|
-
Root units would also work, but split the operator's mental model and require `sudo` for every restart. Not worth the tradeoff for a per-prototype surface.
|
|
64
|
-
|
|
65
|
-
## Enabling and starting
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
systemctl --user daemon-reload
|
|
69
|
-
systemctl --user enable --now "${BRAND}-prototype-<surface>.service"
|
|
70
|
-
|
|
71
|
-
# Verify it stayed up.
|
|
72
|
-
systemctl --user is-active "${BRAND}-prototype-<surface>.service"
|
|
73
|
-
|
|
74
|
-
# If not active, dump the journal and surface verbatim.
|
|
75
|
-
if [ "$(systemctl --user is-active "${BRAND}-prototype-<surface>.service")" != "active" ]; then
|
|
76
|
-
journalctl --user -u "${BRAND}-prototype-<surface>.service" -n 50 --no-pager >&2
|
|
77
|
-
exit 1
|
|
78
|
-
fi
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## Re-run after edit
|
|
82
|
-
|
|
83
|
-
If the skill regenerates the unit file (e.g. the port changed, or the static surface gained a `dist/` directory), the sequence is:
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
systemctl --user daemon-reload
|
|
87
|
-
systemctl --user restart "${BRAND}-prototype-<surface>.service"
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
`daemon-reload` is required after any unit-file edit; `restart` picks up the new `ExecStart`.
|
|
91
|
-
|
|
92
|
-
## Why `Restart=on-failure`, not `always`
|
|
93
|
-
|
|
94
|
-
Dev servers exit 0 in legitimate cases (the operator killed `npm run dev` from another shell to apply a config edit). `on-failure` respects those exits; `always` would fight them.
|
|
95
|
-
|
|
96
|
-
## Logging
|
|
97
|
-
|
|
98
|
-
All output (stdout + stderr from the dev server / file server) goes to the user journal. Read with:
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
journalctl --user -u "${BRAND}-prototype-<surface>.service" -f
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
No file-based log rotation needed — systemd handles journal rotation.
|