@gurulu/cli 0.4.6 → 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 (180) 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 -853
  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 -18
  45. package/dist/commands/chat.js +0 -117
  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 -349
  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 -65
  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/sites.d.ts +0 -18
  89. package/dist/commands/sites.js +0 -139
  90. package/dist/commands/skad.d.ts +0 -18
  91. package/dist/commands/skad.js +0 -53
  92. package/dist/commands/sourcemap.d.ts +0 -33
  93. package/dist/commands/sourcemap.js +0 -204
  94. package/dist/commands/status.d.ts +0 -7
  95. package/dist/commands/status.js +0 -136
  96. package/dist/commands/upgrade.d.ts +0 -21
  97. package/dist/commands/upgrade.js +0 -183
  98. package/dist/commands/warehouse.d.ts +0 -20
  99. package/dist/commands/warehouse.js +0 -65
  100. package/dist/commands/warehouses.d.ts +0 -17
  101. package/dist/commands/warehouses.js +0 -182
  102. package/dist/commands/watch.d.ts +0 -45
  103. package/dist/commands/watch.js +0 -258
  104. package/dist/commands/whoami.d.ts +0 -9
  105. package/dist/commands/whoami.js +0 -50
  106. package/dist/config.d.ts +0 -75
  107. package/dist/config.js +0 -329
  108. package/dist/frameworks/detect.d.ts +0 -8
  109. package/dist/frameworks/detect.js +0 -444
  110. package/dist/install-intent-proposal.d.ts +0 -99
  111. package/dist/install-intent-proposal.js +0 -202
  112. package/dist/utils/api.d.ts +0 -20
  113. package/dist/utils/api.js +0 -47
  114. package/dist/utils/config.d.ts +0 -13
  115. package/dist/utils/config.js +0 -30
  116. package/dist/utils/confirm.d.ts +0 -17
  117. package/dist/utils/confirm.js +0 -40
  118. package/dist/utils/dry-run.d.ts +0 -20
  119. package/dist/utils/dry-run.js +0 -67
  120. package/dist/utils/from-file.d.ts +0 -9
  121. package/dist/utils/from-file.js +0 -72
  122. package/dist/utils/redact.d.ts +0 -14
  123. package/dist/utils/redact.js +0 -48
  124. package/dist/utils/ui.d.ts +0 -14
  125. package/dist/utils/ui.js +0 -59
  126. package/scripts/.gitkeep +0 -0
  127. package/scripts/README-gurulu-agentic-install.md +0 -114
  128. package/scripts/README-gurulu-scan.md +0 -98
  129. package/scripts/audit-cli-scopes.mjs +0 -204
  130. package/scripts/backfill-tenant-id.mjs +0 -172
  131. package/scripts/backfill-tenant-links.ts +0 -252
  132. package/scripts/backup-clickhouse.sh +0 -27
  133. package/scripts/backup-postgres.sh +0 -19
  134. package/scripts/bootstrap-runtime-schema.mjs +0 -87
  135. package/scripts/bootstrap-stripe.mjs +0 -158
  136. package/scripts/gurulu-agentic-install.lib.cjs +0 -762
  137. package/scripts/gurulu-agentic-install.mjs +0 -623
  138. package/scripts/gurulu-scan.lib.cjs +0 -1509
  139. package/scripts/gurulu-scan.mjs +0 -91
  140. package/scripts/gurulu-verify-install.lib.cjs +0 -334
  141. package/scripts/gurulu-verify-install.mjs +0 -59
  142. package/scripts/init-ssl.sh +0 -26
  143. package/scripts/migrate-flow-graph-enums.sh +0 -86
  144. package/scripts/monitor-disk.sh +0 -24
  145. package/scripts/patches/astro.patch.cjs +0 -74
  146. package/scripts/patches/auto-instrument/ast-helper.cjs +0 -480
  147. package/scripts/patches/auto-instrument/astro.cjs +0 -273
  148. package/scripts/patches/auto-instrument/express.cjs +0 -383
  149. package/scripts/patches/auto-instrument/fastify.cjs +0 -262
  150. package/scripts/patches/auto-instrument/hono.cjs +0 -392
  151. package/scripts/patches/auto-instrument/index.cjs +0 -80
  152. package/scripts/patches/auto-instrument/nestjs.cjs +0 -286
  153. package/scripts/patches/auto-instrument/nextjs-app-router.cjs +0 -345
  154. package/scripts/patches/auto-instrument/nextjs-pages.cjs +0 -361
  155. package/scripts/patches/auto-instrument/remix.cjs +0 -168
  156. package/scripts/patches/auto-instrument/sdk-helper-map.cjs +0 -241
  157. package/scripts/patches/auto-instrument/singleton-helper.cjs +0 -193
  158. package/scripts/patches/auto-instrument/sveltekit.cjs +0 -161
  159. package/scripts/patches/auto-instrument/vite-react.cjs +0 -37
  160. package/scripts/patches/auto-instrument/vue.cjs +0 -196
  161. package/scripts/patches/express.patch.cjs +0 -99
  162. package/scripts/patches/fastify.patch.cjs +0 -108
  163. package/scripts/patches/index.cjs +0 -300
  164. package/scripts/patches/nestjs.patch.cjs +0 -112
  165. package/scripts/patches/nextjs-app-router.patch.cjs +0 -97
  166. package/scripts/patches/nextjs-pages.patch.cjs +0 -97
  167. package/scripts/patches/remix.patch.cjs +0 -75
  168. package/scripts/patches/sveltekit.patch.cjs +0 -72
  169. package/scripts/patches/vite-react.patch.cjs +0 -73
  170. package/scripts/patches/vue.patch.cjs +0 -82
  171. package/scripts/renew-ssl.sh +0 -14
  172. package/scripts/resolve-migration.sh +0 -23
  173. package/scripts/seed-cli-dev-keys.mjs +0 -130
  174. package/scripts/seed-test-data.mjs +0 -391
  175. package/scripts/spike-browserless.ts +0 -65
  176. package/scripts/tenant-pivot-consistency-check.mjs +0 -205
  177. package/scripts/tenant-pivot-phase-3-cleanup.lib.cjs +0 -258
  178. package/scripts/tenant-pivot-phase-3-cleanup.mjs +0 -98
  179. package/scripts/test-identity-resolution.ts +0 -804
  180. 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 };