@gurulu/cli 0.4.7 → 1.0.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 (182) hide show
  1. package/LICENSE +92 -0
  2. package/README.md +35 -106
  3. package/dist/bin.d.ts +3 -0
  4. package/dist/bin.d.ts.map +1 -0
  5. package/dist/bin.js +25410 -0
  6. package/dist/commands/auth.d.ts +23 -20
  7. package/dist/commands/auth.d.ts.map +1 -0
  8. package/dist/commands/doctor.d.ts +20 -6
  9. package/dist/commands/doctor.d.ts.map +1 -0
  10. package/dist/commands/init.d.ts +25 -11
  11. package/dist/commands/init.d.ts.map +1 -0
  12. package/dist/commands/pull.d.ts +13 -0
  13. package/dist/commands/pull.d.ts.map +1 -0
  14. package/dist/commands/push.d.ts +40 -0
  15. package/dist/commands/push.d.ts.map +1 -0
  16. package/dist/commands/validate.d.ts +36 -0
  17. package/dist/commands/validate.d.ts.map +1 -0
  18. package/dist/index.d.ts +4 -1
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +24985 -876
  21. package/dist/lib/api.d.ts +139 -0
  22. package/dist/lib/api.d.ts.map +1 -0
  23. package/dist/lib/codegen.d.ts +4 -0
  24. package/dist/lib/codegen.d.ts.map +1 -0
  25. package/dist/lib/config.d.ts +43 -0
  26. package/dist/lib/config.d.ts.map +1 -0
  27. package/package.json +40 -20
  28. package/bin/gurulu.js +0 -2
  29. package/dist/api-client.d.ts +0 -33
  30. package/dist/api-client.js +0 -175
  31. package/dist/commands/add-server.d.ts +0 -9
  32. package/dist/commands/add-server.js +0 -162
  33. package/dist/commands/alerts.d.ts +0 -27
  34. package/dist/commands/alerts.js +0 -309
  35. package/dist/commands/api-keys.d.ts +0 -20
  36. package/dist/commands/api-keys.js +0 -130
  37. package/dist/commands/attribution.d.ts +0 -22
  38. package/dist/commands/attribution.js +0 -111
  39. package/dist/commands/audiences.d.ts +0 -23
  40. package/dist/commands/audiences.js +0 -243
  41. package/dist/commands/audit.d.ts +0 -20
  42. package/dist/commands/audit.js +0 -130
  43. package/dist/commands/auth.js +0 -249
  44. package/dist/commands/chat.d.ts +0 -19
  45. package/dist/commands/chat.js +0 -118
  46. package/dist/commands/config.d.ts +0 -10
  47. package/dist/commands/config.js +0 -92
  48. package/dist/commands/consent.d.ts +0 -27
  49. package/dist/commands/consent.js +0 -233
  50. package/dist/commands/conversion-paths.d.ts +0 -19
  51. package/dist/commands/conversion-paths.js +0 -55
  52. package/dist/commands/db.d.ts +0 -25
  53. package/dist/commands/db.js +0 -330
  54. package/dist/commands/destinations.d.ts +0 -20
  55. package/dist/commands/destinations.js +0 -191
  56. package/dist/commands/doctor.js +0 -360
  57. package/dist/commands/errors.d.ts +0 -27
  58. package/dist/commands/errors.js +0 -121
  59. package/dist/commands/events.d.ts +0 -33
  60. package/dist/commands/events.js +0 -371
  61. package/dist/commands/experiments.d.ts +0 -22
  62. package/dist/commands/experiments.js +0 -264
  63. package/dist/commands/funnels.d.ts +0 -17
  64. package/dist/commands/funnels.js +0 -203
  65. package/dist/commands/goals.d.ts +0 -18
  66. package/dist/commands/goals.js +0 -214
  67. package/dist/commands/heatmap.d.ts +0 -27
  68. package/dist/commands/heatmap.js +0 -112
  69. package/dist/commands/identity.d.ts +0 -29
  70. package/dist/commands/identity.js +0 -328
  71. package/dist/commands/init.js +0 -215
  72. package/dist/commands/insights.d.ts +0 -10
  73. package/dist/commands/insights.js +0 -77
  74. package/dist/commands/install.d.ts +0 -259
  75. package/dist/commands/install.js +0 -1590
  76. package/dist/commands/login.d.ts +0 -20
  77. package/dist/commands/login.js +0 -170
  78. package/dist/commands/logout.d.ts +0 -10
  79. package/dist/commands/logout.js +0 -41
  80. package/dist/commands/playground.d.ts +0 -11
  81. package/dist/commands/playground.js +0 -47
  82. package/dist/commands/releases.d.ts +0 -17
  83. package/dist/commands/releases.js +0 -54
  84. package/dist/commands/replay.d.ts +0 -18
  85. package/dist/commands/replay.js +0 -64
  86. package/dist/commands/secrets.d.ts +0 -19
  87. package/dist/commands/secrets.js +0 -145
  88. package/dist/commands/setup.d.ts +0 -21
  89. package/dist/commands/setup.js +0 -67
  90. package/dist/commands/sites.d.ts +0 -18
  91. package/dist/commands/sites.js +0 -139
  92. package/dist/commands/skad.d.ts +0 -18
  93. package/dist/commands/skad.js +0 -53
  94. package/dist/commands/sourcemap.d.ts +0 -33
  95. package/dist/commands/sourcemap.js +0 -204
  96. package/dist/commands/status.d.ts +0 -7
  97. package/dist/commands/status.js +0 -136
  98. package/dist/commands/upgrade.d.ts +0 -21
  99. package/dist/commands/upgrade.js +0 -183
  100. package/dist/commands/warehouse.d.ts +0 -20
  101. package/dist/commands/warehouse.js +0 -65
  102. package/dist/commands/warehouses.d.ts +0 -17
  103. package/dist/commands/warehouses.js +0 -182
  104. package/dist/commands/watch.d.ts +0 -45
  105. package/dist/commands/watch.js +0 -258
  106. package/dist/commands/whoami.d.ts +0 -9
  107. package/dist/commands/whoami.js +0 -50
  108. package/dist/config.d.ts +0 -75
  109. package/dist/config.js +0 -329
  110. package/dist/frameworks/detect.d.ts +0 -8
  111. package/dist/frameworks/detect.js +0 -458
  112. package/dist/install-intent-proposal.d.ts +0 -99
  113. package/dist/install-intent-proposal.js +0 -202
  114. package/dist/utils/api.d.ts +0 -20
  115. package/dist/utils/api.js +0 -47
  116. package/dist/utils/config.d.ts +0 -13
  117. package/dist/utils/config.js +0 -30
  118. package/dist/utils/confirm.d.ts +0 -17
  119. package/dist/utils/confirm.js +0 -40
  120. package/dist/utils/dry-run.d.ts +0 -20
  121. package/dist/utils/dry-run.js +0 -67
  122. package/dist/utils/from-file.d.ts +0 -9
  123. package/dist/utils/from-file.js +0 -72
  124. package/dist/utils/redact.d.ts +0 -14
  125. package/dist/utils/redact.js +0 -48
  126. package/dist/utils/ui.d.ts +0 -14
  127. package/dist/utils/ui.js +0 -59
  128. package/scripts/.gitkeep +0 -0
  129. package/scripts/README-gurulu-agentic-install.md +0 -114
  130. package/scripts/README-gurulu-scan.md +0 -98
  131. package/scripts/audit-cli-scopes.mjs +0 -204
  132. package/scripts/backfill-tenant-id.mjs +0 -172
  133. package/scripts/backfill-tenant-links.ts +0 -252
  134. package/scripts/backup-clickhouse.sh +0 -27
  135. package/scripts/backup-postgres.sh +0 -19
  136. package/scripts/bootstrap-runtime-schema.mjs +0 -87
  137. package/scripts/bootstrap-stripe.mjs +0 -158
  138. package/scripts/gurulu-agentic-install.lib.cjs +0 -762
  139. package/scripts/gurulu-agentic-install.mjs +0 -623
  140. package/scripts/gurulu-scan.lib.cjs +0 -1509
  141. package/scripts/gurulu-scan.mjs +0 -91
  142. package/scripts/gurulu-verify-install.lib.cjs +0 -334
  143. package/scripts/gurulu-verify-install.mjs +0 -59
  144. package/scripts/init-ssl.sh +0 -26
  145. package/scripts/migrate-flow-graph-enums.sh +0 -86
  146. package/scripts/monitor-disk.sh +0 -24
  147. package/scripts/patches/astro.patch.cjs +0 -74
  148. package/scripts/patches/auto-instrument/ast-helper.cjs +0 -480
  149. package/scripts/patches/auto-instrument/astro.cjs +0 -273
  150. package/scripts/patches/auto-instrument/express.cjs +0 -383
  151. package/scripts/patches/auto-instrument/fastify.cjs +0 -262
  152. package/scripts/patches/auto-instrument/hono.cjs +0 -392
  153. package/scripts/patches/auto-instrument/index.cjs +0 -80
  154. package/scripts/patches/auto-instrument/nestjs.cjs +0 -286
  155. package/scripts/patches/auto-instrument/nextjs-app-router.cjs +0 -345
  156. package/scripts/patches/auto-instrument/nextjs-pages.cjs +0 -361
  157. package/scripts/patches/auto-instrument/remix.cjs +0 -168
  158. package/scripts/patches/auto-instrument/sdk-helper-map.cjs +0 -241
  159. package/scripts/patches/auto-instrument/singleton-helper.cjs +0 -193
  160. package/scripts/patches/auto-instrument/sveltekit.cjs +0 -161
  161. package/scripts/patches/auto-instrument/vite-react.cjs +0 -37
  162. package/scripts/patches/auto-instrument/vue.cjs +0 -196
  163. package/scripts/patches/express.patch.cjs +0 -99
  164. package/scripts/patches/fastify.patch.cjs +0 -108
  165. package/scripts/patches/index.cjs +0 -300
  166. package/scripts/patches/nestjs.patch.cjs +0 -112
  167. package/scripts/patches/nextjs-app-router.patch.cjs +0 -97
  168. package/scripts/patches/nextjs-pages.patch.cjs +0 -97
  169. package/scripts/patches/remix.patch.cjs +0 -75
  170. package/scripts/patches/sveltekit.patch.cjs +0 -72
  171. package/scripts/patches/vite-react.patch.cjs +0 -73
  172. package/scripts/patches/vue.patch.cjs +0 -82
  173. package/scripts/renew-ssl.sh +0 -14
  174. package/scripts/resolve-migration.sh +0 -23
  175. package/scripts/seed-cli-dev-keys.mjs +0 -130
  176. package/scripts/seed-test-data.mjs +0 -391
  177. package/scripts/spike-browserless.ts +0 -65
  178. package/scripts/tenant-pivot-consistency-check.mjs +0 -205
  179. package/scripts/tenant-pivot-phase-3-cleanup.lib.cjs +0 -258
  180. package/scripts/tenant-pivot-phase-3-cleanup.mjs +0 -98
  181. package/scripts/test-identity-resolution.ts +0 -804
  182. package/scripts/validate-gurulu-schemas.mjs +0 -79
@@ -1,91 +0,0 @@
1
- #!/usr/bin/env node
2
- // gurulu-scan.mjs — standalone repo scanner CLI (Phase 13 B2).
3
- //
4
- // Detection logic lives in `scripts/gurulu-scan.lib.cjs` so it can be reused
5
- // by ts-jest tests via `require()`. This wrapper exposes the same API as an
6
- // ESM module and, when invoked directly, runs the CLI.
7
- //
8
- // Usage:
9
- // node scripts/gurulu-scan.mjs <repo-path> [--output <file>] [--quiet]
10
-
11
- import { createRequire } from 'node:module';
12
- import { promises as fs } from 'node:fs';
13
- import path from 'node:path';
14
- import { fileURLToPath } from 'node:url';
15
-
16
- const require = createRequire(import.meta.url);
17
- const lib = require('./gurulu-scan.lib.cjs');
18
-
19
- export const SCAN_VERSION = lib.SCAN_VERSION;
20
- export const detectFramework = lib.detectFramework;
21
- export const detectORM = lib.detectORM;
22
- export const detectAuth = lib.detectAuth;
23
- export const detectRoutes = lib.detectRoutes;
24
- export const detectMutations = lib.detectMutations;
25
- export const scanRepo = lib.scanRepo;
26
- // Phase 18.6 A1
27
- export const extractSchemaTables = lib.extractSchemaTables;
28
- export const extractDependencyFingerprint = lib.extractDependencyFingerprint;
29
- export const extractUiStrings = lib.extractUiStrings;
30
- export const extractI18nKeys = lib.extractI18nKeys;
31
- export const extractFolderStructureHints = lib.extractFolderStructureHints;
32
- export const extractEnvVarHints = lib.extractEnvVarHints;
33
- export const extractCommentBlockHints = lib.extractCommentBlockHints;
34
-
35
- // ---------------------------------------------------------------------------
36
- // CLI entry
37
- // ---------------------------------------------------------------------------
38
- function parseArgs(argv) {
39
- const args = { _: [], output: null, quiet: false };
40
- for (let i = 0; i < argv.length; i++) {
41
- const a = argv[i];
42
- if (a === '--output' || a === '-o' || a === '--out') {
43
- args.output = argv[++i];
44
- } else if (a === '--quiet' || a === '-q') {
45
- args.quiet = true;
46
- } else if (a === '--help' || a === '-h') {
47
- args.help = true;
48
- } else {
49
- args._.push(a);
50
- }
51
- }
52
- return args;
53
- }
54
-
55
- async function main() {
56
- const args = parseArgs(process.argv.slice(2));
57
- if (args.help || args._.length === 0) {
58
- process.stdout.write(
59
- 'Usage: node scripts/gurulu-scan.mjs <repo-path> [--output <file>] [--quiet]\n',
60
- );
61
- process.exit(args.help ? 0 : 1);
62
- }
63
- const rootPath = args._[0];
64
- const log = args.quiet ? () => {} : (m) => process.stderr.write(m + '\n');
65
- const plan = await scanRepo(rootPath, { log });
66
- const json = JSON.stringify(plan, null, 2);
67
- if (args.output) {
68
- await fs.writeFile(args.output, json + '\n', 'utf8');
69
- if (!args.quiet) process.stderr.write(`[scan] wrote ${args.output}\n`);
70
- } else {
71
- process.stdout.write(json + '\n');
72
- }
73
- }
74
-
75
- const invokedDirectly =
76
- typeof process !== 'undefined' &&
77
- process.argv[1] &&
78
- (() => {
79
- try {
80
- return fileURLToPath(import.meta.url) === path.resolve(process.argv[1]);
81
- } catch {
82
- return false;
83
- }
84
- })();
85
-
86
- if (invokedDirectly) {
87
- main().catch((err) => {
88
- process.stderr.write(`[scan] ERROR: ${err.stack || err.message || err}\n`);
89
- process.exit(1);
90
- });
91
- }
@@ -1,334 +0,0 @@
1
- // gurulu-verify-install.lib.cjs
2
- //
3
- // Pure-CJS library used by both the `.mjs` CLI wrapper and the ts-jest
4
- // test suite. Contains the entire verification loop implementation so
5
- // no jest runtime has to pull in ESM.
6
- //
7
- // See scripts/gurulu-verify-install.mjs for the CLI front-end.
8
-
9
- 'use strict';
10
-
11
- const { createServer } = require('node:http');
12
- const { spawn } = require('node:child_process');
13
- const fs = require('node:fs');
14
- const path = require('node:path');
15
-
16
- // ---------------------------------------------------------------------------
17
- // Argv parsing
18
- // ---------------------------------------------------------------------------
19
-
20
- function parseArgs(argv) {
21
- const args = {
22
- path: null,
23
- framework: null,
24
- siteId: null,
25
- tenantId: null,
26
- ingestUrl: null,
27
- port: 3000,
28
- timeout: 30000,
29
- dryRun: false,
30
- };
31
- const positional = [];
32
- for (let i = 0; i < argv.length; i++) {
33
- const a = argv[i];
34
- if (a === '--framework') args.framework = argv[++i];
35
- else if (a === '--site-id') args.siteId = argv[++i];
36
- else if (a === '--tenant-id') args.tenantId = argv[++i];
37
- else if (a === '--ingest-url') args.ingestUrl = argv[++i];
38
- else if (a === '--port') args.port = Number(argv[++i]);
39
- else if (a === '--timeout') args.timeout = Number(argv[++i]);
40
- else if (a === '--dry-run') args.dryRun = true;
41
- else if (!a.startsWith('--')) positional.push(a);
42
- }
43
- if (positional.length > 0) args.path = positional[0];
44
- return args;
45
- }
46
-
47
- // ---------------------------------------------------------------------------
48
- // Package manager detection (inline, mirrors the CLI helper)
49
- // ---------------------------------------------------------------------------
50
-
51
- function detectPackageManager(repoRoot) {
52
- if (fs.existsSync(path.join(repoRoot, 'pnpm-lock.yaml'))) return 'pnpm';
53
- if (fs.existsSync(path.join(repoRoot, 'yarn.lock'))) return 'yarn';
54
- return 'npm';
55
- }
56
-
57
- // ---------------------------------------------------------------------------
58
- // Dev command resolution
59
- // ---------------------------------------------------------------------------
60
-
61
- function resolveDevCommand(framework, repoRoot, pm = 'npm') {
62
- const fw = (framework || '').toLowerCase();
63
- if (fw.startsWith('next') || fw === 'nextjs' || fw === 'nextjs-app' || fw === 'nextjs-pages') {
64
- return { cmd: pm, args: ['run', 'dev'] };
65
- }
66
- if (fw === 'vite' || fw === 'vite-react' || fw === 'vite-vue') {
67
- return { cmd: pm, args: ['run', 'dev'] };
68
- }
69
- if (fw === 'express' || fw === 'node') {
70
- let entry = 'index.js';
71
- const pkgPath = path.join(repoRoot, 'package.json');
72
- if (fs.existsSync(pkgPath)) {
73
- try {
74
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
75
- if (pkg.main) entry = pkg.main;
76
- else if (pkg.scripts && pkg.scripts.start) {
77
- const m = /node\s+([^\s]+)/.exec(pkg.scripts.start);
78
- if (m) entry = m[1];
79
- } else if (fs.existsSync(path.join(repoRoot, 'app.js'))) entry = 'app.js';
80
- else if (fs.existsSync(path.join(repoRoot, 'server.js'))) entry = 'server.js';
81
- } catch {
82
- // ignore
83
- }
84
- }
85
- return { cmd: 'node', args: [entry] };
86
- }
87
- return { cmd: pm, args: ['run', 'dev'] };
88
- }
89
-
90
- function healthPathFor(framework) {
91
- const fw = (framework || '').toLowerCase();
92
- if (fw === 'express' || fw === 'node') return '/health';
93
- return '/';
94
- }
95
-
96
- // ---------------------------------------------------------------------------
97
- // Capture server
98
- // ---------------------------------------------------------------------------
99
-
100
- function startCaptureServer() {
101
- return new Promise((resolve, reject) => {
102
- const events = [];
103
- const server = createServer((req, res) => {
104
- if (req.method === 'POST' && req.url && req.url.startsWith('/api/ingest/v1/collect')) {
105
- let body = '';
106
- req.on('data', (chunk) => (body += chunk.toString()));
107
- req.on('end', () => {
108
- let parsed = null;
109
- try {
110
- parsed = JSON.parse(body);
111
- } catch {
112
- parsed = { _raw: body };
113
- }
114
- if (Array.isArray(parsed)) {
115
- for (const ev of parsed) events.push(ev);
116
- } else if (parsed && Array.isArray(parsed.events)) {
117
- for (const ev of parsed.events) events.push(ev);
118
- } else if (parsed) {
119
- events.push(parsed);
120
- }
121
- res.writeHead(200, { 'content-type': 'application/json' });
122
- res.end(JSON.stringify({ ok: true }));
123
- });
124
- return;
125
- }
126
- if (req.method === 'POST' && req.url && req.url.startsWith('/api/ingest/v1/health')) {
127
- res.writeHead(200, { 'content-type': 'application/json' });
128
- res.end(JSON.stringify({ ok: true }));
129
- return;
130
- }
131
- res.writeHead(404);
132
- res.end();
133
- });
134
- server.on('error', reject);
135
- server.listen(0, '127.0.0.1', () => {
136
- const addr = server.address();
137
- const port = typeof addr === 'object' && addr ? addr.port : 0;
138
- resolve({
139
- url: `http://127.0.0.1:${port}`,
140
- port,
141
- events,
142
- close: () => new Promise((r) => server.close(() => r())),
143
- });
144
- });
145
- });
146
- }
147
-
148
- // ---------------------------------------------------------------------------
149
- // Polling
150
- // ---------------------------------------------------------------------------
151
-
152
- function defaultSleep(ms) {
153
- return new Promise((r) => setTimeout(r, ms));
154
- }
155
-
156
- async function waitForUrl(url, timeoutMs, fetchImpl = globalThis.fetch, sleep = defaultSleep) {
157
- const deadline = Date.now() + timeoutMs;
158
- let lastErr = null;
159
- while (Date.now() < deadline) {
160
- try {
161
- const res = await fetchImpl(url);
162
- if (res && res.status && res.status < 500) return true;
163
- } catch (err) {
164
- lastErr = err;
165
- }
166
- await sleep(250);
167
- }
168
- throw new Error(`Timeout waiting for ${url}${lastErr ? `: ${lastErr.message || lastErr}` : ''}`);
169
- }
170
-
171
- // ---------------------------------------------------------------------------
172
- // Event assertion
173
- // ---------------------------------------------------------------------------
174
-
175
- function findPageViewEvent(events, siteId, tenantId) {
176
- for (const ev of events) {
177
- if (!ev) continue;
178
- const name = ev.event_name || ev.eventName || ev.name || ev.type;
179
- if (name !== '$page_view') continue;
180
- const sid = ev.site_id || ev.siteId || (ev.context && ev.context.site_id);
181
- const tid = ev.tenant_id || ev.tenantId || (ev.context && ev.context.tenant_id);
182
- if (sid !== siteId) continue;
183
- if (tid !== tenantId) continue;
184
- return ev;
185
- }
186
- return null;
187
- }
188
-
189
- // ---------------------------------------------------------------------------
190
- // Main verifier
191
- // ---------------------------------------------------------------------------
192
-
193
- async function runVerify(args, deps) {
194
- const result = {
195
- ok: false,
196
- framework: args.framework,
197
- capturedEventCount: 0,
198
- pageViewSeen: false,
199
- reason: null,
200
- dryRun: !!args.dryRun,
201
- };
202
-
203
- if (!args.path) {
204
- result.reason = 'missing repo path';
205
- return result;
206
- }
207
- if (!args.siteId || !args.tenantId) {
208
- result.reason = 'missing --site-id / --tenant-id';
209
- return result;
210
- }
211
- if (!args.framework) {
212
- result.reason = 'missing --framework';
213
- return result;
214
- }
215
-
216
- const repoRoot = path.resolve(args.path);
217
- const pm = detectPackageManager(repoRoot);
218
- const dev = resolveDevCommand(args.framework, repoRoot, pm);
219
- const healthPath = healthPathFor(args.framework);
220
-
221
- if (args.dryRun) {
222
- result.ok = true;
223
- result.reason = 'dry-run';
224
- result.plan = {
225
- pm,
226
- dev,
227
- healthPath,
228
- port: args.port,
229
- timeout: args.timeout,
230
- };
231
- if (deps.log && deps.log.info) {
232
- deps.log.info(
233
- `[verify] dry-run plan: ${dev.cmd} ${dev.args.join(' ')} in ${repoRoot} ` +
234
- `(pm=${pm}, port=${args.port}, health=${healthPath})`,
235
- );
236
- }
237
- return result;
238
- }
239
-
240
- const capture = await deps.startCapture();
241
- if (deps.log && deps.log.info) {
242
- deps.log.info(`[verify] capture server listening at ${capture.url}`);
243
- }
244
-
245
- let child = null;
246
- let browser = null;
247
- try {
248
- const envOverrides = {
249
- NEXT_PUBLIC_GURULU_INGEST_URL: capture.url,
250
- GURULU_INGEST_URL: capture.url,
251
- VITE_GURULU_INGEST_URL: capture.url,
252
- NEXT_PUBLIC_GURULU_SITE_ID: args.siteId,
253
- NEXT_PUBLIC_GURULU_TENANT_ID: args.tenantId,
254
- PORT: String(args.port),
255
- };
256
- child = deps.spawnDev({
257
- cmd: dev.cmd,
258
- args: dev.args,
259
- cwd: repoRoot,
260
- env: Object.assign({}, process.env, envOverrides),
261
- });
262
-
263
- const targetUrl = `http://127.0.0.1:${args.port}${healthPath}`;
264
- await deps.waitForUrl(targetUrl, args.timeout);
265
-
266
- browser = await deps.launchBrowser();
267
- const context = await browser.newContext();
268
- const page = await context.newPage();
269
- await page.goto(`http://127.0.0.1:${args.port}/`, { waitUntil: 'networkidle' });
270
- await new Promise((r) => setTimeout(r, 50));
271
-
272
- result.capturedEventCount = capture.events.length;
273
- const hit = findPageViewEvent(capture.events, args.siteId, args.tenantId);
274
- if (!hit) {
275
- result.reason = 'no page_view received';
276
- return result;
277
- }
278
- result.pageViewSeen = true;
279
- result.ok = true;
280
- return result;
281
- } catch (err) {
282
- result.reason = err && err.message ? err.message : String(err);
283
- return result;
284
- } finally {
285
- try {
286
- if (browser && typeof browser.close === 'function') await browser.close();
287
- } catch {}
288
- try {
289
- if (child && typeof child.kill === 'function') child.kill('SIGTERM');
290
- } catch {}
291
- try {
292
- await capture.close();
293
- } catch {}
294
- }
295
- }
296
-
297
- // ---------------------------------------------------------------------------
298
- // Default deps (real implementations)
299
- // ---------------------------------------------------------------------------
300
-
301
- function createDefaultDeps() {
302
- return {
303
- startCapture: startCaptureServer,
304
- spawnDev: ({ cmd, args, cwd, env }) =>
305
- spawn(cmd, args, {
306
- cwd,
307
- env,
308
- stdio: ['ignore', 'pipe', 'pipe'],
309
- shell: process.platform === 'win32',
310
- }),
311
- waitForUrl: (url, timeout) => waitForUrl(url, timeout),
312
- launchBrowser: async () => {
313
- const { chromium } = require('playwright-core');
314
- return chromium.launch({ headless: true });
315
- },
316
- log: {
317
- info: (m) => process.stderr.write(`${m}\n`),
318
- warn: (m) => process.stderr.write(`${m}\n`),
319
- error: (m) => process.stderr.write(`${m}\n`),
320
- },
321
- };
322
- }
323
-
324
- module.exports = {
325
- parseArgs,
326
- detectPackageManager,
327
- resolveDevCommand,
328
- healthPathFor,
329
- startCaptureServer,
330
- waitForUrl,
331
- findPageViewEvent,
332
- runVerify,
333
- createDefaultDeps,
334
- };
@@ -1,59 +0,0 @@
1
- #!/usr/bin/env node
2
- // gurulu-verify-install.mjs — standalone post-install smoke test CLI
3
- // (Phase 16 A3).
4
- //
5
- // Verification logic lives in `scripts/gurulu-verify-install.lib.cjs` so
6
- // it can be reused by ts-jest tests via `require()`. This wrapper exposes
7
- // the same API as an ESM module and, when invoked directly, runs the CLI.
8
- //
9
- // Usage:
10
- // node scripts/gurulu-verify-install.mjs <repo-path> \
11
- // --framework nextjs-app --site-id demo --tenant-id demo \
12
- // [--ingest-url <url>] [--port 3000] [--timeout 30000] [--dry-run]
13
-
14
- import { createRequire } from 'node:module';
15
- import path from 'node:path';
16
- import { fileURLToPath } from 'node:url';
17
-
18
- const require = createRequire(import.meta.url);
19
- const lib = require('./gurulu-verify-install.lib.cjs');
20
-
21
- export const parseArgs = lib.parseArgs;
22
- export const detectPackageManager = lib.detectPackageManager;
23
- export const resolveDevCommand = lib.resolveDevCommand;
24
- export const healthPathFor = lib.healthPathFor;
25
- export const startCaptureServer = lib.startCaptureServer;
26
- export const waitForUrl = lib.waitForUrl;
27
- export const findPageViewEvent = lib.findPageViewEvent;
28
- export const runVerify = lib.runVerify;
29
- export const createDefaultDeps = lib.createDefaultDeps;
30
-
31
- const isMain = (() => {
32
- try {
33
- const thisFile = fileURLToPath(import.meta.url);
34
- return process.argv[1] && path.resolve(process.argv[1]) === path.resolve(thisFile);
35
- } catch {
36
- return false;
37
- }
38
- })();
39
-
40
- if (isMain) {
41
- const args = lib.parseArgs(process.argv.slice(2));
42
- const deps = lib.createDefaultDeps();
43
- lib
44
- .runVerify(args, deps)
45
- .then((result) => {
46
- process.stdout.write(`VERIFY_RESULT ${JSON.stringify(result)}\n`);
47
- process.exit(result.ok ? 0 : 1);
48
- })
49
- .catch((err) => {
50
- process.stderr.write(`[verify] fatal: ${err && err.stack ? err.stack : err}\n`);
51
- process.stdout.write(
52
- `VERIFY_RESULT ${JSON.stringify({
53
- ok: false,
54
- reason: String(err && err.message ? err.message : err),
55
- })}\n`,
56
- );
57
- process.exit(2);
58
- });
59
- }
@@ -1,26 +0,0 @@
1
- #!/bin/bash
2
- set -e
3
-
4
- DOMAIN="${DOMAIN:-gurulu.io}"
5
- EMAIL="${SSL_EMAIL:-admin@gurulu.io}"
6
-
7
- echo "[ssl] Initializing SSL for ${DOMAIN}..."
8
-
9
- # Create certbot webroot
10
- mkdir -p /var/www/certbot
11
-
12
- # Request certificate
13
- docker run --rm \
14
- -v "$(pwd)/certbot/conf:/etc/letsencrypt" \
15
- -v "$(pwd)/certbot/www:/var/www/certbot" \
16
- certbot/certbot certonly \
17
- --webroot \
18
- --webroot-path=/var/www/certbot \
19
- --email "$EMAIL" \
20
- --agree-tos \
21
- --no-eff-email \
22
- -d "$DOMAIN" \
23
- -d "www.${DOMAIN}"
24
-
25
- echo "[ssl] Certificate obtained for ${DOMAIN}"
26
- echo "[ssl] Files at: certbot/conf/live/${DOMAIN}/"
@@ -1,86 +0,0 @@
1
- #!/usr/bin/env bash
2
- # =============================================================================
3
- # Migration: flow-graph enum extension (W2.1)
4
- #
5
- # Extends ClickHouse enums on gurulu.flow_nodes.node_type and
6
- # gurulu.flow_edges.edge_type with new values needed by the flow-graph
7
- # processor rework in W2.2.
8
- #
9
- # flow_nodes.node_type: + 'step'=9, 'state_change'=10, 'drawer'=11, 'tab'=12
10
- # flow_edges.edge_type: + 'advances_to'=9, 'interacts_with'=10, 'verifies_as'=11
11
- #
12
- # Idempotent: checks DESCRIBE TABLE output for the new enum values before
13
- # running; if already present, exits 0. Existing rows are untouched.
14
- # =============================================================================
15
- set -euo pipefail
16
-
17
- CONTAINER="${CLICKHOUSE_CONTAINER:-gurulu-clickhouse}"
18
- DB="${CLICKHOUSE_DB:-gurulu}"
19
-
20
- ch_query() {
21
- docker exec "$CONTAINER" clickhouse-client --query "$1"
22
- }
23
-
24
- log() { printf '[migrate-flow-graph-enums] %s\n' "$*"; }
25
- die() { printf '[migrate-flow-graph-enums] ERROR: %s\n' "$*" >&2; exit 1; }
26
-
27
- # --- Preflight ---------------------------------------------------------------
28
- if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then
29
- die "ClickHouse container '${CONTAINER}' is not running"
30
- fi
31
-
32
- log "Inspecting current enum definitions..."
33
- NODE_DESC=$(ch_query "DESCRIBE TABLE ${DB}.flow_nodes FORMAT TSVRaw" | awk -F'\t' '$1=="node_type"{print $2}')
34
- EDGE_DESC=$(ch_query "DESCRIBE TABLE ${DB}.flow_edges FORMAT TSVRaw" | awk -F'\t' '$1=="edge_type"{print $2}')
35
-
36
- if [[ -z "$NODE_DESC" ]]; then die "Could not read flow_nodes.node_type definition"; fi
37
- if [[ -z "$EDGE_DESC" ]]; then die "Could not read flow_edges.edge_type definition"; fi
38
-
39
- log "flow_nodes.node_type = ${NODE_DESC}"
40
- log "flow_edges.edge_type = ${EDGE_DESC}"
41
-
42
- NODE_NEEDED=("'step' = 9" "'state_change' = 10" "'drawer' = 11" "'tab' = 12")
43
- EDGE_NEEDED=("'advances_to' = 9" "'interacts_with' = 10" "'verifies_as' = 11")
44
-
45
- node_has_all=true
46
- for v in "${NODE_NEEDED[@]}"; do
47
- if [[ "$NODE_DESC" != *"$v"* ]]; then node_has_all=false; break; fi
48
- done
49
-
50
- edge_has_all=true
51
- for v in "${EDGE_NEEDED[@]}"; do
52
- if [[ "$EDGE_DESC" != *"$v"* ]]; then edge_has_all=false; break; fi
53
- done
54
-
55
- if $node_has_all && $edge_has_all; then
56
- log "already migrated — new enum values are present on both tables"
57
- log "flow_nodes count: $(ch_query "SELECT count() FROM ${DB}.flow_nodes")"
58
- log "flow_edges count: $(ch_query "SELECT count() FROM ${DB}.flow_edges")"
59
- exit 0
60
- fi
61
-
62
- # --- Apply ALTERs ------------------------------------------------------------
63
- NODE_ENUM="Enum8('page' = 1, 'modal' = 2, 'form' = 3, 'action' = 4, 'api_call' = 5, 'success' = 6, 'error' = 7, 'milestone' = 8, 'step' = 9, 'state_change' = 10, 'drawer' = 11, 'tab' = 12)"
64
- EDGE_ENUM="Enum8('navigates' = 1, 'opens' = 2, 'submits' = 3, 'triggers' = 4, 'resolves' = 5, 'fails' = 6, 'completes' = 7, 'abandons' = 8, 'advances_to' = 9, 'interacts_with' = 10, 'verifies_as' = 11)"
65
-
66
- if ! $node_has_all; then
67
- log "ALTER flow_nodes.node_type -> extended enum"
68
- ch_query "ALTER TABLE ${DB}.flow_nodes MODIFY COLUMN node_type ${NODE_ENUM} DEFAULT 'page'"
69
- fi
70
-
71
- if ! $edge_has_all; then
72
- log "ALTER flow_edges.edge_type -> extended enum"
73
- ch_query "ALTER TABLE ${DB}.flow_edges MODIFY COLUMN edge_type ${EDGE_ENUM} DEFAULT 'navigates'"
74
- fi
75
-
76
- # --- Verify ------------------------------------------------------------------
77
- log "Post-migration DESCRIBE:"
78
- ch_query "DESCRIBE TABLE ${DB}.flow_nodes" | awk -F'\t' '$1=="node_type"{print " node_type: "$2}'
79
- ch_query "DESCRIBE TABLE ${DB}.flow_edges" | awk -F'\t' '$1=="edge_type"{print " edge_type: "$2}'
80
-
81
- NODE_COUNT=$(ch_query "SELECT count() FROM ${DB}.flow_nodes")
82
- EDGE_COUNT=$(ch_query "SELECT count() FROM ${DB}.flow_edges")
83
-
84
- log "Sanity check — flow_nodes count: ${NODE_COUNT}"
85
- log "Sanity check — flow_edges count: ${EDGE_COUNT}"
86
- log "Migration complete."
@@ -1,24 +0,0 @@
1
- #!/bin/bash
2
- # Disk usage monitor — runs via cron every 6 hours
3
- # Logs to /data/backups/monitor.log
4
- # Exit codes: 0=OK, 1=WARNING, 2=CRITICAL
5
-
6
- THRESHOLD_WARN=80
7
- THRESHOLD_CRIT=90
8
-
9
- USAGE=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
10
- DATA_USAGE=$(df /data | tail -1 | awk '{print $5}' | tr -d '%' 2>/dev/null || echo "0")
11
-
12
- TIMESTAMP=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
13
-
14
- if [ "$USAGE" -ge "$THRESHOLD_CRIT" ] || [ "$DATA_USAGE" -ge "$THRESHOLD_CRIT" ]; then
15
- echo "[$TIMESTAMP] CRITICAL: Disk usage root=${USAGE}% data=${DATA_USAGE}%"
16
- # TODO: send alert (Discord webhook or email)
17
- exit 2
18
- elif [ "$USAGE" -ge "$THRESHOLD_WARN" ] || [ "$DATA_USAGE" -ge "$THRESHOLD_WARN" ]; then
19
- echo "[$TIMESTAMP] WARNING: Disk usage root=${USAGE}% data=${DATA_USAGE}%"
20
- exit 1
21
- else
22
- echo "[$TIMESTAMP] OK: Disk usage root=${USAGE}% data=${DATA_USAGE}%"
23
- exit 0
24
- fi
@@ -1,74 +0,0 @@
1
- // astro.patch.cjs — Phase 18.7 A1
2
- //
3
- // Detects an Astro project by the presence of a layout or index page
4
- // `.astro` file. Astro processes `<script>` tags by default (Vite bundle);
5
- // we use the `is:inline` directive so Astro emits our tracker tag exactly
6
- // as written, without touching it.
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
-
11
- const NAME = 'astro';
12
- const CANDIDATES = [
13
- 'src/layouts/Layout.astro',
14
- 'src/layouts/BaseLayout.astro',
15
- 'src/layouts/MainLayout.astro',
16
- 'src/pages/index.astro',
17
- ];
18
-
19
- const MARKER = 'data-gurulu-install="1"';
20
-
21
- function detect(repoRoot) {
22
- for (const rel of CANDIDATES) {
23
- if (fs.existsSync(path.join(repoRoot, rel))) {
24
- return { framework: NAME, files: [rel] };
25
- }
26
- }
27
- return null;
28
- }
29
-
30
- function buildScriptTag(injection) {
31
- const pkAttr = injection.publishableKey
32
- ? ` data-gurulu-publishable-key="${injection.publishableKey}"`
33
- : '';
34
- return (
35
- ` <script\n` +
36
- ` is:inline\n` +
37
- ` src="${injection.scriptSrc}"\n` +
38
- ` data-gurulu-site-id="${injection.siteId}"\n` +
39
- ` data-gurulu-tenant-id="${injection.tenantId}"${pkAttr}\n` +
40
- ` data-features="errors,replay,advanced"\n` +
41
- ` ${MARKER}\n` +
42
- ` async\n` +
43
- ` ></script>\n`
44
- );
45
- }
46
-
47
- function injectScriptTag(source, injection) {
48
- if (source.includes(MARKER)) return source;
49
- const tag = buildScriptTag(injection);
50
- if (/<\/body>/.test(source)) {
51
- return source.replace(/<\/body>/, `${tag} </body>`);
52
- }
53
- if (/<\/head>/.test(source)) {
54
- return source.replace(/<\/head>/, `${tag} </head>`);
55
- }
56
- return source + '\n' + tag;
57
- }
58
-
59
- function plan(ctx) {
60
- const detection = detect(ctx.repoRoot);
61
- if (!detection) return { changes: [], notes: ['no-astro-layout'] };
62
- const changes = [];
63
- for (const rel of detection.files) {
64
- const abs = path.join(ctx.repoRoot, rel);
65
- const before = fs.readFileSync(abs, 'utf8');
66
- const after = injectScriptTag(before, ctx.injection);
67
- if (after !== before) {
68
- changes.push({ relPath: rel, before, after, reason: 'inject-script-tag' });
69
- }
70
- }
71
- return { changes, notes: [] };
72
- }
73
-
74
- module.exports = { name: NAME, detect, plan };