@gurulu/cli 0.4.2 → 0.4.3
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.
- package/dist/commands/attribution.d.ts +22 -0
- package/dist/commands/attribution.js +111 -0
- package/dist/commands/conversion-paths.d.ts +19 -0
- package/dist/commands/conversion-paths.js +55 -0
- package/dist/commands/errors.d.ts +27 -0
- package/dist/commands/errors.js +121 -0
- package/dist/commands/identity.d.ts +13 -0
- package/dist/commands/identity.js +85 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/install.d.ts +5 -0
- package/dist/commands/install.js +186 -9
- package/dist/commands/releases.d.ts +17 -0
- package/dist/commands/releases.js +54 -0
- package/dist/commands/replay.d.ts +18 -0
- package/dist/commands/replay.js +64 -0
- package/dist/commands/skad.d.ts +18 -0
- package/dist/commands/skad.js +53 -0
- package/dist/frameworks/detect.d.ts +1 -1
- package/dist/frameworks/detect.js +60 -0
- package/dist/index.js +162 -2
- package/package.json +1 -1
- package/scripts/gurulu-agentic-install.lib.cjs +32 -4
- package/scripts/gurulu-agentic-install.mjs +10 -2
package/dist/index.js
CHANGED
|
@@ -41,6 +41,13 @@ const sourcemap_1 = require("./commands/sourcemap");
|
|
|
41
41
|
const db_1 = require("./commands/db");
|
|
42
42
|
// Sprint B Group VII B14 — live event stream
|
|
43
43
|
const watch_1 = require("./commands/watch");
|
|
44
|
+
// Sprint E SE-A / SE-B — analytics + observability CLI surface
|
|
45
|
+
const attribution_1 = require("./commands/attribution");
|
|
46
|
+
const releases_1 = require("./commands/releases");
|
|
47
|
+
const skad_1 = require("./commands/skad");
|
|
48
|
+
const errors_1 = require("./commands/errors");
|
|
49
|
+
const replay_1 = require("./commands/replay");
|
|
50
|
+
const conversion_paths_1 = require("./commands/conversion-paths");
|
|
44
51
|
(0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
45
52
|
.scriptName('gurulu')
|
|
46
53
|
.option('profile', { type: 'string', describe: 'Use a specific profile (default: personal)' })
|
|
@@ -458,18 +465,42 @@ const watch_1 = require("./commands/watch");
|
|
|
458
465
|
json: args.json,
|
|
459
466
|
profile: args.profile,
|
|
460
467
|
}))
|
|
461
|
-
.command('identity <action> [sub]', '
|
|
462
|
-
.positional('action', {
|
|
468
|
+
.command('identity <action> [sub]', 'Identity state + writes (decay stats | transfers list | cdc-sources list | identify | alias | merge)', (y) => y
|
|
469
|
+
.positional('action', {
|
|
470
|
+
type: 'string',
|
|
471
|
+
describe: 'decay | transfers | cdc-sources | identify | alias | merge',
|
|
472
|
+
})
|
|
463
473
|
.positional('sub', { type: 'string', describe: 'Subaction (stats, list)' })
|
|
464
474
|
.option('direction', { type: 'string', describe: 'outbound | inbound | all' })
|
|
465
475
|
.option('status', { type: 'string', describe: 'Filter by transfer status' })
|
|
466
476
|
.option('limit', { type: 'number', describe: 'Max rows' })
|
|
477
|
+
// identify / alias / merge
|
|
478
|
+
.option('site', { type: 'string', describe: 'Site ID (write actions)' })
|
|
479
|
+
.option('user-id', { type: 'string', describe: 'User ID (identify)' })
|
|
480
|
+
.option('email', { type: 'string', describe: 'Email (identify)' })
|
|
481
|
+
.option('phone', { type: 'string', describe: 'Phone (identify)' })
|
|
482
|
+
.option('traits', { type: 'string', describe: 'Traits as JSON object (identify)' })
|
|
483
|
+
.option('previous-user-id', { type: 'string', describe: 'Anonymous/old user id (alias)' })
|
|
484
|
+
.option('new-user-id', { type: 'string', describe: 'New canonical user id (alias)' })
|
|
485
|
+
.option('canonical-id', { type: 'string', describe: 'Winner canonical profile id (merge)' })
|
|
486
|
+
.option('duplicate-id', { type: 'string', describe: 'Loser canonical profile id (merge)' })
|
|
487
|
+
.option('yes', { type: 'boolean', alias: 'y', describe: 'Skip confirmation' })
|
|
467
488
|
.option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, identity_1.identityCommand)({
|
|
468
489
|
action: args.action,
|
|
469
490
|
sub: args.sub,
|
|
470
491
|
direction: args.direction,
|
|
471
492
|
status: args.status,
|
|
472
493
|
limit: args.limit,
|
|
494
|
+
site: args.site,
|
|
495
|
+
userId: args['user-id'],
|
|
496
|
+
email: args.email,
|
|
497
|
+
phone: args.phone,
|
|
498
|
+
traits: args.traits,
|
|
499
|
+
previousUserId: args['previous-user-id'],
|
|
500
|
+
newUserId: args['new-user-id'],
|
|
501
|
+
canonicalId: args['canonical-id'],
|
|
502
|
+
duplicateId: args['duplicate-id'],
|
|
503
|
+
yes: args.yes,
|
|
473
504
|
json: args.json,
|
|
474
505
|
profile: args.profile,
|
|
475
506
|
}))
|
|
@@ -571,6 +602,135 @@ const watch_1 = require("./commands/watch");
|
|
|
571
602
|
tail: args.tail,
|
|
572
603
|
json: args.json,
|
|
573
604
|
profile: args.profile,
|
|
605
|
+
}))
|
|
606
|
+
// ── Sprint E SE-A/B — analytics + observability CLI surface ──────────
|
|
607
|
+
.command('attribution <action>', 'Attribution analytics (report, compare, channels)', (y) => y
|
|
608
|
+
.positional('action', { type: 'string', describe: 'report | compare | channels' })
|
|
609
|
+
.option('site', { type: 'string', describe: 'Site ID' })
|
|
610
|
+
.option('from', { type: 'string', describe: 'ISO date (inclusive)' })
|
|
611
|
+
.option('to', { type: 'string', describe: 'ISO date (inclusive)' })
|
|
612
|
+
.option('range', { type: 'string', describe: '7d | 30d | 90d shorthand' })
|
|
613
|
+
.option('model', { type: 'string', describe: 'last-touch | first-touch | linear | time-decay | position-based | data-driven' })
|
|
614
|
+
.option('baseline-model', { type: 'string', describe: 'Baseline model id (compare)' })
|
|
615
|
+
.option('variant-model', { type: 'string', describe: 'Variant model id (compare)' })
|
|
616
|
+
.option('conversion-event', { type: 'string', describe: 'Conversion event (default $purchase)' })
|
|
617
|
+
.option('format', { type: 'string', describe: 'json | table' })
|
|
618
|
+
.option('json', { type: 'boolean', describe: 'JSON output (default)' }), (args) => (0, attribution_1.attributionCommand)({
|
|
619
|
+
action: args.action,
|
|
620
|
+
site: args.site,
|
|
621
|
+
from: args.from,
|
|
622
|
+
to: args.to,
|
|
623
|
+
range: args.range,
|
|
624
|
+
model: args.model,
|
|
625
|
+
baselineModel: args['baseline-model'],
|
|
626
|
+
variantModel: args['variant-model'],
|
|
627
|
+
conversionEvent: args['conversion-event'],
|
|
628
|
+
format: args.format,
|
|
629
|
+
json: args.json,
|
|
630
|
+
profile: args.profile,
|
|
631
|
+
}))
|
|
632
|
+
.command('releases <action>', 'Release health (list)', (y) => y
|
|
633
|
+
.positional('action', { type: 'string', describe: 'list' })
|
|
634
|
+
.option('site', { type: 'string', describe: 'Site ID' })
|
|
635
|
+
.option('range', { type: 'string', describe: '24h | 7d | 30d (default 7d)' })
|
|
636
|
+
.option('environment', { type: 'string', describe: 'Optional environment filter' })
|
|
637
|
+
.option('limit', { type: 'number', describe: 'Max rows (default 20)' })
|
|
638
|
+
.option('format', { type: 'string', describe: 'json | table' })
|
|
639
|
+
.option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, releases_1.releasesCommand)({
|
|
640
|
+
action: args.action,
|
|
641
|
+
site: args.site,
|
|
642
|
+
range: args.range,
|
|
643
|
+
environment: args.environment,
|
|
644
|
+
limit: args.limit,
|
|
645
|
+
format: args.format,
|
|
646
|
+
json: args.json,
|
|
647
|
+
profile: args.profile,
|
|
648
|
+
}))
|
|
649
|
+
.command('skad <action>', 'Apple SKAdNetwork postbacks (postbacks)', (y) => y
|
|
650
|
+
.positional('action', { type: 'string', describe: 'postbacks' })
|
|
651
|
+
.option('site', { type: 'string', describe: 'Site ID' })
|
|
652
|
+
.option('from', { type: 'string', describe: 'ISO ts or 7d/24h' })
|
|
653
|
+
.option('to', { type: 'string', describe: 'ISO ts (default now)' })
|
|
654
|
+
.option('goal', { type: 'string', describe: 'Filter by resolved goal' })
|
|
655
|
+
.option('limit', { type: 'number', describe: 'Max rows (1..500)' })
|
|
656
|
+
.option('format', { type: 'string', describe: 'json | table' })
|
|
657
|
+
.option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, skad_1.skadCommand)({
|
|
658
|
+
action: args.action,
|
|
659
|
+
site: args.site,
|
|
660
|
+
from: args.from,
|
|
661
|
+
to: args.to,
|
|
662
|
+
goal: args.goal,
|
|
663
|
+
limit: args.limit,
|
|
664
|
+
format: args.format,
|
|
665
|
+
json: args.json,
|
|
666
|
+
profile: args.profile,
|
|
667
|
+
}))
|
|
668
|
+
.command('errors <action>', 'Error tracking (list, detail, resolve, mute)', (y) => y
|
|
669
|
+
.positional('action', { type: 'string', describe: 'list | detail | resolve | mute | upload-sourcemap | upload-native-symbols' })
|
|
670
|
+
.option('site', { type: 'string', describe: 'Site ID' })
|
|
671
|
+
.option('fingerprint', { type: 'string', describe: 'Error group fingerprint' })
|
|
672
|
+
.option('level', { type: 'string', describe: 'error | warning | info' })
|
|
673
|
+
.option('resolved', { type: 'string', describe: 'true | false' })
|
|
674
|
+
.option('environment', { type: 'string', describe: 'Environment filter' })
|
|
675
|
+
.option('release-id', { type: 'string', describe: 'Filter to a release' })
|
|
676
|
+
.option('limit', { type: 'number', describe: 'Max rows' })
|
|
677
|
+
.option('duration', { type: 'string', describe: 'Mute duration (e.g. 24h, 7d)' })
|
|
678
|
+
.option('format', { type: 'string', describe: 'json | table' })
|
|
679
|
+
.option('yes', { type: 'boolean', alias: 'y', describe: 'Skip confirmation' })
|
|
680
|
+
.option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, errors_1.errorsCommand)({
|
|
681
|
+
action: args.action,
|
|
682
|
+
site: args.site,
|
|
683
|
+
fingerprint: args.fingerprint,
|
|
684
|
+
level: args.level,
|
|
685
|
+
resolved: args.resolved,
|
|
686
|
+
environment: args.environment,
|
|
687
|
+
releaseId: args['release-id'],
|
|
688
|
+
limit: args.limit,
|
|
689
|
+
duration: args.duration,
|
|
690
|
+
format: args.format,
|
|
691
|
+
yes: args.yes,
|
|
692
|
+
json: args.json,
|
|
693
|
+
profile: args.profile,
|
|
694
|
+
}))
|
|
695
|
+
.command('replay <action>', 'Session replay (list, get)', (y) => y
|
|
696
|
+
.positional('action', { type: 'string', describe: 'list | get' })
|
|
697
|
+
.option('site', { type: 'string', describe: 'Site ID' })
|
|
698
|
+
.option('session-id', { type: 'string', describe: 'Session id (for get)' })
|
|
699
|
+
.option('range', { type: 'string', describe: '7d | 30d (default 7d)' })
|
|
700
|
+
.option('has-error', { type: 'boolean', describe: 'Filter to sessions with errors' })
|
|
701
|
+
.option('limit', { type: 'number', describe: 'Max rows' })
|
|
702
|
+
.option('format', { type: 'string', describe: 'json | table' })
|
|
703
|
+
.option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, replay_1.replayCommand)({
|
|
704
|
+
action: args.action,
|
|
705
|
+
site: args.site,
|
|
706
|
+
sessionId: args['session-id'],
|
|
707
|
+
range: args.range,
|
|
708
|
+
hasError: args['has-error'],
|
|
709
|
+
limit: args.limit,
|
|
710
|
+
format: args.format,
|
|
711
|
+
json: args.json,
|
|
712
|
+
profile: args.profile,
|
|
713
|
+
}))
|
|
714
|
+
.command('conversion-paths <action>', 'Conversion path analytics (list)', (y) => y
|
|
715
|
+
.positional('action', { type: 'string', describe: 'list' })
|
|
716
|
+
.option('site', { type: 'string', describe: 'Site ID' })
|
|
717
|
+
.option('from', { type: 'string', describe: 'ISO date' })
|
|
718
|
+
.option('to', { type: 'string', describe: 'ISO date' })
|
|
719
|
+
.option('range', { type: 'string', describe: '7d | 30d | 90d' })
|
|
720
|
+
.option('conversion-event', { type: 'string', describe: 'Conversion event' })
|
|
721
|
+
.option('limit', { type: 'number', describe: 'Max rows' })
|
|
722
|
+
.option('format', { type: 'string', describe: 'json | table' })
|
|
723
|
+
.option('json', { type: 'boolean', describe: 'JSON output' }), (args) => (0, conversion_paths_1.conversionPathsCommand)({
|
|
724
|
+
action: args.action,
|
|
725
|
+
site: args.site,
|
|
726
|
+
from: args.from,
|
|
727
|
+
to: args.to,
|
|
728
|
+
range: args.range,
|
|
729
|
+
conversionEvent: args['conversion-event'],
|
|
730
|
+
limit: args.limit,
|
|
731
|
+
format: args.format,
|
|
732
|
+
json: args.json,
|
|
733
|
+
profile: args.profile,
|
|
574
734
|
}))
|
|
575
735
|
.demandCommand(1, 'Run gurulu --help for available commands')
|
|
576
736
|
.strict()
|
package/package.json
CHANGED
|
@@ -380,16 +380,44 @@ function generateServerMap(scan /*, opts*/) {
|
|
|
380
380
|
};
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Sprint E1.3b — singularize plural table/model names to canonical singular.
|
|
385
|
+
* The scan layer normalises mutations as `{ op: '<model>.<operation>' }` so we
|
|
386
|
+
* derive the operation from `m.op` when `m.operation` isn't present, and
|
|
387
|
+
* collapse common irregular plurals to their singular form so e.g. raw-SQL
|
|
388
|
+
* `INSERT INTO orders` and a Drizzle `pgTable('orders', ...)` both fold into
|
|
389
|
+
* the same `order` row in db-map.
|
|
390
|
+
*/
|
|
391
|
+
const SINGULARIZE_IRREGULARS = {
|
|
392
|
+
users: 'user',
|
|
393
|
+
orders: 'order',
|
|
394
|
+
items: 'item',
|
|
395
|
+
};
|
|
396
|
+
function singularizeModelName(raw) {
|
|
397
|
+
const lower = String(raw || '').toLowerCase();
|
|
398
|
+
if (!lower) return lower;
|
|
399
|
+
if (Object.prototype.hasOwnProperty.call(SINGULARIZE_IRREGULARS, lower)) {
|
|
400
|
+
return SINGULARIZE_IRREGULARS[lower];
|
|
401
|
+
}
|
|
402
|
+
return lower;
|
|
403
|
+
}
|
|
404
|
+
|
|
383
405
|
/** db-map.json — table → event emitter mapping from detected mutations. */
|
|
384
406
|
function generateDbMap(scan, opts) {
|
|
385
407
|
const byModel = new Map();
|
|
386
408
|
for (const m of scan.mutations || []) {
|
|
387
|
-
const
|
|
388
|
-
if (!
|
|
389
|
-
const
|
|
409
|
+
const rawName = String(m.model || '').trim();
|
|
410
|
+
if (!rawName) continue;
|
|
411
|
+
const name = singularizeModelName(rawName);
|
|
412
|
+
// Sprint E1.3b — `scan.mutations` from gurulu-scan.lib.cjs normalises the
|
|
413
|
+
// operation as `m.op = "<model>.<operation>"` and drops `m.operation`.
|
|
414
|
+
// Derive the operation verb from whichever field is populated.
|
|
415
|
+
const operation =
|
|
416
|
+
m.operation || (typeof m.op === 'string' ? m.op.split('.').pop() : null);
|
|
417
|
+
const op = cdcOp(operation);
|
|
390
418
|
if (!op) continue;
|
|
391
419
|
if (!byModel.has(name)) byModel.set(name, new Set());
|
|
392
|
-
const key = `${op}::${inferEventName(name,
|
|
420
|
+
const key = `${op}::${inferEventName(name, operation)}`;
|
|
393
421
|
byModel.get(name).add(key);
|
|
394
422
|
}
|
|
395
423
|
|
|
@@ -278,10 +278,13 @@ async function runPatchMode(repoRoot, args) {
|
|
|
278
278
|
process.exit(1);
|
|
279
279
|
}
|
|
280
280
|
const framework = args.framework || 'auto';
|
|
281
|
-
const { patcher, detection, error } = patches.resolvePatcher(repoRoot, framework);
|
|
281
|
+
const { patcher, detection, error, unsupportedFramework } = patches.resolvePatcher(repoRoot, framework);
|
|
282
282
|
if (error) {
|
|
283
283
|
process.stderr.write(`Error: ${error}\n`);
|
|
284
|
-
|
|
284
|
+
// Sprint E1.6 — exit 6 specifically for mobile / not-yet-supported
|
|
285
|
+
// frameworks so the wrapping CLI can surface docs links instead of a
|
|
286
|
+
// generic install failure.
|
|
287
|
+
process.exit(unsupportedFramework ? 6 : 1);
|
|
285
288
|
}
|
|
286
289
|
if (!patcher) {
|
|
287
290
|
process.stderr.write('No supported framework detected in ' + repoRoot + '\n');
|
|
@@ -412,6 +415,11 @@ async function runPatchMode(repoRoot, args) {
|
|
|
412
415
|
`Auto-instrument failed (${reason}); rollback threw: ${err.stack || err.message || err}\n`,
|
|
413
416
|
);
|
|
414
417
|
}
|
|
418
|
+
// Sprint E1.1 — exit code 5 signals "patches rolled back" so install.ts
|
|
419
|
+
// can skip the npm-install / .env-merge / ingest-ping steps and surface
|
|
420
|
+
// the rollback to the user. The wrapping CLI converts this into a
|
|
421
|
+
// partially-installed state with `summary.rolledBack=true`.
|
|
422
|
+
process.exit(5);
|
|
415
423
|
}
|
|
416
424
|
if (args.autoInstrument) {
|
|
417
425
|
const intentPath = args['intent-result'];
|