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