thinkwork-cli 0.12.15 → 0.12.17

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 (152) hide show
  1. package/dist/{api-client-4VT3FWOH.js → api-client-JUBCQQDF.js} +1 -1
  2. package/dist/{chunk-H7AB42ES.js → chunk-STY44FQO.js} +13 -0
  3. package/dist/cli.js +1107 -10225
  4. package/dist/commands/enterprise/templates/deploy-repo/docs/runbook.md +3 -8
  5. package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +1 -184
  6. package/dist/drizzle/0050_brain_v0_entity_subtype.sql +2 -2
  7. package/dist/drizzle/0051_brain_v0_tenant_entity_pages.sql +6 -6
  8. package/dist/drizzle/0052_brain_v0_external_refs.sql +2 -2
  9. package/dist/drizzle/0053_brain_v0_unresolved_mentions_subtype.sql +2 -2
  10. package/dist/drizzle/0145_knowledge_graph_thread_ingest.sql +14 -14
  11. package/dist/drizzle/0146_knowledge_graph_source_scope.sql +1 -1
  12. package/dist/drizzle/0157_knowledge_graph_observations_source.sql +2 -2
  13. package/dist/drizzle/0158_wiki_tenant_scope.sql +1 -1
  14. package/dist/drizzle/0165_plugin_premium_entitlements.sql +2 -2
  15. package/dist/drizzle/{0166_company_brain_substrate_contract.sql → 0166_brain_substrate_contract.sql} +9 -9
  16. package/dist/drizzle/{0167_company_brain_artifact_manifest_runtime.sql → 0167_brain_artifact_manifest_runtime.sql} +5 -5
  17. package/dist/drizzle/0168_eval_skill_gate.sql +1 -1
  18. package/dist/drizzle/0183_okf_artifact_manifests.sql +1 -1
  19. package/dist/drizzle/0197_rcm_context_columns.sql +28 -0
  20. package/dist/drizzle/0200_eval_results_execution_tier.sql +40 -0
  21. package/dist/drizzle/0201_brain_dream_runs.sql +72 -0
  22. package/dist/drizzle/0202_backfill_thread_participants_from_senders.sql +110 -0
  23. package/dist/drizzle/0203_threads_mode_override.sql +61 -0
  24. package/dist/drizzle/0204_thread_turns_triggering_message_id.sql +48 -0
  25. package/dist/drizzle/0205_routines_git_python.sql +185 -0
  26. package/dist/drizzle/0206_agent_loop_versions_routine_actions.sql +40 -0
  27. package/dist/drizzle/0207_tenant_credentials_github_repo_kind.sql +52 -0
  28. package/dist/drizzle/0208_knowledge_graph_neutral_identifiers.sql +253 -0
  29. package/dist/drizzle/0209_living_artifacts_schema.sql +109 -0
  30. package/dist/drizzle/0210_think137_automations_additive_schema.sql +113 -0
  31. package/dist/drizzle/0211_backfill_agent_loop_versions_target_spec.sql +120 -0
  32. package/dist/drizzle/0212_fold_webhooks_into_automations.sql +312 -0
  33. package/dist/drizzle/0213_agent_loop_versions_judge_evidence_nullable.sql +63 -0
  34. package/dist/drizzle/0214_think137_drop_judge_evidence_roi.sql +99 -0
  35. package/dist/drizzle/0215_agent_loop_versions_goal_worker_policy_nullable.sql +70 -0
  36. package/dist/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql +81 -0
  37. package/dist/drizzle/0217_agents_capability_folder_dispatch.sql +12 -0
  38. package/dist/drizzle/0218_document_plates.sql +30 -0
  39. package/dist/drizzle/0219_document_section_waivers.sql +29 -0
  40. package/dist/drizzle/0220_artifacts_created_by_user.sql +21 -0
  41. package/dist/drizzle/0221_artifact_shares.sql +29 -0
  42. package/dist/drizzle/0221_workflow_interpreter_thin_slice.sql +71 -0
  43. package/dist/drizzle/0222_compliance_drainer_select_audit_events.sql +21 -0
  44. package/dist/drizzle/0223_document_conformance_reports.sql +38 -0
  45. package/dist/drizzle/0224_artifact_refresh_state.sql +11 -0
  46. package/dist/drizzle/0225_think216_agent_loop_convergence.sql +21 -0
  47. package/dist/drizzle/0226_drop_n8n_agent_step_runs.sql +6 -0
  48. package/dist/drizzle/0227_analyst_reader_role.sql +1055 -0
  49. package/dist/drizzle/0228_tenant_credentials_rds_iam_kind.sql +62 -0
  50. package/dist/drizzle/0229_analyst_reader_rds_iam_grant.sql +79 -0
  51. package/dist/drizzle/0230_analyst_rls.sql +1613 -0
  52. package/dist/drizzle/0231_cost_events_cache_write_enforcement_exempt.sql +11 -0
  53. package/dist/plugins/catalog/package.json +0 -1
  54. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +0 -1
  55. package/dist/plugins/catalog/src/__tests__/catalog.test.ts +16 -8
  56. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +56 -0
  57. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +0 -2
  58. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -5
  59. package/dist/plugins/catalog/src/contracts.ts +112 -0
  60. package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -6
  61. package/dist/plugins/n8n/README.md +4 -36
  62. package/dist/plugins/n8n/src/deployment/managed-app.ts +0 -17
  63. package/dist/plugins/n8n/src/manifest.ts +0 -5
  64. package/dist/plugins/n8n/terraform/n8n/main.tf +0 -26
  65. package/dist/plugins/n8n/terraform/n8n/outputs.tf +0 -5
  66. package/dist/plugins/n8n/terraform/n8n/variables.tf +0 -6
  67. package/dist/plugins/n8n/test/manifest.test.ts +0 -1
  68. package/dist/plugins/twenty/package.json +5 -0
  69. package/dist/plugins/twenty/scripts/lib/__tests__/load-records.test.ts +232 -0
  70. package/dist/plugins/twenty/scripts/lib/__tests__/mappers.test.ts +489 -0
  71. package/dist/plugins/twenty/scripts/lib/__tests__/members-ensure.test.ts +199 -0
  72. package/dist/plugins/twenty/scripts/lib/__tests__/provision-members-db.test.ts +43 -0
  73. package/dist/plugins/twenty/scripts/lib/__tests__/schema-ensure.test.ts +230 -0
  74. package/dist/plugins/twenty/scripts/lib/__tests__/twenty-client.test.ts +207 -0
  75. package/dist/plugins/twenty/scripts/lib/lastmile-reader.ts +282 -0
  76. package/dist/plugins/twenty/scripts/lib/load-attachments.ts +220 -0
  77. package/dist/plugins/twenty/scripts/lib/load-records.ts +680 -0
  78. package/dist/plugins/twenty/scripts/lib/mappers.ts +559 -0
  79. package/dist/plugins/twenty/scripts/lib/members-ensure.ts +381 -0
  80. package/dist/plugins/twenty/scripts/lib/provision-members-db.ts +191 -0
  81. package/dist/plugins/twenty/scripts/lib/schema-ensure.ts +425 -0
  82. package/dist/plugins/twenty/scripts/lib/twenty-client.ts +246 -0
  83. package/dist/plugins/twenty/scripts/migrate-lastmile.ts +610 -0
  84. package/dist/plugins/twenty/scripts/provision-twenty-members.ts +214 -0
  85. package/dist/plugins/twenty/src/manifest.ts +99 -0
  86. package/dist/plugins/twenty/terraform/twenty/README.md +1 -1
  87. package/dist/plugins/twenty/test/manifest.test.ts +12 -2
  88. package/dist/terraform/examples/greenfield/main.tf +90 -380
  89. package/dist/terraform/examples/greenfield/terraform.tfvars.example +0 -1
  90. package/dist/terraform/modules/app/agentcore-pi/README.md +1 -3
  91. package/dist/terraform/modules/app/agentcore-pi/main.tf +36 -32
  92. package/dist/terraform/modules/app/agentcore-pi/variables.tf +23 -15
  93. package/dist/terraform/modules/app/agentcore-runtime/main.tf +3 -3
  94. package/dist/terraform/modules/app/customer-domain/main.tf +16 -2
  95. package/dist/terraform/modules/app/deployment-control-plane/main.tf +5 -0
  96. package/dist/terraform/modules/app/deployment-control-plane/runner.py +85 -207
  97. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +56 -2
  98. package/dist/terraform/modules/app/hindsight-memory/main.tf +52 -4
  99. package/dist/terraform/modules/app/lambda-api/eval-fanout.tf +7 -0
  100. package/dist/terraform/modules/app/lambda-api/handlers.tf +493 -89
  101. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +215 -96
  102. package/dist/terraform/modules/app/lambda-api/main.tf +24 -5
  103. package/dist/terraform/modules/app/lambda-api/outputs.tf +19 -11
  104. package/dist/terraform/modules/app/lambda-api/variables.tf +94 -67
  105. package/dist/terraform/modules/app/ses-email/main.tf +14 -2
  106. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/main.tf +493 -0
  107. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/outputs.tf +34 -0
  108. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/variables.tf +32 -0
  109. package/dist/terraform/modules/data/aurora-postgres/main.tf +55 -0
  110. package/dist/terraform/modules/data/aurora-postgres/outputs.tf +15 -0
  111. package/dist/terraform/modules/data/s3-buckets/main.tf +26 -0
  112. package/dist/terraform/modules/thinkwork/README.md +0 -1
  113. package/dist/terraform/modules/thinkwork/main.tf +167 -446
  114. package/dist/terraform/modules/thinkwork/outputs.tf +2 -142
  115. package/dist/terraform/modules/thinkwork/variables.tf +55 -309
  116. package/dist/terraform/schema.graphql +4 -0
  117. package/dist/workspace-defaults/files/AGENTS.md +45 -31
  118. package/dist/workspace-defaults/files/CONTEXT.md +48 -4
  119. package/dist/workspace-defaults/files/GUARDRAILS.md +18 -0
  120. package/dist/workspace-defaults/files/MEMORY_GUIDE.md +32 -4
  121. package/dist/workspace-defaults/files/SPACE.md +34 -19
  122. package/dist/workspace-defaults/files/TOOLS.md +30 -0
  123. package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +154 -0
  124. package/dist/workspace-defaults/files/skills/document-composer/references/authoring-rules.md +101 -0
  125. package/package.json +2 -1
  126. package/dist/plugins/company-brain/README.md +0 -37
  127. package/dist/plugins/company-brain/package.json +0 -33
  128. package/dist/plugins/company-brain/runtime/cognee/Dockerfile +0 -11
  129. package/dist/plugins/company-brain/smoke/cognee-managed-app-smoke.mjs +0 -458
  130. package/dist/plugins/company-brain/smoke/cognee-memory-cutover-smoke.mjs +0 -539
  131. package/dist/plugins/company-brain/smoke/company-brain-context-engine-smoke.mjs +0 -312
  132. package/dist/plugins/company-brain/smoke/company-brain-operations-smoke.mjs +0 -449
  133. package/dist/plugins/company-brain/smoke/company-brain-plugin-smoke.mjs +0 -655
  134. package/dist/plugins/company-brain/smoke/hindsight-memory-isolation-smoke.mjs +0 -474
  135. package/dist/plugins/company-brain/src/api/cognee-client.ts +0 -896
  136. package/dist/plugins/company-brain/src/api/cognee-cluster-identity.ts +0 -49
  137. package/dist/plugins/company-brain/src/api/cognee-memory-scope.ts +0 -84
  138. package/dist/plugins/company-brain/src/api/context-engine-provider.ts +0 -888
  139. package/dist/plugins/company-brain/src/api/migration.ts +0 -665
  140. package/dist/plugins/company-brain/src/deployment/cognee-managed-app.ts +0 -396
  141. package/dist/plugins/company-brain/src/index.ts +0 -62
  142. package/dist/plugins/company-brain/src/manifest.ts +0 -112
  143. package/dist/plugins/company-brain/terraform/cognee/README.md +0 -263
  144. package/dist/plugins/company-brain/terraform/cognee/main.tf +0 -766
  145. package/dist/plugins/company-brain/terraform/cognee/outputs.tf +0 -144
  146. package/dist/plugins/company-brain/terraform/cognee/variables.tf +0 -402
  147. package/dist/plugins/company-brain/test/api/cognee-client.test.ts +0 -562
  148. package/dist/plugins/company-brain/test/api/cognee-memory-scope.test.ts +0 -74
  149. package/dist/plugins/company-brain/test/api/context-engine-provider.test.ts +0 -447
  150. package/dist/plugins/company-brain/test/api/migration.test.ts +0 -465
  151. package/dist/plugins/company-brain/test/manifest.test.ts +0 -95
  152. package/dist/plugins/company-brain/tsconfig.json +0 -8
@@ -1,312 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Read-only Company Brain Context Engine smoke (THNK-20 / U5a).
4
- *
5
- * Dry-run is the default. Set SMOKE_ENABLE_COMPANY_BRAIN_CONTEXT=1 to run live
6
- * against a deployed stage. Live mode calls the existing MCP Context Engine
7
- * endpoint with service auth and does not mutate production state.
8
- *
9
- * Live env:
10
- * CONTEXT_ENGINE_MCP_URL or API_CONTEXT_ENGINE_MCP_URL
11
- * Optional fallback: VITE_GRAPHQL_HTTP_URL / GRAPHQL_HTTP_URL / API_GRAPHQL_URL
12
- * with /graphql replaced by /mcp/context-engine.
13
- * API_AUTH_SECRET or THINKWORK_API_SECRET
14
- * SMOKE_TENANT_ID=<tenant uuid>
15
- * SMOKE_USER_ID=<users.id in that tenant>
16
- *
17
- * Optional:
18
- * SMOKE_COMPANY_BRAIN_CONTEXT_QUERY="Acme renewal risk"
19
- * SMOKE_COMPANY_BRAIN_EXPECTED_TERM="procurement"
20
- * SMOKE_COMPANY_BRAIN_SOURCE_KIND=thread
21
- * SMOKE_COMPANY_BRAIN_SOURCE_TYPE=thread_message
22
- * SMOKE_COMPANY_BRAIN_DATASET_ID=<dataset/source scope>
23
- * SMOKE_COMPANY_BRAIN_NODESETS=customer-success,renewals
24
- */
25
-
26
- import fs from "node:fs";
27
- import path from "node:path";
28
- import process from "node:process";
29
- import { attachSmokeEvidence } from "../../../scripts/smoke/deployment-evidence.mjs";
30
-
31
- const LIVE_ENABLED = process.env.SMOKE_ENABLE_COMPANY_BRAIN_CONTEXT === "1";
32
- const TIMEOUT_MS = Number(process.env.SMOKE_TIMEOUT_MS || 20_000);
33
- const env = { ...loadEnvFile(), ...process.env };
34
-
35
- const graphqlUrl = first(
36
- env.VITE_GRAPHQL_HTTP_URL,
37
- env.GRAPHQL_HTTP_URL,
38
- env.API_GRAPHQL_URL,
39
- );
40
- const mcpUrl = first(
41
- env.CONTEXT_ENGINE_MCP_URL,
42
- env.API_CONTEXT_ENGINE_MCP_URL,
43
- deriveMcpUrl(graphqlUrl),
44
- );
45
- const apiSecret = first(env.API_AUTH_SECRET, env.THINKWORK_API_SECRET);
46
- const tenantId = first(env.SMOKE_TENANT_ID, env.TENANT_ID);
47
- const userId = first(env.SMOKE_USER_ID, env.SMOKE_ADMIN_USER_ID);
48
- const query = first(
49
- env.SMOKE_COMPANY_BRAIN_CONTEXT_QUERY,
50
- "Acme renewal risk and next best action",
51
- );
52
- const expectedTerm = first(env.SMOKE_COMPANY_BRAIN_EXPECTED_TERM);
53
-
54
- const checks = [];
55
-
56
- if (!LIVE_ENABLED) {
57
- console.log(
58
- JSON.stringify(
59
- await attachSmokeEvidence(
60
- "company-brain-context-engine",
61
- {
62
- ok: true,
63
- skippedLive: true,
64
- reason:
65
- "set SMOKE_ENABLE_COMPANY_BRAIN_CONTEXT=1 to run the deployed read-only Context Engine Brain smoke",
66
- dryRun: {
67
- requiredWhenRunning: [
68
- "CONTEXT_ENGINE_MCP_URL or GraphQL URL that can derive /mcp/context-engine",
69
- "API_AUTH_SECRET or THINKWORK_API_SECRET",
70
- "SMOKE_TENANT_ID",
71
- "SMOKE_USER_ID",
72
- ],
73
- optionalScope: [
74
- "SMOKE_COMPANY_BRAIN_CONTEXT_QUERY",
75
- "SMOKE_COMPANY_BRAIN_EXPECTED_TERM",
76
- "SMOKE_COMPANY_BRAIN_SOURCE_KIND",
77
- "SMOKE_COMPANY_BRAIN_SOURCE_TYPE",
78
- "SMOKE_COMPANY_BRAIN_DATASET_ID",
79
- "SMOKE_COMPANY_BRAIN_NODESETS",
80
- ],
81
- verifies: [
82
- "query_brain_context returns Company Brain hits through Context Engine",
83
- "Brain provider status exposes active read route, shadow migration, vault provenance, and provider-local retrieval posture",
84
- "Brain hits include untrusted source-data boundary metadata",
85
- "Brain answer is better than memory-only for the named workflow by hit count or expected-term match",
86
- "query_memory_context remains a separate Cognee-backed user/space memory path rather than an implicit Brain fallback",
87
- ],
88
- },
89
- },
90
- env,
91
- ),
92
- null,
93
- 2,
94
- ),
95
- );
96
- process.exit(0);
97
- }
98
-
99
- try {
100
- await runLiveSmoke();
101
- const failed = checks.filter((check) => !check.ok);
102
- console.log(
103
- JSON.stringify(
104
- await attachSmokeEvidence(
105
- "company-brain-context-engine",
106
- {
107
- ok: failed.length === 0,
108
- query,
109
- checks,
110
- },
111
- env,
112
- ),
113
- null,
114
- 2,
115
- ),
116
- );
117
- process.exit(failed.length === 0 ? 0 : 1);
118
- } catch (error) {
119
- console.error(
120
- JSON.stringify(
121
- await attachSmokeEvidence(
122
- "company-brain-context-engine",
123
- {
124
- ok: false,
125
- query,
126
- error: error instanceof Error ? error.message : String(error),
127
- checks,
128
- },
129
- env,
130
- ),
131
- null,
132
- 2,
133
- ),
134
- );
135
- process.exit(1);
136
- }
137
-
138
- async function runLiveSmoke() {
139
- requireEnv("CONTEXT_ENGINE_MCP_URL", mcpUrl);
140
- requireEnv("API_AUTH_SECRET or THINKWORK_API_SECRET", apiSecret);
141
- requireEnv("SMOKE_TENANT_ID", tenantId);
142
- requireEnv("SMOKE_USER_ID", userId);
143
-
144
- const brain = await callTool("query_brain_context", {
145
- query,
146
- mode: "answer",
147
- scope: "team",
148
- limit: 5,
149
- sourceKind: first(env.SMOKE_COMPANY_BRAIN_SOURCE_KIND),
150
- sourceType: first(env.SMOKE_COMPANY_BRAIN_SOURCE_TYPE),
151
- datasetId: first(env.SMOKE_COMPANY_BRAIN_DATASET_ID),
152
- nodeSetIds: csv(env.SMOKE_COMPANY_BRAIN_NODESETS),
153
- onlyContext: true,
154
- });
155
- const memory = await callTool("query_memory_context", {
156
- query,
157
- mode: "answer",
158
- scope: "personal",
159
- limit: 5,
160
- });
161
-
162
- const brainContent = brain.result?.structuredContent;
163
- const memoryContent = memory.result?.structuredContent;
164
- const brainHits = Array.isArray(brainContent?.hits) ? brainContent.hits : [];
165
- const memoryHits = Array.isArray(memoryContent?.hits)
166
- ? memoryContent.hits
167
- : [];
168
- const brainProviders = Array.isArray(brainContent?.providers)
169
- ? brainContent.providers
170
- : [];
171
- const brainProvider = brainProviders.find(
172
- (provider) => provider.providerId === "brain",
173
- );
174
- const brainText = JSON.stringify(brain.result ?? {});
175
-
176
- assert("Brain provider returns at least one hit", brainHits.length > 0, {
177
- brainHitCount: brainHits.length,
178
- });
179
- assert(
180
- "Brain provider exposes provider-local status",
181
- Boolean(brainProvider),
182
- { providers: brainProviders },
183
- );
184
- assert(
185
- "Brain provider exposes migration-aware read posture",
186
- Boolean(
187
- brainProvider?.metadata?.readPosture?.active?.role === "active" &&
188
- brainProvider.metadata.readPosture.vault?.role === "vault",
189
- ),
190
- { readPosture: brainProvider?.metadata?.readPosture ?? null },
191
- );
192
- assert(
193
- "Brain hits carry untrusted source-data boundaries",
194
- brainHits.every(
195
- (hit) =>
196
- hit?.metadata?.sourceDataPolicy ||
197
- hit?.provenance?.metadata?.instructionBoundary ===
198
- "untrusted_source_data",
199
- ),
200
- { sampleHit: brainHits[0] ?? null },
201
- );
202
-
203
- const betterByCount = brainHits.length > memoryHits.length;
204
- const betterByTerm = expectedTerm
205
- ? brainText.toLowerCase().includes(expectedTerm.toLowerCase())
206
- : false;
207
- assert(
208
- "Brain answer is materially better than memory-only for named workflow",
209
- betterByCount || betterByTerm,
210
- {
211
- brainHitCount: brainHits.length,
212
- memoryHitCount: memoryHits.length,
213
- expectedTerm: expectedTerm ?? null,
214
- betterByCount,
215
- betterByTerm,
216
- },
217
- );
218
- }
219
-
220
- async function callTool(name, args) {
221
- const body = {
222
- jsonrpc: "2.0",
223
- id: name,
224
- method: "tools/call",
225
- params: {
226
- name,
227
- arguments: Object.fromEntries(
228
- Object.entries(args).filter(
229
- ([, value]) =>
230
- value !== undefined &&
231
- value !== null &&
232
- (!Array.isArray(value) || value.length > 0),
233
- ),
234
- ),
235
- },
236
- };
237
- const response = await fetch(mcpUrl, {
238
- method: "POST",
239
- headers: {
240
- "Content-Type": "application/json",
241
- Authorization: `Bearer ${apiSecret}`,
242
- "x-tenant-id": tenantId,
243
- "x-user-id": userId,
244
- },
245
- body: JSON.stringify(body),
246
- signal: AbortSignal.timeout(TIMEOUT_MS),
247
- });
248
- const payload = await response.json().catch(() => ({}));
249
- if (!response.ok || payload.error) {
250
- throw new Error(
251
- `${name} failed: ${JSON.stringify(payload.error ?? payload)}`,
252
- );
253
- }
254
- return payload;
255
- }
256
-
257
- function assert(name, ok, details = {}) {
258
- checks.push({ name, ok, ...details });
259
- if (!ok) {
260
- throw new Error(`${name} failed`);
261
- }
262
- }
263
-
264
- function requireEnv(name, value) {
265
- if (!value) throw new Error(`${name} is required`);
266
- }
267
-
268
- function deriveMcpUrl(url) {
269
- if (!url) return null;
270
- try {
271
- const parsed = new URL(url);
272
- parsed.pathname = parsed.pathname.replace(/\/graphql\/?$/, "");
273
- parsed.pathname = `${parsed.pathname.replace(/\/$/, "")}/mcp/context-engine`;
274
- parsed.search = "";
275
- parsed.hash = "";
276
- return parsed.toString();
277
- } catch {
278
- return null;
279
- }
280
- }
281
-
282
- function csv(value) {
283
- return value
284
- ? value
285
- .split(",")
286
- .map((item) => item.trim())
287
- .filter(Boolean)
288
- : undefined;
289
- }
290
-
291
- function first(...values) {
292
- return values
293
- .find((value) => typeof value === "string" && value.trim())
294
- ?.trim();
295
- }
296
-
297
- function loadEnvFile() {
298
- const candidates = [
299
- path.resolve("apps/web/.env"),
300
- path.resolve("terraform/examples/greenfield/.env"),
301
- ];
302
- const output = {};
303
- for (const file of candidates) {
304
- if (!fs.existsSync(file)) continue;
305
- for (const line of fs.readFileSync(file, "utf8").split(/\r?\n/)) {
306
- const match = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/);
307
- if (!match) continue;
308
- output[match[1]] = match[2].replace(/^['"]|['"]$/g, "");
309
- }
310
- }
311
- return output;
312
- }