@gurulu/cli 0.4.7 → 1.0.1

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 (190) hide show
  1. package/LICENSE +92 -0
  2. package/README.md +35 -106
  3. package/dist/bin.d.ts +3 -0
  4. package/dist/bin.d.ts.map +1 -0
  5. package/dist/bin.js +25751 -0
  6. package/dist/commands/auth.d.ts +23 -20
  7. package/dist/commands/auth.d.ts.map +1 -0
  8. package/dist/commands/doctor.d.ts +20 -6
  9. package/dist/commands/doctor.d.ts.map +1 -0
  10. package/dist/commands/init.d.ts +33 -11
  11. package/dist/commands/init.d.ts.map +1 -0
  12. package/dist/commands/pull.d.ts +13 -0
  13. package/dist/commands/pull.d.ts.map +1 -0
  14. package/dist/commands/push.d.ts +40 -0
  15. package/dist/commands/push.d.ts.map +1 -0
  16. package/dist/commands/validate.d.ts +36 -0
  17. package/dist/commands/validate.d.ts.map +1 -0
  18. package/dist/index.d.ts +4 -1
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +25326 -876
  21. package/dist/lib/api.d.ts +139 -0
  22. package/dist/lib/api.d.ts.map +1 -0
  23. package/dist/lib/codegen.d.ts +4 -0
  24. package/dist/lib/codegen.d.ts.map +1 -0
  25. package/dist/lib/config.d.ts +43 -0
  26. package/dist/lib/config.d.ts.map +1 -0
  27. package/dist/lib/detect.d.ts +27 -0
  28. package/dist/lib/detect.d.ts.map +1 -0
  29. package/dist/lib/detect.js +106 -0
  30. package/dist/lib/exec-install.d.ts +21 -0
  31. package/dist/lib/exec-install.d.ts.map +1 -0
  32. package/dist/lib/install-plan.d.ts +25 -0
  33. package/dist/lib/install-plan.d.ts.map +1 -0
  34. package/dist/lib/install-plan.js +161 -0
  35. package/package.json +51 -20
  36. package/bin/gurulu.js +0 -2
  37. package/dist/api-client.d.ts +0 -33
  38. package/dist/api-client.js +0 -175
  39. package/dist/commands/add-server.d.ts +0 -9
  40. package/dist/commands/add-server.js +0 -162
  41. package/dist/commands/alerts.d.ts +0 -27
  42. package/dist/commands/alerts.js +0 -309
  43. package/dist/commands/api-keys.d.ts +0 -20
  44. package/dist/commands/api-keys.js +0 -130
  45. package/dist/commands/attribution.d.ts +0 -22
  46. package/dist/commands/attribution.js +0 -111
  47. package/dist/commands/audiences.d.ts +0 -23
  48. package/dist/commands/audiences.js +0 -243
  49. package/dist/commands/audit.d.ts +0 -20
  50. package/dist/commands/audit.js +0 -130
  51. package/dist/commands/auth.js +0 -249
  52. package/dist/commands/chat.d.ts +0 -19
  53. package/dist/commands/chat.js +0 -118
  54. package/dist/commands/config.d.ts +0 -10
  55. package/dist/commands/config.js +0 -92
  56. package/dist/commands/consent.d.ts +0 -27
  57. package/dist/commands/consent.js +0 -233
  58. package/dist/commands/conversion-paths.d.ts +0 -19
  59. package/dist/commands/conversion-paths.js +0 -55
  60. package/dist/commands/db.d.ts +0 -25
  61. package/dist/commands/db.js +0 -330
  62. package/dist/commands/destinations.d.ts +0 -20
  63. package/dist/commands/destinations.js +0 -191
  64. package/dist/commands/doctor.js +0 -360
  65. package/dist/commands/errors.d.ts +0 -27
  66. package/dist/commands/errors.js +0 -121
  67. package/dist/commands/events.d.ts +0 -33
  68. package/dist/commands/events.js +0 -371
  69. package/dist/commands/experiments.d.ts +0 -22
  70. package/dist/commands/experiments.js +0 -264
  71. package/dist/commands/funnels.d.ts +0 -17
  72. package/dist/commands/funnels.js +0 -203
  73. package/dist/commands/goals.d.ts +0 -18
  74. package/dist/commands/goals.js +0 -214
  75. package/dist/commands/heatmap.d.ts +0 -27
  76. package/dist/commands/heatmap.js +0 -112
  77. package/dist/commands/identity.d.ts +0 -29
  78. package/dist/commands/identity.js +0 -328
  79. package/dist/commands/init.js +0 -215
  80. package/dist/commands/insights.d.ts +0 -10
  81. package/dist/commands/insights.js +0 -77
  82. package/dist/commands/install.d.ts +0 -259
  83. package/dist/commands/install.js +0 -1590
  84. package/dist/commands/login.d.ts +0 -20
  85. package/dist/commands/login.js +0 -170
  86. package/dist/commands/logout.d.ts +0 -10
  87. package/dist/commands/logout.js +0 -41
  88. package/dist/commands/playground.d.ts +0 -11
  89. package/dist/commands/playground.js +0 -47
  90. package/dist/commands/releases.d.ts +0 -17
  91. package/dist/commands/releases.js +0 -54
  92. package/dist/commands/replay.d.ts +0 -18
  93. package/dist/commands/replay.js +0 -64
  94. package/dist/commands/secrets.d.ts +0 -19
  95. package/dist/commands/secrets.js +0 -145
  96. package/dist/commands/setup.d.ts +0 -21
  97. package/dist/commands/setup.js +0 -67
  98. package/dist/commands/sites.d.ts +0 -18
  99. package/dist/commands/sites.js +0 -139
  100. package/dist/commands/skad.d.ts +0 -18
  101. package/dist/commands/skad.js +0 -53
  102. package/dist/commands/sourcemap.d.ts +0 -33
  103. package/dist/commands/sourcemap.js +0 -204
  104. package/dist/commands/status.d.ts +0 -7
  105. package/dist/commands/status.js +0 -136
  106. package/dist/commands/upgrade.d.ts +0 -21
  107. package/dist/commands/upgrade.js +0 -183
  108. package/dist/commands/warehouse.d.ts +0 -20
  109. package/dist/commands/warehouse.js +0 -65
  110. package/dist/commands/warehouses.d.ts +0 -17
  111. package/dist/commands/warehouses.js +0 -182
  112. package/dist/commands/watch.d.ts +0 -45
  113. package/dist/commands/watch.js +0 -258
  114. package/dist/commands/whoami.d.ts +0 -9
  115. package/dist/commands/whoami.js +0 -50
  116. package/dist/config.d.ts +0 -75
  117. package/dist/config.js +0 -329
  118. package/dist/frameworks/detect.d.ts +0 -8
  119. package/dist/frameworks/detect.js +0 -458
  120. package/dist/install-intent-proposal.d.ts +0 -99
  121. package/dist/install-intent-proposal.js +0 -202
  122. package/dist/utils/api.d.ts +0 -20
  123. package/dist/utils/api.js +0 -47
  124. package/dist/utils/config.d.ts +0 -13
  125. package/dist/utils/config.js +0 -30
  126. package/dist/utils/confirm.d.ts +0 -17
  127. package/dist/utils/confirm.js +0 -40
  128. package/dist/utils/dry-run.d.ts +0 -20
  129. package/dist/utils/dry-run.js +0 -67
  130. package/dist/utils/from-file.d.ts +0 -9
  131. package/dist/utils/from-file.js +0 -72
  132. package/dist/utils/redact.d.ts +0 -14
  133. package/dist/utils/redact.js +0 -48
  134. package/dist/utils/ui.d.ts +0 -14
  135. package/dist/utils/ui.js +0 -59
  136. package/scripts/.gitkeep +0 -0
  137. package/scripts/README-gurulu-agentic-install.md +0 -114
  138. package/scripts/README-gurulu-scan.md +0 -98
  139. package/scripts/audit-cli-scopes.mjs +0 -204
  140. package/scripts/backfill-tenant-id.mjs +0 -172
  141. package/scripts/backfill-tenant-links.ts +0 -252
  142. package/scripts/backup-clickhouse.sh +0 -27
  143. package/scripts/backup-postgres.sh +0 -19
  144. package/scripts/bootstrap-runtime-schema.mjs +0 -87
  145. package/scripts/bootstrap-stripe.mjs +0 -158
  146. package/scripts/gurulu-agentic-install.lib.cjs +0 -762
  147. package/scripts/gurulu-agentic-install.mjs +0 -623
  148. package/scripts/gurulu-scan.lib.cjs +0 -1509
  149. package/scripts/gurulu-scan.mjs +0 -91
  150. package/scripts/gurulu-verify-install.lib.cjs +0 -334
  151. package/scripts/gurulu-verify-install.mjs +0 -59
  152. package/scripts/init-ssl.sh +0 -26
  153. package/scripts/migrate-flow-graph-enums.sh +0 -86
  154. package/scripts/monitor-disk.sh +0 -24
  155. package/scripts/patches/astro.patch.cjs +0 -74
  156. package/scripts/patches/auto-instrument/ast-helper.cjs +0 -480
  157. package/scripts/patches/auto-instrument/astro.cjs +0 -273
  158. package/scripts/patches/auto-instrument/express.cjs +0 -383
  159. package/scripts/patches/auto-instrument/fastify.cjs +0 -262
  160. package/scripts/patches/auto-instrument/hono.cjs +0 -392
  161. package/scripts/patches/auto-instrument/index.cjs +0 -80
  162. package/scripts/patches/auto-instrument/nestjs.cjs +0 -286
  163. package/scripts/patches/auto-instrument/nextjs-app-router.cjs +0 -345
  164. package/scripts/patches/auto-instrument/nextjs-pages.cjs +0 -361
  165. package/scripts/patches/auto-instrument/remix.cjs +0 -168
  166. package/scripts/patches/auto-instrument/sdk-helper-map.cjs +0 -241
  167. package/scripts/patches/auto-instrument/singleton-helper.cjs +0 -193
  168. package/scripts/patches/auto-instrument/sveltekit.cjs +0 -161
  169. package/scripts/patches/auto-instrument/vite-react.cjs +0 -37
  170. package/scripts/patches/auto-instrument/vue.cjs +0 -196
  171. package/scripts/patches/express.patch.cjs +0 -99
  172. package/scripts/patches/fastify.patch.cjs +0 -108
  173. package/scripts/patches/index.cjs +0 -300
  174. package/scripts/patches/nestjs.patch.cjs +0 -112
  175. package/scripts/patches/nextjs-app-router.patch.cjs +0 -97
  176. package/scripts/patches/nextjs-pages.patch.cjs +0 -97
  177. package/scripts/patches/remix.patch.cjs +0 -75
  178. package/scripts/patches/sveltekit.patch.cjs +0 -72
  179. package/scripts/patches/vite-react.patch.cjs +0 -73
  180. package/scripts/patches/vue.patch.cjs +0 -82
  181. package/scripts/renew-ssl.sh +0 -14
  182. package/scripts/resolve-migration.sh +0 -23
  183. package/scripts/seed-cli-dev-keys.mjs +0 -130
  184. package/scripts/seed-test-data.mjs +0 -391
  185. package/scripts/spike-browserless.ts +0 -65
  186. package/scripts/tenant-pivot-consistency-check.mjs +0 -205
  187. package/scripts/tenant-pivot-phase-3-cleanup.lib.cjs +0 -258
  188. package/scripts/tenant-pivot-phase-3-cleanup.mjs +0 -98
  189. package/scripts/test-identity-resolution.ts +0 -804
  190. 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
- }
@@ -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;
@@ -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
- }
@@ -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
- }
@@ -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>;