@gurulu/cli 0.4.7 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +92 -0
- package/README.md +35 -106
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +25410 -0
- package/dist/commands/auth.d.ts +23 -20
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/doctor.d.ts +20 -6
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/init.d.ts +25 -11
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/pull.d.ts +13 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/push.d.ts +40 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/validate.d.ts +36 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24985 -876
- package/dist/lib/api.d.ts +139 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/codegen.d.ts +4 -0
- package/dist/lib/codegen.d.ts.map +1 -0
- package/dist/lib/config.d.ts +43 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/package.json +40 -20
- package/bin/gurulu.js +0 -2
- package/dist/api-client.d.ts +0 -33
- package/dist/api-client.js +0 -175
- package/dist/commands/add-server.d.ts +0 -9
- package/dist/commands/add-server.js +0 -162
- package/dist/commands/alerts.d.ts +0 -27
- package/dist/commands/alerts.js +0 -309
- package/dist/commands/api-keys.d.ts +0 -20
- package/dist/commands/api-keys.js +0 -130
- package/dist/commands/attribution.d.ts +0 -22
- package/dist/commands/attribution.js +0 -111
- package/dist/commands/audiences.d.ts +0 -23
- package/dist/commands/audiences.js +0 -243
- package/dist/commands/audit.d.ts +0 -20
- package/dist/commands/audit.js +0 -130
- package/dist/commands/auth.js +0 -249
- package/dist/commands/chat.d.ts +0 -19
- package/dist/commands/chat.js +0 -118
- package/dist/commands/config.d.ts +0 -10
- package/dist/commands/config.js +0 -92
- package/dist/commands/consent.d.ts +0 -27
- package/dist/commands/consent.js +0 -233
- package/dist/commands/conversion-paths.d.ts +0 -19
- package/dist/commands/conversion-paths.js +0 -55
- package/dist/commands/db.d.ts +0 -25
- package/dist/commands/db.js +0 -330
- package/dist/commands/destinations.d.ts +0 -20
- package/dist/commands/destinations.js +0 -191
- package/dist/commands/doctor.js +0 -360
- package/dist/commands/errors.d.ts +0 -27
- package/dist/commands/errors.js +0 -121
- package/dist/commands/events.d.ts +0 -33
- package/dist/commands/events.js +0 -371
- package/dist/commands/experiments.d.ts +0 -22
- package/dist/commands/experiments.js +0 -264
- package/dist/commands/funnels.d.ts +0 -17
- package/dist/commands/funnels.js +0 -203
- package/dist/commands/goals.d.ts +0 -18
- package/dist/commands/goals.js +0 -214
- package/dist/commands/heatmap.d.ts +0 -27
- package/dist/commands/heatmap.js +0 -112
- package/dist/commands/identity.d.ts +0 -29
- package/dist/commands/identity.js +0 -328
- package/dist/commands/init.js +0 -215
- package/dist/commands/insights.d.ts +0 -10
- package/dist/commands/insights.js +0 -77
- package/dist/commands/install.d.ts +0 -259
- package/dist/commands/install.js +0 -1590
- package/dist/commands/login.d.ts +0 -20
- package/dist/commands/login.js +0 -170
- package/dist/commands/logout.d.ts +0 -10
- package/dist/commands/logout.js +0 -41
- package/dist/commands/playground.d.ts +0 -11
- package/dist/commands/playground.js +0 -47
- package/dist/commands/releases.d.ts +0 -17
- package/dist/commands/releases.js +0 -54
- package/dist/commands/replay.d.ts +0 -18
- package/dist/commands/replay.js +0 -64
- package/dist/commands/secrets.d.ts +0 -19
- package/dist/commands/secrets.js +0 -145
- package/dist/commands/setup.d.ts +0 -21
- package/dist/commands/setup.js +0 -67
- package/dist/commands/sites.d.ts +0 -18
- package/dist/commands/sites.js +0 -139
- package/dist/commands/skad.d.ts +0 -18
- package/dist/commands/skad.js +0 -53
- package/dist/commands/sourcemap.d.ts +0 -33
- package/dist/commands/sourcemap.js +0 -204
- package/dist/commands/status.d.ts +0 -7
- package/dist/commands/status.js +0 -136
- package/dist/commands/upgrade.d.ts +0 -21
- package/dist/commands/upgrade.js +0 -183
- package/dist/commands/warehouse.d.ts +0 -20
- package/dist/commands/warehouse.js +0 -65
- package/dist/commands/warehouses.d.ts +0 -17
- package/dist/commands/warehouses.js +0 -182
- package/dist/commands/watch.d.ts +0 -45
- package/dist/commands/watch.js +0 -258
- package/dist/commands/whoami.d.ts +0 -9
- package/dist/commands/whoami.js +0 -50
- package/dist/config.d.ts +0 -75
- package/dist/config.js +0 -329
- package/dist/frameworks/detect.d.ts +0 -8
- package/dist/frameworks/detect.js +0 -458
- package/dist/install-intent-proposal.d.ts +0 -99
- package/dist/install-intent-proposal.js +0 -202
- package/dist/utils/api.d.ts +0 -20
- package/dist/utils/api.js +0 -47
- package/dist/utils/config.d.ts +0 -13
- package/dist/utils/config.js +0 -30
- package/dist/utils/confirm.d.ts +0 -17
- package/dist/utils/confirm.js +0 -40
- package/dist/utils/dry-run.d.ts +0 -20
- package/dist/utils/dry-run.js +0 -67
- package/dist/utils/from-file.d.ts +0 -9
- package/dist/utils/from-file.js +0 -72
- package/dist/utils/redact.d.ts +0 -14
- package/dist/utils/redact.js +0 -48
- package/dist/utils/ui.d.ts +0 -14
- package/dist/utils/ui.js +0 -59
- package/scripts/.gitkeep +0 -0
- package/scripts/README-gurulu-agentic-install.md +0 -114
- package/scripts/README-gurulu-scan.md +0 -98
- package/scripts/audit-cli-scopes.mjs +0 -204
- package/scripts/backfill-tenant-id.mjs +0 -172
- package/scripts/backfill-tenant-links.ts +0 -252
- package/scripts/backup-clickhouse.sh +0 -27
- package/scripts/backup-postgres.sh +0 -19
- package/scripts/bootstrap-runtime-schema.mjs +0 -87
- package/scripts/bootstrap-stripe.mjs +0 -158
- package/scripts/gurulu-agentic-install.lib.cjs +0 -762
- package/scripts/gurulu-agentic-install.mjs +0 -623
- package/scripts/gurulu-scan.lib.cjs +0 -1509
- package/scripts/gurulu-scan.mjs +0 -91
- package/scripts/gurulu-verify-install.lib.cjs +0 -334
- package/scripts/gurulu-verify-install.mjs +0 -59
- package/scripts/init-ssl.sh +0 -26
- package/scripts/migrate-flow-graph-enums.sh +0 -86
- package/scripts/monitor-disk.sh +0 -24
- package/scripts/patches/astro.patch.cjs +0 -74
- package/scripts/patches/auto-instrument/ast-helper.cjs +0 -480
- package/scripts/patches/auto-instrument/astro.cjs +0 -273
- package/scripts/patches/auto-instrument/express.cjs +0 -383
- package/scripts/patches/auto-instrument/fastify.cjs +0 -262
- package/scripts/patches/auto-instrument/hono.cjs +0 -392
- package/scripts/patches/auto-instrument/index.cjs +0 -80
- package/scripts/patches/auto-instrument/nestjs.cjs +0 -286
- package/scripts/patches/auto-instrument/nextjs-app-router.cjs +0 -345
- package/scripts/patches/auto-instrument/nextjs-pages.cjs +0 -361
- package/scripts/patches/auto-instrument/remix.cjs +0 -168
- package/scripts/patches/auto-instrument/sdk-helper-map.cjs +0 -241
- package/scripts/patches/auto-instrument/singleton-helper.cjs +0 -193
- package/scripts/patches/auto-instrument/sveltekit.cjs +0 -161
- package/scripts/patches/auto-instrument/vite-react.cjs +0 -37
- package/scripts/patches/auto-instrument/vue.cjs +0 -196
- package/scripts/patches/express.patch.cjs +0 -99
- package/scripts/patches/fastify.patch.cjs +0 -108
- package/scripts/patches/index.cjs +0 -300
- package/scripts/patches/nestjs.patch.cjs +0 -112
- package/scripts/patches/nextjs-app-router.patch.cjs +0 -97
- package/scripts/patches/nextjs-pages.patch.cjs +0 -97
- package/scripts/patches/remix.patch.cjs +0 -75
- package/scripts/patches/sveltekit.patch.cjs +0 -72
- package/scripts/patches/vite-react.patch.cjs +0 -73
- package/scripts/patches/vue.patch.cjs +0 -82
- package/scripts/renew-ssl.sh +0 -14
- package/scripts/resolve-migration.sh +0 -23
- package/scripts/seed-cli-dev-keys.mjs +0 -130
- package/scripts/seed-test-data.mjs +0 -391
- package/scripts/spike-browserless.ts +0 -65
- package/scripts/tenant-pivot-consistency-check.mjs +0 -205
- package/scripts/tenant-pivot-phase-3-cleanup.lib.cjs +0 -258
- package/scripts/tenant-pivot-phase-3-cleanup.mjs +0 -98
- package/scripts/test-identity-resolution.ts +0 -804
- package/scripts/validate-gurulu-schemas.mjs +0 -79
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Phase 19.5 W2 B8 — `gurulu identity decay|transfers|cdc-sources`.
|
|
4
|
-
*
|
|
5
|
-
* Sprint E SE-D — `gurulu identity identify|alias|merge` write surface
|
|
6
|
-
* (mirrors MCP `gurulu.identity.identify` / `.alias` / `.merge`).
|
|
7
|
-
*/
|
|
8
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
-
if (k2 === undefined) k2 = k;
|
|
10
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
-
}
|
|
14
|
-
Object.defineProperty(o, k2, desc);
|
|
15
|
-
}) : (function(o, m, k, k2) {
|
|
16
|
-
if (k2 === undefined) k2 = k;
|
|
17
|
-
o[k2] = m[k];
|
|
18
|
-
}));
|
|
19
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
-
}) : function(o, v) {
|
|
22
|
-
o["default"] = v;
|
|
23
|
-
});
|
|
24
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
-
var ownKeys = function(o) {
|
|
26
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
-
var ar = [];
|
|
28
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
-
return ar;
|
|
30
|
-
};
|
|
31
|
-
return ownKeys(o);
|
|
32
|
-
};
|
|
33
|
-
return function (mod) {
|
|
34
|
-
if (mod && mod.__esModule) return mod;
|
|
35
|
-
var result = {};
|
|
36
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
-
__setModuleDefault(result, mod);
|
|
38
|
-
return result;
|
|
39
|
-
};
|
|
40
|
-
})();
|
|
41
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
-
exports.identityCommand = identityCommand;
|
|
43
|
-
const api_client_1 = require("../api-client");
|
|
44
|
-
const ui_1 = require("../utils/ui");
|
|
45
|
-
const fs = __importStar(require("fs"));
|
|
46
|
-
const path = __importStar(require("path"));
|
|
47
|
-
async function identityCommand(args) {
|
|
48
|
-
const action = args.action || '';
|
|
49
|
-
switch (action) {
|
|
50
|
-
case 'decay':
|
|
51
|
-
return decayCmd(args);
|
|
52
|
-
case 'transfers':
|
|
53
|
-
return transfersCmd(args);
|
|
54
|
-
case 'cdc-sources':
|
|
55
|
-
return cdcSourcesCmd(args);
|
|
56
|
-
case 'identify':
|
|
57
|
-
return identifyCmd(args);
|
|
58
|
-
case 'alias':
|
|
59
|
-
return aliasCmd(args);
|
|
60
|
-
case 'merge':
|
|
61
|
-
return mergeCmd(args);
|
|
62
|
-
case 'bulk':
|
|
63
|
-
return bulkCmd(args);
|
|
64
|
-
default:
|
|
65
|
-
(0, ui_1.error)(`Unknown identity action: ${action}`);
|
|
66
|
-
(0, ui_1.info)('Usage: gurulu identity [decay stats|transfers list|cdc-sources list|identify|alias|merge|bulk]');
|
|
67
|
-
process.exit(1);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
async function decayCmd(args) {
|
|
71
|
-
// `gurulu identity decay stats` is the only supported form for Phase 19.5.
|
|
72
|
-
if (args.sub && args.sub !== 'stats') {
|
|
73
|
-
(0, ui_1.error)(`Unknown identity decay sub: ${args.sub}`);
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
const body = await (0, api_client_1.cliApiJson)('/api/cli/identity/decay/stats', {
|
|
77
|
-
profile: args.profile,
|
|
78
|
-
});
|
|
79
|
-
if (args.json) {
|
|
80
|
-
process.stdout.write(JSON.stringify(body, null, 2) + '\n');
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
process.stdout.write(`Retired claims: ${body.totalRetiredClaims}\n`);
|
|
84
|
-
process.stdout.write(`Retired tags: ${body.totalRetiredTags}\n`);
|
|
85
|
-
process.stdout.write(`Recent jobs: ${(body.recentRuns || []).length}\n`);
|
|
86
|
-
process.stdout.write(`Histogram:\n`);
|
|
87
|
-
for (const [k, v] of Object.entries(body.decayHistogram || {})) {
|
|
88
|
-
process.stdout.write(` ${k}: ${v}\n`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
async function transfersCmd(args) {
|
|
92
|
-
if (args.sub && args.sub !== 'list') {
|
|
93
|
-
(0, ui_1.error)(`Unknown identity transfers sub: ${args.sub}`);
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}
|
|
96
|
-
const qs = new URLSearchParams();
|
|
97
|
-
if (args.direction)
|
|
98
|
-
qs.set('direction', args.direction);
|
|
99
|
-
if (args.status)
|
|
100
|
-
qs.set('status', args.status);
|
|
101
|
-
if (args.limit)
|
|
102
|
-
qs.set('limit', String(args.limit));
|
|
103
|
-
const path = `/api/cli/identity/transfers${qs.toString() ? `?${qs.toString()}` : ''}`;
|
|
104
|
-
const body = await (0, api_client_1.cliApiJson)(path, { profile: args.profile });
|
|
105
|
-
if (args.json) {
|
|
106
|
-
process.stdout.write(JSON.stringify(body, null, 2) + '\n');
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
const rows = body.transfers || [];
|
|
110
|
-
if (rows.length === 0) {
|
|
111
|
-
(0, ui_1.info)('No transfers.');
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
process.stdout.write(['ID', 'SOURCE', 'TARGET', 'STATUS', 'INITIATED'].join('\t') + '\n');
|
|
115
|
-
for (const t of rows) {
|
|
116
|
-
process.stdout.write([
|
|
117
|
-
t.id,
|
|
118
|
-
t.sourceTenantId,
|
|
119
|
-
t.targetTenantId,
|
|
120
|
-
t.status,
|
|
121
|
-
String(t.initiatedAt || '-'),
|
|
122
|
-
].join('\t') + '\n');
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
async function identifyCmd(args) {
|
|
126
|
-
if (!args.site || !args.userId) {
|
|
127
|
-
(0, ui_1.error)('Usage: gurulu identity identify --site=<id> --user-id=<id> [--email=...] [--phone=...] [--traits=<json>]');
|
|
128
|
-
process.exit(1);
|
|
129
|
-
}
|
|
130
|
-
let traits;
|
|
131
|
-
if (args.traits) {
|
|
132
|
-
try {
|
|
133
|
-
traits = JSON.parse(args.traits);
|
|
134
|
-
}
|
|
135
|
-
catch {
|
|
136
|
-
(0, ui_1.error)('--traits must be valid JSON');
|
|
137
|
-
process.exit(1);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
const body = await (0, api_client_1.cliApiJson)('/api/cli/identity/identify', {
|
|
141
|
-
profile: args.profile,
|
|
142
|
-
method: 'POST',
|
|
143
|
-
json: {
|
|
144
|
-
siteId: args.site,
|
|
145
|
-
userId: args.userId,
|
|
146
|
-
email: args.email,
|
|
147
|
-
phone: args.phone,
|
|
148
|
-
traits,
|
|
149
|
-
},
|
|
150
|
-
});
|
|
151
|
-
if (args.json) {
|
|
152
|
-
process.stdout.write(JSON.stringify(body, null, 2) + '\n');
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
(0, ui_1.success)(`Identified ${args.userId}.`);
|
|
156
|
-
if (body.canonicalPersonId) {
|
|
157
|
-
process.stdout.write(` canonicalPersonId: ${body.canonicalPersonId}\n`);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
async function aliasCmd(args) {
|
|
161
|
-
if (!args.site || !args.previousUserId || !args.newUserId) {
|
|
162
|
-
(0, ui_1.error)('Usage: gurulu identity alias --site=<id> --previous-user-id=<anon> --new-user-id=<u123>');
|
|
163
|
-
process.exit(1);
|
|
164
|
-
}
|
|
165
|
-
const body = await (0, api_client_1.cliApiJson)('/api/cli/identity/alias', {
|
|
166
|
-
profile: args.profile,
|
|
167
|
-
method: 'POST',
|
|
168
|
-
json: {
|
|
169
|
-
siteId: args.site,
|
|
170
|
-
previousUserId: args.previousUserId,
|
|
171
|
-
newUserId: args.newUserId,
|
|
172
|
-
},
|
|
173
|
-
});
|
|
174
|
-
if (args.json) {
|
|
175
|
-
process.stdout.write(JSON.stringify(body, null, 2) + '\n');
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
(0, ui_1.success)(`Aliased ${args.previousUserId} → ${args.newUserId}.`);
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* `gurulu identity bulk --site=<id> --file=<path> [--format=csv|json]
|
|
182
|
-
* [--resume-from=<offset>]`.
|
|
183
|
-
*
|
|
184
|
-
* Streams a CSV or JSON file to `/api/ingest/v1/identify/bulk` for
|
|
185
|
-
* server-side fan-out. The endpoint chunks records server-side (500 per
|
|
186
|
-
* tx); we only enforce a 10 000-record max here as a friendly preflight.
|
|
187
|
-
*
|
|
188
|
-
* `--resume-from` is forwarded as the `x-resume-from` header so a partial
|
|
189
|
-
* import can pick up where it left off without reuploading the whole
|
|
190
|
-
* file.
|
|
191
|
-
*/
|
|
192
|
-
async function bulkCmd(args) {
|
|
193
|
-
if (!args.site || !args.file) {
|
|
194
|
-
(0, ui_1.error)('Usage: gurulu identity bulk --site=<id> --file=<path> [--format=csv|json] [--resume-from=<offset>]');
|
|
195
|
-
process.exit(1);
|
|
196
|
-
}
|
|
197
|
-
const filePath = path.resolve(args.file);
|
|
198
|
-
if (!fs.existsSync(filePath)) {
|
|
199
|
-
(0, ui_1.error)(`File not found: ${filePath}`);
|
|
200
|
-
process.exit(1);
|
|
201
|
-
}
|
|
202
|
-
// Auto-detect format from extension when --format is not supplied.
|
|
203
|
-
let format = (args.format || '').toLowerCase();
|
|
204
|
-
if (!format) {
|
|
205
|
-
const ext = path.extname(filePath).slice(1).toLowerCase();
|
|
206
|
-
format = ext === 'csv' ? 'csv' : 'json';
|
|
207
|
-
}
|
|
208
|
-
if (format !== 'csv' && format !== 'json') {
|
|
209
|
-
(0, ui_1.error)('--format must be csv or json');
|
|
210
|
-
process.exit(1);
|
|
211
|
-
}
|
|
212
|
-
const raw = fs.readFileSync(filePath, 'utf8');
|
|
213
|
-
let body;
|
|
214
|
-
let contentType;
|
|
215
|
-
if (format === 'csv') {
|
|
216
|
-
contentType = 'text/csv';
|
|
217
|
-
body = raw;
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
contentType = 'application/json';
|
|
221
|
-
// Allow files that are either { records: [...] } or a bare array.
|
|
222
|
-
let parsed;
|
|
223
|
-
try {
|
|
224
|
-
parsed = JSON.parse(raw);
|
|
225
|
-
}
|
|
226
|
-
catch (err) {
|
|
227
|
-
(0, ui_1.error)(`JSON file is invalid: ${err.message}`);
|
|
228
|
-
process.exit(1);
|
|
229
|
-
}
|
|
230
|
-
const records = Array.isArray(parsed)
|
|
231
|
-
? parsed
|
|
232
|
-
: Array.isArray(parsed?.records)
|
|
233
|
-
? parsed.records
|
|
234
|
-
: null;
|
|
235
|
-
if (!records) {
|
|
236
|
-
(0, ui_1.error)('JSON file must be an array or { records: [...] }');
|
|
237
|
-
process.exit(1);
|
|
238
|
-
}
|
|
239
|
-
body = JSON.stringify({ siteId: args.site, records });
|
|
240
|
-
}
|
|
241
|
-
const headers = { 'content-type': contentType };
|
|
242
|
-
if (args.resumeFrom && args.resumeFrom > 0) {
|
|
243
|
-
headers['x-resume-from'] = String(args.resumeFrom);
|
|
244
|
-
}
|
|
245
|
-
const url = `/api/ingest/v1/identify/bulk?site=${encodeURIComponent(args.site)}`;
|
|
246
|
-
let parsedBody;
|
|
247
|
-
try {
|
|
248
|
-
const res = await (0, api_client_1.cliApi)(url, {
|
|
249
|
-
profile: args.profile,
|
|
250
|
-
method: 'POST',
|
|
251
|
-
headers,
|
|
252
|
-
body,
|
|
253
|
-
});
|
|
254
|
-
const text = await res.text();
|
|
255
|
-
parsedBody = text ? JSON.parse(text) : {};
|
|
256
|
-
if (!res.ok) {
|
|
257
|
-
throw new api_client_1.CliApiError(res.status, parsedBody?.error || 'http_error', parsedBody?.message || `HTTP ${res.status}`, parsedBody);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
catch (err) {
|
|
261
|
-
if (err instanceof api_client_1.CliApiError) {
|
|
262
|
-
(0, ui_1.error)(`Bulk import failed: ${err.code} (${err.message})`);
|
|
263
|
-
process.exit(1);
|
|
264
|
-
}
|
|
265
|
-
throw err;
|
|
266
|
-
}
|
|
267
|
-
if (args.json) {
|
|
268
|
-
process.stdout.write(JSON.stringify(parsedBody, null, 2) + '\n');
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
(0, ui_1.success)(`Bulk identify done. processed=${parsedBody.processed ?? 0} succeeded=${parsedBody.succeeded ?? 0} failed=${parsedBody.failed ?? 0}`);
|
|
272
|
-
if (parsedBody.errors && parsedBody.errors.length > 0) {
|
|
273
|
-
const sample = parsedBody.errors.slice(0, 5);
|
|
274
|
-
(0, ui_1.info)(`First ${sample.length} error(s):`);
|
|
275
|
-
for (const e of sample) {
|
|
276
|
-
process.stdout.write(` [${e.index}] ${e.message}\n`);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
async function mergeCmd(args) {
|
|
281
|
-
if (!args.site || !args.canonicalId || !args.duplicateId) {
|
|
282
|
-
(0, ui_1.error)('Usage: gurulu identity merge --site=<id> --canonical-id=<winner> --duplicate-id=<loser>');
|
|
283
|
-
process.exit(1);
|
|
284
|
-
}
|
|
285
|
-
const body = await (0, api_client_1.cliApiJson)('/api/cli/identity/merge', {
|
|
286
|
-
profile: args.profile,
|
|
287
|
-
method: 'POST',
|
|
288
|
-
json: {
|
|
289
|
-
siteId: args.site,
|
|
290
|
-
canonicalId: args.canonicalId,
|
|
291
|
-
duplicateId: args.duplicateId,
|
|
292
|
-
},
|
|
293
|
-
});
|
|
294
|
-
if (args.json) {
|
|
295
|
-
process.stdout.write(JSON.stringify(body, null, 2) + '\n');
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
(0, ui_1.success)(`Merged ${args.duplicateId} → ${args.canonicalId}.`);
|
|
299
|
-
}
|
|
300
|
-
async function cdcSourcesCmd(args) {
|
|
301
|
-
if (args.sub && args.sub !== 'list') {
|
|
302
|
-
(0, ui_1.error)(`Unknown identity cdc-sources sub: ${args.sub}`);
|
|
303
|
-
process.exit(1);
|
|
304
|
-
}
|
|
305
|
-
const body = await (0, api_client_1.cliApiJson)('/api/cli/identity/cdc-sources', {
|
|
306
|
-
profile: args.profile,
|
|
307
|
-
});
|
|
308
|
-
if (args.json) {
|
|
309
|
-
process.stdout.write(JSON.stringify(body, null, 2) + '\n');
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
const rows = body.sources || [];
|
|
313
|
-
if (rows.length === 0) {
|
|
314
|
-
(0, ui_1.info)('No CDC sources configured.');
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
process.stdout.write(['ID', 'NAME', 'TYPE', 'TABLE', 'ACTIVE', 'LAST_POLL'].join('\t') + '\n');
|
|
318
|
-
for (const s of rows) {
|
|
319
|
-
process.stdout.write([
|
|
320
|
-
s.id,
|
|
321
|
-
s.name,
|
|
322
|
-
s.sourceType,
|
|
323
|
-
s.tableName,
|
|
324
|
-
s.isActive ? 'yes' : 'no',
|
|
325
|
-
String(s.lastPollAt || '-'),
|
|
326
|
-
].join('\t') + '\n');
|
|
327
|
-
}
|
|
328
|
-
}
|
package/dist/commands/init.js
DELETED
|
@@ -1,215 +0,0 @@
|
|
|
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
|
-
exports.initCommand = initCommand;
|
|
7
|
-
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const config_1 = require("../config");
|
|
10
|
-
const api_client_1 = require("../api-client");
|
|
11
|
-
const detect_1 = require("../frameworks/detect");
|
|
12
|
-
const ui_1 = require("../utils/ui");
|
|
13
|
-
async function initCommand(args) {
|
|
14
|
-
const projectDir = process.cwd();
|
|
15
|
-
if (args.json) {
|
|
16
|
-
return initJSON(args, projectDir);
|
|
17
|
-
}
|
|
18
|
-
(0, ui_1.banner)();
|
|
19
|
-
// Step 1: Detect framework
|
|
20
|
-
let framework = args.framework || (0, detect_1.detectFramework)(projectDir);
|
|
21
|
-
(0, ui_1.info)(`Detected framework: ${(0, ui_1.bold)((0, detect_1.getFrameworkDisplayName)(framework))}`);
|
|
22
|
-
if (!args.framework && !args.noInteractive && framework === 'unknown') {
|
|
23
|
-
console.log('');
|
|
24
|
-
(0, ui_1.warn)('Could not auto-detect framework.');
|
|
25
|
-
const frameworks = ['nextjs-app', 'nextjs-pages', 'react-vite', 'react-cra', 'vue3', 'nuxt3', 'svelte', 'sveltekit', 'astro', 'express', 'fastify', 'hono', 'nestjs', 'react-native', 'ios-swift', 'android-kotlin', 'flutter', 'html'];
|
|
26
|
-
const idx = await (0, ui_1.promptSelect)(' Select your framework: ', frameworks.map(f => (0, detect_1.getFrameworkDisplayName)(f)));
|
|
27
|
-
framework = frameworks[idx];
|
|
28
|
-
(0, ui_1.info)(`Using: ${(0, ui_1.bold)((0, detect_1.getFrameworkDisplayName)(framework))}`);
|
|
29
|
-
}
|
|
30
|
-
// Step 2: Get site credentials
|
|
31
|
-
let siteId = args.siteId || process.env.GURULU_SITE_ID;
|
|
32
|
-
let token = args.token || process.env.GURULU_TOKEN;
|
|
33
|
-
if (!siteId || !token) {
|
|
34
|
-
let profile;
|
|
35
|
-
try {
|
|
36
|
-
profile = await (0, config_1.loadActiveProfile)({ profile: args.profile });
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
profile = null;
|
|
40
|
-
}
|
|
41
|
-
if (profile && !siteId) {
|
|
42
|
-
try {
|
|
43
|
-
const data = await (0, api_client_1.cliApiJson)('/api/cli/sites', { preloadedProfile: profile });
|
|
44
|
-
const sites = data.sites || [];
|
|
45
|
-
if (sites.length === 0) {
|
|
46
|
-
(0, ui_1.error)('No sites found. Create a site at https://gurulu.io first.');
|
|
47
|
-
process.exit(1);
|
|
48
|
-
}
|
|
49
|
-
if (sites.length === 1 || args.noInteractive) {
|
|
50
|
-
siteId = sites[0].id;
|
|
51
|
-
(0, ui_1.info)(`Using site: ${(0, ui_1.bold)(sites[0].domain)}`);
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
console.log('');
|
|
55
|
-
const idx = await (0, ui_1.promptSelect)(' Select a site: ', sites.map(s => `${s.domain} (${s.id})`));
|
|
56
|
-
siteId = sites[idx].id;
|
|
57
|
-
}
|
|
58
|
-
// Fetch site details to get publishable key as token
|
|
59
|
-
if (!token) {
|
|
60
|
-
(0, ui_1.step)('Fetching site credentials...');
|
|
61
|
-
const siteData = await (0, api_client_1.cliApiJson)(`/api/cli/sites/${encodeURIComponent(siteId)}`, { preloadedProfile: profile });
|
|
62
|
-
token = siteData.site?.publishableKey || '';
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
catch (err) {
|
|
66
|
-
if (!args.noInteractive) {
|
|
67
|
-
(0, ui_1.warn)('Could not fetch sites. Enter credentials manually.');
|
|
68
|
-
if (!siteId)
|
|
69
|
-
siteId = await (0, ui_1.prompt)(' Site ID: ');
|
|
70
|
-
if (!token)
|
|
71
|
-
token = await (0, ui_1.prompt)(' Site Token: ');
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
(0, ui_1.error)(`Failed to fetch credentials: ${err.message}`);
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
else if (!args.noInteractive) {
|
|
80
|
-
if (!profile) {
|
|
81
|
-
(0, ui_1.warn)('Not authenticated. Run "gurulu login" first, or provide --site-id and --token.');
|
|
82
|
-
console.log('');
|
|
83
|
-
}
|
|
84
|
-
if (!siteId)
|
|
85
|
-
siteId = await (0, ui_1.prompt)(' Site ID: ');
|
|
86
|
-
if (!token)
|
|
87
|
-
token = await (0, ui_1.prompt)(' Site Token: ');
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
(0, ui_1.error)('Site ID and token required in non-interactive mode. Use --site-id and --token, or run "gurulu login" first.');
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if (!siteId || !token) {
|
|
95
|
-
(0, ui_1.error)('Site ID and token are required.');
|
|
96
|
-
process.exit(1);
|
|
97
|
-
}
|
|
98
|
-
// Step 3: Generate setup code
|
|
99
|
-
const snippet = (0, detect_1.getSetupSnippet)(framework, siteId, token);
|
|
100
|
-
console.log('');
|
|
101
|
-
if (args.dryRun) {
|
|
102
|
-
(0, ui_1.info)('Dry run - showing what would be done:');
|
|
103
|
-
console.log('');
|
|
104
|
-
if (snippet.file) {
|
|
105
|
-
console.log(` ${(0, ui_1.dim)('File:')} ${(0, ui_1.cyan)(snippet.file)}`);
|
|
106
|
-
}
|
|
107
|
-
console.log(` ${(0, ui_1.dim)('Instruction:')} ${snippet.instruction}`);
|
|
108
|
-
console.log('');
|
|
109
|
-
console.log((0, ui_1.dim)(' --- Code ---'));
|
|
110
|
-
snippet.code.split('\n').forEach(line => {
|
|
111
|
-
console.log(` ${(0, ui_1.dim)(line)}`);
|
|
112
|
-
});
|
|
113
|
-
console.log((0, ui_1.dim)(' --- End ---'));
|
|
114
|
-
console.log('');
|
|
115
|
-
if (framework !== 'html') {
|
|
116
|
-
console.log(` ${(0, ui_1.dim)('.env.local would include:')}`);
|
|
117
|
-
console.log(` NEXT_PUBLIC_GURULU_SITE_ID=${siteId}`);
|
|
118
|
-
console.log(` NEXT_PUBLIC_GURULU_TOKEN=${token}`);
|
|
119
|
-
}
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
// Step 4: Write setup code
|
|
123
|
-
if (snippet.file) {
|
|
124
|
-
const filePath = path_1.default.join(projectDir, snippet.file);
|
|
125
|
-
const fileDir = path_1.default.dirname(filePath);
|
|
126
|
-
if (fs_1.default.existsSync(filePath)) {
|
|
127
|
-
(0, ui_1.warn)(`File already exists: ${snippet.file}`);
|
|
128
|
-
if (!args.noInteractive) {
|
|
129
|
-
const answer = await (0, ui_1.prompt)(' Overwrite? (y/N): ');
|
|
130
|
-
if (answer.toLowerCase() !== 'y') {
|
|
131
|
-
(0, ui_1.info)('Skipping file creation.');
|
|
132
|
-
printNextSteps(snippet, siteId, token, framework);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
(0, ui_1.info)('Skipping existing file in non-interactive mode.');
|
|
138
|
-
printNextSteps(snippet, siteId, token, framework);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
fs_1.default.mkdirSync(fileDir, { recursive: true });
|
|
143
|
-
fs_1.default.writeFileSync(filePath, snippet.code);
|
|
144
|
-
(0, ui_1.success)(`Created ${snippet.file}`);
|
|
145
|
-
}
|
|
146
|
-
// Step 5: Update .env.local (for frontend frameworks)
|
|
147
|
-
const frontendFrameworks = ['nextjs-app', 'nextjs-pages', 'react-vite', 'react-cra', 'vue3', 'nuxt3', 'svelte', 'sveltekit', 'astro'];
|
|
148
|
-
if (frontendFrameworks.includes(framework)) {
|
|
149
|
-
const envFile = path_1.default.join(projectDir, '.env.local');
|
|
150
|
-
const envPrefix = framework.startsWith('nuxt') ? 'NUXT_PUBLIC' : framework.startsWith('next') ? 'NEXT_PUBLIC' : 'VITE';
|
|
151
|
-
const envLines = [
|
|
152
|
-
`${envPrefix}_GURULU_SITE_ID=${siteId}`,
|
|
153
|
-
`${envPrefix}_GURULU_TOKEN=${token}`,
|
|
154
|
-
];
|
|
155
|
-
let existingEnv = '';
|
|
156
|
-
if (fs_1.default.existsSync(envFile)) {
|
|
157
|
-
existingEnv = fs_1.default.readFileSync(envFile, 'utf-8');
|
|
158
|
-
}
|
|
159
|
-
const newLines = envLines.filter(line => {
|
|
160
|
-
const key = line.split('=')[0];
|
|
161
|
-
return !existingEnv.includes(key);
|
|
162
|
-
});
|
|
163
|
-
if (newLines.length > 0) {
|
|
164
|
-
const separator = existingEnv && !existingEnv.endsWith('\n') ? '\n' : '';
|
|
165
|
-
const header = existingEnv ? '' : '# Gurulu.io Analytics\n';
|
|
166
|
-
fs_1.default.appendFileSync(envFile, `${separator}${header}${newLines.join('\n')}\n`);
|
|
167
|
-
(0, ui_1.success)('Updated .env.local with site credentials');
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
(0, ui_1.info)('.env.local already has Gurulu credentials');
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
// Step 6: Print next steps
|
|
174
|
-
printNextSteps(snippet, siteId, token, framework);
|
|
175
|
-
}
|
|
176
|
-
function printNextSteps(snippet, siteId, token, framework) {
|
|
177
|
-
console.log('');
|
|
178
|
-
console.log((0, ui_1.bold)(' Next steps:'));
|
|
179
|
-
console.log('');
|
|
180
|
-
(0, ui_1.step)(snippet.instruction);
|
|
181
|
-
const mobileFrameworks = ['react-native', 'ios-swift', 'android-kotlin', 'flutter'];
|
|
182
|
-
if (framework === 'html' || mobileFrameworks.includes(framework)) {
|
|
183
|
-
console.log('');
|
|
184
|
-
console.log((0, ui_1.dim)(' Copy this snippet:'));
|
|
185
|
-
console.log('');
|
|
186
|
-
snippet.code.split('\n').forEach(line => {
|
|
187
|
-
console.log(` ${line}`);
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
console.log('');
|
|
191
|
-
(0, ui_1.step)(`Run ${(0, ui_1.cyan)('gurulu status')} to verify the connection`);
|
|
192
|
-
(0, ui_1.step)(`Run ${(0, ui_1.cyan)('gurulu events')} to see detected events`);
|
|
193
|
-
(0, ui_1.step)(`Visit ${(0, ui_1.cyan)('https://gurulu.io')} to view your dashboard`);
|
|
194
|
-
console.log('');
|
|
195
|
-
(0, ui_1.success)('Setup complete!');
|
|
196
|
-
console.log('');
|
|
197
|
-
}
|
|
198
|
-
async function initJSON(args, projectDir) {
|
|
199
|
-
const framework = args.framework || (0, detect_1.detectFramework)(projectDir);
|
|
200
|
-
const siteId = args.siteId || process.env.GURULU_SITE_ID || '';
|
|
201
|
-
const token = args.token || process.env.GURULU_TOKEN || '';
|
|
202
|
-
const snippet = (0, detect_1.getSetupSnippet)(framework, siteId, token);
|
|
203
|
-
const output = {
|
|
204
|
-
framework,
|
|
205
|
-
frameworkName: (0, detect_1.getFrameworkDisplayName)(framework),
|
|
206
|
-
siteId,
|
|
207
|
-
snippet: {
|
|
208
|
-
file: snippet.file,
|
|
209
|
-
code: snippet.code,
|
|
210
|
-
instruction: snippet.instruction,
|
|
211
|
-
},
|
|
212
|
-
dryRun: !!args.dryRun,
|
|
213
|
-
};
|
|
214
|
-
console.log(JSON.stringify(output, null, 2));
|
|
215
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 19.5 W2 B5 — `gurulu insights today|history|weekly`.
|
|
3
|
-
*/
|
|
4
|
-
export interface InsightsArgs {
|
|
5
|
-
action?: string;
|
|
6
|
-
days?: number;
|
|
7
|
-
json?: boolean;
|
|
8
|
-
profile?: string;
|
|
9
|
-
}
|
|
10
|
-
export declare function insightsCommand(args: InsightsArgs): Promise<void>;
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Phase 19.5 W2 B5 — `gurulu insights today|history|weekly`.
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.insightsCommand = insightsCommand;
|
|
7
|
-
const api_client_1 = require("../api-client");
|
|
8
|
-
const ui_1 = require("../utils/ui");
|
|
9
|
-
async function insightsCommand(args) {
|
|
10
|
-
const action = args.action || 'today';
|
|
11
|
-
switch (action) {
|
|
12
|
-
case 'today':
|
|
13
|
-
return todayCmd(args);
|
|
14
|
-
case 'history':
|
|
15
|
-
case 'weekly':
|
|
16
|
-
return historyCmd(args);
|
|
17
|
-
default:
|
|
18
|
-
(0, ui_1.error)(`Unknown insights action: ${action}`);
|
|
19
|
-
(0, ui_1.info)('Usage: gurulu insights [today|history|weekly]');
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function formatHighlight(h) {
|
|
24
|
-
if (typeof h === 'string')
|
|
25
|
-
return h;
|
|
26
|
-
if (h && typeof h === 'object') {
|
|
27
|
-
const o = h;
|
|
28
|
-
const arrow = o.trend === 'up' ? '↑' : o.trend === 'down' ? '↓' : '→';
|
|
29
|
-
const value = typeof o.value === 'number' ? o.value.toLocaleString() : String(o.value ?? '');
|
|
30
|
-
const delta = typeof o.delta === 'number' ? ` (${o.delta >= 0 ? '+' : ''}${o.delta.toFixed(1)}% ${arrow})` : '';
|
|
31
|
-
return `${o.label ?? 'metric'}: ${value}${delta}`;
|
|
32
|
-
}
|
|
33
|
-
return String(h);
|
|
34
|
-
}
|
|
35
|
-
async function todayCmd(args) {
|
|
36
|
-
try {
|
|
37
|
-
const body = await (0, api_client_1.cliApiJson)('/api/cli/insights', {
|
|
38
|
-
profile: args.profile,
|
|
39
|
-
});
|
|
40
|
-
if (args.json) {
|
|
41
|
-
process.stdout.write(JSON.stringify(body, null, 2) + '\n');
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const i = body.insight;
|
|
45
|
-
process.stdout.write(`Date: ${String(i.date).slice(0, 10)}\n`);
|
|
46
|
-
process.stdout.write(`Summary: ${i.summary || '-'}\n`);
|
|
47
|
-
if (Array.isArray(i.highlights)) {
|
|
48
|
-
process.stdout.write(`Highlights:\n`);
|
|
49
|
-
for (const h of i.highlights)
|
|
50
|
-
process.stdout.write(` - ${formatHighlight(h)}\n`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch (err) {
|
|
54
|
-
if (err?.status === 404) {
|
|
55
|
-
(0, ui_1.info)('No daily insight available yet.');
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
throw err;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
async function historyCmd(args) {
|
|
62
|
-
const days = args.days ?? 7;
|
|
63
|
-
const body = await (0, api_client_1.cliApiJson)(`/api/cli/insights/history?days=${days}`, { profile: args.profile });
|
|
64
|
-
if (args.json) {
|
|
65
|
-
process.stdout.write(JSON.stringify(body, null, 2) + '\n');
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
const rows = body.insights || [];
|
|
69
|
-
if (rows.length === 0) {
|
|
70
|
-
(0, ui_1.info)('No insights in the selected window.');
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
process.stdout.write(['DATE', 'SUMMARY'].join('\t') + '\n');
|
|
74
|
-
for (const r of rows) {
|
|
75
|
-
process.stdout.write([String(r.date).slice(0, 10), String(r.summary || '-').slice(0, 80)].join('\t') + '\n');
|
|
76
|
-
}
|
|
77
|
-
}
|