@descix/cli 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/agent-assets/guides/README.md +1 -1
- package/agent-assets/guides/dynamic-site-pattern.md +7 -16
- package/agent-assets/instructions/app-asset-validation.md +1 -1
- package/agent-assets/reference/architecture.md +20 -25
- package/agent-assets/reference/local-dev.md +1 -6
- package/agent-assets/reference/mcp-integration.md +15 -9
- package/agent-assets/reference/platform-api-consumer.md +11 -5
- package/agent-assets/reference/workspace-config.md +2 -2
- package/bin/descix.js +211 -688
- package/lib/command-visibility.js +213 -0
- package/lib/commands/corpus.js +2 -5
- package/lib/commands/kb.js +2 -2
- package/lib/commands/media-upload.js +176 -0
- package/lib/commands/retired-kb-sync.js +5 -27
- package/lib/commands/status.js +0 -9
- package/lib/output.js +31 -0
- package/lib/sdk-assets.js +28 -0
- package/lib/verb-invokes.generated.json +233 -0
- package/lib/workspace-config.js +9 -44
- package/package.json +6 -2
- package/templates/agent-claude.md +1 -3
- package/templates/agent-cline.md +1 -2
- package/templates/agent-copilot.md +1 -2
- package/templates/agent-cursor.md +1 -2
- package/templates/scaffolds/microservice/SERVICE_README_sdk.md +1 -1
- package/templates/scaffolds/microservice/templates/SERVICE_README_TEMPLATE.md +2 -3
- package/agent-assets/rules/descix_mcp.template.mdc +0 -243
- package/lib/commands/briefer/errors.js +0 -88
- package/lib/commands/briefer/index.js +0 -322
- package/lib/commands/briefer/sources/canonical-sources.js +0 -129
- package/lib/commands/briefer/sources/entitlements.js +0 -220
- package/lib/commands/briefer/sources/environments.js +0 -205
- package/lib/commands/briefer/sources/identifiers.js +0 -214
- package/lib/commands/briefer/sources/microservice-deploy.js +0 -267
- package/lib/commands/briefer/sources/routing.js +0 -275
- package/lib/commands/briefer/sources/what-is-not.js +0 -223
- package/lib/commands/briefer/stitcher.js +0 -134
- package/lib/commands/briefer/util/source-reader.js +0 -502
- package/lib/commands/mcp.js +0 -273
- package/lib/commands/update.js +0 -581
- package/lib/wizard/setup.js +0 -585
- package/templates/descix_mcp.mdc +0 -122
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* §1 — Identifiers & invariants extractor (M2 implementation).
|
|
3
|
-
*
|
|
4
|
-
* Per WS-DESCIX-BRIEFER-CLI scope doc §2.2 §1, this extractor reads:
|
|
5
|
-
* - Domain pattern from provision-platform-lb.js:120 (host construction)
|
|
6
|
-
* - GCS path pattern from provision-platform-lb.js:123 (pathPrefix)
|
|
7
|
-
* - product_id === app_id invariant from hydrateUtils.js
|
|
8
|
-
* - Agentic-app / communities-as-apps invariant from the BEAST CEO record
|
|
9
|
-
* `apps/unk-beast/kb/Org/agentic-app-invariant-2026-05-26.jsonl`
|
|
10
|
-
* - the AI (model, thinking) precedence chain from geminiInteractions.js::resolveModelThinkingPair()
|
|
11
|
-
*
|
|
12
|
-
* Extraction method: regex+checksum-of-surrounding-lines (NOT AST).
|
|
13
|
-
* Per scope §9.1 third-order question, decision documented 2026-05-26: regex
|
|
14
|
-
* keeps the CLI dep-light; AST may be added per-extractor later if needed.
|
|
15
|
-
*
|
|
16
|
-
* HARD-FAIL behavior:
|
|
17
|
-
* - Missing source file → BRIEFER-SRC-NOT-FOUND
|
|
18
|
-
* - Regex pattern doesn't match → BRIEFER-SRC-NOT-FOUND
|
|
19
|
-
* - Pattern matches outside expected line range → BRIEFER-PARSE-FAIL
|
|
20
|
-
* - BEAST agentic-app record missing → BRIEFER-SRC-NOT-FOUND (a hard
|
|
21
|
-
* invariant — this is the CEO-broadcast canonical record)
|
|
22
|
-
*/
|
|
23
|
-
import {
|
|
24
|
-
readSourceFile,
|
|
25
|
-
findInLines,
|
|
26
|
-
makeCitation,
|
|
27
|
-
sliceRange
|
|
28
|
-
} from '../util/source-reader.js';
|
|
29
|
-
import { BrieferExtractorError, BRIEFER_ERROR_CODES } from '../errors.js';
|
|
30
|
-
// The (model, thinking) precedence chain in words, from its ONE owner. The briefer
|
|
31
|
-
// QUOTES the served contract rather than paraphrasing it — a briefer paraphrase of a
|
|
32
|
-
// chain is exactly how the old five-step list outlived the code it described.
|
|
33
|
-
import { MODEL_THINKING_CHAIN } from '@descix/platform-api/mcp-tools';
|
|
34
|
-
|
|
35
|
-
export const SECTION = {
|
|
36
|
-
number: 1,
|
|
37
|
-
heading: '1. Identifiers & invariants',
|
|
38
|
-
sourceFiles: [
|
|
39
|
-
'DeSciX/DeSciX_Cloud/microservice/admin/scripts/deploy/provision-platform-lb.js',
|
|
40
|
-
'DeSciX/DeSciX_Core/descix-platform-api/src/entitlements/index.js',
|
|
41
|
-
'DeSciX/DeSciX_Cloud/microservice/services/geminiInteractions.js',
|
|
42
|
-
'apps/unk-beast/kb/Org/agentic-app-invariant-2026-05-26.jsonl'
|
|
43
|
-
]
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const MESH_FILE = 'DeSciX/DeSciX_Cloud/microservice/admin/scripts/deploy/provision-platform-lb.js';
|
|
47
|
-
const HYDRATE_FILE = 'DeSciX/DeSciX_Core/descix-platform-api/src/entitlements/index.js';
|
|
48
|
-
const GEMINI_FILE = 'DeSciX/DeSciX_Cloud/microservice/services/geminiInteractions.js';
|
|
49
|
-
const AGENTIC_RECORD = 'apps/unk-beast/kb/Org/agentic-app-invariant-2026-05-26.jsonl';
|
|
50
|
-
|
|
51
|
-
export async function extract({ env, cliPaths } = {}) {
|
|
52
|
-
if (!env) {
|
|
53
|
-
throw new BrieferExtractorError({
|
|
54
|
-
code: BRIEFER_ERROR_CODES.NOT_IMPLEMENTED,
|
|
55
|
-
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
56
|
-
source: 'extractor argument',
|
|
57
|
-
expected: 'env (dev|demo|prod) provided by caller',
|
|
58
|
-
recovery: 'Pass {env} from the briefer entry point.'
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
if (!cliPaths) {
|
|
62
|
-
throw new BrieferExtractorError({
|
|
63
|
-
code: BRIEFER_ERROR_CODES.NOT_IMPLEMENTED,
|
|
64
|
-
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
65
|
-
source: 'extractor argument',
|
|
66
|
-
expected: 'cliPaths provided by caller',
|
|
67
|
-
recovery: 'Pass {cliPaths} from the briefer entry point.'
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ── Source 1: provision-platform-lb.js — domain + gcsPath patterns ──
|
|
72
|
-
const mesh = await readSourceFile({
|
|
73
|
-
cliPaths,
|
|
74
|
-
relPath: MESH_FILE,
|
|
75
|
-
section: `§${SECTION.number} ${SECTION.heading}`
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// Host pattern: canonical line shifted to L214 after WS-DESCIX-SINGLETON-PROVISIONER
|
|
79
|
-
// (CEO-D-2026-05-26-SINGLETON-PROVISIONER-UNIFIED, 2026-05-26) added the
|
|
80
|
-
// SINGLETON_APPS branch + buildSingletonMatcher helper near top of file. The
|
|
81
|
-
// line is the canonical UNIFORM host formula (which a singleton override then
|
|
82
|
-
// shadows via `effectiveHost` for daita) — briefer extractor still keys on
|
|
83
|
-
// this exact construct as the platform routing invariant. Range expanded to
|
|
84
|
-
// 100-350 to tolerate NEG helpers + matcher builders in provision-platform-lb.js.
|
|
85
|
-
const hostMatch = findInLines({
|
|
86
|
-
lines: mesh.lines,
|
|
87
|
-
regex: /const\s+host\s*=\s*env\s*===\s*['"]prod['"]\s*\?/,
|
|
88
|
-
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
89
|
-
source: `${MESH_FILE}:214 (uniform host pattern; CEO-D-2026-05-26-LB-ROUTING-UNIFORM)`,
|
|
90
|
-
expected: `const host = env === 'prod' ? ... : ... construct`,
|
|
91
|
-
recovery: `Re-locate the host construct in ${MESH_FILE} and update sources/identifiers.js.`,
|
|
92
|
-
expectedRange: [100, 350]
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// gcsPath pattern: canonical line shifted to L223 after WS-DESCIX-SINGLETON-PROVISIONER.
|
|
96
|
-
// This is the non-singleton (uniform) pathPrefix; the singleton override
|
|
97
|
-
// (pathPrefixSingletonOverride) is a few lines below. Range matches hostMatch.
|
|
98
|
-
const gcsMatch = findInLines({
|
|
99
|
-
lines: mesh.lines,
|
|
100
|
-
regex: /const\s+pathPrefix\s*=\s*`\/\$\{env\}\/\$\{app\}\/site`/,
|
|
101
|
-
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
102
|
-
source: `${MESH_FILE}:223 (uniform gcsPath pattern; singleton override at ~L228)`,
|
|
103
|
-
expected: 'const pathPrefix = `/${env}/${app}/site` (template literal)',
|
|
104
|
-
recovery: `Re-locate the gcsPath construct in ${MESH_FILE}.`,
|
|
105
|
-
expectedRange: [100, 350]
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// ── Source 2: hydrateUtils.js — product_id===app_id invariant ──
|
|
109
|
-
const hydrate = await readSourceFile({
|
|
110
|
-
cliPaths,
|
|
111
|
-
relPath: HYDRATE_FILE,
|
|
112
|
-
section: `§${SECTION.number} ${SECTION.heading}`
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
const hydrateMatch = findInLines({
|
|
116
|
-
lines: hydrate.lines,
|
|
117
|
-
regex: /hydrateCommunityIdFromProducts/,
|
|
118
|
-
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
119
|
-
source: `${HYDRATE_FILE} (hydrateCommunityIdFromProducts definition)`,
|
|
120
|
-
expected: 'hydrateCommunityIdFromProducts function declaration',
|
|
121
|
-
recovery: `Verify hydrateCommunityIdFromProducts is still exported from ${HYDRATE_FILE}.`
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// ── Source 3: geminiInteractions.js — the (model, thinking) pair owner ──
|
|
125
|
-
const gemini = await readSourceFile({
|
|
126
|
-
cliPaths,
|
|
127
|
-
relPath: GEMINI_FILE,
|
|
128
|
-
section: `§${SECTION.number} ${SECTION.heading}`
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
const resolverMatch = findInLines({
|
|
132
|
-
lines: gemini.lines,
|
|
133
|
-
regex: /export\s+function\s+resolveModelThinkingPair\b/,
|
|
134
|
-
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
135
|
-
source: `${GEMINI_FILE} (resolveModelThinkingPair)`,
|
|
136
|
-
expected: 'export function resolveModelThinkingPair(...)',
|
|
137
|
-
recovery: `resolveModelThinkingPair has been moved or renamed. Re-locate and update the extractor.`
|
|
138
|
-
});
|
|
139
|
-
// Capture the resolver body — from the export to the next closing brace at
|
|
140
|
-
// column 0. We'll scan forward a bounded window and slice for the briefer.
|
|
141
|
-
const resolverEnd = findResolverEnd(gemini.lines, resolverMatch.lineNumber);
|
|
142
|
-
const resolverBody = sliceRange(gemini.lines, resolverMatch.lineNumber, resolverEnd);
|
|
143
|
-
|
|
144
|
-
// ── Source 4: BEAST CEO record — agentic-app invariant ──
|
|
145
|
-
const beast = await readSourceFile({
|
|
146
|
-
cliPaths,
|
|
147
|
-
relPath: AGENTIC_RECORD,
|
|
148
|
-
section: `§${SECTION.number} ${SECTION.heading}`
|
|
149
|
-
});
|
|
150
|
-
// The record is JSONL; the canonical claim is one of the question/answer
|
|
151
|
-
// chunks. We don't parse the JSONL — we just verify the record EXISTS and
|
|
152
|
-
// SHA the whole file for citation purposes. The CEO broadcast title is the
|
|
153
|
-
// contract; the briefer prose embeds it verbatim.
|
|
154
|
-
const beastSha = makeCitation({
|
|
155
|
-
file: AGENTIC_RECORD,
|
|
156
|
-
lines: `1-${beast.lines.length}`,
|
|
157
|
-
anchor: 'AGENTIC-APP-INHERITANCE-INVARIANT-2026-05-26',
|
|
158
|
-
fileLines: beast.lines
|
|
159
|
-
}).sha;
|
|
160
|
-
|
|
161
|
-
// ── Render the briefer markdown ──
|
|
162
|
-
const hostLine = mesh.lines[hostMatch.lineNumber - 1].trim();
|
|
163
|
-
const gcsLine = mesh.lines[gcsMatch.lineNumber - 1].trim();
|
|
164
|
-
|
|
165
|
-
const markdown = [
|
|
166
|
-
`- **Apps are the central unit. Everything is an agentic app.** Communities are themselves agentic apps that share IP with other apps in the same community. The platform is fundamentally an agentic-app store, not a generic SaaS platform with apps bolted on. — \`${AGENTIC_RECORD}\` (CEO broadcast \`AGENTIC-APP-INHERITANCE-INVARIANT-2026-05-26\`, sha=\`${beastSha}\`)`,
|
|
167
|
-
`- \`app_id\` is a globally unique slug (e.g., \`daita\`, \`powch\`, \`egpt\`). Same value across DEV, DEMO, PROD. Never contains the environment.`,
|
|
168
|
-
`- \`community_id\` is operational metadata stored INSIDE \`Products/{app_id}\`. Never parsed from URL. Never trusted from client.`,
|
|
169
|
-
`- \`product_id === app_id\` — the same opaque slug. Server re-derives \`community_id\` from \`Products/{app_id}\` via \`hydrateCommunityIdFromProducts()\`. — \`${HYDRATE_FILE}:${hydrateMatch.lineNumber}\``,
|
|
170
|
-
`- Domain pattern: \`{app_id}.{env}.descix.net\` for DEV/DEMO; \`{app_id}.descix.net\` for PROD. — \`${MESH_FILE}:${hostMatch.lineNumber}\``,
|
|
171
|
-
' ```js',
|
|
172
|
-
` ${hostLine}`,
|
|
173
|
-
' ```',
|
|
174
|
-
`- GCS path: \`{env}/{app_id}/site/\`. Flat — no community in the path. — \`${MESH_FILE}:${gcsMatch.lineNumber}\``,
|
|
175
|
-
' ```js',
|
|
176
|
-
` ${gcsLine}`,
|
|
177
|
-
' ```',
|
|
178
|
-
`- Wildcard TLS cert: \`*.descix.net\` plus per-env wildcards. ONE cert. No per-app cert provisioning.`,
|
|
179
|
-
`- **AI (model, thinking) precedence.** ${MODEL_THINKING_CHAIN} Owner: \`resolveModelThinkingPair()\` in \`${GEMINI_FILE}:${resolverMatch.lineNumber}\` — the ONE resolver; the model chain and the thinking chain used to be separate and could assemble a pair no config declared.`,
|
|
180
|
-
' ```js',
|
|
181
|
-
indentBlock(resolverBody, ' '),
|
|
182
|
-
' ```'
|
|
183
|
-
].join('\n');
|
|
184
|
-
|
|
185
|
-
const citations = [
|
|
186
|
-
makeCitation({ file: MESH_FILE, lines: String(hostMatch.lineNumber), anchor: 'host pattern', fileLines: mesh.lines }),
|
|
187
|
-
makeCitation({ file: MESH_FILE, lines: String(gcsMatch.lineNumber), anchor: 'gcsPath pattern', fileLines: mesh.lines }),
|
|
188
|
-
makeCitation({ file: HYDRATE_FILE, lines: String(hydrateMatch.lineNumber), anchor: 'hydrateCommunityIdFromProducts', fileLines: hydrate.lines }),
|
|
189
|
-
makeCitation({ file: GEMINI_FILE, lines: `${resolverMatch.lineNumber}-${resolverEnd}`, anchor: 'resolveModelThinkingPair', fileLines: gemini.lines }),
|
|
190
|
-
{ file: AGENTIC_RECORD, lines: `1-${beast.lines.length}`, anchor: 'AGENTIC-APP-INHERITANCE-INVARIANT-2026-05-26', sha: beastSha }
|
|
191
|
-
];
|
|
192
|
-
|
|
193
|
-
return { markdown, citations };
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Walk forward from the `export function resolveModelThinkingPair` line until we hit
|
|
198
|
-
* a closing brace at column 0. Bounded scan window of 80 lines so a runaway
|
|
199
|
-
* regex on a refactored file doesn't drag the whole file into the briefer.
|
|
200
|
-
*/
|
|
201
|
-
function findResolverEnd(lines, startLine) {
|
|
202
|
-
const MAX = Math.min(lines.length, startLine + 80);
|
|
203
|
-
for (let i = startLine; i < MAX; i++) {
|
|
204
|
-
if (lines[i] === '}') return i + 1;
|
|
205
|
-
}
|
|
206
|
-
// If we don't find a clean close, fall back to startLine+30 — we want some
|
|
207
|
-
// body in the briefer even if the resolver was reformatted; --check will
|
|
208
|
-
// catch the SHA change.
|
|
209
|
-
return Math.min(lines.length, startLine + 30);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function indentBlock(block, indent) {
|
|
213
|
-
return block.split('\n').map(l => indent + l).join('\n');
|
|
214
|
-
}
|
|
@@ -1,267 +0,0 @@
|
|
|
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
|
-
}
|