@cloudgrid-io/mcp 0.11.2 → 0.13.0

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 (98) hide show
  1. package/package.json +2 -1
  2. package/src/corpus/capability-map.md +40 -6
  3. package/src/corpus/examples/ai-app/index.md +237 -0
  4. package/src/corpus/examples/api-service/index.md +190 -0
  5. package/src/corpus/examples/crm/index.md +273 -0
  6. package/src/corpus/examples/kanban/index.md +256 -0
  7. package/src/corpus/templates/admin-dashboard/README.md +75 -0
  8. package/src/corpus/templates/admin-dashboard/cloudgrid.yaml +15 -0
  9. package/src/corpus/templates/admin-dashboard/index.md +78 -0
  10. package/src/corpus/templates/admin-dashboard/services/web/app/api/orders/route.js +64 -0
  11. package/src/corpus/templates/admin-dashboard/services/web/app/api/users/route.js +63 -0
  12. package/src/corpus/templates/admin-dashboard/services/web/app/layout.js +39 -0
  13. package/src/corpus/templates/admin-dashboard/services/web/app/page.js +73 -0
  14. package/src/corpus/templates/admin-dashboard/services/web/app/resource-table.js +94 -0
  15. package/src/corpus/templates/admin-dashboard/services/web/lib/db.js +32 -0
  16. package/src/corpus/templates/admin-dashboard/services/web/package.json +16 -0
  17. package/src/corpus/templates/ai-app/README.md +95 -0
  18. package/src/corpus/templates/ai-app/cloudgrid.yaml +20 -0
  19. package/src/corpus/templates/ai-app/index.md +269 -0
  20. package/src/corpus/templates/ai-app/services/web/app/api/chat/route.js +51 -0
  21. package/src/corpus/templates/ai-app/services/web/app/chat.js +60 -0
  22. package/src/corpus/templates/ai-app/services/web/app/layout.js +36 -0
  23. package/src/corpus/templates/ai-app/services/web/app/page.js +25 -0
  24. package/src/corpus/templates/ai-app/services/web/lib/db.js +32 -0
  25. package/src/corpus/templates/ai-app/services/web/package.json +17 -0
  26. package/src/corpus/templates/api-docs/README.md +28 -0
  27. package/src/corpus/templates/api-docs/cloudgrid.yaml +10 -0
  28. package/src/corpus/templates/api-docs/index.html +136 -0
  29. package/src/corpus/templates/api-docs/index.md +30 -0
  30. package/src/corpus/templates/api-service/README.md +87 -0
  31. package/src/corpus/templates/api-service/cloudgrid.yaml +18 -0
  32. package/src/corpus/templates/api-service/index.md +225 -0
  33. package/src/corpus/templates/api-service/services/api/package.json +13 -0
  34. package/src/corpus/templates/api-service/services/api/src/index.js +132 -0
  35. package/src/corpus/templates/changelog/README.md +27 -0
  36. package/src/corpus/templates/changelog/cloudgrid.yaml +10 -0
  37. package/src/corpus/templates/changelog/index.html +80 -0
  38. package/src/corpus/templates/changelog/index.md +30 -0
  39. package/src/corpus/templates/crm/README.md +72 -0
  40. package/src/corpus/templates/crm/cloudgrid.yaml +15 -0
  41. package/src/corpus/templates/crm/index.md +68 -0
  42. package/src/corpus/templates/crm/services/web/app/api/contacts/route.js +80 -0
  43. package/src/corpus/templates/crm/services/web/app/contact-manager.js +102 -0
  44. package/src/corpus/templates/crm/services/web/app/layout.js +35 -0
  45. package/src/corpus/templates/crm/services/web/app/page.js +34 -0
  46. package/src/corpus/templates/crm/services/web/lib/db.js +32 -0
  47. package/src/corpus/templates/crm/services/web/package.json +16 -0
  48. package/src/corpus/templates/docs-site/README.md +28 -0
  49. package/src/corpus/templates/docs-site/cloudgrid.yaml +10 -0
  50. package/src/corpus/templates/docs-site/index.html +122 -0
  51. package/src/corpus/templates/docs-site/index.md +30 -0
  52. package/src/corpus/templates/kanban/README.md +72 -0
  53. package/src/corpus/templates/kanban/cloudgrid.yaml +15 -0
  54. package/src/corpus/templates/kanban/index.md +68 -0
  55. package/src/corpus/templates/kanban/services/web/app/api/cards/route.js +70 -0
  56. package/src/corpus/templates/kanban/services/web/app/board.js +93 -0
  57. package/src/corpus/templates/kanban/services/web/app/layout.js +37 -0
  58. package/src/corpus/templates/kanban/services/web/app/page.js +32 -0
  59. package/src/corpus/templates/kanban/services/web/lib/db.js +32 -0
  60. package/src/corpus/templates/kanban/services/web/package.json +16 -0
  61. package/src/corpus/templates/portfolio/README.md +29 -0
  62. package/src/corpus/templates/portfolio/cloudgrid.yaml +10 -0
  63. package/src/corpus/templates/portfolio/index.html +126 -0
  64. package/src/corpus/templates/portfolio/index.md +30 -0
  65. package/src/corpus/templates/saas-marketing/README.md +30 -0
  66. package/src/corpus/templates/saas-marketing/cloudgrid.yaml +10 -0
  67. package/src/corpus/templates/saas-marketing/index.html +212 -0
  68. package/src/corpus/templates/saas-marketing/index.md +30 -0
  69. package/src/corpus/templates/status-page/README.md +28 -0
  70. package/src/corpus/templates/status-page/cloudgrid.yaml +10 -0
  71. package/src/corpus/templates/status-page/index.html +117 -0
  72. package/src/corpus/templates/status-page/index.md +30 -0
  73. package/src/corpus/templates/task-manager/README.md +72 -0
  74. package/src/corpus/templates/task-manager/cloudgrid.yaml +15 -0
  75. package/src/corpus/templates/task-manager/index.md +68 -0
  76. package/src/corpus/templates/task-manager/services/web/app/api/tasks/route.js +77 -0
  77. package/src/corpus/templates/task-manager/services/web/app/layout.js +39 -0
  78. package/src/corpus/templates/task-manager/services/web/app/page.js +33 -0
  79. package/src/corpus/templates/task-manager/services/web/app/task-list.js +110 -0
  80. package/src/corpus/templates/task-manager/services/web/lib/db.js +32 -0
  81. package/src/corpus/templates/task-manager/services/web/package.json +16 -0
  82. package/src/corpus/templates/waitlist/README.md +38 -0
  83. package/src/corpus/templates/waitlist/cloudgrid.yaml +10 -0
  84. package/src/corpus/templates/waitlist/index.html +69 -0
  85. package/src/corpus/templates/waitlist/index.md +41 -0
  86. package/src/corpus/workflows/admin-dashboard.md +102 -0
  87. package/src/corpus/workflows/ai-app.md +136 -0
  88. package/src/corpus/workflows/api-docs.md +36 -0
  89. package/src/corpus/workflows/api-service.md +118 -0
  90. package/src/corpus/workflows/changelog.md +33 -0
  91. package/src/corpus/workflows/crm.md +101 -0
  92. package/src/corpus/workflows/docs-site.md +35 -0
  93. package/src/corpus/workflows/kanban.md +102 -0
  94. package/src/corpus/workflows/portfolio.md +35 -0
  95. package/src/corpus/workflows/saas-marketing.md +37 -0
  96. package/src/corpus/workflows/status-page.md +35 -0
  97. package/src/corpus/workflows/task-manager.md +101 -0
  98. package/src/corpus/workflows/waitlist.md +42 -0
@@ -0,0 +1,10 @@
1
+ # static — deploys as an inspiration (instant, works on hosted) via gridctl_drop;
2
+ # this yaml is only for plugging it as an OWNED static runtime (async, local
3
+ # edition only). To publish the page, prefer gridctl_drop, not grid plug.
4
+ #
5
+ # needs: {} # none — static, no infrastructure
6
+ name: saas-marketing
7
+ services:
8
+ web:
9
+ type: static
10
+ path: /
@@ -0,0 +1,212 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>{{PRODUCT_NAME}} — {{TAGLINE}}</title>
7
+ <style>
8
+ :root {
9
+ --bg: #0b0f17;
10
+ --panel: #121826;
11
+ --panel-2: #0f1523;
12
+ --fg: #e8edf5;
13
+ --muted: #9aa7bd;
14
+ --accent: #5b8cff;
15
+ --accent-2: #7c6bff;
16
+ --border: #223049;
17
+ --ok: #34d399;
18
+ }
19
+ * { box-sizing: border-box; margin: 0; padding: 0; }
20
+ html { scroll-behavior: smooth; }
21
+ body {
22
+ background: var(--bg);
23
+ color: var(--fg);
24
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
25
+ -webkit-font-smoothing: antialiased;
26
+ line-height: 1.55;
27
+ }
28
+ a { color: inherit; }
29
+ section { padding: clamp(3rem, 8vw, 6rem) clamp(1.25rem, 6vw, 6rem); border-top: 1px solid var(--border); }
30
+ .container { max-width: 1080px; margin: 0 auto; }
31
+ nav {
32
+ display: flex; align-items: center; justify-content: space-between; gap: 1rem;
33
+ padding: 1.1rem clamp(1.25rem, 6vw, 6rem);
34
+ border-bottom: 1px solid var(--border);
35
+ position: sticky; top: 0; background: rgba(11,15,23,0.85); backdrop-filter: blur(8px); z-index: 10;
36
+ }
37
+ nav .brand { font-weight: 600; letter-spacing: -0.01em; }
38
+ nav .links { display: flex; gap: 1.5rem; }
39
+ nav .links a { color: var(--muted); text-decoration: none; font-size: 0.95rem; }
40
+ nav .links a:hover { color: var(--fg); }
41
+ .btn {
42
+ display: inline-block; background: var(--accent); color: #fff; text-decoration: none;
43
+ padding: 0.75rem 1.4rem; border-radius: 10px; font-weight: 600; font-size: 0.95rem;
44
+ border: 1px solid transparent;
45
+ }
46
+ .btn:hover { filter: brightness(1.08); }
47
+ .btn.ghost { background: transparent; border-color: var(--border); color: var(--fg); }
48
+ @media (max-width: 640px) { nav .links { display: none; } }
49
+
50
+ .hero { text-align: center; display: grid; gap: 1.25rem; place-items: center; border-top: none; }
51
+ .hero .eyebrow { color: var(--accent); font-weight: 600; font-size: 0.9rem; letter-spacing: 0.02em; }
52
+ .hero h1 { font-size: clamp(2.4rem, 6vw, 4.2rem); line-height: 1.05; letter-spacing: -0.02em; max-width: 18ch; }
53
+ .hero p { font-size: clamp(1.05rem, 2.4vw, 1.4rem); color: var(--muted); max-width: 52ch; }
54
+ .hero .actions { display: flex; gap: 0.75rem; flex-wrap: wrap; justify-content: center; margin-top: 0.5rem; }
55
+
56
+ .section-head { text-align: center; display: grid; gap: 0.75rem; place-items: center; margin-bottom: 2.5rem; }
57
+ .section-head h2 { font-size: clamp(1.8rem, 4.5vw, 2.6rem); letter-spacing: -0.01em; }
58
+ .section-head p { color: var(--muted); max-width: 48ch; }
59
+
60
+ .features { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
61
+ .feature { background: var(--panel); border: 1px solid var(--border); border-radius: 14px; padding: 1.75rem; }
62
+ .feature .icon { width: 40px; height: 40px; border-radius: 10px; background: linear-gradient(135deg, var(--accent), var(--accent-2)); margin-bottom: 1rem; }
63
+ .feature h3 { font-size: 1.15rem; margin-bottom: 0.5rem; letter-spacing: -0.01em; }
64
+ .feature p { color: var(--muted); }
65
+
66
+ .pricing { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); align-items: start; }
67
+ .plan { background: var(--panel); border: 1px solid var(--border); border-radius: 16px; padding: 2rem; display: grid; gap: 1rem; }
68
+ .plan.featured { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
69
+ .plan .badge { align-self: start; font-size: 0.75rem; color: var(--accent); border: 1px solid var(--accent); border-radius: 999px; padding: 0.15rem 0.6rem; }
70
+ .plan h3 { font-size: 1.3rem; letter-spacing: -0.01em; }
71
+ .plan .price { font-size: 2.4rem; font-weight: 700; letter-spacing: -0.02em; }
72
+ .plan .price span { font-size: 1rem; color: var(--muted); font-weight: 400; }
73
+ .plan ul { list-style: none; display: grid; gap: 0.6rem; color: var(--muted); }
74
+ .plan li::before { content: "✓"; color: var(--ok); margin-right: 0.5rem; }
75
+
76
+ .testimonials { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
77
+ .quote { background: var(--panel-2); border: 1px solid var(--border); border-radius: 14px; padding: 1.75rem; }
78
+ .quote blockquote { font-size: 1.05rem; margin-bottom: 1rem; }
79
+ .quote .who { color: var(--muted); font-size: 0.9rem; }
80
+
81
+ .faq { display: grid; gap: 0.75rem; max-width: 760px; margin: 0 auto; }
82
+ details { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 1rem 1.25rem; }
83
+ details summary { cursor: pointer; font-weight: 600; list-style: none; display: flex; justify-content: space-between; align-items: center; }
84
+ details summary::-webkit-details-marker { display: none; }
85
+ details summary::after { content: "+"; color: var(--muted); }
86
+ details[open] summary::after { content: "–"; }
87
+ details p { color: var(--muted); margin-top: 0.75rem; }
88
+
89
+ .cta-band { text-align: center; display: grid; gap: 1.25rem; place-items: center; background: linear-gradient(135deg, rgba(91,140,255,0.12), rgba(124,107,255,0.12)); }
90
+ .cta-band h2 { font-size: clamp(1.8rem, 4.5vw, 3rem); letter-spacing: -0.01em; max-width: 22ch; }
91
+
92
+ footer { padding: 2.5rem clamp(1.25rem, 6vw, 6rem); border-top: 1px solid var(--border); color: var(--muted); font-size: 0.9rem; text-align: center; }
93
+ </style>
94
+ </head>
95
+ <body>
96
+ <nav>
97
+ <span class="brand">{{PRODUCT_NAME}}</span>
98
+ <div class="links">
99
+ <a href="#features">Features</a>
100
+ <a href="#pricing">Pricing</a>
101
+ <a href="#faq">FAQ</a>
102
+ </div>
103
+ <a class="btn" href="{{CTA_HREF}}">{{CTA_LABEL}}</a>
104
+ </nav>
105
+
106
+ <section class="hero">
107
+ <span class="eyebrow">{{HERO_EYEBROW}}</span>
108
+ <h1>{{HERO_HEADLINE}}</h1>
109
+ <p>{{HERO_SUBHEAD}}</p>
110
+ <div class="actions">
111
+ <a class="btn" href="{{CTA_HREF}}">{{CTA_LABEL}}</a>
112
+ <a class="btn ghost" href="#features">See how it works</a>
113
+ </div>
114
+ </section>
115
+
116
+ <section id="features">
117
+ <div class="container">
118
+ <div class="section-head">
119
+ <h2>{{FEATURES_HEADLINE}}</h2>
120
+ <p>{{FEATURES_SUBHEAD}}</p>
121
+ </div>
122
+ <div class="features">
123
+ <!-- FEATURES_START -->
124
+ <div class="feature"><div class="icon"></div><h3>{{FEATURE_1_TITLE}}</h3><p>{{FEATURE_1_BODY}}</p></div>
125
+ <div class="feature"><div class="icon"></div><h3>{{FEATURE_2_TITLE}}</h3><p>{{FEATURE_2_BODY}}</p></div>
126
+ <div class="feature"><div class="icon"></div><h3>{{FEATURE_3_TITLE}}</h3><p>{{FEATURE_3_BODY}}</p></div>
127
+ <div class="feature"><div class="icon"></div><h3>{{FEATURE_4_TITLE}}</h3><p>{{FEATURE_4_BODY}}</p></div>
128
+ <!-- Add more <div class="feature">...</div> blocks as needed. -->
129
+ <!-- FEATURES_END -->
130
+ </div>
131
+ </div>
132
+ </section>
133
+
134
+ <section id="pricing">
135
+ <div class="container">
136
+ <div class="section-head">
137
+ <h2>{{PRICING_HEADLINE}}</h2>
138
+ <p>{{PRICING_SUBHEAD}}</p>
139
+ </div>
140
+ <div class="pricing">
141
+ <!-- PRICING_START -->
142
+ <div class="plan">
143
+ <h3>{{PLAN_1_NAME}}</h3>
144
+ <div class="price">{{PLAN_1_PRICE}}<span>/{{PLAN_1_PERIOD}}</span></div>
145
+ <ul>
146
+ <li>{{PLAN_1_FEATURE_1}}</li>
147
+ <li>{{PLAN_1_FEATURE_2}}</li>
148
+ <li>{{PLAN_1_FEATURE_3}}</li>
149
+ </ul>
150
+ <a class="btn ghost" href="{{CTA_HREF}}">{{PLAN_1_CTA}}</a>
151
+ </div>
152
+ <div class="plan featured">
153
+ <span class="badge">Most popular</span>
154
+ <h3>{{PLAN_2_NAME}}</h3>
155
+ <div class="price">{{PLAN_2_PRICE}}<span>/{{PLAN_2_PERIOD}}</span></div>
156
+ <ul>
157
+ <li>{{PLAN_2_FEATURE_1}}</li>
158
+ <li>{{PLAN_2_FEATURE_2}}</li>
159
+ <li>{{PLAN_2_FEATURE_3}}</li>
160
+ </ul>
161
+ <a class="btn" href="{{CTA_HREF}}">{{PLAN_2_CTA}}</a>
162
+ </div>
163
+ <div class="plan">
164
+ <h3>{{PLAN_3_NAME}}</h3>
165
+ <div class="price">{{PLAN_3_PRICE}}<span>/{{PLAN_3_PERIOD}}</span></div>
166
+ <ul>
167
+ <li>{{PLAN_3_FEATURE_1}}</li>
168
+ <li>{{PLAN_3_FEATURE_2}}</li>
169
+ <li>{{PLAN_3_FEATURE_3}}</li>
170
+ </ul>
171
+ <a class="btn ghost" href="{{CTA_HREF}}">{{PLAN_3_CTA}}</a>
172
+ </div>
173
+ <!-- PRICING_END -->
174
+ </div>
175
+ </div>
176
+ </section>
177
+
178
+ <section id="testimonials">
179
+ <div class="container">
180
+ <div class="section-head"><h2>{{TESTIMONIALS_HEADLINE}}</h2></div>
181
+ <div class="testimonials">
182
+ <!-- TESTIMONIALS_START -->
183
+ <div class="quote"><blockquote>{{QUOTE_1}}</blockquote><div class="who">{{QUOTE_1_WHO}}</div></div>
184
+ <div class="quote"><blockquote>{{QUOTE_2}}</blockquote><div class="who">{{QUOTE_2_WHO}}</div></div>
185
+ <div class="quote"><blockquote>{{QUOTE_3}}</blockquote><div class="who">{{QUOTE_3_WHO}}</div></div>
186
+ <!-- TESTIMONIALS_END -->
187
+ </div>
188
+ </div>
189
+ </section>
190
+
191
+ <section id="faq">
192
+ <div class="container">
193
+ <div class="section-head"><h2>{{FAQ_HEADLINE}}</h2></div>
194
+ <div class="faq">
195
+ <!-- FAQ_START -->
196
+ <details><summary>{{FAQ_1_Q}}</summary><p>{{FAQ_1_A}}</p></details>
197
+ <details><summary>{{FAQ_2_Q}}</summary><p>{{FAQ_2_A}}</p></details>
198
+ <details><summary>{{FAQ_3_Q}}</summary><p>{{FAQ_3_A}}</p></details>
199
+ <details><summary>{{FAQ_4_Q}}</summary><p>{{FAQ_4_A}}</p></details>
200
+ <!-- FAQ_END -->
201
+ </div>
202
+ </div>
203
+ </section>
204
+
205
+ <section class="cta-band">
206
+ <h2>{{CLOSING_HEADLINE}}</h2>
207
+ <a class="btn" href="{{CTA_HREF}}">{{CTA_LABEL}}</a>
208
+ </section>
209
+
210
+ <footer>{{FOOTER_TEXT}}</footer>
211
+ </body>
212
+ </html>
@@ -0,0 +1,30 @@
1
+ # Template: saas-marketing (static)
2
+
3
+ A single self-contained HTML SaaS marketing site (hero, features, pricing tiers, testimonials, FAQ, and a closing call to action). Richer than the landing-page template. **No infrastructure** — no database, no backend, no `needs:`.
4
+ State (if any) lives only in memory and is lost on refresh.
5
+
6
+ ## How it deploys
7
+
8
+ To publish this page, use **`gridctl_drop`** (or `gridctl_plug` with the inline
9
+ HTML): it deploys as an **inspiration** — instant, and works on the **hosted**
10
+ edition (Claude Web) as well as local. The reference `cloudgrid.yaml` in this
11
+ directory (`type: static`) is ONLY for plugging the page as an OWNED static
12
+ runtime (async, local edition only); the fast path is `gridctl_drop`.
13
+
14
+ The fillable HTML is the real template — fetch it with
15
+ `gridctl_fetch("template", "saas-marketing")` and fill in the user's content.
16
+
17
+ ## cloudgrid.yaml (reference only)
18
+
19
+ ```yaml
20
+ # static — deploys as an inspiration (instant, works on hosted) via gridctl_drop;
21
+ # this yaml is only for plugging it as an OWNED static runtime (async, local
22
+ # edition only). To publish the page, prefer gridctl_drop, not grid plug.
23
+ #
24
+ # needs: {} # none — static, no infrastructure
25
+ name: saas-marketing
26
+ services:
27
+ web:
28
+ type: static
29
+ path: /
30
+ ```
@@ -0,0 +1,28 @@
1
+ # status-page
2
+
3
+ A single self-contained HTML service status page: an overall banner, a component list with up / degraded / down pills, per-component uptime bars, and an incident history log. Status is baked into the page.
4
+
5
+ Static inspiration — a single self-contained `index.html` (inline CSS and JS,
6
+ no external CDNs). It deploys instantly via `gridctl_drop` and works on every
7
+ CloudGrid edition, including hosted.
8
+
9
+ ## Fill it in
10
+
11
+ Fetch the template with `gridctl_fetch("template", "status-page")`, then replace the
12
+ placeholders:
13
+
14
+ - The overall status banner text and state class (`ok` / `degraded` / `down`).
15
+ - One row per component: name, a status pill, and a 90-day uptime bar (the bar segments are inline divs you set to ok/degraded/down).
16
+ - The incident history entries: date, title, state, and a short resolution note.
17
+ - All state is baked into the markup — this is a display-only static page, no live polling.
18
+
19
+ Keep it a single self-contained HTML file. Do not add external scripts,
20
+ stylesheets, fonts, or large embedded media.
21
+
22
+ ## Deploy
23
+
24
+ - Hosted MCP edition: call the drop tool with the HTML.
25
+ - Local MCP / CLI edition: write the HTML to a file and run `gridctl plug`.
26
+
27
+ It deploys as an inspiration and returns a live share URL right away. Re-deploy
28
+ with the same entity id to update the same URL.
@@ -0,0 +1,10 @@
1
+ # static — deploys as an inspiration (instant, works on hosted) via gridctl_drop;
2
+ # this yaml is only for plugging it as an OWNED static runtime (async, local
3
+ # edition only). To publish the page, prefer gridctl_drop, not grid plug.
4
+ #
5
+ # needs: {} # none — static, no infrastructure
6
+ name: status-page
7
+ services:
8
+ web:
9
+ type: static
10
+ path: /
@@ -0,0 +1,117 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>{{SERVICE_NAME}} status</title>
7
+ <style>
8
+ :root {
9
+ --bg: #0b0f17; --panel: #121826; --fg: #e8edf5; --muted: #9aa7bd;
10
+ --border: #223049; --ok: #34d399; --degraded: #fbbf24; --down: #f87171;
11
+ }
12
+ * { box-sizing: border-box; margin: 0; padding: 0; }
13
+ body {
14
+ background: var(--bg); color: var(--fg);
15
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
16
+ -webkit-font-smoothing: antialiased; line-height: 1.5;
17
+ }
18
+ .wrap { max-width: 840px; margin: 0 auto; padding: clamp(1.5rem, 5vw, 3rem) 1.25rem; }
19
+ header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.75rem; gap: 1rem; flex-wrap: wrap; }
20
+ header .brand { font-weight: 600; font-size: 1.15rem; letter-spacing: -0.01em; }
21
+ header .updated { color: var(--muted); font-size: 0.85rem; }
22
+
23
+ .banner { border-radius: 14px; padding: 1.25rem 1.5rem; margin-bottom: 2rem; font-weight: 600; display: flex; align-items: center; gap: 0.75rem; border: 1px solid var(--border); }
24
+ .banner .dot { width: 12px; height: 12px; border-radius: 50%; }
25
+ .banner.ok { background: rgba(52,211,153,0.1); } .banner.ok .dot { background: var(--ok); }
26
+ .banner.degraded { background: rgba(251,191,36,0.1); } .banner.degraded .dot { background: var(--degraded); }
27
+ .banner.down { background: rgba(248,113,113,0.1); } .banner.down .dot { background: var(--down); }
28
+
29
+ h2 { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin: 2rem 0 0.75rem; }
30
+ .component { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 1.1rem 1.25rem; margin-bottom: 0.75rem; }
31
+ .component .top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.75rem; }
32
+ .component .name { font-weight: 600; }
33
+ .pill { font-size: 0.8rem; padding: 0.2rem 0.65rem; border-radius: 999px; font-weight: 600; }
34
+ .pill.ok { color: var(--ok); background: rgba(52,211,153,0.12); }
35
+ .pill.degraded { color: var(--degraded); background: rgba(251,191,36,0.12); }
36
+ .pill.down { color: var(--down); background: rgba(248,113,113,0.12); }
37
+ .uptime { display: flex; gap: 2px; align-items: flex-end; height: 30px; }
38
+ .uptime span { flex: 1; height: 100%; border-radius: 2px; background: var(--ok); opacity: 0.85; }
39
+ .uptime span.degraded { background: var(--degraded); }
40
+ .uptime span.down { background: var(--down); }
41
+ .uptime-label { display: flex; justify-content: space-between; color: var(--muted); font-size: 0.75rem; margin-top: 0.4rem; }
42
+
43
+ .incident { border-left: 3px solid var(--border); padding: 0.5rem 0 0.5rem 1rem; margin-bottom: 1.5rem; }
44
+ .incident.ok { border-color: var(--ok); }
45
+ .incident.degraded { border-color: var(--degraded); }
46
+ .incident.down { border-color: var(--down); }
47
+ .incident .date { color: var(--muted); font-size: 0.8rem; }
48
+ .incident .title { font-weight: 600; margin: 0.2rem 0; }
49
+ .incident p { color: var(--muted); font-size: 0.92rem; }
50
+ footer { color: var(--muted); font-size: 0.85rem; margin-top: 2.5rem; text-align: center; }
51
+ </style>
52
+ </head>
53
+ <body>
54
+ <div class="wrap">
55
+ <header>
56
+ <span class="brand">{{SERVICE_NAME}}</span>
57
+ <span class="updated">Last updated {{LAST_UPDATED}}</span>
58
+ </header>
59
+
60
+ <!-- Overall banner: set the class to ok / degraded / down. -->
61
+ <div class="banner ok">
62
+ <span class="dot"></span>
63
+ <span>{{OVERALL_STATUS_TEXT}}</span>
64
+ </div>
65
+
66
+ <h2>Components</h2>
67
+ <!-- COMPONENTS_START -->
68
+ <!-- One .component per service. Set the .pill class (ok/degraded/down) and
69
+ mark any non-ok bars in .uptime with class degraded or down. -->
70
+ <div class="component">
71
+ <div class="top"><span class="name">{{COMPONENT_1_NAME}}</span><span class="pill ok">Operational</span></div>
72
+ <div class="uptime">
73
+ <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
74
+ <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
75
+ <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
76
+ </div>
77
+ <div class="uptime-label"><span>90 days ago</span><span>{{COMPONENT_1_UPTIME}} uptime</span><span>today</span></div>
78
+ </div>
79
+ <div class="component">
80
+ <div class="top"><span class="name">{{COMPONENT_2_NAME}}</span><span class="pill degraded">Degraded</span></div>
81
+ <div class="uptime">
82
+ <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
83
+ <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span class="degraded"></span><span class="degraded"></span>
84
+ <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
85
+ </div>
86
+ <div class="uptime-label"><span>90 days ago</span><span>{{COMPONENT_2_UPTIME}} uptime</span><span>today</span></div>
87
+ </div>
88
+ <div class="component">
89
+ <div class="top"><span class="name">{{COMPONENT_3_NAME}}</span><span class="pill ok">Operational</span></div>
90
+ <div class="uptime">
91
+ <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
92
+ <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
93
+ <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
94
+ </div>
95
+ <div class="uptime-label"><span>90 days ago</span><span>{{COMPONENT_3_UPTIME}} uptime</span><span>today</span></div>
96
+ </div>
97
+ <!-- COMPONENTS_END -->
98
+
99
+ <h2>Incident history</h2>
100
+ <!-- INCIDENTS_START -->
101
+ <div class="incident ok">
102
+ <div class="date">{{INCIDENT_1_DATE}}</div>
103
+ <div class="title">{{INCIDENT_1_TITLE}}</div>
104
+ <p>{{INCIDENT_1_BODY}}</p>
105
+ </div>
106
+ <div class="incident down">
107
+ <div class="date">{{INCIDENT_2_DATE}}</div>
108
+ <div class="title">{{INCIDENT_2_TITLE}}</div>
109
+ <p>{{INCIDENT_2_BODY}}</p>
110
+ </div>
111
+ <!-- Add more .incident blocks (class ok/degraded/down) as needed. -->
112
+ <!-- INCIDENTS_END -->
113
+
114
+ <footer>{{FOOTER_TEXT}}</footer>
115
+ </div>
116
+ </body>
117
+ </html>
@@ -0,0 +1,30 @@
1
+ # Template: status-page (static)
2
+
3
+ A single self-contained HTML service status page: an overall banner, a component list with up / degraded / down pills, per-component uptime bars, and an incident history log. Status is baked into the page. **No infrastructure** — no database, no backend, no `needs:`.
4
+ State (if any) lives only in memory and is lost on refresh.
5
+
6
+ ## How it deploys
7
+
8
+ To publish this page, use **`gridctl_drop`** (or `gridctl_plug` with the inline
9
+ HTML): it deploys as an **inspiration** — instant, and works on the **hosted**
10
+ edition (Claude Web) as well as local. The reference `cloudgrid.yaml` in this
11
+ directory (`type: static`) is ONLY for plugging the page as an OWNED static
12
+ runtime (async, local edition only); the fast path is `gridctl_drop`.
13
+
14
+ The fillable HTML is the real template — fetch it with
15
+ `gridctl_fetch("template", "status-page")` and fill in the user's content.
16
+
17
+ ## cloudgrid.yaml (reference only)
18
+
19
+ ```yaml
20
+ # static — deploys as an inspiration (instant, works on hosted) via gridctl_drop;
21
+ # this yaml is only for plugging it as an OWNED static runtime (async, local
22
+ # edition only). To publish the page, prefer gridctl_drop, not grid plug.
23
+ #
24
+ # needs: {} # none — static, no infrastructure
25
+ name: status-page
26
+ services:
27
+ web:
28
+ type: static
29
+ path: /
30
+ ```
@@ -0,0 +1,72 @@
1
+ # task-manager template — persistent Next.js + Mongo task list
2
+
3
+ A real, deployable task manager — richer than the bare `app-with-data` to-do
4
+ starter. Tasks live in the grid-shared MongoDB, so they survive refresh and are
5
+ shared across users/sessions. Same proven shape, adapted to a `tasks` domain
6
+ (title, done, due, priority) with a complete-toggle and an active/done filter.
7
+
8
+ ## How the grid gives you a database
9
+
10
+ You do **not** provision a database or set a connection string. In
11
+ `cloudgrid.yaml` you declare `needs: { database: true }`, and the grid:
12
+
13
+ - provisions shared Mongo for the app, and
14
+ - injects the connection string as the **`DATABASE_MONGODB_URL`** environment
15
+ variable (plus the legacy `MONGODB_URL` alias) — at dev-time (`grid dev`) and
16
+ at runtime (after `grid plug`).
17
+
18
+ The app reads it via `process.env.DATABASE_MONGODB_URL` (with a legacy
19
+ `process.env.MONGODB_URL` fallback) in `services/web/lib/db.js` — **lazily,
20
+ inside the `getDb` getter, never at module top level** (a top-level read fails
21
+ `next build`, which imports the module for route analysis before the grid injects
22
+ the var). Never hardcode a connection string; never commit a secret.
23
+
24
+ > **Declare `needs: { database: true }` (the canonical shape).** The deployer
25
+ > provisions Mongo and injects `DATABASE_MONGODB_URL` (plus the legacy
26
+ > `MONGODB_URL` alias). `requires:` is the deprecated v1 alias — don't author new
27
+ > yaml with it, and never set `needs:` and `requires:` together (the validator
28
+ > rejects the combination).
29
+
30
+ ## Service layout
31
+
32
+ App code lives under **`services/web/`**, not the template root. `path:` in
33
+ `cloudgrid.yaml` is the URL mount, not the filesystem path — the service named
34
+ `web` means the CLI looks for `services/web/`. Files at the root fail with
35
+ `Error: Service directory not found: …/services/web`.
36
+
37
+ ## Run locally
38
+
39
+ ```bash
40
+ npm install
41
+ grid dev # runs Next.js with DATABASE_MONGODB_URL injected against dev Mongo
42
+ ```
43
+
44
+ ## Deploy
45
+
46
+ ```bash
47
+ grid plug # builds + deploys the folder (async — poll status until live)
48
+ ```
49
+
50
+ A runtime deploy is asynchronous: `plug` returns `status: building`; poll status
51
+ until the app is live, then use the returned live URL. Re-plug the same entity
52
+ to update the same URL.
53
+
54
+ ## File tree
55
+
56
+ ```
57
+ cloudgrid.yaml # name + services.web (nextjs) + needs: { database: true }
58
+ services/web/package.json # next, react, react-dom, mongodb driver only
59
+ services/web/lib/db.js # lazy Mongo client from DATABASE_MONGODB_URL (legacy MONGODB_URL fallback)
60
+ services/web/app/layout.js # root layout + inline CSS
61
+ services/web/app/page.js # server component: reads tasks from Mongo
62
+ services/web/app/task-list.js # client list: POST / PATCH (toggle done) / DELETE, plus a filter
63
+ services/web/app/api/tasks/route.js # GET (list) / POST (add) / PATCH (toggle) / DELETE (remove)
64
+ ```
65
+
66
+ ## Adapt it
67
+
68
+ - Rename the `tasks` collection in `services/web/app/api/tasks/route.js` and
69
+ `services/web/app/page.js`.
70
+ - Change the document fields (tags, projects, assignees, subtasks).
71
+ - Add sorting by due date or priority on top of the existing fields.
72
+ - Add `cache: true` to `needs:` only if you actually need Redis.
@@ -0,0 +1,15 @@
1
+ # Rename this app. The grid injects the DB connection string as an environment
2
+ # variable at runtime — do NOT set it yourself, and never commit a connection
3
+ # string or secret.
4
+ #
5
+ # `needs: { database: true }` is the canonical, recommended shape. The deployer
6
+ # provisions Mongo and injects DATABASE_MONGODB_URL (plus the legacy MONGODB_URL
7
+ # alias). `requires:` is the deprecated v1 alias — don't author new yaml with it,
8
+ # and never set `needs:` and `requires:` together (the validator rejects it).
9
+ name: my-tasks
10
+ services:
11
+ web:
12
+ type: nextjs
13
+ path: /
14
+ needs:
15
+ database: true
@@ -0,0 +1,68 @@
1
+ # Template: task-manager (persistent Next.js + Mongo task list)
2
+
3
+ A real, deployable task manager — richer than the bare `app-with-data` to-do
4
+ starter. Tasks live in the grid-shared MongoDB, so they survive refresh and are
5
+ shared across sessions. Same proven shape, adapted to a `tasks` domain (title,
6
+ done, due, priority) with a complete-toggle and an active/done filter.
7
+
8
+ **Key rules (all proven by the app-with-data end-to-end deploy):**
9
+
10
+ 1. **Service code MUST live under `services/<name>/`, not the repo/template
11
+ root.** `path:` in `cloudgrid.yaml` is the URL mount, NOT the filesystem path.
12
+ The service named `web` → the CLI looks for `services/web/`. App files at the
13
+ root fail with `Error: Service directory not found: …/services/web`.
14
+ 2. **Read the DB connection string LAZILY (inside the getter), never at module
15
+ top level.** The grid injects it as `DATABASE_MONGODB_URL` (plus the legacy
16
+ `MONGODB_URL` alias) at dev-time and runtime; the app reads
17
+ `process.env.DATABASE_MONGODB_URL || process.env.MONGODB_URL` inside `getDb`.
18
+ A top-level read fails `next build`. Never hardcode a connection string; never
19
+ commit a secret.
20
+ 3. **Declare the datastore with `needs: { database: true }`.** The deployer
21
+ provisions Mongo and injects the connection string. `requires:` is the
22
+ deprecated v1 alias; don't author new yaml with it, and never set `needs:` and
23
+ `requires:` together (the validator rejects the combination).
24
+
25
+ ## File tree
26
+
27
+ ```
28
+ cloudgrid.yaml # name + services.web (nextjs) + needs: { database: true }
29
+ services/web/package.json # next, react, react-dom, mongodb driver only
30
+ services/web/lib/db.js # lazy Mongo client from DATABASE_MONGODB_URL (legacy MONGODB_URL fallback)
31
+ services/web/app/layout.js # root layout + inline CSS
32
+ services/web/app/page.js # server component: reads tasks from Mongo
33
+ services/web/app/task-list.js # client list: POST / PATCH (toggle done) / DELETE, plus a filter
34
+ services/web/app/api/tasks/route.js # GET (list) / POST (add) / PATCH (toggle) / DELETE (remove)
35
+ ```
36
+
37
+ ## cloudgrid.yaml
38
+
39
+ ```yaml
40
+ name: my-tasks
41
+ services:
42
+ web:
43
+ type: nextjs
44
+ path: /
45
+ needs:
46
+ database: true
47
+ ```
48
+
49
+ > **Capability:** this template's need is `database: true`. The deployer
50
+ > provisions Mongo and injects `DATABASE_MONGODB_URL` (plus the legacy
51
+ > `MONGODB_URL` alias). `requires:` is the deprecated v1 alias — don't mix it with
52
+ > `needs:` (the validator rejects the combination). See the capability-map for
53
+ > the full injection table.
54
+
55
+ ## The tasks collection
56
+
57
+ Documents: `{ title, done, due, priority, createdAt }` where `priority` is one of
58
+ `low` / `medium` / `high`. The API route on `tasks` does GET (list), POST (add),
59
+ PATCH (toggle done), DELETE (remove); the page is a server component that reads
60
+ the collection and hands it to a client list with an active/done filter.
61
+
62
+ ## Adapt it
63
+
64
+ - Rename the `tasks` collection to your data.
65
+ - Change the document fields (tags, projects, assignees, subtasks).
66
+ - Add sorting by due date or priority on top of the existing fields.
67
+ - Add `cache: true` to `needs:` only if you actually need Redis.
68
+ - Run `grid dev` to test locally, `grid plug` to deploy (async — poll to live).