@descix/cli 1.0.1 → 1.0.4

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 (100) hide show
  1. package/LICENSE.md +671 -0
  2. package/README.md +43 -4
  3. package/agent-assets/guides/README.md +2 -2
  4. package/agent-assets/guides/dynamic-site-pattern.md +29 -14
  5. package/agent-assets/guides/knowledge-only-pattern.md +5 -5
  6. package/agent-assets/guides/microservice-pattern.md +48 -4
  7. package/agent-assets/guides/static-site-pattern.md +8 -0
  8. package/agent-assets/instructions/app-asset-validation.md +5 -5
  9. package/agent-assets/instructions/onboarding-flow.md +4 -4
  10. package/agent-assets/instructions/standard-folder-structure.md +20 -11
  11. package/agent-assets/reference/app-creation.md +42 -20
  12. package/agent-assets/reference/architecture.md +24 -22
  13. package/agent-assets/reference/local-dev.md +230 -67
  14. package/agent-assets/reference/mcp-integration.md +21 -15
  15. package/agent-assets/reference/platform-api-consumer.md +244 -0
  16. package/agent-assets/reference/workspace-config.md +7 -7
  17. package/agent-assets/rules/descix_mcp.template.mdc +8 -8
  18. package/bin/descix.js +2559 -1435
  19. package/bin/kb-deny-lint.mjs +91 -0
  20. package/bin/mcp-server.js +175 -91
  21. package/lib/agent-files.js +37 -25
  22. package/lib/api-client.js +223 -56
  23. package/lib/auth-guard.js +13 -3
  24. package/lib/chat-session.js +187 -0
  25. package/lib/cli-version.js +59 -0
  26. package/lib/commands/airdrop.js +251 -0
  27. package/lib/commands/auth.js +254 -72
  28. package/lib/commands/briefer/errors.js +88 -0
  29. package/lib/commands/briefer/index.js +319 -0
  30. package/lib/commands/briefer/sources/canonical-sources.js +129 -0
  31. package/lib/commands/briefer/sources/entitlements.js +220 -0
  32. package/lib/commands/briefer/sources/environments.js +205 -0
  33. package/lib/commands/briefer/sources/identifiers.js +214 -0
  34. package/lib/commands/briefer/sources/microservice-deploy.js +267 -0
  35. package/lib/commands/briefer/sources/routing.js +275 -0
  36. package/lib/commands/briefer/sources/what-is-not.js +223 -0
  37. package/lib/commands/briefer/stitcher.js +134 -0
  38. package/lib/commands/briefer/util/source-reader.js +502 -0
  39. package/lib/commands/config.js +210 -90
  40. package/lib/commands/corpus.js +978 -0
  41. package/lib/commands/credits.js +122 -0
  42. package/lib/commands/health.js +627 -0
  43. package/lib/commands/kb-list-render.js +32 -0
  44. package/lib/commands/kb.js +190 -413
  45. package/lib/commands/mcp.js +5 -5
  46. package/lib/commands/microservice-restart.js +359 -0
  47. package/lib/commands/model-config.js +313 -0
  48. package/lib/commands/retired-kb-sync.js +134 -0
  49. package/lib/commands/serve.js +26 -1
  50. package/lib/commands/status.js +23 -51
  51. package/lib/commands/update.js +36 -107
  52. package/lib/core/Chunker.js +491 -17
  53. package/lib/core/CorpusDenyLint.js +326 -0
  54. package/lib/core/CorpusWalker.js +357 -0
  55. package/lib/core/Hydrator.js +75 -29
  56. package/lib/core/ManifestLoader.js +533 -0
  57. package/lib/core/SiteWalker.js +218 -0
  58. package/lib/core/Syncer.js +163 -11
  59. package/lib/diagnostic-log.js +41 -0
  60. package/lib/environment-report.js +100 -0
  61. package/lib/global-config.js +4 -1
  62. package/lib/oauth-client.js +164 -0
  63. package/lib/origin.js +143 -0
  64. package/lib/service-api-client.js +95 -0
  65. package/lib/wallet-environment.js +49 -0
  66. package/lib/wallet-file.js +16 -2
  67. package/lib/wizard/setup.js +10 -9
  68. package/lib/workspace-config.js +380 -235
  69. package/lib/workspace-identity.js +92 -0
  70. package/lib/workspace-utils.js +41 -61
  71. package/package.json +19 -6
  72. package/templates/agent-claude.md +157 -9
  73. package/templates/agent-cline.md +68 -8
  74. package/templates/agent-copilot.md +68 -8
  75. package/templates/agent-cursor.md +68 -8
  76. package/templates/alignment-schema.json +20 -0
  77. package/templates/cloud-brain-hook.sh +20 -0
  78. package/templates/drive/README.md +1 -1
  79. package/templates/drive_templates/README.md +2 -2
  80. package/templates/scaffolds/README.md +6 -10
  81. package/templates/scaffolds/microservice/SERVICE_README_sdk.md +104 -149
  82. package/templates/scaffolds/microservice/app.js +51 -10
  83. package/templates/scaffolds/microservice/app.yaml +2 -2
  84. package/templates/scaffolds/microservice/defaults-config-dev.json +4 -0
  85. package/templates/scaffolds/microservice/dev-overrides.example.json +5 -0
  86. package/templates/scaffolds/microservice/manifest.json +0 -1
  87. package/templates/scaffolds/microservice/package.json +3 -3
  88. package/templates/scaffolds/microservice/scripts/register.js +56 -41
  89. package/templates/scaffolds/microservice/services/mcpClient.js +55 -65
  90. package/templates/scaffolds/microservice/services/utils.js +20 -238
  91. package/templates/scaffolds/microservice/templates/SERVICE_README_TEMPLATE.md +24 -0
  92. package/lib/commands/app-wizard.js +0 -627
  93. package/lib/commands/folder.js +0 -291
  94. package/lib/core/PathContext.js +0 -335
  95. package/templates/scaffolds/microservice/dev-overrides.json.example +0 -5
  96. package/templates/scaffolds/site/DeSciXAppSDK.js +0 -62
  97. package/templates/scaffolds/site/README.md +0 -51
  98. package/templates/scaffolds/site/app.js +0 -22
  99. package/templates/scaffolds/site/index.html +0 -42
  100. package/templates/scaffolds/site/styles.css +0 -123
@@ -0,0 +1,267 @@
1
+ /**
2
+ * §4 — Microservice deploy extractor (M2 implementation).
3
+ *
4
+ * Per-app microservice deploy (broker-first model), CONSOLIDATED onto a
5
+ * single canonical path for both apps (WS-DEPLOY-HARDENING item 6 —
6
+ * Powch's divergent `microservice/scripts/deploy.sh` is now a hard-fail
7
+ * deprecation stub, not a second deploy implementation):
8
+ * 1. `gcloud run deploy {app}-{env}` — deploy-service-env.sh → lib/cloud-run-deploy.js
9
+ * 2. `descix microservice register` — Firestore manifest (or self-register on boot)
10
+ *
11
+ * No per-app LB/NEG step for non-core apps. Core platform app NEGs at env standup via provision-platform-lb.js.
12
+ */
13
+ import {
14
+ readSourceFile,
15
+ findInLines,
16
+ makeCitation,
17
+ sliceRange,
18
+ probeGcloudJson
19
+ } from '../util/source-reader.js';
20
+ import { BrieferExtractorError, BRIEFER_ERROR_CODES } from '../errors.js';
21
+
22
+ export const SECTION = {
23
+ number: 4,
24
+ heading: '4. Microservice deploy — current state',
25
+ sourceFiles: [
26
+ 'DeSciX/DeSciX_Powch/microservice/scripts/deploy.sh',
27
+ 'DeSciX/DeSciX_Cloud/microservice/admin/scripts/deploy/deploy-service-env.sh',
28
+ 'DeSciX/DeSciX_Cloud/microservice/admin/scripts/deploy/lib/cloud-run-deploy.js',
29
+ 'DeSciX/DeSciX_Cloud/microservice/admin/scripts/deploy/provision-platform-lb.js',
30
+ 'DeSciX/DeSciX_Core/descix-cli/bin/descix.js'
31
+ ]
32
+ };
33
+
34
+ const POWCH_DEPLOY = 'DeSciX/DeSciX_Powch/microservice/scripts/deploy.sh';
35
+ const CLOUD_DEPLOY = 'DeSciX/DeSciX_Cloud/microservice/admin/scripts/deploy/deploy-service-env.sh';
36
+ const CLOUD_RUN_LIB = 'DeSciX/DeSciX_Cloud/microservice/admin/scripts/deploy/lib/cloud-run-deploy.js';
37
+ const LB_FILE = 'DeSciX/DeSciX_Cloud/microservice/admin/scripts/deploy/provision-platform-lb.js';
38
+ const CLI_FILE = 'DeSciX/DeSciX_Core/descix-cli/bin/descix.js';
39
+
40
+ export async function extract({ env, cliPaths } = {}) {
41
+ if (!env) {
42
+ throw new BrieferExtractorError({
43
+ code: BRIEFER_ERROR_CODES.NOT_IMPLEMENTED,
44
+ section: `§${SECTION.number} ${SECTION.heading}`,
45
+ source: 'extractor argument',
46
+ expected: 'env (dev|demo|prod) provided by caller',
47
+ recovery: 'Pass {env} from the briefer entry point.'
48
+ });
49
+ }
50
+ if (!cliPaths) {
51
+ throw new BrieferExtractorError({
52
+ code: BRIEFER_ERROR_CODES.NOT_IMPLEMENTED,
53
+ section: `§${SECTION.number} ${SECTION.heading}`,
54
+ source: 'extractor argument',
55
+ expected: 'cliPaths provided by caller',
56
+ recovery: 'Pass {cliPaths} from the briefer entry point.'
57
+ });
58
+ }
59
+
60
+ // ── Step 1: gcloud run deploy — Cloud canonical lib is the SOLE source of
61
+ // this fact. Powch's per-app deploy.sh was RETIRED to a hard-fail
62
+ // DEPRECATION stub (WS-DEPLOY-HARDENING item 6) — deploy is now
63
+ // CONSOLIDATED onto deploy-service-env.sh → cloud-run-deploy.js for both
64
+ // apps. We still read the Powch file, but only to ground-truth its
65
+ // retirement marker (fail-loud if the stub itself drifts) — not to
66
+ // extract a second `gcloud run deploy` invocation. ──
67
+ const powch = await readSourceFile({
68
+ cliPaths,
69
+ relPath: POWCH_DEPLOY,
70
+ section: `§${SECTION.number} ${SECTION.heading}`
71
+ });
72
+ const powchRun = findInLines({
73
+ lines: powch.lines,
74
+ regex: /^echo\s+"DEPRECATED:/,
75
+ section: `§${SECTION.number} ${SECTION.heading}`,
76
+ source: `${POWCH_DEPLOY} (retirement stub marker)`,
77
+ expected: 'echo "DEPRECATED: ..." — the WS-DEPLOY-HARDENING item 6 retirement stub',
78
+ recovery: `Re-locate the DEPRECATED marker in ${POWCH_DEPLOY}. If the script has been deleted outright, remove this Powch reference from the §4 extractor entirely (single canonical deploy path already covered by CLOUD_RUN_LIB).`
79
+ });
80
+ const powchDeployEndMatch = findInLines({
81
+ lines: powch.lines,
82
+ regex: /^exit\s+1/,
83
+ section: `§${SECTION.number} ${SECTION.heading}`,
84
+ source: `${POWCH_DEPLOY} (retirement stub exit)`,
85
+ expected: 'exit 1 — the stub hard-fails rather than deploying',
86
+ recovery: `Re-locate the exit 1 in ${POWCH_DEPLOY}.`
87
+ });
88
+ const powchDeployEnd = powchDeployEndMatch.lineNumber;
89
+ const powchDeployBlock = sliceRange(powch.lines, powchRun.lineNumber, powchDeployEnd);
90
+
91
+ // Cloud-side deploy: bash script shells out to shared lib (deployToCloudRun)
92
+ const cloud = await readSourceFile({
93
+ cliPaths,
94
+ relPath: CLOUD_DEPLOY,
95
+ section: `§${SECTION.number} ${SECTION.heading}`
96
+ });
97
+ findInLines({
98
+ lines: cloud.lines,
99
+ regex: /deployToCloudRun/,
100
+ section: `§${SECTION.number} ${SECTION.heading}`,
101
+ source: `${CLOUD_DEPLOY} (deployToCloudRun import)`,
102
+ expected: 'deploy-service-env.sh invokes deployToCloudRun from lib/cloud-run-deploy.js',
103
+ recovery: `Re-locate deployToCloudRun in ${CLOUD_DEPLOY}.`
104
+ });
105
+
106
+ const cloudRunLib = await readSourceFile({
107
+ cliPaths,
108
+ relPath: CLOUD_RUN_LIB,
109
+ section: `§${SECTION.number} ${SECTION.heading}`
110
+ });
111
+ const cloudRun = findInLines({
112
+ lines: cloudRunLib.lines,
113
+ regex: /gcloud run deploy \$\{serviceName\}/,
114
+ section: `§${SECTION.number} ${SECTION.heading}`,
115
+ source: `${CLOUD_RUN_LIB} (gcloud run deploy invocation)`,
116
+ expected: 'gcloud run deploy ${serviceName} ... in cloud-run-deploy.js',
117
+ recovery: `Re-locate the gcloud run deploy in ${CLOUD_RUN_LIB}.`
118
+ });
119
+ const cloudDeployEnd = findContinuationEnd(cloudRunLib.lines, cloudRun.lineNumber);
120
+ const cloudDeployBlock = sliceRange(cloudRunLib.lines, cloudRun.lineNumber, cloudDeployEnd);
121
+
122
+ // Platform LB (env standup only — not per-app)
123
+ const lb = await readSourceFile({
124
+ cliPaths,
125
+ relPath: LB_FILE,
126
+ section: `§${SECTION.number} ${SECTION.heading}`
127
+ });
128
+ const ensureCoreNegMatch = findInLines({
129
+ lines: lb.lines,
130
+ regex: /function\s+ensureCoreNeg\s*\(/,
131
+ section: `§${SECTION.number} ${SECTION.heading}`,
132
+ source: `${LB_FILE} (ensureCoreNeg)`,
133
+ expected: 'function ensureCoreNeg(envCfg, dryRun)',
134
+ recovery: `Re-locate ensureCoreNeg in ${LB_FILE}.`
135
+ });
136
+
137
+ // ── Step 2: descix microservice register CLI command ──
138
+ const cli = await readSourceFile({
139
+ cliPaths,
140
+ relPath: CLI_FILE,
141
+ section: `§${SECTION.number} ${SECTION.heading}`
142
+ });
143
+ // Per-line scan: find `.command('register')` whose preceding non-empty line
144
+ // contains `microserviceCommand`. Disambiguates from program.command('register-delegate')
145
+ // which also exists.
146
+ let registerLine = -1;
147
+ for (let i = 1; i < cli.lines.length; i++) {
148
+ if (/\.command\(['"]register['"]\)/.test(cli.lines[i])) {
149
+ // Look back up to 3 lines for a `microserviceCommand` anchor.
150
+ for (let j = i - 1; j >= Math.max(0, i - 3); j--) {
151
+ if (/microserviceCommand/.test(cli.lines[j])) {
152
+ registerLine = i + 1;
153
+ break;
154
+ }
155
+ }
156
+ if (registerLine !== -1) break;
157
+ }
158
+ }
159
+ if (registerLine === -1) {
160
+ throw new BrieferExtractorError({
161
+ code: BRIEFER_ERROR_CODES.SRC_NOT_FOUND,
162
+ section: `§${SECTION.number} ${SECTION.heading}`,
163
+ source: `${CLI_FILE}:3080-3236 (descix microservice register)`,
164
+ expected: "microserviceCommand followed by .command('register') in bin/descix.js",
165
+ recovery: `Re-locate the microservice register command in ${CLI_FILE}.`,
166
+ detail: "Scanned bin/descix.js — no line matches `.command('register')` within 3 lines of `microserviceCommand`."
167
+ });
168
+ }
169
+ const registerMatch = { lineNumber: registerLine };
170
+
171
+ // KNOWN GAP toggle: scan for `.command('deploy')` whose preceding 3 lines
172
+ // contain a `microserviceCommand` anchor (mirror the register-detect pattern).
173
+ // If found, the gap is closed; if absent, gap text is rendered.
174
+ let gapToggleClosed = false;
175
+ for (let i = 1; i < cli.lines.length; i++) {
176
+ if (/\.command\(['"]deploy['"]\)/.test(cli.lines[i])) {
177
+ for (let j = i - 1; j >= Math.max(0, i - 3); j--) {
178
+ if (/microserviceCommand/.test(cli.lines[j])) {
179
+ gapToggleClosed = true;
180
+ break;
181
+ }
182
+ }
183
+ if (gapToggleClosed) break;
184
+ }
185
+ }
186
+
187
+ // ── M3: live Cloud Run probe (HARD-FAIL on demo/prod, skipped on dev) ──
188
+ const probeCitations = [];
189
+ let runProbeNote = '';
190
+ if (env === 'dev') {
191
+ runProbeNote = '\n_DEV: gcloud probes skipped (local dev — no Cloud Run services to probe)._\n';
192
+ } else {
193
+ const runProbe = await probeGcloudJson({
194
+ command: ['run', 'services', 'list', '--region=us-central1', `--filter=metadata.name~^.*-${env}$`, '--format=json'],
195
+ env,
196
+ section: `§${SECTION.number} ${SECTION.heading}`,
197
+ anchor: 'cloud-run-services-list',
198
+ expected: `JSON array of Cloud Run services matching \`-${env}$\` in us-central1`,
199
+ recovery: `Run 'gcloud auth login' and 'gcloud config set project descix'. The Cloud Run services cross-check is required for env=${env}.`
200
+ });
201
+ const services = (runProbe.json || [])
202
+ .map(svc => ({
203
+ name: (svc.metadata && svc.metadata.name) || '(unknown)',
204
+ region: (svc.metadata && svc.metadata.labels && svc.metadata.labels['cloud.googleapis.com/location']) || 'us-central1'
205
+ }));
206
+ const names = services.map(s => '\`' + s.name + '\`').join(', ');
207
+ runProbeNote = `\n**Live Cloud Run state** (probed \`${runProbe.citation.probe}\`, env=\`${env}\`):\n- Services found: \`${services.length}\`\n- Names: ${names || '_(none)_'}\n`;
208
+ probeCitations.push(runProbe.citation);
209
+ }
210
+
211
+ // ── Render the markdown ──
212
+ const lines = [
213
+ `Today (regen target: \`${env}\`), per-app microservice deploy is **2 steps** (broker-first — no NEG for non-core apps):`,
214
+ ``,
215
+ `**Step 1.** \`gcloud run deploy {app}-{env} ...\` — deploys the Cloud Run service (via \`deploy-service-env.sh\` → \`lib/cloud-run-deploy.js\`; CLI: \`descix microservice deploy\`). This is now the SOLE deploy path for **both** apps — Powch's own \`deploy.sh\` was retired to a hard-fail deprecation stub (WS-DEPLOY-HARDENING item 6) that points callers at this canonical path.`,
216
+ `- Reference (Cloud canonical — sole source of the \`gcloud run deploy\` fact): \`${CLOUD_RUN_LIB}:${cloudRun.lineNumber}\``,
217
+ `- Reference (Powch retirement stub, not a second deploy path): \`${POWCH_DEPLOY}:${powchRun.lineNumber}\``,
218
+ ``,
219
+ 'Verbatim from `' + CLOUD_RUN_LIB + '` (shared deploy lib):',
220
+ ``,
221
+ '```bash',
222
+ cloudDeployBlock,
223
+ '```',
224
+ ``,
225
+ `**Step 2.** \`descix microservice register\` — writes manifest to Firestore \`ServiceManifests\` so Core dispatches via \`proxyToExternalService\`. Self-registration on boot is equivalent. — \`${CLI_FILE}:${registerMatch.lineNumber}\``,
226
+ ``,
227
+ `**Platform LB (core platform apps at standup):** \`deploy-backend-env.sh\` → \`provision-platform-lb.js\` — daita broker NEG + powch NEG + apex/peer URL map. \`deploy-service-env.sh powch\` re-runs provision for powch NEG. — \`${LB_FILE}:${ensureCoreNegMatch.lineNumber}\``,
228
+ ``
229
+ ];
230
+
231
+ if (gapToggleClosed) {
232
+ lines.push(
233
+ `**GAP CLOSED:** \`descix microservice deploy\` exists in the CLI as of this regen — it chains the three steps above. (Detected via grep on \`bin/descix.js\`.)`
234
+ );
235
+ } else {
236
+ lines.push(
237
+ `**KNOWN GAP:** there is NO \`descix microservice deploy\` command that chains all three. Step 2 today is a manual admin-script invocation, not a CLI subcommand. Closing this gap is part of WS-DESCIX-BRIEFER-CLI scope (or a sibling workstream). _Detected dynamically: grep on \`bin/descix.js\` returned no \`microserviceCommand.command('deploy')\` declaration._`
238
+ );
239
+ }
240
+ if (runProbeNote) lines.push(runProbeNote);
241
+
242
+ const markdown = lines.join('\n');
243
+
244
+ const citations = [
245
+ makeCitation({ file: POWCH_DEPLOY, lines: `${powchRun.lineNumber}-${powchDeployEnd}`, anchor: 'DEPRECATED stub (Powch — consolidated onto Cloud canonical path)', fileLines: powch.lines }),
246
+ makeCitation({ file: CLOUD_RUN_LIB, lines: `${cloudRun.lineNumber}-${cloudDeployEnd}`, anchor: 'gcloud run deploy (Cloud canonical lib)', fileLines: cloudRunLib.lines }),
247
+ makeCitation({ file: CLOUD_DEPLOY, lines: 'deployToCloudRun', anchor: 'deploy-service-env.sh → lib', fileLines: cloud.lines }),
248
+ makeCitation({ file: LB_FILE, lines: String(ensureCoreNegMatch.lineNumber), anchor: 'provision-platform-lb.js ensureCoreNeg', fileLines: lb.lines }),
249
+ makeCitation({ file: CLI_FILE, lines: String(registerMatch.lineNumber), anchor: 'descix microservice register', fileLines: cli.lines }),
250
+ ...probeCitations
251
+ ];
252
+
253
+ return { markdown, citations };
254
+ }
255
+
256
+ /**
257
+ * For shell line-continuations (`\\` at line-end), walk forward until the
258
+ * continuation chain ends. Returns the line number of the LAST line in the
259
+ * block (1-indexed).
260
+ */
261
+ function findContinuationEnd(lines, startLine) {
262
+ let i = startLine - 1;
263
+ while (i < lines.length - 1 && /\\\s*$/.test(lines[i])) {
264
+ i++;
265
+ }
266
+ return i + 1;
267
+ }
@@ -0,0 +1,275 @@
1
+ /**
2
+ * §3 — Request routing extractor (M2 implementation).
3
+ *
4
+ * This is THE highest-scrutiny section per the 2026-05-25 audit. The earlier
5
+ * cruft about "Gateway extracts productId + Firestore lookup" lives in V2_docs
6
+ * prose; this extractor pulls the CANONICAL routing model from code:
7
+ * - provision-platform-lb.js — platform LB (core NEG + apex/peer hosts)
8
+ * - serviceManifestManager.js — proxyToExternalService dispatch
9
+ * - apiFront.js — invoke() dispatch (internal + external command split)
10
+ * - app-sdk dev/gateway.js + createViteProxyConfig.js — local dev mirror
11
+ * - Live `gcloud compute url-maps describe descix-discord-app-lb`
12
+ * when gcloud is available and --env=demo|prod
13
+ *
14
+ * --env=dev is HARD-REJECTED in this extractor (per scope §2.1) — DEV has no
15
+ * LB URL map, so routing facts from gcloud cannot be verified.
16
+ */
17
+ import {
18
+ readSourceFile,
19
+ findInLines,
20
+ makeCitation,
21
+ sliceRange,
22
+ probeGcloudJson
23
+ } from '../util/source-reader.js';
24
+ import { BrieferExtractorError, BRIEFER_ERROR_CODES } from '../errors.js';
25
+
26
+ export const SECTION = {
27
+ number: 3,
28
+ heading: '3. Request routing — the single canonical model',
29
+ sourceFiles: [
30
+ 'DeSciX/DeSciX_Cloud/microservice/admin/scripts/deploy/provision-platform-lb.js',
31
+ 'DeSciX/DeSciX_Cloud/microservice/services/serviceManifestManager.js',
32
+ 'DeSciX/DeSciX_Cloud/microservice/services/apiFront.js',
33
+ 'DeSciX/DeSciX_Core/descix-app-sdk/src/dev/gateway.js',
34
+ 'DeSciX/DeSciX_Core/descix-app-sdk/src/dev/createViteProxyConfig.js'
35
+ ]
36
+ };
37
+
38
+ const LB_FILE = 'DeSciX/DeSciX_Cloud/microservice/admin/scripts/deploy/provision-platform-lb.js';
39
+ const SMM_FILE = 'DeSciX/DeSciX_Cloud/microservice/services/serviceManifestManager.js';
40
+ const APIFRONT_FILE = 'DeSciX/DeSciX_Cloud/microservice/services/apiFront.js';
41
+ const GATEWAY_FILE = 'DeSciX/DeSciX_Core/descix-app-sdk/src/dev/gateway.js';
42
+ const PROXY_CONFIG_FILE = 'DeSciX/DeSciX_Core/descix-app-sdk/src/dev/createViteProxyConfig.js';
43
+
44
+ export async function extract({ env, cliPaths } = {}) {
45
+ if (!env) {
46
+ throw new BrieferExtractorError({
47
+ code: BRIEFER_ERROR_CODES.NOT_IMPLEMENTED,
48
+ section: `§${SECTION.number} ${SECTION.heading}`,
49
+ source: 'extractor argument',
50
+ expected: 'env (demo|prod) provided by caller',
51
+ recovery: 'Pass {env} from the briefer entry point.'
52
+ });
53
+ }
54
+ // M2 hard-reject --env=dev for the routing extractor specifically.
55
+ // Per scope §2.1: DEV does not have an LB URL map, querying it would
56
+ // produce a malformed briefer. Other extractors that don't need gcloud
57
+ // may still accept dev; this rejection lives in routing.js.
58
+ if (env === 'dev') {
59
+ throw new BrieferExtractorError({
60
+ code: BRIEFER_ERROR_CODES.PARSE_FAIL,
61
+ section: `§${SECTION.number} ${SECTION.heading}`,
62
+ source: 'env argument',
63
+ expected: 'demo or prod (the envs with a live LB URL map)',
64
+ recovery: 'DEV has no LB URL map; use --env=demo or --env=prod for routing extraction. Other sections may run with --env=dev independently.',
65
+ detail: `Got --env=dev. DEV has no descix-discord-app-lb URL map to verify routing against.`
66
+ });
67
+ }
68
+ if (!cliPaths) {
69
+ throw new BrieferExtractorError({
70
+ code: BRIEFER_ERROR_CODES.NOT_IMPLEMENTED,
71
+ section: `§${SECTION.number} ${SECTION.heading}`,
72
+ source: 'extractor argument',
73
+ expected: 'cliPaths provided by caller',
74
+ recovery: 'Pass {cliPaths} from the briefer entry point.'
75
+ });
76
+ }
77
+
78
+ // ── Source 1: provision-platform-lb.js — platform LB standup ──
79
+ const lb = await readSourceFile({
80
+ cliPaths,
81
+ relPath: LB_FILE,
82
+ section: `§${SECTION.number} ${SECTION.heading}`
83
+ });
84
+
85
+ const ensureCoreNegMatch = findInLines({
86
+ lines: lb.lines,
87
+ regex: /function\s+ensureCoreNeg\s*\(/,
88
+ section: `§${SECTION.number} ${SECTION.heading}`,
89
+ source: `${LB_FILE} (ensureCoreNeg)`,
90
+ expected: 'function ensureCoreNeg(envCfg, dryRun)',
91
+ recovery: `Re-locate ensureCoreNeg in ${LB_FILE}.`,
92
+ expectedRange: [80, 140]
93
+ });
94
+
95
+ const singletonMatcherMatch = findInLines({
96
+ lines: lb.lines,
97
+ regex: /function\s+buildSingletonMatcher\s*\(/,
98
+ section: `§${SECTION.number} ${SECTION.heading}`,
99
+ source: `${LB_FILE} (buildSingletonMatcher)`,
100
+ expected: 'function buildSingletonMatcher(...)',
101
+ recovery: `Re-locate buildSingletonMatcher in ${LB_FILE}.`
102
+ });
103
+
104
+ const apiFrontRuleMatch = findInLines({
105
+ lines: lb.lines,
106
+ regex: /['"`]\/apifront['"`]/,
107
+ section: `§${SECTION.number} ${SECTION.heading}`,
108
+ source: `${LB_FILE} (command-broker /apifront rule)`,
109
+ expected: `'/apifront' in buildCommandBrokerRules`,
110
+ recovery: `Re-locate the /apifront command-broker rule.`
111
+ });
112
+
113
+ // ── Source 2: serviceManifestManager.js — proxyToExternalService ──
114
+ const smm = await readSourceFile({
115
+ cliPaths,
116
+ relPath: SMM_FILE,
117
+ section: `§${SECTION.number} ${SECTION.heading}`
118
+ });
119
+
120
+ const proxyMatch = findInLines({
121
+ lines: smm.lines,
122
+ regex: /export\s+async\s+function\s+proxyToExternalService\s*\(/,
123
+ section: `§${SECTION.number} ${SECTION.heading}`,
124
+ source: `${SMM_FILE}:685-765 (proxyToExternalService)`,
125
+ expected: 'export async function proxyToExternalService(commandName, params, sessionToken = null, req = null)',
126
+ recovery: `Re-locate proxyToExternalService in ${SMM_FILE}.`,
127
+ expectedRange: [650, 800]
128
+ });
129
+
130
+ // ── Source 3: apiFront.js — invoke() dispatch ──
131
+ const apiFront = await readSourceFile({
132
+ cliPaths,
133
+ relPath: APIFRONT_FILE,
134
+ section: `§${SECTION.number} ${SECTION.heading}`
135
+ });
136
+
137
+ const invokeMatch = findInLines({
138
+ lines: apiFront.lines,
139
+ regex: /static\s+async\s+invoke\s*\(\s*commandName/,
140
+ section: `§${SECTION.number} ${SECTION.heading}`,
141
+ source: `${APIFRONT_FILE}:83-186 (CommandHandler.invoke + _invokeExternalCommand)`,
142
+ expected: 'static async invoke(commandName, params, req, res)',
143
+ recovery: `Re-locate CommandHandler.invoke in ${APIFRONT_FILE}.`,
144
+ expectedRange: [50, 200]
145
+ });
146
+ const externalDispatchMatch = findInLines({
147
+ lines: apiFront.lines,
148
+ regex: /static\s+async\s+_invokeExternalCommand\s*\(/,
149
+ section: `§${SECTION.number} ${SECTION.heading}`,
150
+ source: `${APIFRONT_FILE} (_invokeExternalCommand)`,
151
+ expected: 'static async _invokeExternalCommand(commandName, params, req)',
152
+ recovery: `Re-locate _invokeExternalCommand in ${APIFRONT_FILE}.`
153
+ });
154
+
155
+ // ── Source 4: app-sdk gateway.js + createViteProxyConfig.js ──
156
+ const gateway = await readSourceFile({
157
+ cliPaths,
158
+ relPath: GATEWAY_FILE,
159
+ section: `§${SECTION.number} ${SECTION.heading}`
160
+ });
161
+ const proxyConfig = await readSourceFile({
162
+ cliPaths,
163
+ relPath: PROXY_CONFIG_FILE,
164
+ section: `§${SECTION.number} ${SECTION.heading}`
165
+ });
166
+
167
+ // ── M3: live gcloud probe (HARD-FAIL on demo/prod, skipped on dev) ──
168
+ // routing.js already hard-rejects --env=dev above (no LB URL map in DEV),
169
+ // so this probe ALWAYS runs for demo/prod and MUST succeed. probeGcloudJson
170
+ // throws BRIEFER-GCLOUD-FAIL with an actionable recovery message otherwise.
171
+ let gcloudBlock = '';
172
+ let gcloudCitation = null;
173
+ const lbProbe = await probeGcloudJson({
174
+ command: ['compute', 'url-maps', 'describe', 'descix-discord-app-lb', '--format=json'],
175
+ env,
176
+ section: `§${SECTION.number} ${SECTION.heading}`,
177
+ anchor: 'lb-url-map',
178
+ expected: 'JSON describing the descix-discord-app-lb URL map',
179
+ recovery: `Run 'gcloud auth login' and 'gcloud config set project descix'. If the URL map does not exist for env=${env}, that is a real platform-state issue — investigate before regenerating the briefer.`
180
+ });
181
+ // lbProbe is non-null for demo/prod (dev is rejected upstream).
182
+ const hostRuleCount = (lbProbe.json.hostRules || []).length;
183
+ const pathMatcherCount = (lbProbe.json.pathMatchers || []).length;
184
+ const fingerprint = lbProbe.json.fingerprint || 'unknown';
185
+ gcloudBlock = [
186
+ '',
187
+ '**Live LB state** (live `gcloud compute url-maps describe descix-discord-app-lb` at regen time):',
188
+ `- Host rules: \`${hostRuleCount}\``,
189
+ `- Path matchers: \`${pathMatcherCount}\``,
190
+ `- URL map fingerprint: \`${fingerprint}\``,
191
+ `- Probed: \`${lbProbe.citation.probe}\` (env=\`${env}\`)`,
192
+ ''
193
+ ].join('\n');
194
+ gcloudCitation = lbProbe.citation;
195
+
196
+ const singletonBlockEnd = findFunctionEnd(lb.lines, singletonMatcherMatch.lineNumber);
197
+ const singletonBlock = sliceRange(lb.lines, singletonMatcherMatch.lineNumber, singletonBlockEnd);
198
+
199
+ // ── Render the section markdown ──
200
+ const markdown = [
201
+ `Broker-first mesh (WS-MESH-BROKER-ONLY): sites call \`/apifront\`; Core validates entitlements and dispatches via Firestore \`ServiceManifests\`. **No NEG on routine deploy for non-core apps.** No runtime LB lookup for new apps.`,
202
+ ``,
203
+ `Platform LB (\`descix-discord-app-lb\`) is provisioned at platform standup by \`deploy-backend-env.sh\` → \`provision-platform-lb.js\` (re-run after \`deploy-service-env.sh powch\` for powch NEG):`,
204
+ ``,
205
+ `- **daita** broker NEG: \`apifront-http-{env}-neg\` → \`{env}-api-backend\` → Cloud Function \`apiFront-http-{env}\` — \`${LB_FILE}:${ensureCoreNegMatch.lineNumber}\``,
206
+ `- Apex singleton (\`daita\`): \`demo.descix.net\` / \`descix.net\` → GCS \`/{env}/daita/site/\` + \`/apifront\`, \`/mcp\`, \`/api\` → daita broker — \`${LB_FILE}:${singletonMatcherMatch.lineNumber}\``,
207
+ `- **powch** core platform NEG: \`powch-{env}-neg\` → \`powch-{env}-backend\` → Cloud Run \`powch-{env}\` — \`${LB_FILE} (ensureCloudRunAppNeg)\``,
208
+ `- Platform peer host (\`powch.{env}.descix.net\`): GCS site + \`/apifront\`, \`/mcp\` → daita broker; \`/api/*\` → powch NEG`,
209
+ ``,
210
+ `Singleton matcher construction (verbatim from \`${LB_FILE}\` L${singletonMatcherMatch.lineNumber}-${singletonBlockEnd}):`,
211
+ ``,
212
+ '```js',
213
+ singletonBlock,
214
+ '```',
215
+ ``,
216
+ `Runtime dispatch: central CF reads \`externalCommandRegistry\` and calls \`proxyToExternalService()\`. — \`${SMM_FILE}:${proxyMatch.lineNumber}\`, \`${APIFRONT_FILE}:${invokeMatch.lineNumber} (invoke), ${externalDispatchMatch.lineNumber} (_invokeExternalCommand)\``,
217
+ gcloudBlock,
218
+ `**Local \`descix serve\` mirror:** \`${GATEWAY_FILE}\` + \`${PROXY_CONFIG_FILE}\` proxy \`/apifront\`, \`/api\`, \`/mcp\` to Core. Product sites at \`/p/{appId}/\` or static plugin routes.`
219
+ ].join('\n');
220
+
221
+ const citations = [
222
+ makeCitation({ file: LB_FILE, lines: `${ensureCoreNegMatch.lineNumber}-${singletonBlockEnd}`, anchor: 'ensureCoreNeg + buildSingletonMatcher', fileLines: lb.lines }),
223
+ makeCitation({ file: LB_FILE, lines: String(apiFrontRuleMatch.lineNumber), anchor: 'command-broker /apifront', fileLines: lb.lines }),
224
+ makeCitation({ file: SMM_FILE, lines: String(proxyMatch.lineNumber), anchor: 'proxyToExternalService', fileLines: smm.lines }),
225
+ makeCitation({ file: APIFRONT_FILE, lines: String(invokeMatch.lineNumber), anchor: 'CommandHandler.invoke', fileLines: apiFront.lines }),
226
+ makeCitation({ file: APIFRONT_FILE, lines: String(externalDispatchMatch.lineNumber), anchor: '_invokeExternalCommand', fileLines: apiFront.lines }),
227
+ makeCitation({ file: GATEWAY_FILE, lines: `1-${gateway.lines.length}`, anchor: 'local serve gateway', fileLines: gateway.lines }),
228
+ makeCitation({ file: PROXY_CONFIG_FILE, lines: `1-${proxyConfig.lines.length}`, anchor: 'vite proxy rules', fileLines: proxyConfig.lines }),
229
+ gcloudCitation
230
+ ].filter(Boolean);
231
+
232
+ return { markdown, citations };
233
+ }
234
+
235
+ /**
236
+ * Walk forward from `const pathRules = [` until the matching `];`.
237
+ * Bounded to 100 lines so a pathological refactor doesn't drag huge swaths.
238
+ */
239
+ function findFunctionEnd(lines, startLine) {
240
+ const MAX = Math.min(lines.length, startLine + 80);
241
+ let depth = 0;
242
+ let started = false;
243
+ for (let i = startLine - 1; i < MAX; i++) {
244
+ const line = lines[i];
245
+ for (const ch of line) {
246
+ if (ch === '{') { depth++; started = true; }
247
+ else if (ch === '}') {
248
+ depth--;
249
+ if (started && depth === 0) return i + 1;
250
+ }
251
+ }
252
+ }
253
+ return Math.min(lines.length, startLine + 40);
254
+ }
255
+
256
+ function findArrayEnd(lines, startLine) {
257
+ const MAX = Math.min(lines.length, startLine + 100);
258
+ let depth = 0;
259
+ for (let i = startLine - 1; i < MAX; i++) {
260
+ const line = lines[i];
261
+ for (const ch of line) {
262
+ if (ch === '[') depth++;
263
+ else if (ch === ']') {
264
+ depth--;
265
+ if (depth === 0) return i + 1;
266
+ }
267
+ }
268
+ }
269
+ return Math.min(lines.length, startLine + 50);
270
+ }
271
+
272
+ function shortenGcloudErr(msg) {
273
+ if (!msg) return 'unknown error';
274
+ return msg.split('\n')[0].slice(0, 120);
275
+ }