@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,243 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Phase 19.5 W2 B2 — `gurulu audiences list|show`.
|
|
4
|
-
* Phase 20 W2 B1 — `create|update|delete` (with `--dry-run`, `--yes`).
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.audiencesCommand = audiencesCommand;
|
|
8
|
-
const node_fs_1 = require("node:fs");
|
|
9
|
-
const api_client_1 = require("../api-client");
|
|
10
|
-
const ui_1 = require("../utils/ui");
|
|
11
|
-
const confirm_1 = require("../utils/confirm");
|
|
12
|
-
const dry_run_1 = require("../utils/dry-run");
|
|
13
|
-
const from_file_1 = require("../utils/from-file");
|
|
14
|
-
async function audiencesCommand(args) {
|
|
15
|
-
const action = args.action || 'list';
|
|
16
|
-
switch (action) {
|
|
17
|
-
case 'list':
|
|
18
|
-
return listCmd(args);
|
|
19
|
-
case 'show':
|
|
20
|
-
return showCmd(args);
|
|
21
|
-
case 'create':
|
|
22
|
-
return createCmd(args);
|
|
23
|
-
case 'update':
|
|
24
|
-
return updateCmd(args);
|
|
25
|
-
case 'delete':
|
|
26
|
-
return deleteCmd(args);
|
|
27
|
-
case 'export':
|
|
28
|
-
return exportCmd(args);
|
|
29
|
-
default:
|
|
30
|
-
(0, ui_1.error)(`Unknown audiences action: ${action}`);
|
|
31
|
-
(0, ui_1.info)('Usage: gurulu audiences [list|show|create|update|delete|export]');
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
async function listCmd(args) {
|
|
36
|
-
const qs = args.site ? `?siteId=${encodeURIComponent(args.site)}` : '';
|
|
37
|
-
const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences${qs}`, {
|
|
38
|
-
profile: args.profile,
|
|
39
|
-
});
|
|
40
|
-
const audiences = body.audiences || [];
|
|
41
|
-
if (args.json) {
|
|
42
|
-
process.stdout.write(JSON.stringify(body, null, 2) + '\n');
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (audiences.length === 0) {
|
|
46
|
-
(0, ui_1.info)('No audiences yet.');
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
process.stdout.write(['NAME', 'ID', 'SIZE', 'DESTINATIONS', 'UPDATED'].join('\t') + '\n');
|
|
50
|
-
for (const a of audiences) {
|
|
51
|
-
process.stdout.write([
|
|
52
|
-
a.name,
|
|
53
|
-
a.id,
|
|
54
|
-
String(a.estimatedSize ?? '-'),
|
|
55
|
-
String(a.destinationCount ?? 0),
|
|
56
|
-
String(a.updatedAt || '-').slice(0, 19),
|
|
57
|
-
].join('\t') + '\n');
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
async function resolveId(target, profile) {
|
|
61
|
-
const body = await (0, api_client_1.cliApiJson)('/api/cli/audiences', { profile });
|
|
62
|
-
const found = (body.audiences || []).find((a) => a.id === target) ||
|
|
63
|
-
(body.audiences || []).find((a) => a.name === target);
|
|
64
|
-
if (!found)
|
|
65
|
-
throw new Error(`Audience '${target}' not found`);
|
|
66
|
-
return found.id;
|
|
67
|
-
}
|
|
68
|
-
async function showCmd(args) {
|
|
69
|
-
if (!args.target) {
|
|
70
|
-
(0, ui_1.error)('Usage: gurulu audiences show <name-or-id>');
|
|
71
|
-
process.exit(1);
|
|
72
|
-
}
|
|
73
|
-
const id = await resolveId(args.target, args.profile);
|
|
74
|
-
const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences/${encodeURIComponent(id)}`, { profile: args.profile });
|
|
75
|
-
if (args.json) {
|
|
76
|
-
process.stdout.write(JSON.stringify(body, null, 2) + '\n');
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const a = body.audience;
|
|
80
|
-
process.stdout.write(`Name: ${a.name}\n`);
|
|
81
|
-
process.stdout.write(`ID: ${a.id}\n`);
|
|
82
|
-
process.stdout.write(`Description: ${a.description || '-'}\n`);
|
|
83
|
-
process.stdout.write(`Estimated: ${a.estimatedSize ?? '-'}\n`);
|
|
84
|
-
process.stdout.write(`Destinations: ${(a.destinations || []).length}\n`);
|
|
85
|
-
process.stdout.write(`Triggers: ${(body.triggers || []).length}\n`);
|
|
86
|
-
process.stdout.write(`Updated: ${a.updatedAt}\n`);
|
|
87
|
-
}
|
|
88
|
-
async function createCmd(args) {
|
|
89
|
-
let payload = {};
|
|
90
|
-
if (args.fromFile) {
|
|
91
|
-
payload = (0, from_file_1.loadFromFile)(args.fromFile);
|
|
92
|
-
}
|
|
93
|
-
if (args.name)
|
|
94
|
-
payload.name = args.name;
|
|
95
|
-
if (args.site)
|
|
96
|
-
payload.siteId = args.site;
|
|
97
|
-
if (args.description !== undefined)
|
|
98
|
-
payload.description = args.description;
|
|
99
|
-
if (args.rules) {
|
|
100
|
-
try {
|
|
101
|
-
payload.rules = JSON.parse(args.rules);
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
(0, ui_1.error)('--rules must be valid JSON');
|
|
105
|
-
process.exit(1);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if (!payload.name) {
|
|
109
|
-
(0, ui_1.error)('name is required (pass --name or --from-file)');
|
|
110
|
-
process.exit(1);
|
|
111
|
-
}
|
|
112
|
-
if (!payload.siteId) {
|
|
113
|
-
(0, ui_1.error)('site is required (pass --site)');
|
|
114
|
-
process.exit(1);
|
|
115
|
-
}
|
|
116
|
-
const qs = args.dryRun ? '?dryRun=1' : '';
|
|
117
|
-
if (args.dryRun) {
|
|
118
|
-
const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences${qs}`, {
|
|
119
|
-
profile: args.profile,
|
|
120
|
-
method: 'POST',
|
|
121
|
-
json: payload,
|
|
122
|
-
});
|
|
123
|
-
(0, dry_run_1.printDryRun)(body, args.json);
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
const ok = await (0, confirm_1.promptConfirm)(`Create audience '${payload.name}'?`, { yes: args.yes, defaultYes: true });
|
|
127
|
-
if (!ok) {
|
|
128
|
-
(0, ui_1.info)('Aborted.');
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences`, {
|
|
132
|
-
profile: args.profile,
|
|
133
|
-
method: 'POST',
|
|
134
|
-
json: payload,
|
|
135
|
-
});
|
|
136
|
-
if (args.json) {
|
|
137
|
-
process.stdout.write(JSON.stringify(body, null, 2) + '\n');
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
(0, ui_1.success)(`Created audience ${body.audience?.id ?? ''}`);
|
|
141
|
-
}
|
|
142
|
-
async function updateCmd(args) {
|
|
143
|
-
const resolveTarget = args.id || args.target;
|
|
144
|
-
if (!resolveTarget) {
|
|
145
|
-
(0, ui_1.error)('Usage: gurulu audiences update <name-or-id> [--id ...] [--name ...] [--rules ...]');
|
|
146
|
-
process.exit(1);
|
|
147
|
-
}
|
|
148
|
-
const id = await resolveId(resolveTarget, args.profile);
|
|
149
|
-
let payload = {};
|
|
150
|
-
if (args.fromFile) {
|
|
151
|
-
payload = (0, from_file_1.loadFromFile)(args.fromFile);
|
|
152
|
-
}
|
|
153
|
-
if (args.name)
|
|
154
|
-
payload.name = args.name;
|
|
155
|
-
if (args.description !== undefined)
|
|
156
|
-
payload.description = args.description;
|
|
157
|
-
if (args.rules) {
|
|
158
|
-
try {
|
|
159
|
-
payload.rules = JSON.parse(args.rules);
|
|
160
|
-
}
|
|
161
|
-
catch {
|
|
162
|
-
(0, ui_1.error)('--rules must be valid JSON');
|
|
163
|
-
process.exit(1);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
const qs = args.dryRun ? '?dryRun=1' : '';
|
|
167
|
-
if (args.dryRun) {
|
|
168
|
-
const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences/${encodeURIComponent(id)}${qs}`, { profile: args.profile, method: 'PATCH', json: payload });
|
|
169
|
-
(0, dry_run_1.printDryRun)(body, args.json);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
const ok = await (0, confirm_1.promptConfirm)(`Update audience '${args.target}'?`, {
|
|
173
|
-
yes: args.yes,
|
|
174
|
-
defaultYes: true,
|
|
175
|
-
});
|
|
176
|
-
if (!ok) {
|
|
177
|
-
(0, ui_1.info)('Aborted.');
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences/${encodeURIComponent(id)}`, { profile: args.profile, method: 'PATCH', json: payload });
|
|
181
|
-
if (args.json) {
|
|
182
|
-
process.stdout.write(JSON.stringify(body, null, 2) + '\n');
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
(0, ui_1.success)(`Updated audience ${id}`);
|
|
186
|
-
}
|
|
187
|
-
async function deleteCmd(args) {
|
|
188
|
-
const resolveTarget = args.id || args.target;
|
|
189
|
-
if (!resolveTarget) {
|
|
190
|
-
(0, ui_1.error)('Usage: gurulu audiences delete <name-or-id> [--id ...]');
|
|
191
|
-
process.exit(1);
|
|
192
|
-
}
|
|
193
|
-
const id = await resolveId(resolveTarget, args.profile);
|
|
194
|
-
const qs = args.dryRun ? '?dryRun=1' : '';
|
|
195
|
-
if (args.dryRun) {
|
|
196
|
-
const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences/${encodeURIComponent(id)}${qs}`, { profile: args.profile, method: 'DELETE' });
|
|
197
|
-
(0, dry_run_1.printDryRun)(body, args.json);
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
const ok = await (0, confirm_1.promptConfirm)(`About to delete audience '${args.target}'. Continue?`, { yes: args.yes, defaultYes: false });
|
|
201
|
-
if (!ok) {
|
|
202
|
-
(0, ui_1.info)('Aborted.');
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
await (0, api_client_1.cliApiJson)(`/api/cli/audiences/${encodeURIComponent(id)}`, {
|
|
206
|
-
profile: args.profile,
|
|
207
|
-
method: 'DELETE',
|
|
208
|
-
});
|
|
209
|
-
(0, ui_1.success)(`Deleted audience ${id}`);
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* AU5 (audit-2026-04-29-audience.md) — `gurulu audiences export <name-or-id>`.
|
|
213
|
-
* Streams the current audience membership as CSV (default) or JSON. With
|
|
214
|
-
* `--output <path>` we write to disk; otherwise the body goes to stdout.
|
|
215
|
-
*/
|
|
216
|
-
async function exportCmd(args) {
|
|
217
|
-
const target = args.target;
|
|
218
|
-
if (!target) {
|
|
219
|
-
(0, ui_1.error)('Usage: gurulu audiences export <name-or-id> [--format csv|json] [--output file]');
|
|
220
|
-
process.exit(1);
|
|
221
|
-
}
|
|
222
|
-
const id = await resolveId(target, args.profile);
|
|
223
|
-
const format = (args.format || 'csv').toLowerCase();
|
|
224
|
-
if (format !== 'csv' && format !== 'json') {
|
|
225
|
-
(0, ui_1.error)('--format must be csv or json');
|
|
226
|
-
process.exit(1);
|
|
227
|
-
}
|
|
228
|
-
const path = `/api/cli/audiences/${encodeURIComponent(id)}/export?format=${format}&destination=download`;
|
|
229
|
-
const res = await (0, api_client_1.cliApi)(path, { profile: args.profile });
|
|
230
|
-
const body = await res.text();
|
|
231
|
-
if (!res.ok) {
|
|
232
|
-
(0, ui_1.error)(`Export failed (HTTP ${res.status}): ${body.slice(0, 200)}`);
|
|
233
|
-
process.exit(1);
|
|
234
|
-
}
|
|
235
|
-
if (args.output) {
|
|
236
|
-
(0, node_fs_1.writeFileSync)(args.output, body, 'utf8');
|
|
237
|
-
(0, ui_1.success)(`Wrote ${body.length} bytes to ${args.output}`);
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
process.stdout.write(body);
|
|
241
|
-
if (!body.endsWith('\n'))
|
|
242
|
-
process.stdout.write('\n');
|
|
243
|
-
}
|
package/dist/commands/audit.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 20 W3 C4 — `gurulu audit tail` / `gurulu audit export`.
|
|
3
|
-
*
|
|
4
|
-
* Thin wrappers around the new /api/cli/audit/{tail,export} endpoints.
|
|
5
|
-
* Requires a secret key with the `audit:read` scope.
|
|
6
|
-
*/
|
|
7
|
-
export interface AuditArgs {
|
|
8
|
-
action?: string;
|
|
9
|
-
since?: string;
|
|
10
|
-
format?: string;
|
|
11
|
-
limit?: number;
|
|
12
|
-
json?: boolean;
|
|
13
|
-
profile?: string;
|
|
14
|
-
}
|
|
15
|
-
export declare function auditCommand(args: AuditArgs): Promise<void>;
|
|
16
|
-
/**
|
|
17
|
-
* Convert short-form windows like "30d" / "24h" to ISO. Exported for
|
|
18
|
-
* tests.
|
|
19
|
-
*/
|
|
20
|
-
export declare function parseSinceFlag(raw?: string): string | undefined;
|
package/dist/commands/audit.js
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Phase 20 W3 C4 — `gurulu audit tail` / `gurulu audit export`.
|
|
4
|
-
*
|
|
5
|
-
* Thin wrappers around the new /api/cli/audit/{tail,export} endpoints.
|
|
6
|
-
* Requires a secret key with the `audit:read` scope.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.auditCommand = auditCommand;
|
|
10
|
-
exports.parseSinceFlag = parseSinceFlag;
|
|
11
|
-
const api_client_1 = require("../api-client");
|
|
12
|
-
const ui_1 = require("../utils/ui");
|
|
13
|
-
async function auditCommand(args) {
|
|
14
|
-
const action = args.action || 'tail';
|
|
15
|
-
switch (action) {
|
|
16
|
-
case 'tail':
|
|
17
|
-
return tailCmd(args);
|
|
18
|
-
case 'export':
|
|
19
|
-
return exportCmd(args);
|
|
20
|
-
default:
|
|
21
|
-
(0, ui_1.error)(`Unknown audit action: ${action}`);
|
|
22
|
-
(0, ui_1.info)('Usage: gurulu audit [tail|export]');
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Convert short-form windows like "30d" / "24h" to ISO. Exported for
|
|
28
|
-
* tests.
|
|
29
|
-
*/
|
|
30
|
-
function parseSinceFlag(raw) {
|
|
31
|
-
if (!raw)
|
|
32
|
-
return undefined;
|
|
33
|
-
const trimmed = raw.trim();
|
|
34
|
-
const m = trimmed.match(/^(\d+)([mhd])$/);
|
|
35
|
-
if (!m)
|
|
36
|
-
return trimmed; // pass through as ISO
|
|
37
|
-
return trimmed; // backend accepts short form too
|
|
38
|
-
}
|
|
39
|
-
async function tailCmd(args) {
|
|
40
|
-
const qs = new URLSearchParams();
|
|
41
|
-
if (args.since)
|
|
42
|
-
qs.set('since', parseSinceFlag(args.since) ?? '');
|
|
43
|
-
const path = `/api/cli/audit/tail${qs.toString() ? `?${qs.toString()}` : ''}`;
|
|
44
|
-
const res = await (0, api_client_1.cliApi)(path, {
|
|
45
|
-
profile: args.profile,
|
|
46
|
-
headers: { accept: 'text/event-stream' },
|
|
47
|
-
});
|
|
48
|
-
if (!res.body) {
|
|
49
|
-
(0, ui_1.error)('Audit tail returned no body.');
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
(0, ui_1.info)('Streaming audit events — press Ctrl+C to stop');
|
|
53
|
-
const reader = res.body.getReader?.();
|
|
54
|
-
if (!reader) {
|
|
55
|
-
for await (const chunk of res.body) {
|
|
56
|
-
process.stdout.write(formatFrame(Buffer.from(chunk).toString('utf8'), !!args.json));
|
|
57
|
-
}
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const decoder = new TextDecoder();
|
|
61
|
-
// eslint-disable-next-line no-constant-condition
|
|
62
|
-
while (true) {
|
|
63
|
-
const { done, value } = await reader.read();
|
|
64
|
-
if (done)
|
|
65
|
-
return;
|
|
66
|
-
const text = decoder.decode(value, { stream: true });
|
|
67
|
-
process.stdout.write(formatFrame(text, !!args.json));
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function formatFrame(raw, json) {
|
|
71
|
-
if (json)
|
|
72
|
-
return raw;
|
|
73
|
-
const out = [];
|
|
74
|
-
for (const block of raw.split('\n\n')) {
|
|
75
|
-
const lines = block.split('\n');
|
|
76
|
-
let ev = 'message';
|
|
77
|
-
let data = '';
|
|
78
|
-
for (const line of lines) {
|
|
79
|
-
if (line.startsWith('event: '))
|
|
80
|
-
ev = line.slice(7).trim();
|
|
81
|
-
else if (line.startsWith('data: '))
|
|
82
|
-
data += line.slice(6);
|
|
83
|
-
}
|
|
84
|
-
if (data && ev === 'audit_event') {
|
|
85
|
-
try {
|
|
86
|
-
const p = JSON.parse(data);
|
|
87
|
-
out.push(`[${p.createdAt || '-'}] ${p.action || '-'} status=${p.status || '-'} key=${p.secretKeyId || '-'}\n`);
|
|
88
|
-
}
|
|
89
|
-
catch {
|
|
90
|
-
out.push(`[event] ${data}\n`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return out.join('');
|
|
95
|
-
}
|
|
96
|
-
async function exportCmd(args) {
|
|
97
|
-
const qs = new URLSearchParams();
|
|
98
|
-
if (args.since)
|
|
99
|
-
qs.set('since', parseSinceFlag(args.since) ?? '');
|
|
100
|
-
if (args.limit)
|
|
101
|
-
qs.set('limit', String(args.limit));
|
|
102
|
-
const path = `/api/cli/audit/export${qs.toString() ? `?${qs.toString()}` : ''}`;
|
|
103
|
-
const res = await (0, api_client_1.cliApi)(path, {
|
|
104
|
-
profile: args.profile,
|
|
105
|
-
headers: { accept: 'application/x-ndjson' },
|
|
106
|
-
});
|
|
107
|
-
if (!res.body) {
|
|
108
|
-
(0, ui_1.error)('Audit export returned no body.');
|
|
109
|
-
process.exit(1);
|
|
110
|
-
}
|
|
111
|
-
const format = args.format || 'jsonl';
|
|
112
|
-
if (format !== 'jsonl') {
|
|
113
|
-
(0, ui_1.error)(`Unsupported export format: ${format} (only jsonl is implemented)`);
|
|
114
|
-
process.exit(1);
|
|
115
|
-
}
|
|
116
|
-
const reader = res.body.getReader?.();
|
|
117
|
-
if (!reader) {
|
|
118
|
-
for await (const chunk of res.body) {
|
|
119
|
-
process.stdout.write(Buffer.from(chunk));
|
|
120
|
-
}
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
// eslint-disable-next-line no-constant-condition
|
|
124
|
-
while (true) {
|
|
125
|
-
const { done, value } = await reader.read();
|
|
126
|
-
if (done)
|
|
127
|
-
return;
|
|
128
|
-
process.stdout.write(Buffer.from(value));
|
|
129
|
-
}
|
|
130
|
-
}
|
package/dist/commands/auth.js
DELETED
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Phase 18.6 — `gurulu auth` device-link command.
|
|
4
|
-
*
|
|
5
|
-
* Implements the client side of the OAuth 2.0 device authorization grant
|
|
6
|
-
* against `/api/cli/device-link/{start,poll}`. The user-facing flow:
|
|
7
|
-
* 1. POST /start with deviceType=cli + hostname as deviceLabel
|
|
8
|
-
* 2. Print the pairing code + verify URL, try to auto-open the browser
|
|
9
|
-
* 3. Poll /poll every pollIntervalMs until approved / denied / expired
|
|
10
|
-
* 4. On approval, persist the returned secret key into the local profile
|
|
11
|
-
*
|
|
12
|
-
* Backward compat: if the user passes `--key gsk_...` (or `--email` +
|
|
13
|
-
* `--secret-key`), we delegate to the existing `loginCommand` verbatim so
|
|
14
|
-
* the legacy manual flow is unchanged.
|
|
15
|
-
*/
|
|
16
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
-
if (k2 === undefined) k2 = k;
|
|
18
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
-
}
|
|
22
|
-
Object.defineProperty(o, k2, desc);
|
|
23
|
-
}) : (function(o, m, k, k2) {
|
|
24
|
-
if (k2 === undefined) k2 = k;
|
|
25
|
-
o[k2] = m[k];
|
|
26
|
-
}));
|
|
27
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
-
}) : function(o, v) {
|
|
30
|
-
o["default"] = v;
|
|
31
|
-
});
|
|
32
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
-
var ownKeys = function(o) {
|
|
34
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
-
var ar = [];
|
|
36
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
-
return ar;
|
|
38
|
-
};
|
|
39
|
-
return ownKeys(o);
|
|
40
|
-
};
|
|
41
|
-
return function (mod) {
|
|
42
|
-
if (mod && mod.__esModule) return mod;
|
|
43
|
-
var result = {};
|
|
44
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
-
__setModuleDefault(result, mod);
|
|
46
|
-
return result;
|
|
47
|
-
};
|
|
48
|
-
})();
|
|
49
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
-
exports.authCommand = authCommand;
|
|
51
|
-
const os = __importStar(require("os"));
|
|
52
|
-
const child_process_1 = require("child_process");
|
|
53
|
-
const login_1 = require("./login");
|
|
54
|
-
const config_1 = require("../config");
|
|
55
|
-
const api_client_1 = require("../api-client");
|
|
56
|
-
const ui_1 = require("../utils/ui");
|
|
57
|
-
function isHeadless() {
|
|
58
|
-
if (!process.stdout.isTTY)
|
|
59
|
-
return true;
|
|
60
|
-
if (process.env.SSH_CLIENT || process.env.SSH_TTY || process.env.SSH_CONNECTION)
|
|
61
|
-
return true;
|
|
62
|
-
if (process.platform === 'linux' && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY)
|
|
63
|
-
return true;
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
function tryOpenBrowser(url) {
|
|
67
|
-
const platform = process.platform;
|
|
68
|
-
const cmd = platform === 'darwin' ? 'open' : platform === 'win32' ? 'start' : 'xdg-open';
|
|
69
|
-
// On Windows the URL goes as a positional after an empty title arg to `start`.
|
|
70
|
-
const args = platform === 'win32' ? ['', url] : [url];
|
|
71
|
-
try {
|
|
72
|
-
const child = (0, child_process_1.spawn)(cmd, args, {
|
|
73
|
-
detached: true,
|
|
74
|
-
stdio: 'ignore',
|
|
75
|
-
shell: platform === 'win32',
|
|
76
|
-
});
|
|
77
|
-
let errored = false;
|
|
78
|
-
child.on('error', () => {
|
|
79
|
-
errored = true;
|
|
80
|
-
});
|
|
81
|
-
child.unref();
|
|
82
|
-
// spawn() returns synchronously; if the binary is missing on PATH we'll
|
|
83
|
-
// catch the synchronous throw below. The async 'error' handler covers
|
|
84
|
-
// post-fork failures but we can't await it here without blocking the flow.
|
|
85
|
-
return !errored;
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
function sleep(ms) {
|
|
92
|
-
return new Promise((r) => setTimeout(r, ms));
|
|
93
|
-
}
|
|
94
|
-
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
95
|
-
async function authCommand(args) {
|
|
96
|
-
// Back-compat: legacy manual flow.
|
|
97
|
-
const legacyKey = args.key || args.secretKey || args.apiKey || process.env.GURULU_SECRET_KEY;
|
|
98
|
-
if (legacyKey) {
|
|
99
|
-
await (0, login_1.loginCommand)({
|
|
100
|
-
...args,
|
|
101
|
-
secretKey: legacyKey,
|
|
102
|
-
});
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
const profileName = args.profile || 'personal';
|
|
106
|
-
const apiBase = args.apiBase || (0, config_1.defaultApiBase)();
|
|
107
|
-
const deviceLabel = os.hostname() || 'Untitled';
|
|
108
|
-
(0, ui_1.info)('[•] Requesting pairing code...');
|
|
109
|
-
let startBody;
|
|
110
|
-
try {
|
|
111
|
-
const res = await (0, api_client_1.cliApi)('/api/cli/device-link/start', {
|
|
112
|
-
method: 'POST',
|
|
113
|
-
skipAuth: true,
|
|
114
|
-
body: JSON.stringify({ deviceType: 'cli', deviceLabel }),
|
|
115
|
-
preloadedProfile: {
|
|
116
|
-
name: profileName,
|
|
117
|
-
email: 'device-link@gurulu.local',
|
|
118
|
-
secret_key: '',
|
|
119
|
-
api_base: apiBase,
|
|
120
|
-
source: 'env',
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
if (!res.ok) {
|
|
124
|
-
const txt = await res.text();
|
|
125
|
-
(0, ui_1.error)(`Could not start device-link (${res.status}): ${txt}`);
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
startBody = (await res.json());
|
|
129
|
-
}
|
|
130
|
-
catch (err) {
|
|
131
|
-
(0, ui_1.error)(`Could not start device-link: ${err.message}`);
|
|
132
|
-
process.exit(1);
|
|
133
|
-
}
|
|
134
|
-
const { code, verifyUrl, expiresAt, pollIntervalMs } = startBody;
|
|
135
|
-
console.log('');
|
|
136
|
-
console.log(` ${(0, ui_1.dim)('Pairing code:')} ${code}`);
|
|
137
|
-
console.log(` ${(0, ui_1.dim)('Visit:')} ${verifyUrl}`);
|
|
138
|
-
console.log(` ${(0, ui_1.dim)('Device:')} CLI — ${deviceLabel}`);
|
|
139
|
-
console.log('');
|
|
140
|
-
if (args.noBrowser) {
|
|
141
|
-
(0, ui_1.info)(`Open this URL manually: ${verifyUrl}`);
|
|
142
|
-
}
|
|
143
|
-
else if (isHeadless()) {
|
|
144
|
-
(0, ui_1.info)(`Headless/SSH session detected — open this URL on another device: ${verifyUrl}`);
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
const opened = tryOpenBrowser(verifyUrl);
|
|
148
|
-
if (!opened) {
|
|
149
|
-
(0, ui_1.info)(`Could not auto-open the browser. Open this URL manually: ${verifyUrl}`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
const expiresAtMs = new Date(expiresAt).getTime();
|
|
153
|
-
const interval = Math.max(1000, pollIntervalMs || 2000);
|
|
154
|
-
const stdout = process.stdout;
|
|
155
|
-
const isTTY = !!stdout.isTTY;
|
|
156
|
-
let frame = 0;
|
|
157
|
-
let approved = null;
|
|
158
|
-
while (Date.now() < expiresAtMs) {
|
|
159
|
-
if (isTTY) {
|
|
160
|
-
stdout.write(`\r${SPINNER_FRAMES[frame++ % SPINNER_FRAMES.length]} Waiting for approval...`);
|
|
161
|
-
}
|
|
162
|
-
try {
|
|
163
|
-
const res = await (0, api_client_1.cliApi)('/api/cli/device-link/poll', {
|
|
164
|
-
method: 'POST',
|
|
165
|
-
skipAuth: true,
|
|
166
|
-
body: JSON.stringify({ code }),
|
|
167
|
-
preloadedProfile: {
|
|
168
|
-
name: profileName,
|
|
169
|
-
email: 'device-link@gurulu.local',
|
|
170
|
-
secret_key: '',
|
|
171
|
-
api_base: apiBase,
|
|
172
|
-
source: 'env',
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
const json = (await res.json().catch(() => ({})));
|
|
176
|
-
if (res.status === 410) {
|
|
177
|
-
if (isTTY)
|
|
178
|
-
stdout.write('\r');
|
|
179
|
-
(0, ui_1.error)('Pairing code expired. Run `gurulu auth` to try again.');
|
|
180
|
-
process.exit(1);
|
|
181
|
-
}
|
|
182
|
-
if (res.status === 404 || ('error' in json && json.error === 'invalid_code')) {
|
|
183
|
-
if (isTTY)
|
|
184
|
-
stdout.write('\r');
|
|
185
|
-
(0, ui_1.error)('Invalid pairing code.');
|
|
186
|
-
process.exit(1);
|
|
187
|
-
}
|
|
188
|
-
if ('status' in json) {
|
|
189
|
-
if (json.status === 'approved') {
|
|
190
|
-
approved = json;
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
if (json.status === 'denied') {
|
|
194
|
-
if (isTTY)
|
|
195
|
-
stdout.write('\r');
|
|
196
|
-
(0, ui_1.error)('Pairing request denied. Exiting.');
|
|
197
|
-
process.exit(1);
|
|
198
|
-
}
|
|
199
|
-
if (json.status === 'expired') {
|
|
200
|
-
if (isTTY)
|
|
201
|
-
stdout.write('\r');
|
|
202
|
-
(0, ui_1.error)('Pairing code expired. Run `gurulu auth` to try again.');
|
|
203
|
-
process.exit(1);
|
|
204
|
-
}
|
|
205
|
-
// pending → continue
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
catch (err) {
|
|
209
|
-
// Transient network hiccup — continue polling until expiry.
|
|
210
|
-
if (isTTY)
|
|
211
|
-
stdout.write('\r');
|
|
212
|
-
(0, ui_1.info)(`(poll error, retrying: ${err.message})`);
|
|
213
|
-
}
|
|
214
|
-
await sleep(interval);
|
|
215
|
-
}
|
|
216
|
-
if (isTTY)
|
|
217
|
-
stdout.write('\r');
|
|
218
|
-
if (!approved) {
|
|
219
|
-
(0, ui_1.error)('Timed out waiting for approval. Run `gurulu auth` to try again.');
|
|
220
|
-
process.exit(1);
|
|
221
|
-
}
|
|
222
|
-
const fullKey = approved.secretKey.fullKey;
|
|
223
|
-
const userRequestedKeychain = args.useKeychain === true;
|
|
224
|
-
const keychainAvailable = (0, config_1.isKeychainAvailable)();
|
|
225
|
-
if (userRequestedKeychain && !keychainAvailable) {
|
|
226
|
-
(0, ui_1.warn)('Keychain requested via --keychain but the optional `keytar` module is not installed. ' +
|
|
227
|
-
'Falling back to plaintext storage in ~/.gurulu/config.json. ' +
|
|
228
|
-
'Install `keytar` (`npm i -g keytar`) or unset --keychain to silence this warning.');
|
|
229
|
-
}
|
|
230
|
-
const { storedInKeychain } = await (0, config_1.saveProfile)(profileName, {
|
|
231
|
-
email: `cli-${deviceLabel}@gurulu.local`,
|
|
232
|
-
secret_key: fullKey,
|
|
233
|
-
api_base: apiBase,
|
|
234
|
-
}, { useKeychain: args.useKeychain ?? keychainAvailable });
|
|
235
|
-
const tail = fullKey.slice(-6);
|
|
236
|
-
(0, ui_1.success)(`Device paired. Key …${tail} saved to profile "${profileName}".`);
|
|
237
|
-
if (storedInKeychain) {
|
|
238
|
-
(0, ui_1.info)(` Stored in macOS Keychain (${(0, ui_1.dim)('io.gurulu.cli')})`);
|
|
239
|
-
}
|
|
240
|
-
else if (userRequestedKeychain) {
|
|
241
|
-
(0, ui_1.info)(` Stored in plaintext at ~/.gurulu/config.json (keychain unavailable).`);
|
|
242
|
-
}
|
|
243
|
-
if (approved.secretKey.expiresAt) {
|
|
244
|
-
(0, ui_1.info)(` Expires: ${approved.secretKey.expiresAt}`);
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
(0, ui_1.info)(' Expires: never');
|
|
248
|
-
}
|
|
249
|
-
}
|
package/dist/commands/chat.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gurulu Chat — `gurulu chat` CLI command.
|
|
3
|
-
*
|
|
4
|
-
* Single-shot: `gurulu chat "How many events today?"`
|
|
5
|
-
* Interactive: `gurulu chat` (REPL mode)
|
|
6
|
-
*
|
|
7
|
-
* Flags:
|
|
8
|
-
* --json Machine-readable output
|
|
9
|
-
* --show-sql Also print the generated SQL
|
|
10
|
-
*/
|
|
11
|
-
export interface ChatArgs {
|
|
12
|
-
question?: string;
|
|
13
|
-
json?: boolean;
|
|
14
|
-
showSql?: boolean;
|
|
15
|
-
profile?: string;
|
|
16
|
-
context?: string;
|
|
17
|
-
site?: string;
|
|
18
|
-
}
|
|
19
|
-
export declare function chatCommand(args: ChatArgs): Promise<void>;
|