@rashidee/co2 1.3.5 → 1.3.6

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 (74) hide show
  1. package/dist/.co2-dat/app.db +0 -0
  2. package/dist/.co2-dat/app.db-shm +0 -0
  3. package/dist/.co2-dat/app.db-wal +0 -0
  4. package/dist/index.js +893 -321
  5. package/drizzle/0017_git_remote_url.sql +3 -0
  6. package/drizzle/0018_default_branch.sql +3 -0
  7. package/drizzle/0019_git_provider.sql +3 -0
  8. package/drizzle/meta/_journal.json +21 -0
  9. package/package.json +1 -1
  10. package/plugin/skills/util-gencicdscript/SKILL.md +28 -6
  11. package/plugin/skills/util-gencicdscript/references/cicd-app-template.md +128 -2
  12. package/plugin/skills/util-plancicd/SKILL.md +73 -4
  13. package/static/assets/{abnfDiagram-VRR7QNED-DPeOXHkD.js → abnfDiagram-VRR7QNED-CzKpxD9q.js} +1 -1
  14. package/static/assets/{arc-Bb2ZEtXe.js → arc-0jj0p8Qe.js} +1 -1
  15. package/static/assets/{architectureDiagram-ZJ3FMSHR-D4QooL3I.js → architectureDiagram-ZJ3FMSHR-DXUL6S5t.js} +1 -1
  16. package/static/assets/{blockDiagram-677ZJIJ3-BBGaxcqB.js → blockDiagram-677ZJIJ3-BBjsQESd.js} +1 -1
  17. package/static/assets/{c4Diagram-LMCZKHZV-BuFUMezL.js → c4Diagram-LMCZKHZV-CTsLC95S.js} +1 -1
  18. package/static/assets/channel-1MKDXK8w.js +1 -0
  19. package/static/assets/{chunk-2Q5K7J3B-MrR2dFOK.js → chunk-2Q5K7J3B-BpisBKYx.js} +1 -1
  20. package/static/assets/{chunk-32BRIVSS-KMxu8q-J.js → chunk-32BRIVSS-DAfsr4ph.js} +1 -1
  21. package/static/assets/{chunk-5VM5RSS4-Dbm1PUMI.js → chunk-5VM5RSS4-Z3FtY-B6.js} +1 -1
  22. package/static/assets/{chunk-EX3LRPZG-C0dIkyxF.js → chunk-EX3LRPZG-FI9smQaN.js} +1 -1
  23. package/static/assets/{chunk-JWPE2WC7-DbWRh-D_.js → chunk-JWPE2WC7-sOygQj_B.js} +1 -1
  24. package/static/assets/{chunk-MOJQB5TN-Do9cetyI.js → chunk-MOJQB5TN-DCSpai7w.js} +1 -1
  25. package/static/assets/{chunk-RYQCIY6F-CyFiM1A0.js → chunk-RYQCIY6F-CchqtfnI.js} +1 -1
  26. package/static/assets/{chunk-V7JOEXUC-CoFBSdV8.js → chunk-V7JOEXUC-DaOzsfx7.js} +1 -1
  27. package/static/assets/{chunk-VR4S4FIN-ZJvwfHHK.js → chunk-VR4S4FIN-DmxuoVTy.js} +1 -1
  28. package/static/assets/{chunk-XXDRQBXY-D8HPek0x.js → chunk-XXDRQBXY-ChERxfri.js} +1 -1
  29. package/static/assets/classDiagram-OUVF2IWQ-bADaUwoJ.js +1 -0
  30. package/static/assets/classDiagram-v2-EOCWNBFH-bADaUwoJ.js +1 -0
  31. package/static/assets/{cose-bilkent-JH36ORCC-Cirxlh75.js → cose-bilkent-JH36ORCC-CO7_eF6b.js} +1 -1
  32. package/static/assets/{cynefin-VYW2F7L2-DJsuSCkt.js → cynefin-VYW2F7L2-CzQkWtdY.js} +1 -1
  33. package/static/assets/{cynefinDiagram-TSTJHNR4-uBfDZheC.js → cynefinDiagram-TSTJHNR4-BN2o8rFa.js} +1 -1
  34. package/static/assets/{dagre-VKFMJZFB-BI0z1jT0.js → dagre-VKFMJZFB-GLjBE-Xs.js} +1 -1
  35. package/static/assets/{diagram-FQU43EPY-EHIThO2P.js → diagram-FQU43EPY-Cyv7saju.js} +1 -1
  36. package/static/assets/{diagram-G47NLZAW-Dy2XDYy0.js → diagram-G47NLZAW-B9PmZJ3U.js} +1 -1
  37. package/static/assets/{diagram-NH7WQ7WH-C4g4puV-.js → diagram-NH7WQ7WH-ByKD8yJU.js} +1 -1
  38. package/static/assets/{diagram-OA4YK3LP-CQ16Gfax.js → diagram-OA4YK3LP-BvbW7fIS.js} +1 -1
  39. package/static/assets/{diagram-WEI45ONY-B4FV_zLb.js → diagram-WEI45ONY-BgXZ9SJ8.js} +1 -1
  40. package/static/assets/{ebnfDiagram-CCIWWBDH-CHmsZ-vG.js → ebnfDiagram-CCIWWBDH-CXEEJ26B.js} +1 -1
  41. package/static/assets/{erDiagram-Q63AITRT-DCRi5XIy.js → erDiagram-Q63AITRT-CcHs_S16.js} +1 -1
  42. package/static/assets/{flowDiagram-23GEKE2U-BtIBkFJl.js → flowDiagram-23GEKE2U-Cwg3I3GK.js} +1 -1
  43. package/static/assets/{ganttDiagram-NO4QXBWP-CL02h1xy.js → ganttDiagram-NO4QXBWP-ky6w5Lvy.js} +1 -1
  44. package/static/assets/{gitGraphDiagram-IHSO6WYX-v3ovWEkW.js → gitGraphDiagram-IHSO6WYX-DN0eRQ7r.js} +1 -1
  45. package/static/assets/{index-BeEg-X0n.js → index-CCC4-Ibh.js} +172 -170
  46. package/static/assets/{index-Dn_JY-18.css → index-Cz5Fcq3G.css} +1 -1
  47. package/static/assets/{infoDiagram-FWYZ7A6U-CQqDjZFw.js → infoDiagram-FWYZ7A6U-COojw6OC.js} +1 -1
  48. package/static/assets/{ishikawaDiagram-FXEZZL3T-BbtSBTuq.js → ishikawaDiagram-FXEZZL3T-BR5w5ZqM.js} +1 -1
  49. package/static/assets/{journeyDiagram-5HDEW3XC-BYiMuolz.js → journeyDiagram-5HDEW3XC-B5WtTEBM.js} +1 -1
  50. package/static/assets/{kanban-definition-HUTT4EX6-ok10yIH4.js → kanban-definition-HUTT4EX6-DnQ_zg4E.js} +1 -1
  51. package/static/assets/{linear-BzIhXjex.js → linear-C5kGzenz.js} +1 -1
  52. package/static/assets/{mindmap-definition-LN4V7U3C-ByHM6OsT.js → mindmap-definition-LN4V7U3C-BMksxBso.js} +1 -1
  53. package/static/assets/{pegDiagram-2B236MQR-36ZAozwu.js → pegDiagram-2B236MQR-BKrF97iU.js} +1 -1
  54. package/static/assets/{pieDiagram-ENE6RG2P-CiI5CYaa.js → pieDiagram-ENE6RG2P-DZAQkZwC.js} +1 -1
  55. package/static/assets/{quadrantDiagram-ABIIQ3AL-CqfESErc.js → quadrantDiagram-ABIIQ3AL-Dx0-5odM.js} +1 -1
  56. package/static/assets/{railroadDiagram-RFXS5EU6-n-HvWpNy.js → railroadDiagram-RFXS5EU6-BbY-CxL_.js} +1 -1
  57. package/static/assets/{requirementDiagram-TGXJPOKE-a4x2DB1M.js → requirementDiagram-TGXJPOKE-ItSYgvcN.js} +1 -1
  58. package/static/assets/{sankeyDiagram-HTMAVEWB-s-Q6aijB.js → sankeyDiagram-HTMAVEWB-CjIuRRYE.js} +1 -1
  59. package/static/assets/{sequenceDiagram-DBY2YBRQ-DNZkVRTf.js → sequenceDiagram-DBY2YBRQ-B277PT2z.js} +1 -1
  60. package/static/assets/{sizeCapture-X5ZJPWSS-B5Ciccrp.js → sizeCapture-X5ZJPWSS-BYc46yWE.js} +1 -1
  61. package/static/assets/{stateDiagram-2N3HPSRC-BlFZbi2g.js → stateDiagram-2N3HPSRC-Cq41gYoq.js} +1 -1
  62. package/static/assets/stateDiagram-v2-6OUMAXLB-B9V3_HpU.js +1 -0
  63. package/static/assets/{swimlanes-5IMT3BWC-BXRFSwye.js → swimlanes-5IMT3BWC-DMxA2jQL.js} +2 -2
  64. package/static/assets/swimlanesDiagram-G3AALYLV-qx8IX4cU.js +8 -0
  65. package/static/assets/{timeline-definition-FHXFAJF6-D9DwlOxs.js → timeline-definition-FHXFAJF6-CB8iORNI.js} +1 -1
  66. package/static/assets/{vennDiagram-L72KCM5P-C80mLh7C.js → vennDiagram-L72KCM5P-CHwAX4cP.js} +1 -1
  67. package/static/assets/{wardleyDiagram-EHGQE667-ESSoASoQ.js → wardleyDiagram-EHGQE667-DL19kD5s.js} +1 -1
  68. package/static/assets/{xychartDiagram-FW5EYKEG-BHN68FBi.js → xychartDiagram-FW5EYKEG-b271oXxd.js} +1 -1
  69. package/static/index.html +2 -2
  70. package/static/assets/channel-8qA4Tgiu.js +0 -1
  71. package/static/assets/classDiagram-OUVF2IWQ-Q3gj4hh8.js +0 -1
  72. package/static/assets/classDiagram-v2-EOCWNBFH-Q3gj4hh8.js +0 -1
  73. package/static/assets/stateDiagram-v2-6OUMAXLB-BclL0t8p.js +0 -1
  74. package/static/assets/swimlanesDiagram-G3AALYLV-CuNyWLnI.js +0 -8
@@ -0,0 +1,3 @@
1
+ -- v2.2.0: Per-project git remote URL (push destination). Nullable; auto-seeded from an
2
+ -- imported repo's origin, editable in project detail. Contains no embedded credentials.
3
+ ALTER TABLE `projects` ADD `git_remote_url` text;
@@ -0,0 +1,3 @@
1
+ -- v2.3.0: Per-project default (trunk) branch — the branch release branches merge back into on a
2
+ -- version cut (runVersionCutGit). Defaults to `main`; existing rows backfill to `main`.
3
+ ALTER TABLE `projects` ADD `default_branch` text DEFAULT 'main' NOT NULL;
@@ -0,0 +1,3 @@
1
+ -- v2.3.0: Per-project git hosting provider — drives the PAT auth-username convention (GitHub
2
+ -- `x-access-token` vs GitLab `oauth2`). Defaults to `github`; existing rows backfill to `github`.
3
+ ALTER TABLE `projects` ADD `git_provider` text DEFAULT 'github' NOT NULL;
@@ -120,6 +120,27 @@
120
120
  "when": 1784700000000,
121
121
  "tag": "0016_cicd_v2100",
122
122
  "breakpoints": true
123
+ },
124
+ {
125
+ "idx": 17,
126
+ "version": "6",
127
+ "when": 1784800000000,
128
+ "tag": "0017_git_remote_url",
129
+ "breakpoints": true
130
+ },
131
+ {
132
+ "idx": 18,
133
+ "version": "6",
134
+ "when": 1784900000000,
135
+ "tag": "0018_default_branch",
136
+ "breakpoints": true
137
+ },
138
+ {
139
+ "idx": 19,
140
+ "version": "6",
141
+ "when": 1785000000000,
142
+ "tag": "0019_git_provider",
143
+ "breakpoints": true
123
144
  }
124
145
  ]
125
146
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rashidee/co2",
3
- "version": "1.3.5",
3
+ "version": "1.3.6",
4
4
  "description": "Compound Context Studio — self-hosted team context authoring for CO2 projects",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -8,10 +8,15 @@ description: >
8
8
  `**Approved**:` line (stamped by Compound Context Studio's Approve button). Produces
9
9
  cicd/app/ — a zero-npm-dependency node:http app (server.js + cicd.config.json) rendering a
10
10
  bare light-themed page (no header/sidebar/footer, iframe-embeddable) with one nav tab per
11
- application: a controls bar (status badge, clickable app URL opening a new tab, Build /
12
- Start / Stop / Deploy buttons) above that application's log streamed live over a built-in
13
- zero-dependency WebSocket (bounded tail + backpressure drop server-side, trimmed DOM buffer
14
- client-side — no leaks/OOM). Build and Deploy commands come from the approved plan (inferred
11
+ application: a controls bar (status badge, clickable app URL opening a new tab, a Config
12
+ button, and Build / Start / Stop / Deploy buttons) above that application's log streamed live
13
+ over a built-in zero-dependency WebSocket (bounded tail + backpressure drop server-side, trimmed
14
+ DOM buffer client-side — no leaks/OOM). The Config button opens a modal listing the application's
15
+ runtime config files (from the plan's `## Application Configs`) — each with its source
16
+ (ENVIRONMENT.md-derived vs an existing .env/.env-* file), present/MISSING status, required keys
17
+ and full live content — for troubleshooting; and before Build/Start the app materializes any
18
+ missing plan-managed config file into the application folder so it always starts with complete
19
+ configuration. Build and Deploy commands come from the approved plan (inferred
15
20
  from ENVIRONMENT.md/DEVTOOL.md — e.g. Spring Boot jar vs Docker image; deploy via web-server
16
21
  copy, docker run or kubectl apply, or none). Controls are disabled for any application that
17
22
  has never completed a conductor-develop workflow run (checked against the studio's
@@ -58,12 +63,22 @@ Read `references/cicd-app-template.md` for the complete file templates. Produce:
58
63
 
59
64
  1. `cicd/app/cicd.config.json` — from the plan's tables:
60
65
  `{ "port": <CI/CD app port>, "apps": [{ "name", "folder", "method", "port", "build",
61
- "start", "stop", "deployMethod", "deploy", "health" }], "infra": [{ "id", "name", "host",
62
- "port", "url", "description" }] }` — `deployMethod` is the plan's Deploy Method column
66
+ "start", "stop", "deployMethod", "deploy", "health", "configs" }], "infra": [{ "id", "name",
67
+ "host", "port", "url", "description" }] }` — `deployMethod` is the plan's Deploy Method column
63
68
  (`none|web-server|docker|kubernetes`) and `deploy` its command (`null` when the method is
64
69
  `none`). The `infra` array comes from the plan's `## Infrastructure` table (one entry per
65
70
  row); `url` is `null` when the Console URL cell is `-`. Emit `"infra": []` when the plan's
66
71
  Infrastructure table is empty — never invent services the plan does not list.
72
+ - `configs` — the runtime config files each application reads at start, from the plan's
73
+ `## Application Configs` section (one entry per file):
74
+ `{ "path", "label", "source", "managed", "keys", "note", "content" }`. `source` is
75
+ `environment` | `env-file` | `derived`; `path` is relative to the application folder. When
76
+ `managed` is `true`, `content` is the plan's resolved file body (verbatim, credentials
77
+ included per the dev-plaintext rule) — the generated app writes it into the application folder
78
+ before Build/Start if the file is missing, so the app always starts with COMPLETE config. For
79
+ an `env-file` used as-is on disk, set `managed:false` and `content:null`. Copy the plan's
80
+ resolved `content` blocks verbatim — NEVER drop a managed file's content (that body is what
81
+ guarantees the app boots). Emit `"configs": []` for an app the plan lists no config files for.
67
82
  2. `cicd/app/server.js` — the template verbatim (it is generic; all project specifics live in
68
83
  cicd.config.json). Zero npm dependencies.
69
84
  3. Ensure `cicd/logs/` and `cicd/state.json` are ignored: append `cicd/logs/` and
@@ -97,6 +112,13 @@ port, application count) and STOP — the studio starts/embeds the app.
97
112
  - Develop gate: controls stay disabled for an application that never completed a
98
113
  conductor-develop run — checked via `${CO2_STUDIO_URL}/api/cicd-guard/${CO2_PROJECT_ID}`
99
114
  with the CO2_GUARD_TOKEN from the spawn env (refreshed every 30 s, last answer cached).
115
+ - Application config (troubleshooting + completeness): every application tab has a **Config**
116
+ button opening a modal that lists the app's runtime config files (from `configs`) — each with its
117
+ source badge (`environment`/`env-file`/`derived`), present/MISSING status, required keys and full
118
+ live on-disk content. The Config button is ALWAYS enabled (independent of the develop gate) so a
119
+ failed start-up can always be inspected. Before Build and before Start the server materializes any
120
+ `managed` config file whose `content` is set into the application folder when it is missing (never
121
+ overwriting a file already on disk), so an application never fails to come up for want of config.
100
122
  - Log streaming must stay bounded: per-app log file on disk, bounded tail on connect, live
101
123
  append broadcast only, slow subscribers dropped on backpressure, client DOM buffer trimmed.
102
124
  - Already-running detection: `/api/state` decides "is it running?" by a raw TCP connect to the
@@ -148,6 +148,48 @@ function runLogged(app, command, onExit) {
148
148
  return child
149
149
  }
150
150
 
151
+ // ---- Application config (runtime .env etc.) --------------------------------------------
152
+ // Each app declares the config files its start command reads (inferred by util-plancicd from
153
+ // ENVIRONMENT.md or an existing .env/.env-* file). A `managed` file carries the plan's resolved
154
+ // content and is materialized into the application folder before Build/Start when it is missing —
155
+ // so an application always starts with COMPLETE configuration. The Config modal shows every file's
156
+ // live on-disk content for troubleshooting. Paths are relative to the application folder.
157
+ const MAX_CONFIG_BYTES = 200_000
158
+ function configAbsPath(app, cfg) { return join(PROJECT_DIR, app.folder, cfg.path) }
159
+ function ensureConfigs(app) {
160
+ for (const cfg of app.configs || []) {
161
+ if (!cfg.managed || cfg.content == null) continue // env-file configs are used as-is on disk
162
+ const abs = configAbsPath(app, cfg)
163
+ if (existsSync(abs)) continue // never overwrite a config already on disk
164
+ try {
165
+ mkdirSync(join(abs, '..'), { recursive: true })
166
+ writeFileSync(abs, cfg.content)
167
+ logLine(app, `config: wrote ${cfg.path} from the plan (was missing)`)
168
+ } catch (e) {
169
+ logLine(app, `config: FAILED to write ${cfg.path} — ${e.message}`)
170
+ }
171
+ }
172
+ }
173
+ function readConfig(app, cfg) {
174
+ const abs = configAbsPath(app, cfg)
175
+ let exists = false, content = ''
176
+ try {
177
+ if (existsSync(abs)) {
178
+ exists = true
179
+ const buf = readFileSync(abs)
180
+ content = buf.length > MAX_CONFIG_BYTES
181
+ ? buf.slice(0, MAX_CONFIG_BYTES).toString('utf8') + '\n… (truncated)'
182
+ : buf.toString('utf8')
183
+ } else if (cfg.managed && cfg.content != null) {
184
+ content = cfg.content // planned content — not yet written to disk
185
+ }
186
+ } catch (e) { content = `(unreadable: ${e.message})` }
187
+ return {
188
+ path: cfg.path, label: cfg.label || cfg.path, source: cfg.source || 'env-file',
189
+ managed: !!cfg.managed, keys: cfg.keys || [], note: cfg.note || '', exists, content,
190
+ }
191
+ }
192
+
151
193
  // ---- App lifecycle ---------------------------------------------------------------------
152
194
  // Candidate loopback hosts to check for liveness: IPv4 (127.0.0.1) AND IPv6 (::1) loopback
153
195
  // always, plus whatever host the plan's health URL names. Frameworks bind different families —
@@ -187,6 +229,7 @@ function tcpProbe(host, port, timeout = 1000) {
187
229
  function buildApp(app) {
188
230
  if (activity.has(app.name)) return
189
231
  activity.set(app.name, 'building'); lastError.delete(app.name)
232
+ ensureConfigs(app)
190
233
  runLogged(app, app.build, (code) => {
191
234
  activity.delete(app.name)
192
235
  if (code !== 0) lastError.set(app.name, `build exited ${code} — see log`)
@@ -207,6 +250,7 @@ function deployApp(app) {
207
250
  function startApp(app) {
208
251
  if (running.get(app.name) || activity.has(app.name)) return
209
252
  failed.delete(app.name); lastError.delete(app.name)
253
+ ensureConfigs(app)
210
254
  if (app.method === 'container') {
211
255
  runLogged(app, app.start, () => {})
212
256
  saveState(); return
@@ -303,6 +347,19 @@ button:disabled{opacity:.4;cursor:default}
303
347
  .notice{padding:4px 12px;font-size:11px;color:#8a5a00;background:#fff7e6;display:none}
304
348
  .err{padding:4px 12px;font-size:11px;color:#c23b3b;display:none;white-space:pre-wrap}
305
349
  #log{flex:1;margin:0;padding:10px 12px;background:#0f1216;color:#d7dde4;font-size:11px;line-height:1.5;overflow:auto;white-space:pre-wrap}
350
+ .muted{color:#5b6572;font-size:12px}
351
+ .modal{position:fixed;inset:0;background:rgba(0,0,0,.45);display:none;align-items:center;justify-content:center;padding:24px;z-index:20}
352
+ .modal.open{display:flex}
353
+ .modal-panel{background:#fff;border-radius:10px;max-width:920px;width:100%;max-height:85vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 10px 40px rgba(0,0,0,.3)}
354
+ .modal-head{display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid #e1e5ea;font-size:13px}
355
+ .modal-body{padding:12px 14px;overflow:auto}
356
+ .cfg{margin-bottom:16px}
357
+ .cfg h3{font-size:13px;margin:0 0 4px;display:flex;gap:8px;align-items:center}
358
+ .cfg .src{font-size:10px;text-transform:uppercase;border-radius:99px;padding:2px 8px;background:#eef1f5;color:#5b6572}
359
+ .cfg .miss{color:#c23b3b;font-weight:600}
360
+ .cfg .ok{color:#1c8a4a;font-weight:600}
361
+ .cfg .keys{font-size:11px;color:#5b6572;margin:2px 0}
362
+ .cfg pre{background:#0f1216;color:#d7dde4;font-size:11px;padding:8px 10px;border-radius:6px;overflow:auto;max-height:320px;white-space:pre-wrap;margin:4px 0 0}
306
363
  </style></head><body>
307
364
  <div class="top"><strong>CI/CD</strong><span id="branch" class="meta"></span><span style="flex:1"></span><a id="infralink" class="url" href="/infra" target="_blank" rel="noreferrer" style="display:none">Infrastructure ↗</a></div>
308
365
  <div class="tabs" id="tabs"></div>
@@ -310,7 +367,14 @@ button:disabled{opacity:.4;cursor:default}
310
367
  <div class="notice" id="notice"></div>
311
368
  <div class="err" id="err"></div>
312
369
  <pre id="log"></pre>
370
+ <div class="modal" id="cfgModal" onclick="if(event.target===this)closeConfig()">
371
+ <div class="modal-panel">
372
+ <div class="modal-head"><strong id="cfgTitle">Configuration</strong><span style="flex:1"></span><button onclick="closeConfig()">Close</button></div>
373
+ <div class="modal-body" id="cfgBody"></div>
374
+ </div>
375
+ </div>
313
376
  <script>
377
+ function esc(s){ return String(s==null?'':s).replace(/[&<>"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c])) }
314
378
  let state = { apps: [] }
315
379
  let active = null
316
380
  let ws = null, wsTimer = null
@@ -334,6 +398,7 @@ function renderBar(){
334
398
  <span class="meta">\${a.method} · port \${a.port}</span>
335
399
  <a class="url" href="\${url}" target="_blank" rel="noreferrer">\${url}</a>
336
400
  <span style="flex:1"></span>
401
+ <button onclick="openConfig()" title="Show this application's runtime configuration files">Config</button>
337
402
  <button onclick="act('build')" \${!gate||busy||a.phase==='running'?'disabled':''}>Build</button>
338
403
  <button onclick="act('start')" \${!gate||busy||a.phase==='running'?'disabled':''}>Start</button>
339
404
  <button onclick="act('stop')" \${a.phase!=='running'?'disabled':''}>Stop</button>
@@ -378,6 +443,30 @@ function appendLog(text){
378
443
  if (atBottom) el.scrollTop = el.scrollHeight
379
444
  }
380
445
 
446
+ // Config modal — the runtime config files this application needs to start (from the plan). Shows
447
+ // each file's live on-disk content (or the planned content when a managed file is not yet written),
448
+ // its source (environment / env-file / derived), required keys and present/MISSING status — so the
449
+ // user can troubleshoot a start-up that is failing for want of complete configuration.
450
+ async function openConfig(){
451
+ if (!active) return
452
+ const modal = document.getElementById('cfgModal')
453
+ document.getElementById('cfgTitle').textContent = 'Configuration — '+active
454
+ document.getElementById('cfgBody').innerHTML = '<div class="muted">Loading…</div>'
455
+ modal.classList.add('open')
456
+ const s = await fetch('/api/apps/'+encodeURIComponent(active)+'/config').then(r=>r.json()).catch(()=>null)
457
+ const configs = (s && s.configs) || []
458
+ document.getElementById('cfgBody').innerHTML = configs.length ? configs.map(c => \`
459
+ <div class="cfg">
460
+ <h3>\${esc(c.label)} <span class="src">\${esc(c.source)}</span>
461
+ \${c.exists ? '<span class="ok">present</span>' : '<span class="miss">MISSING'+(c.managed&&c.content?' — created on Build/Start':'')+'</span>'}</h3>
462
+ \${c.keys&&c.keys.length ? '<div class="keys">Required keys: '+c.keys.map(esc).join(', ')+'</div>' : ''}
463
+ \${c.note ? '<div class="keys">'+esc(c.note)+'</div>' : ''}
464
+ <pre>\${esc(c.content) || '(empty)'}</pre>
465
+ </div>\`).join('') : '<div class="muted">No runtime configuration declared for this application.</div>'
466
+ }
467
+ function closeConfig(){ document.getElementById('cfgModal').classList.remove('open') }
468
+ document.addEventListener('keydown', e => { if (e.key === 'Escape') closeConfig() })
469
+
381
470
  async function refresh(){
382
471
  const s = await fetch('/api/state').then(r => r.json()).catch(() => null)
383
472
  if (!s) return
@@ -471,6 +560,12 @@ const server = http.createServer(async (req, res) => {
471
560
  }))
472
561
  return json(res, 200, { currentBranch: currentBranch(), hasInfra: (loadConfig().infra || []).length > 0, apps })
473
562
  }
563
+ const cfgm = req.url && req.url.match(/^\/api\/apps\/([^/]+)\/config$/)
564
+ if (cfgm && req.method === 'GET') {
565
+ const app = loadConfig().apps.find((a) => a.name === decodeURIComponent(cfgm[1]))
566
+ if (!app) return json(res, 404, { error: 'unknown app' })
567
+ return json(res, 200, { configs: (app.configs || []).map((c) => readConfig(app, c)) })
568
+ }
474
569
  const m = req.url && req.url.match(/^\/api\/apps\/([^/]+)\/(build|start|stop|deploy)$/)
475
570
  if (m && req.method === 'POST') {
476
571
  const app = loadConfig().apps.find((a) => a.name === decodeURIComponent(m[1]))
@@ -522,7 +617,18 @@ server.listen(PORT, () => console.log(`cicd app on :${PORT}`))
522
617
  Decisions table, one `infra` entry per row of the plan's `## Infrastructure` table.
523
618
  `deployMethod`/`deploy` come from the plan's Deploy columns (`deploy` is `null` when the method
524
619
  is `none`). `infra[].url` is `null` for a headless service (Console URL cell `-`); emit
525
- `"infra": []` when the plan declares no supporting services:
620
+ `"infra": []` when the plan declares no supporting services.
621
+
622
+ Each app also carries a `configs` array — the runtime config files the application reads at start,
623
+ from the plan's `## Application Configs` section (one entry per file). Fields:
624
+ `path` (relative to the application folder), `label`, `source` (`environment` | `env-file` |
625
+ `derived`), `managed` (when `true` the server writes `content` to `path` before Build/Start if the
626
+ file is missing, so start-up always has complete config), `keys` (the required keys the app needs
627
+ to boot), `note`, and `content` (the plan's resolved file body when `managed`; `null`/omitted for an
628
+ `env-file` used as-is on disk). Emit `"configs": []` when an application needs no runtime config
629
+ file. NEVER omit a `managed.content` that the plan resolved — that body is what guarantees the app
630
+ starts configured. Per the dev-environment plaintext rule, `content` may include credentials
631
+ verbatim (this is a local dev CI/CD app).
526
632
 
527
633
  ```json
528
634
  {
@@ -538,7 +644,27 @@ is `none`). `infra[].url` is `null` for a headless service (Console URL cell `-`
538
644
  "stop": "process-kill",
539
645
  "deployMethod": "kubernetes",
540
646
  "deploy": "kubectl apply -f my-app/k8s/",
541
- "health": "http://127.0.0.1:3000/"
647
+ "health": "http://127.0.0.1:3000/",
648
+ "configs": [
649
+ {
650
+ "path": ".env",
651
+ "label": ".env (runtime)",
652
+ "source": "environment",
653
+ "managed": true,
654
+ "keys": ["DATABASE_URL", "REDIS_HOST", "PORT"],
655
+ "note": "Resolved from ENVIRONMENT.md → my-app section",
656
+ "content": "DATABASE_URL=postgres://app:app@127.0.0.1:5432/appdb\nREDIS_HOST=127.0.0.1\nPORT=3000\n"
657
+ },
658
+ {
659
+ "path": ".env-uat",
660
+ "label": ".env-uat (existing)",
661
+ "source": "env-file",
662
+ "managed": false,
663
+ "keys": ["DATABASE_URL", "REDIS_HOST"],
664
+ "note": "Existing env file in the app folder — used as-is",
665
+ "content": null
666
+ }
667
+ ]
542
668
  }
543
669
  ],
544
670
  "infra": [
@@ -10,7 +10,12 @@ description: >
10
10
  method (container vs bare process), build command (e.g. Spring Boot jar vs Docker image),
11
11
  start command, stop mechanism, deploy method + command (none / web-server copy / docker run /
12
12
  kubectl apply — only when the environment calls for one), port and health-check URL, plus a
13
- port for the generated CI/CD app itself. It also enumerates the project's supporting
13
+ port for the generated CI/CD app itself. It also infers each application's RUNTIME CONFIG — the
14
+ config files its start command reads (e.g. `.env`), deciding per file whether the values come
15
+ from ENVIRONMENT.md or from an existing `.env` / `.env-*` file already in the app folder, listing
16
+ the required keys and, for ENVIRONMENT.md-sourced files, the resolved content — so the generated
17
+ CI/CD app can complete the config before start (guaranteeing the app comes up) and show it in a
18
+ per-application Config modal for troubleshooting. It also enumerates the project's supporting
14
19
  infrastructure (3rd-party services from CLAUDE.md's `# Supporting 3rd Party Applications` and
15
20
  ENVIRONMENT.md — host, port and optional console URL) into an `## Infrastructure` section so
16
21
  the generated CI/CD app can surface a status-only Infrastructure page. The plan carries a
@@ -47,6 +52,7 @@ If the argument is missing or the file does not exist, STOP and tell the user to
47
52
  | Dev tools | `DEVTOOL.md` (project root) |
48
53
  | Project context | `CLAUDE.md` (project root; `source/CLAUDE.md` fallback) |
49
54
  | App specification | `<app_folder>/context/specification/SPECIFICATION.md` |
55
+ | App config files | `<app_folder>/.env`, `<app_folder>/.env-*` (and stack-conventional config files) |
50
56
  | Infrastructure | `CLAUDE.md` `# Supporting 3rd Party Applications` + `ENVIRONMENT.md` |
51
57
  | Output plan | `cicd/CICD_PLAN.md` |
52
58
 
@@ -61,6 +67,13 @@ If the argument is missing or the file does not exist, STOP and tell the user to
61
67
  4. For each application, read its SPECIFICATION.md tech-stack section if present; otherwise
62
68
  infer the stack from build files (`package.json` → node, `pom.xml` → spring-boot,
63
69
  `composer.json` → laravel).
70
+ 4a. For each application, discover its RUNTIME CONFIG files: list any existing `.env` / `.env-*`
71
+ in the application folder AND the stack-conventional config the start command reads (node/
72
+ laravel → `.env`; Spring Boot → `application.properties` / `application-<profile>.yml` or a
73
+ `.env` when the app loads one). Read the content of each existing file (values may be reused).
74
+ Cross-reference ENVIRONMENT.md for the per-application config values (DB/cache/broker hosts and
75
+ ports, external-service URLs, credentials, secrets, the app's own PORT) and the Infrastructure
76
+ list from step 5 for the hosts/ports the app must point at.
64
77
  5. Build the **infrastructure list**: one entry per supporting 3rd-party service the project
65
78
  declares. For each, resolve `host` (default `127.0.0.1`), `port` and — when it exposes a web
66
79
  console/UI — a `url`, cross-referencing ENVIRONMENT.md for the actual host/port/endpoint. A
@@ -108,6 +121,35 @@ If the argument is missing or the file does not exist, STOP and tell the user to
108
121
  path is informational — detection keys on the port, not the path).
109
122
  - **CI/CD app port**: 4100 unless taken in `# Port Allocation`; record it in the plan.
110
123
 
124
+ ### Application config (per application)
125
+
126
+ Decide the runtime config the application needs so it STARTS SUCCESSFULLY, and where each value
127
+ comes from. For every config file the start command reads (`path`, relative to the application
128
+ folder):
129
+
130
+ - **source** — classify each file:
131
+ - `env-file` — an existing `.env` / `.env-*` in the app folder already holds the runtime values;
132
+ use it as-is on disk (do NOT re-materialize it). Set `managed: no`, no resolved content.
133
+ - `environment` — there is no complete runtime file; resolve the required values from
134
+ ENVIRONMENT.md (and the Infrastructure hosts/ports) and record the fully-resolved file content.
135
+ Set `managed: yes` so the CI/CD app writes it before Build/Start when missing.
136
+ - `derived` — the runtime file is chosen/combined from existing material (e.g. copy the target
137
+ env's `.env-<env>` to the canonical `.env` the start command reads, or merge an existing
138
+ `.env-*` with ENVIRONMENT.md values). Set `managed: yes` and record the resolved content.
139
+ - **required keys** — enumerate every key the app must have to boot: its own PORT, datastore/cache/
140
+ broker connection strings, external-service URLs, credentials/secrets. Cross-check them against
141
+ ENVIRONMENT.md and the Infrastructure list.
142
+ - **completeness (the point of this)** — every required key MUST be satisfiable from ENVIRONMENT.md
143
+ or an existing env file. If a required key cannot be resolved, still emit the config entry but
144
+ call out the unresolved key in that application's Rationale as a GAP (so the human fixes
145
+ ENVIRONMENT.md before approving) — never silently drop it.
146
+ - **managed content** — for a `managed: yes` file, write the resolved body verbatim into the plan
147
+ as a fenced block (`path=<file>`). Values (including credentials) are emitted verbatim under the
148
+ dev-environment plaintext rule; they must never appear in the Rationale narrative.
149
+
150
+ Only list files the app actually reads at runtime — do not invent config files. An app that needs
151
+ no config file gets an empty config list.
152
+
111
153
  ### Infrastructure (status-only)
112
154
 
113
155
  Supporting 3rd-party services are **not** built/started/deployed by the CI/CD app — they are
@@ -125,9 +167,10 @@ Never emit credentials into the infrastructure entries — status and links only
125
167
 
126
168
  ## Phase 3 — Write the plan
127
169
 
128
- Create `cicd/` if missing and write `cicd/CICD_PLAN.md`:
170
+ Create `cicd/` if missing and write `cicd/CICD_PLAN.md` (shown here in a 4-backtick fence so the
171
+ inner ```env config block renders — the plan file itself uses a normal 3-backtick env fence):
129
172
 
130
- ```markdown
173
+ ````markdown
131
174
  # CI/CD Deployment Plan — <Project Name>
132
175
 
133
176
  **Generated**: <ISO date>
@@ -149,6 +192,32 @@ Create `cicd/` if missing and write `cicd/CICD_PLAN.md`:
149
192
  > **Health** host — so an app is detected whichever family it binds. Use a `localhost` Health host
150
193
  > for Node/Vite/Next apps (they bind IPv6 `::1`) and `127.0.0.1` for JVM apps.
151
194
 
195
+ ## Application Configs
196
+
197
+ The runtime config each application needs to START SUCCESSFULLY. The generated CI/CD app writes any
198
+ `managed=yes` file into the application folder before Build/Start when it is missing, and shows
199
+ every file (source, status, required keys, live content) in the per-application Config modal for
200
+ troubleshooting. One table per application; `Path` is relative to the application folder.
201
+
202
+ ### <App Name> (<folder>)
203
+
204
+ | Path | Source | Managed | Required Keys |
205
+ |---|---|---|---|
206
+ | .env | environment | yes | DATABASE_URL, REDIS_HOST, PORT |
207
+ | .env-uat | env-file | no | DATABASE_URL, REDIS_HOST |
208
+
209
+ For each `Managed=yes` row, follow it with the resolved file body (verbatim — credentials allowed
210
+ under the dev-plaintext rule):
211
+
212
+ ```env path=.env
213
+ DATABASE_URL=postgres://app:app@127.0.0.1:5432/appdb
214
+ REDIS_HOST=127.0.0.1
215
+ PORT=3000
216
+ ```
217
+
218
+ Omit the fenced block for `env-file` rows (the file is used as-is on disk). If an application needs
219
+ no config file, write `_No runtime config files._` under its heading.
220
+
152
221
  ## Infrastructure
153
222
 
154
223
  Supporting 3rd-party services shown status-only (TCP reachability) on the CI/CD app's
@@ -161,7 +230,7 @@ Infrastructure page. Leave the table body empty when the project declares no sup
161
230
  ## Rationale
162
231
 
163
232
  - <per-application: why this method/commands, citing ENVIRONMENT.md/DEVTOOL.md evidence>
164
- ```
233
+ ````
165
234
 
166
235
  Then flip `**Status**: IN PROGRESS` to `**Status**: COMPLETED`. The studio watcher advances
167
236
  only on COMPLETED.
@@ -1 +1 @@
1
- import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-Do9cetyI.js";import{p as f}from"./chunk-JWPE2WC7-DbWRh-D_.js";import{_ as n,l as o}from"./index-BeEg-X0n.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-DJsuSCkt.js";var v=d().RailroadAbnf.parser.LangiumParser,l=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),i=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:i,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return l(e.element);case"AbnfOptionalGroup":return{type:"optional",element:l(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:l(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},w={parser:R,db:a,renderer:u,styles:p};export{w as diagram};
1
+ import{g as p,r as u,d as a}from"./chunk-MOJQB5TN-DCSpai7w.js";import{p as f}from"./chunk-JWPE2WC7-sOygQj_B.js";import{_ as n,l as o}from"./index-CCC4-Ibh.js";import{M as c,b as d}from"./cynefin-VYW2F7L2-CzQkWtdY.js";var v=d().RailroadAbnf.parser.LangiumParser,l=n(e=>{const r=e.alternatives.map(g);return r.length===1?r[0]:{type:"choice",alternatives:r}},"transformAlternation"),g=n(e=>{const r=e.elements.map(y);return r.length===1?r[0]:{type:"sequence",elements:r}},"transformConcatenation"),b=n(e=>{if(e.includes("*")){const[t,s]=e.split("*"),i=t?parseInt(t,10):0,m=s?parseInt(s,10):1/0;return{min:i,max:m}}const r=parseInt(e,10);return{min:r,max:r}},"parseRepeat"),y=n(e=>{const r=A(e.primary);if(!e.repeat)return r;const{min:t,max:s}=b(e.repeat);return t===0&&s===1?{type:"optional",element:r}:{type:"repetition",element:r,min:t,max:s}},"transformElement"),A=n(e=>{switch(e.$type){case"AbnfStringLiteral":return{type:"terminal",value:e.value};case"AbnfNumVal":return{type:"terminal",value:e.value};case"AbnfRuleName":return{type:"nonterminal",name:e.name};case"AbnfGroup":return l(e.element);case"AbnfOptionalGroup":return{type:"optional",element:l(e.element)};default:throw new Error(`Unsupported ABNF primary node: ${e.$type}`)}},"transformPrimary"),P=n(e=>({name:e.name,definition:l(e.definition)}),"transformRule"),h=n(e=>{f(e,a),e.title&&a.setTitle(e.title),e.rules.map(r=>a.addRule(P(r)))},"populateDb"),R={parse:n(e=>{a.clear(),o.debug("[ABNF Parser] Starting Langium parse");const r=v.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new c(r);const t=r.value;o.debug("[ABNF Parser] Parsed rules:",t.rules.length),h(t),o.debug("[ABNF Parser] Parse complete")},"parse"),parser:{yy:a}},w={parser:R,db:a,renderer:u,styles:p};export{w as diagram};
@@ -1 +1 @@
1
- import{Y as ln,$ as an,a0 as y,a1 as tn,a2 as F,a3 as q,a4 as _,a5 as un,a6 as rn,a7 as J,a8 as o,a9 as j,aa as sn,ab as on,ac as fn}from"./index-BeEg-X0n.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(fn(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-an,c=A.apply(this,arguments)-an,B=un(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(un(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<rn)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(on((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-rn/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
1
+ import{Y as ln,$ as an,a0 as y,a1 as tn,a2 as F,a3 as q,a4 as _,a5 as un,a6 as rn,a7 as J,a8 as o,a9 as j,aa as sn,ab as on,ac as fn}from"./index-CCC4-Ibh.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,z,a){var O=I-l,i=D-h,n=z-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function U(l,h,I,D,v,A,z){var a=l-I,O=h-D,i=(z?A:-A)/J(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,B=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*J(fn(0,T*T*R-P*P)),Y=(P*g-m*S)/R,$=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=Y-B,e=$-t,r=w-B,C=p-t;return x*x+e*e>r*r+C*C&&(Y=w,$=p),{cx:Y,cy:$,x01:-n,y01:-d,x11:Y*(v/T-1),y11:$*(v/T-1)}}function hn(){var l=cn,h=yn,I=j(0),D=null,v=gn,A=dn,z=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-an,c=A.apply(this,arguments)-an,B=un(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(B>tn-y)a.moveTo(s*F(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*F(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=B,S=B,Y=z.apply(this,arguments)/2,$=Y>y&&(D?+D.apply(this,arguments):J(u*u+s*s)),w=_(un(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if($>y){var C=sn($/u*q(Y)),K=sn($/s*q(Y));(P-=C*2)>y?(C*=t?1:-1,R+=C,T-=C):(P=0,R=T=(f+c)/2),(S-=K*2)>y?(K*=t?1:-1,m+=K,g-=K):(S=0,m=g=(f+c)/2)}var G=s*F(m),H=s*q(m),L=u*F(T),M=u*q(T);if(w>y){var N=s*F(g),Q=s*q(g),V=u*F(R),W=u*q(R),E;if(B<rn)if(E=pn(G,H,V,W,N,Q,L,M)){var X=G-E[0],Z=H-E[1],b=N-E[0],k=Q-E[1],nn=1/q(on((X*b+Z*k)/(J(X*X+Z*Z)*J(b*b+k*k)))/2),en=J(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=U(V,W,G,H,s,x,t),r=U(N,Q,L,M,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(G,H),a.arc(0,0,s,m,g,!t)):a.moveTo(G,H),!(u>y)||!(P>y)?a.lineTo(L,M):p>y?(e=U(L,M,N,Q,u,-p,t),r=U(G,H,V,W,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-rn/2;return[F(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:j(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:j(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:j(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:j(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:j(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:j(+n),i):A},i.padAngle=function(n){return arguments.length?(z=typeof n=="function"?n:j(+n),i):z},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};