@gurulu/cli 0.4.7 → 1.0.1

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 (190) 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 +25751 -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 +33 -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 +25326 -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/dist/lib/detect.d.ts +27 -0
  28. package/dist/lib/detect.d.ts.map +1 -0
  29. package/dist/lib/detect.js +106 -0
  30. package/dist/lib/exec-install.d.ts +21 -0
  31. package/dist/lib/exec-install.d.ts.map +1 -0
  32. package/dist/lib/install-plan.d.ts +25 -0
  33. package/dist/lib/install-plan.d.ts.map +1 -0
  34. package/dist/lib/install-plan.js +161 -0
  35. package/package.json +51 -20
  36. package/bin/gurulu.js +0 -2
  37. package/dist/api-client.d.ts +0 -33
  38. package/dist/api-client.js +0 -175
  39. package/dist/commands/add-server.d.ts +0 -9
  40. package/dist/commands/add-server.js +0 -162
  41. package/dist/commands/alerts.d.ts +0 -27
  42. package/dist/commands/alerts.js +0 -309
  43. package/dist/commands/api-keys.d.ts +0 -20
  44. package/dist/commands/api-keys.js +0 -130
  45. package/dist/commands/attribution.d.ts +0 -22
  46. package/dist/commands/attribution.js +0 -111
  47. package/dist/commands/audiences.d.ts +0 -23
  48. package/dist/commands/audiences.js +0 -243
  49. package/dist/commands/audit.d.ts +0 -20
  50. package/dist/commands/audit.js +0 -130
  51. package/dist/commands/auth.js +0 -249
  52. package/dist/commands/chat.d.ts +0 -19
  53. package/dist/commands/chat.js +0 -118
  54. package/dist/commands/config.d.ts +0 -10
  55. package/dist/commands/config.js +0 -92
  56. package/dist/commands/consent.d.ts +0 -27
  57. package/dist/commands/consent.js +0 -233
  58. package/dist/commands/conversion-paths.d.ts +0 -19
  59. package/dist/commands/conversion-paths.js +0 -55
  60. package/dist/commands/db.d.ts +0 -25
  61. package/dist/commands/db.js +0 -330
  62. package/dist/commands/destinations.d.ts +0 -20
  63. package/dist/commands/destinations.js +0 -191
  64. package/dist/commands/doctor.js +0 -360
  65. package/dist/commands/errors.d.ts +0 -27
  66. package/dist/commands/errors.js +0 -121
  67. package/dist/commands/events.d.ts +0 -33
  68. package/dist/commands/events.js +0 -371
  69. package/dist/commands/experiments.d.ts +0 -22
  70. package/dist/commands/experiments.js +0 -264
  71. package/dist/commands/funnels.d.ts +0 -17
  72. package/dist/commands/funnels.js +0 -203
  73. package/dist/commands/goals.d.ts +0 -18
  74. package/dist/commands/goals.js +0 -214
  75. package/dist/commands/heatmap.d.ts +0 -27
  76. package/dist/commands/heatmap.js +0 -112
  77. package/dist/commands/identity.d.ts +0 -29
  78. package/dist/commands/identity.js +0 -328
  79. package/dist/commands/init.js +0 -215
  80. package/dist/commands/insights.d.ts +0 -10
  81. package/dist/commands/insights.js +0 -77
  82. package/dist/commands/install.d.ts +0 -259
  83. package/dist/commands/install.js +0 -1590
  84. package/dist/commands/login.d.ts +0 -20
  85. package/dist/commands/login.js +0 -170
  86. package/dist/commands/logout.d.ts +0 -10
  87. package/dist/commands/logout.js +0 -41
  88. package/dist/commands/playground.d.ts +0 -11
  89. package/dist/commands/playground.js +0 -47
  90. package/dist/commands/releases.d.ts +0 -17
  91. package/dist/commands/releases.js +0 -54
  92. package/dist/commands/replay.d.ts +0 -18
  93. package/dist/commands/replay.js +0 -64
  94. package/dist/commands/secrets.d.ts +0 -19
  95. package/dist/commands/secrets.js +0 -145
  96. package/dist/commands/setup.d.ts +0 -21
  97. package/dist/commands/setup.js +0 -67
  98. package/dist/commands/sites.d.ts +0 -18
  99. package/dist/commands/sites.js +0 -139
  100. package/dist/commands/skad.d.ts +0 -18
  101. package/dist/commands/skad.js +0 -53
  102. package/dist/commands/sourcemap.d.ts +0 -33
  103. package/dist/commands/sourcemap.js +0 -204
  104. package/dist/commands/status.d.ts +0 -7
  105. package/dist/commands/status.js +0 -136
  106. package/dist/commands/upgrade.d.ts +0 -21
  107. package/dist/commands/upgrade.js +0 -183
  108. package/dist/commands/warehouse.d.ts +0 -20
  109. package/dist/commands/warehouse.js +0 -65
  110. package/dist/commands/warehouses.d.ts +0 -17
  111. package/dist/commands/warehouses.js +0 -182
  112. package/dist/commands/watch.d.ts +0 -45
  113. package/dist/commands/watch.js +0 -258
  114. package/dist/commands/whoami.d.ts +0 -9
  115. package/dist/commands/whoami.js +0 -50
  116. package/dist/config.d.ts +0 -75
  117. package/dist/config.js +0 -329
  118. package/dist/frameworks/detect.d.ts +0 -8
  119. package/dist/frameworks/detect.js +0 -458
  120. package/dist/install-intent-proposal.d.ts +0 -99
  121. package/dist/install-intent-proposal.js +0 -202
  122. package/dist/utils/api.d.ts +0 -20
  123. package/dist/utils/api.js +0 -47
  124. package/dist/utils/config.d.ts +0 -13
  125. package/dist/utils/config.js +0 -30
  126. package/dist/utils/confirm.d.ts +0 -17
  127. package/dist/utils/confirm.js +0 -40
  128. package/dist/utils/dry-run.d.ts +0 -20
  129. package/dist/utils/dry-run.js +0 -67
  130. package/dist/utils/from-file.d.ts +0 -9
  131. package/dist/utils/from-file.js +0 -72
  132. package/dist/utils/redact.d.ts +0 -14
  133. package/dist/utils/redact.js +0 -48
  134. package/dist/utils/ui.d.ts +0 -14
  135. package/dist/utils/ui.js +0 -59
  136. package/scripts/.gitkeep +0 -0
  137. package/scripts/README-gurulu-agentic-install.md +0 -114
  138. package/scripts/README-gurulu-scan.md +0 -98
  139. package/scripts/audit-cli-scopes.mjs +0 -204
  140. package/scripts/backfill-tenant-id.mjs +0 -172
  141. package/scripts/backfill-tenant-links.ts +0 -252
  142. package/scripts/backup-clickhouse.sh +0 -27
  143. package/scripts/backup-postgres.sh +0 -19
  144. package/scripts/bootstrap-runtime-schema.mjs +0 -87
  145. package/scripts/bootstrap-stripe.mjs +0 -158
  146. package/scripts/gurulu-agentic-install.lib.cjs +0 -762
  147. package/scripts/gurulu-agentic-install.mjs +0 -623
  148. package/scripts/gurulu-scan.lib.cjs +0 -1509
  149. package/scripts/gurulu-scan.mjs +0 -91
  150. package/scripts/gurulu-verify-install.lib.cjs +0 -334
  151. package/scripts/gurulu-verify-install.mjs +0 -59
  152. package/scripts/init-ssl.sh +0 -26
  153. package/scripts/migrate-flow-graph-enums.sh +0 -86
  154. package/scripts/monitor-disk.sh +0 -24
  155. package/scripts/patches/astro.patch.cjs +0 -74
  156. package/scripts/patches/auto-instrument/ast-helper.cjs +0 -480
  157. package/scripts/patches/auto-instrument/astro.cjs +0 -273
  158. package/scripts/patches/auto-instrument/express.cjs +0 -383
  159. package/scripts/patches/auto-instrument/fastify.cjs +0 -262
  160. package/scripts/patches/auto-instrument/hono.cjs +0 -392
  161. package/scripts/patches/auto-instrument/index.cjs +0 -80
  162. package/scripts/patches/auto-instrument/nestjs.cjs +0 -286
  163. package/scripts/patches/auto-instrument/nextjs-app-router.cjs +0 -345
  164. package/scripts/patches/auto-instrument/nextjs-pages.cjs +0 -361
  165. package/scripts/patches/auto-instrument/remix.cjs +0 -168
  166. package/scripts/patches/auto-instrument/sdk-helper-map.cjs +0 -241
  167. package/scripts/patches/auto-instrument/singleton-helper.cjs +0 -193
  168. package/scripts/patches/auto-instrument/sveltekit.cjs +0 -161
  169. package/scripts/patches/auto-instrument/vite-react.cjs +0 -37
  170. package/scripts/patches/auto-instrument/vue.cjs +0 -196
  171. package/scripts/patches/express.patch.cjs +0 -99
  172. package/scripts/patches/fastify.patch.cjs +0 -108
  173. package/scripts/patches/index.cjs +0 -300
  174. package/scripts/patches/nestjs.patch.cjs +0 -112
  175. package/scripts/patches/nextjs-app-router.patch.cjs +0 -97
  176. package/scripts/patches/nextjs-pages.patch.cjs +0 -97
  177. package/scripts/patches/remix.patch.cjs +0 -75
  178. package/scripts/patches/sveltekit.patch.cjs +0 -72
  179. package/scripts/patches/vite-react.patch.cjs +0 -73
  180. package/scripts/patches/vue.patch.cjs +0 -82
  181. package/scripts/renew-ssl.sh +0 -14
  182. package/scripts/resolve-migration.sh +0 -23
  183. package/scripts/seed-cli-dev-keys.mjs +0 -130
  184. package/scripts/seed-test-data.mjs +0 -391
  185. package/scripts/spike-browserless.ts +0 -65
  186. package/scripts/tenant-pivot-consistency-check.mjs +0 -205
  187. package/scripts/tenant-pivot-phase-3-cleanup.lib.cjs +0 -258
  188. package/scripts/tenant-pivot-phase-3-cleanup.mjs +0 -98
  189. package/scripts/test-identity-resolution.ts +0 -804
  190. 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 };