@gurulu/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +66 -0
  2. package/bin/gurulu.js +2 -0
  3. package/dist/api-client.d.ts +27 -0
  4. package/dist/api-client.js +150 -0
  5. package/dist/commands/add-server.d.ts +9 -0
  6. package/dist/commands/add-server.js +155 -0
  7. package/dist/commands/alerts.d.ts +22 -0
  8. package/dist/commands/alerts.js +281 -0
  9. package/dist/commands/api-keys.d.ts +20 -0
  10. package/dist/commands/api-keys.js +130 -0
  11. package/dist/commands/audiences.d.ts +16 -0
  12. package/dist/commands/audiences.js +180 -0
  13. package/dist/commands/audit.d.ts +20 -0
  14. package/dist/commands/audit.js +130 -0
  15. package/dist/commands/auth.d.ts +20 -0
  16. package/dist/commands/auth.js +214 -0
  17. package/dist/commands/chat.d.ts +18 -0
  18. package/dist/commands/chat.js +117 -0
  19. package/dist/commands/config.d.ts +10 -0
  20. package/dist/commands/config.js +92 -0
  21. package/dist/commands/db.d.ts +25 -0
  22. package/dist/commands/db.js +322 -0
  23. package/dist/commands/destinations.d.ts +20 -0
  24. package/dist/commands/destinations.js +191 -0
  25. package/dist/commands/doctor.d.ts +7 -0
  26. package/dist/commands/doctor.js +318 -0
  27. package/dist/commands/events.d.ts +27 -0
  28. package/dist/commands/events.js +147 -0
  29. package/dist/commands/experiments.d.ts +18 -0
  30. package/dist/commands/experiments.js +233 -0
  31. package/dist/commands/identity.d.ts +13 -0
  32. package/dist/commands/identity.js +107 -0
  33. package/dist/commands/init.d.ts +11 -0
  34. package/dist/commands/init.js +215 -0
  35. package/dist/commands/insights.d.ts +10 -0
  36. package/dist/commands/insights.js +65 -0
  37. package/dist/commands/install.d.ts +233 -0
  38. package/dist/commands/install.js +920 -0
  39. package/dist/commands/login.d.ts +20 -0
  40. package/dist/commands/login.js +170 -0
  41. package/dist/commands/logout.d.ts +10 -0
  42. package/dist/commands/logout.js +41 -0
  43. package/dist/commands/playground.d.ts +11 -0
  44. package/dist/commands/playground.js +47 -0
  45. package/dist/commands/sites.d.ts +18 -0
  46. package/dist/commands/sites.js +139 -0
  47. package/dist/commands/sourcemap.d.ts +21 -0
  48. package/dist/commands/sourcemap.js +137 -0
  49. package/dist/commands/status.d.ts +7 -0
  50. package/dist/commands/status.js +136 -0
  51. package/dist/commands/warehouse.d.ts +20 -0
  52. package/dist/commands/warehouse.js +65 -0
  53. package/dist/commands/warehouses.d.ts +17 -0
  54. package/dist/commands/warehouses.js +182 -0
  55. package/dist/commands/whoami.d.ts +9 -0
  56. package/dist/commands/whoami.js +47 -0
  57. package/dist/config.d.ts +75 -0
  58. package/dist/config.js +329 -0
  59. package/dist/frameworks/detect.d.ts +8 -0
  60. package/dist/frameworks/detect.js +362 -0
  61. package/dist/index.d.ts +1 -0
  62. package/dist/index.js +429 -0
  63. package/dist/install-intent-proposal.d.ts +99 -0
  64. package/dist/install-intent-proposal.js +202 -0
  65. package/dist/utils/api.d.ts +20 -0
  66. package/dist/utils/api.js +47 -0
  67. package/dist/utils/config.d.ts +13 -0
  68. package/dist/utils/config.js +30 -0
  69. package/dist/utils/confirm.d.ts +17 -0
  70. package/dist/utils/confirm.js +40 -0
  71. package/dist/utils/dry-run.d.ts +20 -0
  72. package/dist/utils/dry-run.js +67 -0
  73. package/dist/utils/from-file.d.ts +9 -0
  74. package/dist/utils/from-file.js +72 -0
  75. package/dist/utils/ui.d.ts +14 -0
  76. package/dist/utils/ui.js +59 -0
  77. package/package.json +26 -0
package/dist/index.js ADDED
@@ -0,0 +1,429 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const yargs_1 = __importDefault(require("yargs"));
7
+ const helpers_1 = require("yargs/helpers");
8
+ const init_1 = require("./commands/init");
9
+ const login_1 = require("./commands/login");
10
+ const auth_1 = require("./commands/auth");
11
+ const logout_1 = require("./commands/logout");
12
+ const whoami_1 = require("./commands/whoami");
13
+ const config_1 = require("./commands/config");
14
+ const sites_1 = require("./commands/sites");
15
+ const api_keys_1 = require("./commands/api-keys");
16
+ const events_1 = require("./commands/events");
17
+ const status_1 = require("./commands/status");
18
+ const doctor_1 = require("./commands/doctor");
19
+ const add_server_1 = require("./commands/add-server");
20
+ const install_1 = require("./commands/install");
21
+ const warehouse_1 = require("./commands/warehouse");
22
+ // Phase 19.5 W2 — Read-surface subcommands
23
+ const audiences_1 = require("./commands/audiences");
24
+ const destinations_1 = require("./commands/destinations");
25
+ const alerts_1 = require("./commands/alerts");
26
+ const insights_1 = require("./commands/insights");
27
+ const warehouses_1 = require("./commands/warehouses");
28
+ const experiments_1 = require("./commands/experiments");
29
+ const identity_1 = require("./commands/identity");
30
+ const playground_1 = require("./commands/playground");
31
+ // Phase 20 W3 C4 — audit log tail/export
32
+ const audit_1 = require("./commands/audit");
33
+ // Gurulu Chat — NL → SQL analytics
34
+ const chat_1 = require("./commands/chat");
35
+ // Error tracking — source map upload
36
+ const sourcemap_1 = require("./commands/sourcemap");
37
+ // Phase 21 — database connect
38
+ const db_1 = require("./commands/db");
39
+ (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
40
+ .scriptName('gurulu')
41
+ .option('profile', { type: 'string', describe: 'Use a specific profile (default: personal)' })
42
+ .command('init', 'Set up Gurulu analytics in your project', (y) => y
43
+ .option('site-id', { type: 'string', describe: 'Site ID' })
44
+ .option('token', { type: 'string', describe: 'Site token' })
45
+ .option('framework', { type: 'string', describe: 'Override framework detection' })
46
+ .option('no-interactive', { type: 'boolean', describe: 'Non-interactive mode' })
47
+ .option('dry-run', { type: 'boolean', describe: 'Show what would be done' })
48
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => {
49
+ (0, init_1.initCommand)({
50
+ siteId: args['site-id'],
51
+ token: args.token,
52
+ framework: args.framework,
53
+ noInteractive: args['no-interactive'],
54
+ dryRun: args['dry-run'],
55
+ json: args.json,
56
+ });
57
+ })
58
+ .command('login', 'Authenticate with your Gurulu account', (y) => y
59
+ .option('email', { type: 'string', describe: 'Email address' })
60
+ .option('secret-key', { type: 'string', describe: 'Secret key (gsk_live_... or gsk_test_...)' })
61
+ .option('api-key', { type: 'string', describe: 'Legacy flag (alias for --secret-key)' })
62
+ .option('no-interactive', { type: 'boolean', describe: 'Non-interactive mode' })
63
+ .option('keychain', { type: 'boolean', describe: 'Store secret in macOS Keychain if available' })
64
+ .option('api-base', { type: 'string', describe: 'Override API base URL' }), (args) => (0, login_1.loginCommand)({
65
+ profile: args.profile,
66
+ email: args.email,
67
+ secretKey: args['secret-key'],
68
+ apiKey: args['api-key'],
69
+ noInteractive: args['no-interactive'],
70
+ useKeychain: args.keychain,
71
+ apiBase: args['api-base'],
72
+ }))
73
+ .command('auth', 'Authenticate via device-link flow (default) or --key for legacy manual', (y) => y
74
+ .option('key', { type: 'string', describe: 'Secret key (gsk_live_... or gsk_test_...) — legacy manual flow' })
75
+ .option('email', { type: 'string', describe: 'Email address (legacy flow)' })
76
+ .option('secret-key', { type: 'string', describe: 'Secret key (legacy flow alias for --key)' })
77
+ .option('no-browser', { type: 'boolean', describe: 'Do not auto-open the browser' })
78
+ .option('no-interactive', { type: 'boolean', describe: 'Non-interactive mode (legacy flow)' })
79
+ .option('keychain', { type: 'boolean', describe: 'Store secret in macOS Keychain if available' })
80
+ .option('api-base', { type: 'string', describe: 'Override API base URL' }), (args) => (0, auth_1.authCommand)({
81
+ profile: args.profile,
82
+ key: args.key,
83
+ email: args.email,
84
+ secretKey: args['secret-key'],
85
+ noInteractive: args['no-interactive'],
86
+ useKeychain: args.keychain,
87
+ apiBase: args['api-base'],
88
+ noBrowser: args['no-browser'],
89
+ }))
90
+ .command('logout', 'Remove a stored profile', (y) => y
91
+ .option('all', { type: 'boolean', describe: 'Remove all profiles' })
92
+ .option('yes', { type: 'boolean', alias: 'y', describe: 'Skip confirmation' }), (args) => (0, logout_1.logoutCommand)({
93
+ profile: args.profile,
94
+ all: args.all,
95
+ yes: args.yes,
96
+ }))
97
+ .command('whoami', 'Show current authentication state', (y) => y.option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, whoami_1.whoamiCommand)({
98
+ profile: args.profile,
99
+ json: args.json,
100
+ }))
101
+ .command('config <action> [name]', 'Manage CLI profiles', (y) => y
102
+ .positional('action', {
103
+ type: 'string',
104
+ describe: 'list | show | set-default | path',
105
+ })
106
+ .positional('name', { type: 'string', describe: 'Profile name (for show / set-default)' })
107
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, config_1.configCommand)({
108
+ action: args.action,
109
+ name: args.name,
110
+ json: args.json,
111
+ }))
112
+ .command('sites <action> [target]', 'Manage sites (list, create, show, delete, rotate-token)', (y) => y
113
+ .positional('action', {
114
+ type: 'string',
115
+ describe: 'list | create | show | delete | rotate-token',
116
+ })
117
+ .positional('target', { type: 'string', describe: 'Site name or id' })
118
+ .option('name', { type: 'string', describe: 'Site name (for create)' })
119
+ .option('domain', { type: 'string', describe: 'Site domain (for create)' })
120
+ .option('json', { type: 'boolean', describe: 'JSON output' })
121
+ .option('archived', { type: 'boolean', describe: 'Include archived sites' })
122
+ .option('yes', { type: 'boolean', alias: 'y', describe: 'Skip confirmation' }), (args) => (0, sites_1.sitesCommand)({
123
+ action: args.action,
124
+ target: args.target,
125
+ name: args.name,
126
+ domain: args.domain,
127
+ json: args.json,
128
+ archived: args.archived,
129
+ yes: args.yes,
130
+ profile: args.profile,
131
+ }))
132
+ .command('api-keys <action> [id]', 'Manage API keys (list, create, revoke, rotate)', (y) => y
133
+ .positional('action', {
134
+ type: 'string',
135
+ describe: 'list | create | revoke | rotate',
136
+ })
137
+ .positional('id', { type: 'string', describe: 'API key id (for revoke/rotate)' })
138
+ .option('name', { type: 'string', describe: 'Key name (for create)' })
139
+ .option('mode', { type: 'string', choices: ['live', 'test'], describe: 'Key mode' })
140
+ .option('scopes', { type: 'string', describe: 'Comma-separated scopes' })
141
+ .option('filter-mode', { type: 'string', choices: ['live', 'test'], describe: 'Filter list by mode' })
142
+ .option('json', { type: 'boolean', describe: 'JSON output' })
143
+ .option('format', { type: 'string', describe: 'Output format (json)' })
144
+ .option('yes', { type: 'boolean', alias: 'y', describe: 'Skip confirmation' }), (args) => (0, api_keys_1.apiKeysCommand)({
145
+ action: args.action,
146
+ id: args.id,
147
+ name: args.name,
148
+ mode: args.mode,
149
+ scopes: args.scopes,
150
+ filterMode: args['filter-mode'],
151
+ json: args.json,
152
+ format: args.format,
153
+ yes: args.yes,
154
+ profile: args.profile,
155
+ }))
156
+ .command('events <action>', 'View events ingested by Gurulu (list, tail)', (y) => y
157
+ .positional('action', { type: 'string', describe: 'list | tail' })
158
+ .option('site', { type: 'string', describe: 'Site ID' })
159
+ .option('event-name', { type: 'string', describe: 'Filter by event name' })
160
+ .option('filter', { type: 'string', describe: 'Filter expression (event_name:...)' })
161
+ .option('since', { type: 'string', describe: 'ISO timestamp or 1h/24h/7d' })
162
+ .option('limit', { type: 'number', describe: 'Max rows (1..500)' })
163
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => {
164
+ (0, events_1.eventsCommand)({
165
+ action: args.action,
166
+ site: args.site,
167
+ eventName: args['event-name'],
168
+ filter: args.filter,
169
+ since: args.since,
170
+ limit: args.limit,
171
+ json: args.json,
172
+ profile: args.profile,
173
+ });
174
+ })
175
+ .command('status', 'Check SDK health and connection', (y) => y
176
+ .option('site', { type: 'string', describe: 'Site ID' })
177
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => {
178
+ (0, status_1.statusCommand)({ site: args.site, json: args.json });
179
+ })
180
+ .command('doctor', 'Diagnose setup issues', (y) => y
181
+ .option('site', { type: 'string', describe: 'Site ID' })
182
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => {
183
+ (0, doctor_1.doctorCommand)({ site: args.site, json: args.json });
184
+ })
185
+ .command('add-server', 'Add server-side SDK to your project', (y) => y
186
+ .option('site', { type: 'string', describe: 'Site ID' })
187
+ .option('no-interactive', { type: 'boolean', describe: 'Non-interactive mode' })
188
+ .option('dry-run', { type: 'boolean', describe: 'Show what would be done' })
189
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => {
190
+ (0, add_server_1.addServerCommand)({
191
+ site: args.site,
192
+ noInteractive: args['no-interactive'],
193
+ dryRun: args['dry-run'],
194
+ json: args.json,
195
+ });
196
+ })
197
+ .command('install [path]', 'Install Gurulu tracker in a repository', (y) => y
198
+ .positional('path', { type: 'string', describe: 'Target project path (default: cwd)' })
199
+ .option('site', { type: 'string', describe: 'Site name or id (authenticated mode)' })
200
+ .option('site-id', { type: 'string', describe: 'Site ID (legacy fallback)' })
201
+ .option('tenant-id', { type: 'string', describe: 'Tenant ID (legacy fallback)' })
202
+ .option('publishable-key', { type: 'string', describe: 'Publishable key (legacy)' })
203
+ .option('framework', { type: 'string', describe: 'Override framework' })
204
+ .option('dry-run', { type: 'boolean', describe: 'Show plan without applying' })
205
+ .option('apply', { type: 'boolean', describe: 'Apply pending patches' })
206
+ .option('rollback', { type: 'boolean', describe: 'Rollback previously-applied patches' })
207
+ .option('skip-npm', { type: 'boolean', describe: 'Skip package manager install step' })
208
+ .option('skip-env', { type: 'boolean', describe: 'Skip .env file merge' })
209
+ .option('yes', { type: 'boolean', alias: 'y', describe: 'Non-interactive (assume yes)' })
210
+ .option('ingest-url', { type: 'string', describe: 'Override ingest base URL' })
211
+ .option('verify', { type: 'boolean', describe: 'Live smoke test after install' })
212
+ .option('skip-intent', { type: 'boolean', describe: 'Skip install-time intent discovery' })
213
+ .option('intent-dry-run', { type: 'boolean', describe: 'Show intent proposal without pre-seeding' })
214
+ .option('vertical', { type: 'string', describe: 'Vertical hint for intent analyzer' })
215
+ // BEGIN: Agent B (Phase 18.7) — auto-instrument opt-in flag
216
+ .option('auto-instrument', {
217
+ type: 'boolean',
218
+ description: 'Also auto-instrument route handlers with gurulu.track() calls (opt-in)',
219
+ default: false,
220
+ })
221
+ .option('auto-properties', {
222
+ type: 'boolean',
223
+ default: false,
224
+ description: 'Opt-in: ask Minimax to propose properties for each auto-instrumented handler (Phase 20 W1 A2)',
225
+ }),
226
+ // END: Agent B (Phase 18.7)
227
+ (args) => {
228
+ (0, install_1.installCommand)({
229
+ path: args.path,
230
+ site: args.site,
231
+ siteId: args['site-id'],
232
+ tenantId: args['tenant-id'],
233
+ publishableKey: args['publishable-key'],
234
+ framework: args.framework,
235
+ dryRun: args['dry-run'],
236
+ apply: args.apply,
237
+ rollback: args.rollback,
238
+ skipNpm: args['skip-npm'],
239
+ skipEnv: args['skip-env'],
240
+ yes: args.yes,
241
+ ingestUrl: args['ingest-url'],
242
+ verify: args.verify,
243
+ profile: args.profile,
244
+ skipIntent: args['skip-intent'],
245
+ intentDryRun: args['intent-dry-run'],
246
+ verticalHint: args.vertical,
247
+ autoInstrument: args['auto-instrument'],
248
+ autoProperties: args['auto-properties'],
249
+ });
250
+ })
251
+ .command('warehouse <action>', 'Warehouse exports (BigQuery)', (y) => y
252
+ .positional('action', { type: 'string', describe: 'Action: export' })
253
+ .option('tenant', { type: 'string', describe: 'Tenant id (forward-compat)' })
254
+ .option('export', { type: 'string', describe: 'Warehouse export id' })
255
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => {
256
+ if (args.action !== 'export') {
257
+ console.error(`Unknown warehouse action: ${args.action}`);
258
+ process.exit(1);
259
+ }
260
+ (0, warehouse_1.warehouseExportCommand)({
261
+ tenant: args.tenant,
262
+ export: args.export,
263
+ json: args.json,
264
+ });
265
+ })
266
+ // ── Phase 19.5 W2 — read-surface subcommands ─────────────────────────
267
+ .command('audiences <action> [target]', 'View audiences (list, show)', (y) => y
268
+ .positional('action', { type: 'string', describe: 'list | show' })
269
+ .positional('target', { type: 'string', describe: 'Audience name or id' })
270
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, audiences_1.audiencesCommand)({
271
+ action: args.action,
272
+ target: args.target,
273
+ json: args.json,
274
+ profile: args.profile,
275
+ }))
276
+ .command('destinations <action> [target]', 'View activation destinations (list, show)', (y) => y
277
+ .positional('action', { type: 'string', describe: 'list | show' })
278
+ .positional('target', { type: 'string', describe: 'Destination id' })
279
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, destinations_1.destinationsCommand)({
280
+ action: args.action,
281
+ target: args.target,
282
+ json: args.json,
283
+ profile: args.profile,
284
+ }))
285
+ .command('alerts <action> [sub] [target]', 'View anomaly alerts (list, show <id>, channels list)', (y) => y
286
+ .positional('action', { type: 'string', describe: 'list | show | channels' })
287
+ .positional('sub', { type: 'string', describe: 'Subaction (e.g. list for channels)' })
288
+ .positional('target', { type: 'string', describe: 'Alert id (for show)' })
289
+ .option('severity', { type: 'string', describe: 'low | medium | high | critical' })
290
+ .option('acknowledged', { type: 'string', describe: 'true | false' })
291
+ .option('limit', { type: 'number', describe: 'Max rows (1..200)' })
292
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, alerts_1.alertsCommand)({
293
+ action: args.action,
294
+ sub: args.sub,
295
+ target: args.target,
296
+ severity: args.severity,
297
+ acknowledged: args.acknowledged,
298
+ limit: args.limit,
299
+ json: args.json,
300
+ profile: args.profile,
301
+ }))
302
+ .command('insights <action>', 'View daily insights (today, history, weekly)', (y) => y
303
+ .positional('action', { type: 'string', describe: 'today | history | weekly' })
304
+ .option('days', { type: 'number', describe: 'History window in days (1..30)' })
305
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, insights_1.insightsCommand)({
306
+ action: args.action,
307
+ days: args.days,
308
+ json: args.json,
309
+ profile: args.profile,
310
+ }))
311
+ .command('warehouses <action> [target]', 'View warehouse exports (list, runs <export-id>)', (y) => y
312
+ .positional('action', { type: 'string', describe: 'list | runs' })
313
+ .positional('target', { type: 'string', describe: 'Warehouse export id' })
314
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, warehouses_1.warehousesCommand)({
315
+ action: args.action,
316
+ target: args.target,
317
+ json: args.json,
318
+ profile: args.profile,
319
+ }))
320
+ .command('experiments <action> [target]', 'View experiments (list, show <key>, results <key>)', (y) => y
321
+ .positional('action', { type: 'string', describe: 'list | show | results' })
322
+ .positional('target', { type: 'string', describe: 'Experiment key or id' })
323
+ .option('conversion-event', { type: 'string', describe: 'Conversion event name' })
324
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, experiments_1.experimentsCommand)({
325
+ action: args.action,
326
+ target: args.target,
327
+ conversionEvent: args['conversion-event'],
328
+ json: args.json,
329
+ profile: args.profile,
330
+ }))
331
+ .command('identity <action> [sub]', 'View identity state (decay stats, transfers list, cdc-sources list)', (y) => y
332
+ .positional('action', { type: 'string', describe: 'decay | transfers | cdc-sources' })
333
+ .positional('sub', { type: 'string', describe: 'Subaction (stats, list)' })
334
+ .option('direction', { type: 'string', describe: 'outbound | inbound | all' })
335
+ .option('status', { type: 'string', describe: 'Filter by transfer status' })
336
+ .option('limit', { type: 'number', describe: 'Max rows' })
337
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, identity_1.identityCommand)({
338
+ action: args.action,
339
+ sub: args.sub,
340
+ direction: args.direction,
341
+ status: args.status,
342
+ limit: args.limit,
343
+ json: args.json,
344
+ profile: args.profile,
345
+ }))
346
+ .command('playground <action>', 'View playground sessions (list)', (y) => y
347
+ .positional('action', { type: 'string', describe: 'list' })
348
+ .option('status', { type: 'string', describe: 'active | closed' })
349
+ .option('limit', { type: 'number', describe: 'Max rows (1..100)' })
350
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, playground_1.playgroundCommand)({
351
+ action: args.action,
352
+ status: args.status,
353
+ limit: args.limit,
354
+ json: args.json,
355
+ profile: args.profile,
356
+ }))
357
+ // ── Phase 20 W3 C4 — audit log tail/export ───────────────────────────
358
+ .command('audit <action>', 'Stream or export the CLI audit log (tail, export)', (y) => y
359
+ .positional('action', { type: 'string', describe: 'tail | export' })
360
+ .option('since', { type: 'string', describe: 'ISO ts or 1h/24h/30d window' })
361
+ .option('limit', { type: 'number', describe: 'Max rows for export (1..10000)' })
362
+ .option('format', { type: 'string', describe: 'Export format (jsonl)' })
363
+ .option('json', { type: 'boolean', describe: 'Raw SSE frames for tail' }), (args) => (0, audit_1.auditCommand)({
364
+ action: args.action,
365
+ since: args.since,
366
+ limit: args.limit,
367
+ format: args.format,
368
+ json: args.json,
369
+ profile: args.profile,
370
+ }))
371
+ // ── Gurulu Chat — NL → SQL analytics ─────────────────────────────────
372
+ .command('chat [question]', 'Ask analytics questions in natural language (NL → SQL)', (y) => y
373
+ .positional('question', { type: 'string', describe: 'Question to ask (omit for REPL mode)' })
374
+ .option('json', { type: 'boolean', describe: 'Machine-readable JSON output' })
375
+ .option('show-sql', { type: 'boolean', describe: 'Also print the generated SQL' })
376
+ .option('context', { type: 'string', describe: 'Additional context for the query' }), (args) => (0, chat_1.chatCommand)({
377
+ question: args.question,
378
+ json: args.json,
379
+ showSql: args['show-sql'],
380
+ context: args.context,
381
+ profile: args.profile,
382
+ }))
383
+ // ── Error tracking — source map upload ────────────────────────────────
384
+ .command('sourcemap <action>', 'Upload source maps for error deobfuscation (upload)', (y) => y
385
+ .positional('action', { type: 'string', describe: 'upload' })
386
+ .option('release', { type: 'string', describe: 'Release version (e.g. v1.0.0)' })
387
+ .option('dir', { type: 'string', describe: 'Directory containing .map files' })
388
+ .option('site', { type: 'string', describe: 'Site ID' })
389
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, sourcemap_1.sourcemapCommand)({
390
+ action: args.action,
391
+ release: args.release,
392
+ dir: args.dir,
393
+ site: args.site,
394
+ json: args.json,
395
+ profile: args.profile,
396
+ }))
397
+ // ── Phase 21 — database connect ────────────────────────────────────────
398
+ .command('db <action> [target]', 'Connect, list, sync, or remove database sources', (y) => y
399
+ .positional('action', { type: 'string', describe: 'connect | list | sync | remove' })
400
+ .positional('target', { type: 'string', describe: 'Source ID (for sync/remove)' })
401
+ .option('type', { type: 'string', describe: 'Database type: postgres | mysql' })
402
+ .option('host', { type: 'string', describe: 'Database host' })
403
+ .option('port', { type: 'number', describe: 'Database port' })
404
+ .option('database', { type: 'string', describe: 'Database name' })
405
+ .option('user', { type: 'string', describe: 'Database user' })
406
+ .option('password', { type: 'string', describe: 'Database password' })
407
+ .option('table', { type: 'string', describe: 'Table(s) to sync (comma-separated)' })
408
+ .option('schedule', { type: 'string', describe: 'Sync schedule: hourly | daily | manual' })
409
+ .option('no-interactive', { type: 'boolean', describe: 'Non-interactive mode' })
410
+ .option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, db_1.dbCommand)({
411
+ action: args.action,
412
+ target: args.target,
413
+ type: args.type,
414
+ host: args.host,
415
+ port: args.port,
416
+ database: args.database,
417
+ user: args.user,
418
+ password: args.password,
419
+ table: args.table,
420
+ schedule: args.schedule,
421
+ noInteractive: args['no-interactive'],
422
+ json: args.json,
423
+ profile: args.profile,
424
+ }))
425
+ .demandCommand(1, 'Run gurulu --help for available commands')
426
+ .strict()
427
+ .help()
428
+ .version()
429
+ .parse();
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Phase 18.6 — CLI install-time intent proposal rendering + interaction.
3
+ *
4
+ * Pure UI logic (plus a small interactive loop) that turns an `InstallIntent`
5
+ * payload into a human-readable proposal and lets the user toggle individual
6
+ * events/funnels before sending the accepted subset to the backend. This file
7
+ * has NO hard limit on event/funnel count — a full iGaming intent with 20
8
+ * events is rendered in full.
9
+ *
10
+ * Testability: the render function returns a plain string so snapshot-style
11
+ * tests can assert structure. The interactive runner accepts injected I/O so
12
+ * tests can drive it without real stdin/stdout.
13
+ */
14
+ export type Confidence = 'high' | 'medium' | 'low';
15
+ export interface ProposedEvent {
16
+ name: string;
17
+ category: 'acquisition' | 'activation' | 'retention' | 'revenue' | 'referral' | 'compliance' | 'support' | 'engagement';
18
+ source: {
19
+ route?: string;
20
+ mutation?: string;
21
+ inferred?: boolean;
22
+ inferredFrom?: string;
23
+ };
24
+ properties: string[];
25
+ confidence: Confidence;
26
+ reasoning: string;
27
+ }
28
+ export interface ProposedFunnel {
29
+ name: string;
30
+ category: 'activation' | 'monetization' | 'retention' | 'compliance' | 'engagement';
31
+ steps: string[];
32
+ reasoning: string;
33
+ }
34
+ export interface InstallIntent {
35
+ vertical: string;
36
+ confidence: Confidence;
37
+ reasoning: string;
38
+ alternativeVerticals: Array<{
39
+ name: string;
40
+ confidence: Confidence;
41
+ note?: string;
42
+ }>;
43
+ events: ProposedEvent[];
44
+ funnels: ProposedFunnel[];
45
+ analyzerMode: 'llm' | 'heuristic';
46
+ modelVersion?: string;
47
+ promptVersion?: string;
48
+ }
49
+ export interface ProposalState {
50
+ intent: InstallIntent;
51
+ eventSelected: boolean[];
52
+ funnelSelected: boolean[];
53
+ }
54
+ export declare function initProposalState(intent: InstallIntent): ProposalState;
55
+ /**
56
+ * Render the proposal to a plain string (TTY colour escapes applied by the
57
+ * caller if `colorize` is true). No truncation — every event/funnel is shown.
58
+ */
59
+ export declare function renderProposal(state: ProposalState, opts?: {
60
+ colorize?: boolean;
61
+ }): string;
62
+ export interface ApplyCommandResult {
63
+ state: ProposalState;
64
+ done: boolean;
65
+ quit: boolean;
66
+ }
67
+ /**
68
+ * Apply a single user command to the proposal state. Exported so tests can
69
+ * exercise the state machine directly without any I/O.
70
+ */
71
+ export declare function applyCommand(state: ProposalState, raw: string): ApplyCommandResult;
72
+ export interface ProposalIo {
73
+ print: (line: string) => void;
74
+ prompt: (q: string) => Promise<string>;
75
+ isPiped: boolean;
76
+ }
77
+ export interface ProposalDecision {
78
+ accepted: {
79
+ events: ProposedEvent[];
80
+ funnels: ProposedFunnel[];
81
+ };
82
+ rejected: {
83
+ events: ProposedEvent[];
84
+ funnels: ProposedFunnel[];
85
+ };
86
+ quit: boolean;
87
+ }
88
+ export declare function splitDecision(state: ProposalState): ProposalDecision;
89
+ export interface RunProposalOpts {
90
+ intent: InstallIntent;
91
+ io: ProposalIo;
92
+ /** If true (piped / --yes / CI), skip interactive loop and accept everything. */
93
+ nonInteractive?: boolean;
94
+ }
95
+ /**
96
+ * Drive the interactive proposal loop. Returns the accepted + rejected split
97
+ * plus a `quit` flag the caller uses to decide whether to continue the install.
98
+ */
99
+ export declare function runProposal(opts: RunProposalOpts): Promise<ProposalDecision>;