archgraph-argo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +115 -0
  3. package/argo/package.json +8 -0
  4. package/argo/rules/intent-architecture-global-rule.md +45 -0
  5. package/argo/schema/ImplementationToCodingHandoff.schema.json +252 -0
  6. package/argo/schema/ImplementationToIntentTraceProposal.schema.json +180 -0
  7. package/argo/schema/IntentToImplementationHandoff.schema.json +75 -0
  8. package/argo/schema/SystemArchitecture.schema.json +378 -0
  9. package/argo/schema/archimate3.2.pdf +0 -0
  10. package/argo/scripts/ARCHITECTURE.md +57 -0
  11. package/argo/scripts/archimate32-rules.js +12301 -0
  12. package/argo/scripts/argo-mcp-server.js +629 -0
  13. package/argo/scripts/argo-paths.js +77 -0
  14. package/argo/scripts/ensureArgoHarnessEnvironment.js +340 -0
  15. package/argo/scripts/generateArchitectureDiffPlantuml.js +466 -0
  16. package/argo/scripts/graph-rag/ARCHITECTURE.md +192 -0
  17. package/argo/scripts/graph-rag/canonicalProjectionAuthority.js +45 -0
  18. package/argo/scripts/graph-rag/defaultSemanticRetrieval.js +969 -0
  19. package/argo/scripts/graph-rag/embeddingQualificationGate.js +59 -0
  20. package/argo/scripts/graph-rag/externalProductionConfig.js +74 -0
  21. package/argo/scripts/graph-rag/liveEmbeddingIndexGate.js +129 -0
  22. package/argo/scripts/graph-rag/liveEmbeddingNeo4jBoundary.js +137 -0
  23. package/argo/scripts/graph-rag/liveEmbeddingProviderClient.js +49 -0
  24. package/argo/scripts/graph-rag/liveEmbeddingProviderConfig.js +481 -0
  25. package/argo/scripts/graph-rag/mutationEmbeddingVectorLifecycle.js +1261 -0
  26. package/argo/scripts/graph-rag/neo4jNativeRetrieval.js +37 -0
  27. package/argo/scripts/graph-rag/productionGraphRagRuntime.js +1624 -0
  28. package/argo/scripts/graph-rag/semantic-persistence/ARCHITECTURE.md +51 -0
  29. package/argo/scripts/graph-rag/semantic-persistence/productionSemanticBackfill.js +241 -0
  30. package/argo/scripts/graph-rag/semantic-persistence/productionSemanticCheckpointStore.js +99 -0
  31. package/argo/scripts/graph-rag/semantic-persistence/productionSemanticNeo4jAdapter.js +149 -0
  32. package/argo/scripts/graph-rag/semantic-persistence/productionSemanticProjectionStore.js +171 -0
  33. package/argo/scripts/graph-rag/semanticOperatorError.js +38 -0
  34. package/argo/scripts/graph-rag/semanticOperatorJourney.js +459 -0
  35. package/argo/scripts/graph-rag/semanticReadinessAttestationStore.js +398 -0
  36. package/argo/scripts/graph-rag/systemMetadataCommandAdapter.js +269 -0
  37. package/argo/scripts/graph-semantics.js +220 -0
  38. package/argo/scripts/neo4j-system-architecture-store.js +777 -0
  39. package/argo/scripts/repositoryArgoEnvironment.js +101 -0
  40. package/argo/scripts/runArchitectureTests.js +583 -0
  41. package/argo/scripts/semanticOperatorJourneyCli.js +91 -0
  42. package/argo/scripts/syncSystemArchitectureToNeo4j.js +67 -0
  43. package/argo/scripts/systemarchitecture-mcp-server.js +2965 -0
  44. package/argo/scripts/test-executors/_template.js +58 -0
  45. package/argo/scripts/test-executors/default.js +199 -0
  46. package/argo/scripts/validateStageHandoff.js +459 -0
  47. package/argo/scripts/validateSystemArchitecture.js +254 -0
  48. package/argo/scripts/validateTraceProposal.js +181 -0
  49. package/argo/scripts/validator-mcp-server.js +377 -0
  50. package/argo/skills/argo-init/SKILL.md +110 -0
  51. package/bin/argo-deploy.js +12 -0
  52. package/install-argo.ps1 +112 -0
  53. package/package.json +28 -0
  54. package/vendor/neo4j-driver-6.2.0.tgz +0 -0
@@ -0,0 +1,481 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+ const {
4
+ createSystemMetadataCommandAdapter,
5
+ } = require('./systemMetadataCommandAdapter.js');
6
+ const {
7
+ getArgoEnvPath,
8
+ } = require('../argo-paths.js');
9
+
10
+ const CONFIG_KEYS = Object.freeze([
11
+ 'ARGO_EMBEDDING_BASE_URL',
12
+ 'ARGO_EMBEDDING_MODEL',
13
+ 'ARGO_EMBEDDING_PROVIDER',
14
+ 'ARGO_EMBEDDING_MODEL_VERSION',
15
+ 'ARGO_EMBEDDING_DIMENSIONS',
16
+ 'ARGO_NEO4J_DATABASE_URL',
17
+ 'ARGO_NEO4J_DATABASE_USERNAME',
18
+ 'ARGO_NEO4J_DATABASE_PASSWORD',
19
+ 'QWEN_KEY',
20
+ ]);
21
+ const OPTIONAL_CONFIG_KEYS = Object.freeze([
22
+ 'ARGO_NEO4J_DATABASE',
23
+ ]);
24
+ const OPT_IN_KEYS = Object.freeze({
25
+ ARGO_LIVE_PROVIDER_E2E: 'LIVE_PROVIDER_E2E_OPT_IN_REQUIRED',
26
+ ARGO_W31_LIVE_MUTATION_VECTOR_E2E: 'W31_MUTATION_VECTOR_E2E_OPT_IN_REQUIRED',
27
+ });
28
+ const READABLE_KEYS = Object.freeze([
29
+ ...CONFIG_KEYS,
30
+ ...OPTIONAL_CONFIG_KEYS,
31
+ ...Object.keys(OPT_IN_KEYS),
32
+ ]);
33
+ const LEGACY_KEYS = Object.freeze(['ARGO_NEO4J_URI', 'ARGO_NEO4J_USERNAME', 'ARGO_NEO4J_PASSWORD']);
34
+ const PROHIBITED_RUNTIME_FIELD_KEYS = Object.freeze(['neo4jUri', 'embeddingCredential']);
35
+ const SECRET_KEYS = new Set(['ARGO_NEO4J_DATABASE_PASSWORD', 'QWEN_KEY']);
36
+ const APPROVED = Object.freeze({
37
+ ARGO_EMBEDDING_BASE_URL: 'https://llm-clids9mqc5o1mbvb.cn-beijing.maas.aliyuncs.com/compatible-mode/v1',
38
+ ARGO_EMBEDDING_MODEL: 'qwen3.7-text-embedding',
39
+ ARGO_EMBEDDING_PROVIDER: 'alibaba-cloud-model-studio-openai-compatible-cn-beijing',
40
+ ARGO_EMBEDDING_MODEL_VERSION: 'qualification-2026-07-25',
41
+ ARGO_EMBEDDING_DIMENSIONS: '1024',
42
+ });
43
+ const issuedAdapters = new WeakSet();
44
+ const issuedTraces = new WeakSet();
45
+ const invalidTraces = new WeakSet();
46
+ const prohibitedTraces = new WeakSet();
47
+
48
+ async function resolveApprovedLiveConfiguration(options) {
49
+ requireProductionOptions(options);
50
+ const repositoryRoot = requireRoot(options.repositoryRoot);
51
+ return resolveTrusted({
52
+ repositoryRoot,
53
+ requiredOptIns: resolveRequiredOptIns(options),
54
+ adapters: {
55
+ filesystem: fs,
56
+ systemMetadata: createSystemMetadataCommandAdapter({ repositoryRoot }),
57
+ },
58
+ source: createTrustedSource({
59
+ behavior: productionSourceBehavior(repositoryRoot),
60
+ }),
61
+ });
62
+ }
63
+
64
+ async function withApprovedLiveConfigurationTestComposition(
65
+ { sourceBehavior, adapters = {}, observeTrace },
66
+ callback,
67
+ ) {
68
+ if (!sourceBehavior || typeof callback !== 'function') throw safeError('SOURCE_ADAPTER_UNTRUSTED');
69
+ const source = createTrustedSource({ behavior: sourceBehavior, observeTrace });
70
+ const resolver = Object.freeze(options => resolveTrusted({
71
+ repositoryRoot: requireRoot(options && options.repositoryRoot),
72
+ requiredOptIns: resolveRequiredOptIns(options),
73
+ adapters,
74
+ source,
75
+ }));
76
+ return callback(resolver);
77
+ }
78
+
79
+ async function resolveTrusted({
80
+ repositoryRoot,
81
+ requiredOptIns = ['ARGO_LIVE_PROVIDER_E2E'],
82
+ adapters,
83
+ source,
84
+ }) {
85
+ if (!issuedAdapters.has(source)) throw safeError('SOURCE_ADAPTER_UNTRUSTED');
86
+ const filesystem = adapters.filesystem || fs;
87
+ const canonicalFilePath = getArgoEnvPath();
88
+ const configuredFilePath = source.filePath();
89
+ const fileExists = filesystem.existsSync(configuredFilePath);
90
+ if (fileExists || path.resolve(configuredFilePath) !== path.resolve(canonicalFilePath)) {
91
+ preflightFile({
92
+ canonicalFilePath,
93
+ configuredFilePath,
94
+ filesystem,
95
+ adapters,
96
+ });
97
+ }
98
+
99
+ const processValues = new Map();
100
+ for (const key of [...READABLE_KEYS, ...LEGACY_KEYS, ...PROHIBITED_RUNTIME_FIELD_KEYS]) {
101
+ const envelope = source.readProcessKey(key);
102
+ validateEnvelope(envelope, source, key, null, 'process');
103
+ processValues.set(key, envelope.value);
104
+ }
105
+ if ([...LEGACY_KEYS, ...PROHIBITED_RUNTIME_FIELD_KEYS].some(key => present(processValues.get(key)))) {
106
+ throw safeError('SECRET_SOURCE_PROVENANCE_PROHIBITED');
107
+ }
108
+
109
+ const fileValues = new Map();
110
+ if (fileExists) {
111
+ const records = source.readFileEntries(configuredFilePath);
112
+ if (!Array.isArray(records)) throw safeError('SOURCE_TRACE_INVALID');
113
+ for (const record of records) {
114
+ if (!record || !Object.isFrozen(record)) throw safeError('SOURCE_TRACE_UNTRUSTED');
115
+ validateTrace(record.trace, source, record.key, configuredFilePath, 'file');
116
+ if (fileValues.has(record.key)) throw safeError('SECRET_FILE_DUPLICATE_KEY');
117
+ if (!READABLE_KEYS.includes(record.key)) {
118
+ throw safeError(PROHIBITED_RUNTIME_FIELD_KEYS.includes(record.key) || secretLooking(record.key)
119
+ ? 'SECRET_FILE_UNKNOWN_KEY'
120
+ : 'LIVE_PROVIDER_CONFIGURATION_REQUIRED');
121
+ }
122
+ fileValues.set(record.key, record.value);
123
+ }
124
+ }
125
+
126
+ const normalized = {};
127
+ const attribution = {};
128
+ for (const key of CONFIG_KEYS) {
129
+ const processValue = processValues.get(key);
130
+ const fileValue = fileValues.get(key);
131
+ if (present(processValue) && present(fileValue) && processValue !== fileValue) {
132
+ throw safeError(SECRET_KEYS.has(key)
133
+ ? 'SECRET_SOURCE_CONFLICT'
134
+ : 'LIVE_PROVIDER_CONFIGURATION_CONFLICT');
135
+ }
136
+ if (present(processValue)) {
137
+ normalized[key] = processValue;
138
+ attribution[key] = 'process';
139
+ } else if (present(fileValue)) {
140
+ normalized[key] = fileValue;
141
+ attribution[key] = 'file';
142
+ } else {
143
+ const error = safeError(SECRET_KEYS.has(key)
144
+ ? 'APPROVED_SECRET_REQUIRED'
145
+ : 'LIVE_PROVIDER_CONFIGURATION_REQUIRED');
146
+ error.field = key;
147
+ throw error;
148
+ }
149
+ }
150
+ for (const key of requiredOptIns) {
151
+ const processValue = processValues.get(key);
152
+ const fileValue = fileValues.get(key);
153
+ if (present(processValue) && present(fileValue) && processValue !== fileValue) {
154
+ throw safeError('LIVE_PROVIDER_CONFIGURATION_CONFLICT');
155
+ }
156
+ const selectedValue = present(processValue) ? processValue : fileValue;
157
+ if (selectedValue !== '1') {
158
+ throw safeError(OPT_IN_KEYS[key]);
159
+ }
160
+ attribution[key] = present(processValue) ? 'process' : 'file';
161
+ }
162
+ for (const [key, expected] of Object.entries(APPROVED)) {
163
+ if (normalized[key] !== expected) throw safeError('LIVE_PROVIDER_CONFIGURATION_REQUIRED');
164
+ }
165
+ const configuration = Object.freeze({
166
+ embeddingBaseUrl: normalized.ARGO_EMBEDDING_BASE_URL,
167
+ embeddingModel: normalized.ARGO_EMBEDDING_MODEL,
168
+ embeddingProvider: normalized.ARGO_EMBEDDING_PROVIDER,
169
+ embeddingModelVersion: normalized.ARGO_EMBEDDING_MODEL_VERSION,
170
+ embeddingDimensions: 1024,
171
+ neo4jDatabaseUrl: normalized.ARGO_NEO4J_DATABASE_URL,
172
+ neo4jDatabaseUsername: normalized.ARGO_NEO4J_DATABASE_USERNAME,
173
+ neo4jDatabasePassword: normalized.ARGO_NEO4J_DATABASE_PASSWORD,
174
+ neo4jDatabase: optionalDatabaseName(processValues.get('ARGO_NEO4J_DATABASE') || fileValues.get('ARGO_NEO4J_DATABASE'), repositoryRoot),
175
+ qwenKey: normalized.QWEN_KEY,
176
+ });
177
+ return Object.freeze({
178
+ ...configuration,
179
+ configuration,
180
+ attribution: Object.freeze({ ...attribution }),
181
+ });
182
+ }
183
+
184
+ function optionalDatabaseName(value, repositoryRoot) {
185
+ if (present(value)) return String(value).trim();
186
+ const repoName = path.basename(repositoryRoot);
187
+ const normalized = String(repoName)
188
+ .toLowerCase()
189
+ .replace(/[^a-z0-9.-]+/g, '-')
190
+ .replace(/^-+|-+$/g, '')
191
+ .replace(/\.{2,}/g, '.')
192
+ .replace(/-{2,}/g, '-');
193
+ const safe = normalized || 'workspace';
194
+ const prefixed = /^[a-z]/.test(safe) ? safe : `db-${safe}`;
195
+ return prefixed.slice(0, 63);
196
+ }
197
+
198
+ function createTrustedSource({ behavior, observeTrace }) {
199
+ const adapter = {
200
+ filePath: () => path.resolve(String(behavior.expectedFilePath)),
201
+ readProcessKey(key) {
202
+ const selected = selectProcessOperation(behavior, key);
203
+ return Object.freeze({
204
+ value: selected.read(key),
205
+ trace: issueTrace(selected.makeTrace(key), selected.kind, observeTrace),
206
+ });
207
+ },
208
+ readFileEntries(requestedPath) {
209
+ const selected = selectFileOperation(behavior);
210
+ const entries = selected.read(requestedPath);
211
+ if (!Array.isArray(entries)) return entries;
212
+ return entries.map(([key, value]) => Object.freeze({
213
+ key,
214
+ value,
215
+ trace: issueTrace(selected.makeTrace(key, requestedPath), selected.kind, observeTrace),
216
+ }));
217
+ },
218
+ observeValidation(traceValue, trusted) {
219
+ if (observeTrace) observeTrace(Object.freeze({ phase: 'validated', trace: traceValue, trusted }));
220
+ },
221
+ };
222
+ Object.freeze(adapter);
223
+ issuedAdapters.add(adapter);
224
+ return adapter;
225
+ }
226
+
227
+ function selectProcessOperation(behavior, key) {
228
+ const mutations = [
229
+ ['readCliKey', () => makeTrace('cli', 'cli', key, 'read', [key]), 'prohibited'],
230
+ ['readLiteralKey', () => makeTrace('literal', 'literal', key, 'read', [key]), 'prohibited'],
231
+ ['readFallbackKey', () => makeTrace('process', null, key, 'fallback', [key]), 'prohibited'],
232
+ ['readAliasedProcessKey', () => makeTrace('process', null, key, 'read', ['QWEN_ALIAS', key]), 'prohibited'],
233
+ ['readIndirectProcessKey', () => makeTrace('process', null, key, 'indirect', ['configuration', 'credentials', key]), 'prohibited'],
234
+ ['readProcessKeyWithForgedTrace', () => makeTrace('process', null, key, 'read', [key]), 'forged'],
235
+ ['readProcessKeyWithMutableTrace', () => makeTrace('process', null, key, 'read', [key]), 'mutable'],
236
+ ['readProcessKeyWithInvalidTraceSchema', () => ({ sourceKind: 'process' }), 'invalid'],
237
+ ['readProcessKeyWithRequestedKeyMismatch', () => makeTrace('process', null, `${key}-mismatch`, 'read', [key]), 'invalid'],
238
+ ['readProcessKeyWithMissingTraceField', () => ({
239
+ sourceKind: 'process', path: null, key, operation: 'read',
240
+ }), 'invalid'],
241
+ ['readProcessKeyWithExtraTraceField', () => ({
242
+ ...makeTrace('process', null, key, 'read', [key]), extra: true,
243
+ }), 'invalid'],
244
+ ['readProcessKeyWithWrongTraceFieldType', () => makeTrace('process', null, key, 'read', key), 'invalid'],
245
+ ];
246
+ if (key === 'QWEN_KEY') {
247
+ for (const [method, make, kind] of mutations) {
248
+ if (typeof behavior[method] === 'function') return { read: behavior[method], makeTrace: make, kind };
249
+ }
250
+ }
251
+ return {
252
+ read: behavior.readProcessKey,
253
+ makeTrace: requested => makeTrace('process', null, requested, 'read', [requested]),
254
+ kind: 'issued',
255
+ };
256
+ }
257
+
258
+ function selectFileOperation(behavior) {
259
+ if (typeof behavior.readFileEntriesWithRequestedPathMismatch === 'function') {
260
+ return {
261
+ read: behavior.readFileEntriesWithRequestedPathMismatch,
262
+ makeTrace: (key, requested) => makeTrace('file', `${requested}.mismatch`, key, 'read', [key]),
263
+ kind: 'invalid',
264
+ };
265
+ }
266
+ return {
267
+ read: behavior.readFileEntries,
268
+ makeTrace: (key, requested) => makeTrace('file', requested, key, 'read', [key]),
269
+ kind: 'issued',
270
+ };
271
+ }
272
+
273
+ function issueTrace(candidate, kind, observeTrace) {
274
+ const aliasChain = Array.isArray(candidate.aliasChain)
275
+ ? (kind === 'mutable' ? [...candidate.aliasChain] : Object.freeze([...candidate.aliasChain]))
276
+ : candidate.aliasChain;
277
+ const value = kind === 'mutable'
278
+ ? { ...candidate, aliasChain }
279
+ : Object.freeze({ ...candidate, aliasChain });
280
+ if (kind !== 'forged') issuedTraces.add(value);
281
+ if (kind === 'invalid') invalidTraces.add(value);
282
+ if (kind === 'prohibited') prohibitedTraces.add(value);
283
+ if (observeTrace) observeTrace(Object.freeze({ phase: 'issued', trace: value }));
284
+ return value;
285
+ }
286
+
287
+ function validateEnvelope(envelope, source, key, requestedPath, sourceKind) {
288
+ if (!envelope || !Object.isFrozen(envelope)) throw safeError('SOURCE_TRACE_UNTRUSTED');
289
+ validateTrace(envelope.trace, source, key, requestedPath, sourceKind);
290
+ }
291
+
292
+ function validateTrace(value, source, key, requestedPath, sourceKind) {
293
+ const trusted = issuedTraces.has(value)
294
+ && Object.isFrozen(value)
295
+ && Object.isFrozen(value && value.aliasChain);
296
+ source.observeValidation(value, trusted);
297
+ if (!trusted) throw safeError('SOURCE_TRACE_UNTRUSTED');
298
+ if (invalidTraces.has(value)) throw safeError('SOURCE_TRACE_INVALID');
299
+ const fields = value && typeof value === 'object' ? Object.keys(value).sort().join(',') : '';
300
+ if (
301
+ fields !== 'aliasChain,key,operation,path,sourceKind'
302
+ || typeof value.sourceKind !== 'string'
303
+ || !(value.path === null || typeof value.path === 'string')
304
+ || typeof value.key !== 'string'
305
+ || typeof value.operation !== 'string'
306
+ || !Array.isArray(value.aliasChain)
307
+ || value.aliasChain.some(item => typeof item !== 'string')
308
+ ) {
309
+ throw safeError('SOURCE_TRACE_INVALID');
310
+ }
311
+ if (prohibitedTraces.has(value)) throw safeError('SECRET_SOURCE_PROVENANCE_PROHIBITED');
312
+ if (
313
+ value.sourceKind !== sourceKind
314
+ || value.path !== requestedPath
315
+ || value.key !== key
316
+ || value.operation !== 'read'
317
+ || value.aliasChain.length !== 1
318
+ || value.aliasChain[0] !== key
319
+ ) {
320
+ throw safeError('SOURCE_TRACE_INVALID');
321
+ }
322
+ }
323
+
324
+ function makeTrace(sourceKind, sourcePath, key, operation, aliasChain) {
325
+ return { sourceKind, path: sourcePath, key, operation, aliasChain };
326
+ }
327
+
328
+ function preflightFile({ canonicalFilePath, configuredFilePath, filesystem, adapters }) {
329
+ if (path.resolve(configuredFilePath) !== path.resolve(canonicalFilePath)) {
330
+ throw safeError('SECRET_FILE_PATH_PROHIBITED');
331
+ }
332
+ const stat = filesystem.lstatSync(configuredFilePath);
333
+ if (!stat.isFile() || stat.isSymbolicLink()) throw safeError('SECRET_FILE_REPARSE_PROHIBITED');
334
+ if (path.resolve(filesystem.realpathSync(configuredFilePath)) !== path.resolve(canonicalFilePath)) {
335
+ throw safeError('SECRET_FILE_PATH_PROHIBITED');
336
+ }
337
+
338
+ let ignored;
339
+ let tracked;
340
+ let aclEvidence;
341
+ if (adapters.systemMetadata) {
342
+ const insideRepository = adapters.systemMetadata.isSecretFileInsideGitRepository().status === 0;
343
+ ignored = insideRepository ? adapters.systemMetadata.isSecretFileIgnored() : true;
344
+ tracked = insideRepository ? adapters.systemMetadata.isSecretFileTracked() : false;
345
+ const identityResult = adapters.systemMetadata.readCurrentIdentity();
346
+ const aclResult = adapters.systemMetadata.readSecretFileAcl();
347
+ aclEvidence = {
348
+ status: aclResult.status,
349
+ stdout: aclResult.stdout,
350
+ identity: identityResult.status === 0 ? identityResult.stdout.trim() : '',
351
+ };
352
+ } else {
353
+ ignored = adapters.git.isIgnored(configuredFilePath);
354
+ tracked = adapters.git.isTracked(configuredFilePath);
355
+ aclEvidence = adapters.acl.inspect(configuredFilePath);
356
+ }
357
+ if (tracked) throw safeError('SECRET_FILE_TRACKED');
358
+ if (!ignored) throw safeError('SECRET_FILE_NOT_IGNORED');
359
+ validateAcl(aclEvidence);
360
+ }
361
+
362
+ function validateAcl(result) {
363
+ if (!result || result.status !== 0 || typeof result.stdout !== 'string' || !result.identity) {
364
+ throw safeError('SECRET_FILE_ACL_UNVERIFIABLE');
365
+ }
366
+ const permissions = parseAcl(result.stdout);
367
+ const current = permissions.get(String(result.identity).toLowerCase());
368
+ if (!current || !current.allow || current.deny) throw safeError('SECRET_FILE_ACL_UNSAFE');
369
+ for (const broad of ['everyone', 'builtin\\users', 'authenticated users', 'nt authority\\authenticated users']) {
370
+ const permission = permissions.get(broad);
371
+ if (permission && permission.allow && !permission.deny) throw safeError('SECRET_FILE_ACL_UNSAFE');
372
+ }
373
+ }
374
+
375
+ function parseAcl(output) {
376
+ const result = new Map();
377
+ for (const line of output.split(/\r?\n/)) {
378
+ const normalizedLine = line.trim().replace(/^[A-Za-z]:\\.*?\.env\s+/, '');
379
+ const match = normalizedLine.match(/^(.+?):((?:\([^)]*\))+)\s*$/);
380
+ if (!match) continue;
381
+ const tokens = [...match[2].matchAll(/\(([^)]*)\)/g)].map(item => item[1].toUpperCase());
382
+ if (!tokens.some(token => /^(?:F|M|R|RX)$/.test(token))) continue;
383
+ const key = match[1].trim().toLowerCase();
384
+ const entry = result.get(key) || { allow: false, deny: false };
385
+ if (tokens.includes('DENY')) entry.deny = true;
386
+ else entry.allow = true;
387
+ result.set(key, entry);
388
+ }
389
+ return result;
390
+ }
391
+
392
+ function productionSourceBehavior(repositoryRoot) {
393
+ return Object.freeze({
394
+ expectedFilePath: getArgoEnvPath(),
395
+ readProcessKey: key => process.env[key],
396
+ readFileEntries: filePath => parseEnv(fs.readFileSync(filePath, 'utf8')),
397
+ });
398
+ }
399
+
400
+ function parseEnv(text) {
401
+ const entries = [];
402
+ for (const raw of String(text).split(/\r?\n/)) {
403
+ const line = raw.trim();
404
+ if (!line || line.startsWith('#')) continue;
405
+ const equals = line.indexOf('=');
406
+ if (equals < 1) throw safeError('SECRET_FILE_INVALID');
407
+ entries.push([line.slice(0, equals).trim(), line.slice(equals + 1)]);
408
+ }
409
+ return entries;
410
+ }
411
+
412
+ function requireRoot(value) {
413
+ if (typeof value !== 'string' || value.trim() === '') throw safeError('LIVE_PROVIDER_CONFIGURATION_REQUIRED');
414
+ return path.resolve(value);
415
+ }
416
+
417
+ function requireProductionOptions(options) {
418
+ if (
419
+ !options
420
+ || typeof options !== 'object'
421
+ || Array.isArray(options)
422
+ || !Reflect.ownKeys(options).every(key => (
423
+ key === 'repositoryRoot'
424
+ || key === 'requiredOptIns'
425
+ || key === 'useCase'
426
+ ))
427
+ || !Reflect.ownKeys(options).includes('repositoryRoot')
428
+ ) {
429
+ throw safeError('SOURCE_ADAPTER_UNTRUSTED');
430
+ }
431
+ }
432
+
433
+ function resolveRequiredOptIns(options) {
434
+ requireProductionOptions(options);
435
+ if (Object.prototype.hasOwnProperty.call(options, 'useCase')) {
436
+ if (
437
+ options.useCase !== 'production-semantic-query'
438
+ || Object.prototype.hasOwnProperty.call(options, 'requiredOptIns')
439
+ ) {
440
+ throw safeError('SOURCE_ADAPTER_UNTRUSTED');
441
+ }
442
+ return [];
443
+ }
444
+ return normalizeRequiredOptIns(options.requiredOptIns);
445
+ }
446
+
447
+ function normalizeRequiredOptIns(value) {
448
+ const requested = value === undefined ? ['ARGO_LIVE_PROVIDER_E2E'] : value;
449
+ if (!Array.isArray(requested) || requested.length === 0) {
450
+ throw safeError('SOURCE_ADAPTER_UNTRUSTED');
451
+ }
452
+ const normalized = [];
453
+ for (const key of requested) {
454
+ if (!Object.prototype.hasOwnProperty.call(OPT_IN_KEYS, key)) {
455
+ throw safeError('SOURCE_ADAPTER_UNTRUSTED');
456
+ }
457
+ if (!normalized.includes(key)) {
458
+ normalized.push(key);
459
+ }
460
+ }
461
+ return normalized;
462
+ }
463
+
464
+ function present(value) {
465
+ return typeof value === 'string' && value.trim() !== '';
466
+ }
467
+
468
+ function secretLooking(key) {
469
+ return /(?:key|password|secret|token|credential)/i.test(String(key));
470
+ }
471
+
472
+ function safeError(category) {
473
+ const error = new Error(category);
474
+ error.category = category;
475
+ return error;
476
+ }
477
+
478
+ module.exports = {
479
+ resolveApprovedLiveConfiguration,
480
+ withApprovedLiveConfigurationTestComposition,
481
+ };