@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.
- package/LICENSE.md +671 -0
- package/README.md +43 -4
- package/agent-assets/guides/README.md +2 -2
- package/agent-assets/guides/dynamic-site-pattern.md +29 -14
- package/agent-assets/guides/knowledge-only-pattern.md +5 -5
- package/agent-assets/guides/microservice-pattern.md +48 -4
- package/agent-assets/guides/static-site-pattern.md +8 -0
- package/agent-assets/instructions/app-asset-validation.md +5 -5
- package/agent-assets/instructions/onboarding-flow.md +4 -4
- package/agent-assets/instructions/standard-folder-structure.md +20 -11
- package/agent-assets/reference/app-creation.md +42 -20
- package/agent-assets/reference/architecture.md +24 -22
- package/agent-assets/reference/local-dev.md +230 -67
- package/agent-assets/reference/mcp-integration.md +21 -15
- package/agent-assets/reference/platform-api-consumer.md +244 -0
- package/agent-assets/reference/workspace-config.md +7 -7
- package/agent-assets/rules/descix_mcp.template.mdc +8 -8
- package/bin/descix.js +2559 -1435
- package/bin/kb-deny-lint.mjs +91 -0
- package/bin/mcp-server.js +175 -91
- package/lib/agent-files.js +37 -25
- package/lib/api-client.js +223 -56
- package/lib/auth-guard.js +13 -3
- package/lib/chat-session.js +187 -0
- package/lib/cli-version.js +59 -0
- package/lib/commands/airdrop.js +251 -0
- package/lib/commands/auth.js +254 -72
- package/lib/commands/briefer/errors.js +88 -0
- package/lib/commands/briefer/index.js +319 -0
- package/lib/commands/briefer/sources/canonical-sources.js +129 -0
- package/lib/commands/briefer/sources/entitlements.js +220 -0
- package/lib/commands/briefer/sources/environments.js +205 -0
- package/lib/commands/briefer/sources/identifiers.js +214 -0
- package/lib/commands/briefer/sources/microservice-deploy.js +267 -0
- package/lib/commands/briefer/sources/routing.js +275 -0
- package/lib/commands/briefer/sources/what-is-not.js +223 -0
- package/lib/commands/briefer/stitcher.js +134 -0
- package/lib/commands/briefer/util/source-reader.js +502 -0
- package/lib/commands/config.js +210 -90
- package/lib/commands/corpus.js +978 -0
- package/lib/commands/credits.js +122 -0
- package/lib/commands/health.js +627 -0
- package/lib/commands/kb-list-render.js +32 -0
- package/lib/commands/kb.js +190 -413
- package/lib/commands/mcp.js +5 -5
- package/lib/commands/microservice-restart.js +359 -0
- package/lib/commands/model-config.js +313 -0
- package/lib/commands/retired-kb-sync.js +134 -0
- package/lib/commands/serve.js +26 -1
- package/lib/commands/status.js +23 -51
- package/lib/commands/update.js +36 -107
- package/lib/core/Chunker.js +491 -17
- package/lib/core/CorpusDenyLint.js +326 -0
- package/lib/core/CorpusWalker.js +357 -0
- package/lib/core/Hydrator.js +75 -29
- package/lib/core/ManifestLoader.js +533 -0
- package/lib/core/SiteWalker.js +218 -0
- package/lib/core/Syncer.js +163 -11
- package/lib/diagnostic-log.js +41 -0
- package/lib/environment-report.js +100 -0
- package/lib/global-config.js +4 -1
- package/lib/oauth-client.js +164 -0
- package/lib/origin.js +143 -0
- package/lib/service-api-client.js +95 -0
- package/lib/wallet-environment.js +49 -0
- package/lib/wallet-file.js +16 -2
- package/lib/wizard/setup.js +10 -9
- package/lib/workspace-config.js +380 -235
- package/lib/workspace-identity.js +92 -0
- package/lib/workspace-utils.js +41 -61
- package/package.json +19 -6
- package/templates/agent-claude.md +157 -9
- package/templates/agent-cline.md +68 -8
- package/templates/agent-copilot.md +68 -8
- package/templates/agent-cursor.md +68 -8
- package/templates/alignment-schema.json +20 -0
- package/templates/cloud-brain-hook.sh +20 -0
- package/templates/drive/README.md +1 -1
- package/templates/drive_templates/README.md +2 -2
- package/templates/scaffolds/README.md +6 -10
- package/templates/scaffolds/microservice/SERVICE_README_sdk.md +104 -149
- package/templates/scaffolds/microservice/app.js +51 -10
- package/templates/scaffolds/microservice/app.yaml +2 -2
- package/templates/scaffolds/microservice/defaults-config-dev.json +4 -0
- package/templates/scaffolds/microservice/dev-overrides.example.json +5 -0
- package/templates/scaffolds/microservice/manifest.json +0 -1
- package/templates/scaffolds/microservice/package.json +3 -3
- package/templates/scaffolds/microservice/scripts/register.js +56 -41
- package/templates/scaffolds/microservice/services/mcpClient.js +55 -65
- package/templates/scaffolds/microservice/services/utils.js +20 -238
- package/templates/scaffolds/microservice/templates/SERVICE_README_TEMPLATE.md +24 -0
- package/lib/commands/app-wizard.js +0 -627
- package/lib/commands/folder.js +0 -291
- package/lib/core/PathContext.js +0 -335
- package/templates/scaffolds/microservice/dev-overrides.json.example +0 -5
- package/templates/scaffolds/site/DeSciXAppSDK.js +0 -62
- package/templates/scaffolds/site/README.md +0 -51
- package/templates/scaffolds/site/app.js +0 -22
- package/templates/scaffolds/site/index.html +0 -42
- package/templates/scaffolds/site/styles.css +0 -123
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §2 — Environments extractor (M2 implementation).
|
|
3
|
+
*
|
|
4
|
+
* Per WS-DESCIX-BRIEFER-CLI scope doc §2.2 §2, this extractor reads:
|
|
5
|
+
* - DEV/DEMO/PROD parallelism — architectural fact distilled from
|
|
6
|
+
* V2_docs/architecture/platform-runtime-mental-model.md (cite the doc).
|
|
7
|
+
* - ENV_CONFIG block from provision-platform-lb.js:32-47.
|
|
8
|
+
* - DEPLOY_ENV-bound-once hard-fail from descix-cloud-core/src/config.js
|
|
9
|
+
* (locate dynamically; canonical briefer line range is allowed to drift).
|
|
10
|
+
* - Secret Manager config name pattern from DeSciX_Cloud/CLAUDE.md (cite the
|
|
11
|
+
* doc since the values are env-keyed and live in env-vars/secrets, not
|
|
12
|
+
* code) — locate the "Secret Manager — Per-Environment Isolation" table.
|
|
13
|
+
*
|
|
14
|
+
* Optional gcloud verification: list Cloud Function names matching `apiFront-http`
|
|
15
|
+
* and Firestore database names matching `descix-{env}` / `descix`.
|
|
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: 2,
|
|
28
|
+
heading: '2. Environments',
|
|
29
|
+
sourceFiles: [
|
|
30
|
+
'DeSciX/V2_docs/architecture/platform-runtime-mental-model.md',
|
|
31
|
+
'DeSciX/DeSciX_Cloud/microservice/admin/scripts/deploy/provision-platform-lb.js',
|
|
32
|
+
'DeSciX/DeSciX_Core/descix-cloud-core/src/config.js',
|
|
33
|
+
'DeSciX/DeSciX_Cloud/CLAUDE.md'
|
|
34
|
+
]
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const RUNTIME_MM = 'DeSciX/V2_docs/architecture/platform-runtime-mental-model.md';
|
|
38
|
+
const MESH_FILE = 'DeSciX/DeSciX_Cloud/microservice/admin/scripts/deploy/provision-platform-lb.js';
|
|
39
|
+
const CONFIG_FILE = 'DeSciX/DeSciX_Core/descix-cloud-core/src/config.js';
|
|
40
|
+
const CLOUD_CLAUDE = 'DeSciX/DeSciX_Cloud/CLAUDE.md';
|
|
41
|
+
|
|
42
|
+
export async function extract({ env, cliPaths } = {}) {
|
|
43
|
+
if (!env) {
|
|
44
|
+
throw new BrieferExtractorError({
|
|
45
|
+
code: BRIEFER_ERROR_CODES.NOT_IMPLEMENTED,
|
|
46
|
+
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
47
|
+
source: 'extractor argument',
|
|
48
|
+
expected: 'env (dev|demo|prod) provided by caller',
|
|
49
|
+
recovery: 'Pass {env} from the briefer entry point.'
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (!cliPaths) {
|
|
53
|
+
throw new BrieferExtractorError({
|
|
54
|
+
code: BRIEFER_ERROR_CODES.NOT_IMPLEMENTED,
|
|
55
|
+
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
56
|
+
source: 'extractor argument',
|
|
57
|
+
expected: 'cliPaths provided by caller',
|
|
58
|
+
recovery: 'Pass {cliPaths} from the briefer entry point.'
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── Source 1: ENV_CONFIG in provision-platform-lb.js ──
|
|
63
|
+
const mesh = await readSourceFile({
|
|
64
|
+
cliPaths,
|
|
65
|
+
relPath: MESH_FILE,
|
|
66
|
+
section: `§${SECTION.number} ${SECTION.heading}`
|
|
67
|
+
});
|
|
68
|
+
const envConfigMatch = findInLines({
|
|
69
|
+
lines: mesh.lines,
|
|
70
|
+
regex: /const\s+ENV_CONFIG\s*=\s*\{/,
|
|
71
|
+
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
72
|
+
source: `${MESH_FILE} (ENV_CONFIG)`,
|
|
73
|
+
expected: 'const ENV_CONFIG = { prod: {...}, demo: {...}, dev: {...} }',
|
|
74
|
+
recovery: `Re-locate the ENV_CONFIG construct in ${MESH_FILE}.`,
|
|
75
|
+
expectedRange: [20, 60]
|
|
76
|
+
});
|
|
77
|
+
const envConfigEnd = findObjectEnd(mesh.lines, envConfigMatch.lineNumber);
|
|
78
|
+
const envConfigBlock = sliceRange(mesh.lines, envConfigMatch.lineNumber, envConfigEnd);
|
|
79
|
+
|
|
80
|
+
// ── Source 2: config.js — DEPLOY_ENV-bound-once hard-fail ──
|
|
81
|
+
const config = await readSourceFile({
|
|
82
|
+
cliPaths,
|
|
83
|
+
relPath: CONFIG_FILE,
|
|
84
|
+
section: `§${SECTION.number} ${SECTION.heading}`
|
|
85
|
+
});
|
|
86
|
+
const deployEnvBoundMatch = findInLines({
|
|
87
|
+
lines: config.lines,
|
|
88
|
+
regex: /FATAL:\s*DEPLOY_ENV\s+not\s+set/,
|
|
89
|
+
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
90
|
+
source: `${CONFIG_FILE} (DEPLOY_ENV bound-once hard-fail)`,
|
|
91
|
+
expected: 'throw with message starting "[CloudConfig] FATAL: DEPLOY_ENV not set"',
|
|
92
|
+
recovery: `Re-locate the DEPLOY_ENV hard-fail in ${CONFIG_FILE}.`
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// ── Source 3: runtime-mental-model.md doc cite (distilled architectural fact) ──
|
|
96
|
+
// We just verify the doc exists + record SHA of the canonical range. The doc
|
|
97
|
+
// is the source for "DEV/DEMO/PROD are fully parallel" because that's an
|
|
98
|
+
// assertion spread across many code paths — too noisy to grep.
|
|
99
|
+
const rmm = await readSourceFile({
|
|
100
|
+
cliPaths,
|
|
101
|
+
relPath: RUNTIME_MM,
|
|
102
|
+
section: `§${SECTION.number} ${SECTION.heading}`
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// ── Source 4: Cloud CLAUDE.md Secret Manager table ──
|
|
106
|
+
const claude = await readSourceFile({
|
|
107
|
+
cliPaths,
|
|
108
|
+
relPath: CLOUD_CLAUDE,
|
|
109
|
+
section: `§${SECTION.number} ${SECTION.heading}`
|
|
110
|
+
});
|
|
111
|
+
const secretTableMatch = findInLines({
|
|
112
|
+
lines: claude.lines,
|
|
113
|
+
regex: /descix_config_demo/,
|
|
114
|
+
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
115
|
+
source: `${CLOUD_CLAUDE} (Secret Manager Per-Environment Isolation table)`,
|
|
116
|
+
expected: 'descix_config_demo row in the per-env secret table',
|
|
117
|
+
recovery: `Re-locate the Secret Manager isolation table in ${CLOUD_CLAUDE}.`
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// ── M3: live gcloud probes (HARD-FAIL on demo/prod, skipped on dev) ──
|
|
121
|
+
const probeCitations = [];
|
|
122
|
+
let gcloudCfNote = '';
|
|
123
|
+
let gcloudFsNote = '';
|
|
124
|
+
|
|
125
|
+
if (env === 'dev') {
|
|
126
|
+
// DEV is local. Probe is intentionally skipped; emit a stanza so the
|
|
127
|
+
// operator knows live state was not verified for this regen.
|
|
128
|
+
gcloudCfNote = '\n_DEV: gcloud probes skipped (local dev environment — no Cloud Functions or Firestore to probe)._\n';
|
|
129
|
+
} else {
|
|
130
|
+
const cfProbe = await probeGcloudJson({
|
|
131
|
+
command: ['functions', 'list', '--v2', '--format=json'],
|
|
132
|
+
env,
|
|
133
|
+
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
134
|
+
anchor: 'cloud-functions-list',
|
|
135
|
+
expected: 'JSON array of gen2 Cloud Functions for project descix',
|
|
136
|
+
recovery: `Run 'gcloud auth login' and 'gcloud config set project descix'. The Cloud Functions list cross-check is required for env=${env}.`
|
|
137
|
+
});
|
|
138
|
+
const apiFrontCfs = (cfProbe.json || [])
|
|
139
|
+
.filter(f => /apiFront-http/.test(f.name || ''))
|
|
140
|
+
.map(f => (f.name || '').split('/').pop());
|
|
141
|
+
gcloudCfNote = `\n_Live Cloud Functions matching \`apiFront-http\` (probed \`${cfProbe.citation.probe}\`, env=\`${env}\`):_ ${apiFrontCfs.length > 0 ? apiFrontCfs.map(n => '\`' + n + '\`').join(', ') : '_(none found)_'}\n`;
|
|
142
|
+
probeCitations.push(cfProbe.citation);
|
|
143
|
+
|
|
144
|
+
const fsProbe = await probeGcloudJson({
|
|
145
|
+
command: ['firestore', 'databases', 'list', '--format=json'],
|
|
146
|
+
env,
|
|
147
|
+
section: `§${SECTION.number} ${SECTION.heading}`,
|
|
148
|
+
anchor: 'firestore-databases-list',
|
|
149
|
+
expected: 'JSON array of Firestore databases for project descix',
|
|
150
|
+
recovery: `Run 'gcloud auth login' and 'gcloud config set project descix'. The Firestore databases cross-check is required for env=${env}.`
|
|
151
|
+
});
|
|
152
|
+
const dbs = (fsProbe.json || [])
|
|
153
|
+
.map(d => (d.name || '').split('/').pop())
|
|
154
|
+
.filter(n => /^descix/.test(n));
|
|
155
|
+
gcloudFsNote = `\n_Live Firestore databases matching \`descix*\` (probed \`${fsProbe.citation.probe}\`, env=\`${env}\`):_ ${dbs.length > 0 ? dbs.map(n => '\`' + n + '\`').join(', ') : '_(none found)_'}\n`;
|
|
156
|
+
probeCitations.push(fsProbe.citation);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── Render markdown ──
|
|
160
|
+
const markdown = [
|
|
161
|
+
`DEV / DEMO / PROD are **fully parallel cloud deployments.** They co-exist; PROD does not replace DEMO. — \`${RUNTIME_MM}\``,
|
|
162
|
+
``,
|
|
163
|
+
`Per env, isolated:`,
|
|
164
|
+
`- Cloud Function: \`apiFront-http-{env}\` (and \`apiFront-http\` for PROD)`,
|
|
165
|
+
`- Firestore DB: \`descix-{env}\` (and \`descix\` for PROD)`,
|
|
166
|
+
`- Secret Manager config: \`descix_config_{env}\` (DEV/DEMO) or \`descix_config\` with version aliases (PROD/PREVIEW) — \`${CLOUD_CLAUDE}:${secretTableMatch.lineNumber}\``,
|
|
167
|
+
`- GCS bucket prefix: \`{env}/\``,
|
|
168
|
+
`- LB host suffix: \`.{env}.descix.net\` for DEV/DEMO, \`.descix.net\` for PROD`,
|
|
169
|
+
``,
|
|
170
|
+
`ENV_CONFIG (the deploy-time env table — \`${MESH_FILE}:${envConfigMatch.lineNumber}-${envConfigEnd}\`):`,
|
|
171
|
+
``,
|
|
172
|
+
'```js',
|
|
173
|
+
envConfigBlock,
|
|
174
|
+
'```',
|
|
175
|
+
``,
|
|
176
|
+
`One process serves exactly one env, determined by \`DEPLOY_ENV\`. The cloud-core bootstrap **hard-fails** at construct time if \`DEPLOY_ENV\` is unset — there is no default. — \`${CONFIG_FILE}:${deployEnvBoundMatch.lineNumber}\``,
|
|
177
|
+
gcloudCfNote,
|
|
178
|
+
gcloudFsNote
|
|
179
|
+
].join('\n');
|
|
180
|
+
|
|
181
|
+
const citations = [
|
|
182
|
+
makeCitation({ file: MESH_FILE, lines: `${envConfigMatch.lineNumber}-${envConfigEnd}`, anchor: 'ENV_CONFIG', fileLines: mesh.lines }),
|
|
183
|
+
makeCitation({ file: CONFIG_FILE, lines: String(deployEnvBoundMatch.lineNumber), anchor: 'DEPLOY_ENV bound-once', fileLines: config.lines }),
|
|
184
|
+
makeCitation({ file: RUNTIME_MM, lines: `1-${rmm.lines.length}`, anchor: 'DEV/DEMO/PROD parallelism', fileLines: rmm.lines }),
|
|
185
|
+
makeCitation({ file: CLOUD_CLAUDE, lines: String(secretTableMatch.lineNumber), anchor: 'Secret Manager per-env isolation', fileLines: claude.lines }),
|
|
186
|
+
...probeCitations
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
return { markdown, citations };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function findObjectEnd(lines, startLine) {
|
|
193
|
+
const MAX = Math.min(lines.length, startLine + 50);
|
|
194
|
+
let depth = 0;
|
|
195
|
+
for (let i = startLine - 1; i < MAX; i++) {
|
|
196
|
+
for (const ch of lines[i]) {
|
|
197
|
+
if (ch === '{') depth++;
|
|
198
|
+
else if (ch === '}') {
|
|
199
|
+
depth--;
|
|
200
|
+
if (depth === 0) return i + 1;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return Math.min(lines.length, startLine + 30);
|
|
205
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
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
|
+
}
|