@gurulu/cli 0.1.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 (77) hide show
  1. package/README.md +66 -0
  2. package/bin/gurulu.js +2 -0
  3. package/dist/api-client.d.ts +27 -0
  4. package/dist/api-client.js +150 -0
  5. package/dist/commands/add-server.d.ts +9 -0
  6. package/dist/commands/add-server.js +155 -0
  7. package/dist/commands/alerts.d.ts +22 -0
  8. package/dist/commands/alerts.js +281 -0
  9. package/dist/commands/api-keys.d.ts +20 -0
  10. package/dist/commands/api-keys.js +130 -0
  11. package/dist/commands/audiences.d.ts +16 -0
  12. package/dist/commands/audiences.js +180 -0
  13. package/dist/commands/audit.d.ts +20 -0
  14. package/dist/commands/audit.js +130 -0
  15. package/dist/commands/auth.d.ts +20 -0
  16. package/dist/commands/auth.js +214 -0
  17. package/dist/commands/chat.d.ts +18 -0
  18. package/dist/commands/chat.js +117 -0
  19. package/dist/commands/config.d.ts +10 -0
  20. package/dist/commands/config.js +92 -0
  21. package/dist/commands/db.d.ts +25 -0
  22. package/dist/commands/db.js +322 -0
  23. package/dist/commands/destinations.d.ts +20 -0
  24. package/dist/commands/destinations.js +191 -0
  25. package/dist/commands/doctor.d.ts +7 -0
  26. package/dist/commands/doctor.js +318 -0
  27. package/dist/commands/events.d.ts +27 -0
  28. package/dist/commands/events.js +147 -0
  29. package/dist/commands/experiments.d.ts +18 -0
  30. package/dist/commands/experiments.js +233 -0
  31. package/dist/commands/identity.d.ts +13 -0
  32. package/dist/commands/identity.js +107 -0
  33. package/dist/commands/init.d.ts +11 -0
  34. package/dist/commands/init.js +215 -0
  35. package/dist/commands/insights.d.ts +10 -0
  36. package/dist/commands/insights.js +65 -0
  37. package/dist/commands/install.d.ts +233 -0
  38. package/dist/commands/install.js +920 -0
  39. package/dist/commands/login.d.ts +20 -0
  40. package/dist/commands/login.js +170 -0
  41. package/dist/commands/logout.d.ts +10 -0
  42. package/dist/commands/logout.js +41 -0
  43. package/dist/commands/playground.d.ts +11 -0
  44. package/dist/commands/playground.js +47 -0
  45. package/dist/commands/sites.d.ts +18 -0
  46. package/dist/commands/sites.js +139 -0
  47. package/dist/commands/sourcemap.d.ts +21 -0
  48. package/dist/commands/sourcemap.js +137 -0
  49. package/dist/commands/status.d.ts +7 -0
  50. package/dist/commands/status.js +136 -0
  51. package/dist/commands/warehouse.d.ts +20 -0
  52. package/dist/commands/warehouse.js +65 -0
  53. package/dist/commands/warehouses.d.ts +17 -0
  54. package/dist/commands/warehouses.js +182 -0
  55. package/dist/commands/whoami.d.ts +9 -0
  56. package/dist/commands/whoami.js +47 -0
  57. package/dist/config.d.ts +75 -0
  58. package/dist/config.js +329 -0
  59. package/dist/frameworks/detect.d.ts +8 -0
  60. package/dist/frameworks/detect.js +362 -0
  61. package/dist/index.d.ts +1 -0
  62. package/dist/index.js +429 -0
  63. package/dist/install-intent-proposal.d.ts +99 -0
  64. package/dist/install-intent-proposal.js +202 -0
  65. package/dist/utils/api.d.ts +20 -0
  66. package/dist/utils/api.js +47 -0
  67. package/dist/utils/config.d.ts +13 -0
  68. package/dist/utils/config.js +30 -0
  69. package/dist/utils/confirm.d.ts +17 -0
  70. package/dist/utils/confirm.js +40 -0
  71. package/dist/utils/dry-run.d.ts +20 -0
  72. package/dist/utils/dry-run.js +67 -0
  73. package/dist/utils/from-file.d.ts +9 -0
  74. package/dist/utils/from-file.js +72 -0
  75. package/dist/utils/ui.d.ts +14 -0
  76. package/dist/utils/ui.js +59 -0
  77. package/package.json +26 -0
@@ -0,0 +1,281 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 19.5 W2 B4 — `gurulu alerts list|show|channels list`.
4
+ * Phase 20 W2 B3 — `create|update|delete` + channels create/update/delete.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.alertsCommand = alertsCommand;
8
+ const api_client_1 = require("../api-client");
9
+ const ui_1 = require("../utils/ui");
10
+ const confirm_1 = require("../utils/confirm");
11
+ const dry_run_1 = require("../utils/dry-run");
12
+ const from_file_1 = require("../utils/from-file");
13
+ async function alertsCommand(args) {
14
+ const action = args.action || 'list';
15
+ switch (action) {
16
+ case 'list':
17
+ return listCmd(args);
18
+ case 'show':
19
+ return showCmd(args);
20
+ case 'create':
21
+ return createCmd(args);
22
+ case 'update':
23
+ return updateCmd(args);
24
+ case 'delete':
25
+ return deleteCmd(args);
26
+ case 'channels':
27
+ return channelsCmd(args);
28
+ default:
29
+ (0, ui_1.error)(`Unknown alerts action: ${action}`);
30
+ (0, ui_1.info)('Usage: gurulu alerts [list|show|create|update|delete|channels]');
31
+ process.exit(1);
32
+ }
33
+ }
34
+ async function listCmd(args) {
35
+ const qs = new URLSearchParams();
36
+ if (args.severity)
37
+ qs.set('severity', args.severity);
38
+ if (args.acknowledged)
39
+ qs.set('acknowledged', args.acknowledged);
40
+ if (args.limit)
41
+ qs.set('limit', String(args.limit));
42
+ const path = `/api/cli/alerts${qs.toString() ? `?${qs.toString()}` : ''}`;
43
+ const body = await (0, api_client_1.cliApiJson)(path, { profile: args.profile });
44
+ if (args.json) {
45
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
46
+ return;
47
+ }
48
+ const rows = body.alerts || [];
49
+ if (rows.length === 0) {
50
+ (0, ui_1.info)('No alerts.');
51
+ return;
52
+ }
53
+ process.stdout.write(['ID', 'METRIC', 'SEVERITY', 'DIRECTION', 'DETECTED'].join('\t') + '\n');
54
+ for (const a of rows) {
55
+ process.stdout.write([a.id, a.metric, a.severity, a.direction, String(a.detectedAt)].join('\t') + '\n');
56
+ }
57
+ }
58
+ async function showCmd(args) {
59
+ if (!args.target) {
60
+ (0, ui_1.error)('Usage: gurulu alerts show <id>');
61
+ process.exit(1);
62
+ }
63
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/alerts/${encodeURIComponent(args.target)}`, { profile: args.profile });
64
+ if (args.json) {
65
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
66
+ return;
67
+ }
68
+ const a = body.alert;
69
+ process.stdout.write(`ID: ${a.id}\n`);
70
+ process.stdout.write(`Metric: ${a.metric}\n`);
71
+ process.stdout.write(`Severity: ${a.severity}\n`);
72
+ process.stdout.write(`Direction: ${a.direction}\n`);
73
+ process.stdout.write(`Observed: ${a.observed}\n`);
74
+ process.stdout.write(`Baseline: ${a.baseline}\n`);
75
+ process.stdout.write(`zScore: ${a.zScore}\n`);
76
+ process.stdout.write(`Detected: ${a.detectedAt}\n`);
77
+ process.stdout.write(`Ack'd: ${a.acknowledgedAt || '-'}\n`);
78
+ process.stdout.write(`Deliveries: ${(a.deliveries || []).length}\n`);
79
+ }
80
+ async function createCmd(args) {
81
+ let payload = {};
82
+ if (args.fromFile)
83
+ payload = (0, from_file_1.loadFromFile)(args.fromFile);
84
+ if (args.metric)
85
+ payload.metric = args.metric;
86
+ if (args.severity)
87
+ payload.severity = args.severity;
88
+ if (!payload.metric || !payload.severity) {
89
+ (0, ui_1.error)('metric and severity are required.');
90
+ process.exit(1);
91
+ }
92
+ if (args.dryRun) {
93
+ const body = await (0, api_client_1.cliApiJson)('/api/cli/alerts?dryRun=1', {
94
+ profile: args.profile,
95
+ method: 'POST',
96
+ json: payload,
97
+ });
98
+ (0, dry_run_1.printDryRun)(body, args.json);
99
+ return;
100
+ }
101
+ const ok = await (0, confirm_1.promptConfirm)(`Create alert '${payload.metric}'?`, {
102
+ yes: args.yes,
103
+ defaultYes: true,
104
+ });
105
+ if (!ok)
106
+ return (0, ui_1.info)('Aborted.');
107
+ const body = await (0, api_client_1.cliApiJson)('/api/cli/alerts', {
108
+ profile: args.profile,
109
+ method: 'POST',
110
+ json: payload,
111
+ });
112
+ if (args.json) {
113
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
114
+ return;
115
+ }
116
+ (0, ui_1.success)(`Created alert ${body.alert?.id ?? ''}`);
117
+ }
118
+ async function updateCmd(args) {
119
+ if (!args.target) {
120
+ (0, ui_1.error)('Usage: gurulu alerts update <id> [--note ...] [--ack]');
121
+ process.exit(1);
122
+ }
123
+ const payload = {};
124
+ if (args.note !== undefined)
125
+ payload.note = args.note;
126
+ if (args.ack === true)
127
+ payload.acknowledge = true;
128
+ if (args.dryRun) {
129
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/alerts/${encodeURIComponent(args.target)}?dryRun=1`, { profile: args.profile, method: 'PATCH', json: payload });
130
+ (0, dry_run_1.printDryRun)(body, args.json);
131
+ return;
132
+ }
133
+ const ok = await (0, confirm_1.promptConfirm)(`Update alert '${args.target}'?`, {
134
+ yes: args.yes,
135
+ defaultYes: true,
136
+ });
137
+ if (!ok)
138
+ return (0, ui_1.info)('Aborted.');
139
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/alerts/${encodeURIComponent(args.target)}`, { profile: args.profile, method: 'PATCH', json: payload });
140
+ if (args.json) {
141
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
142
+ return;
143
+ }
144
+ (0, ui_1.success)(`Updated alert ${args.target}`);
145
+ }
146
+ async function deleteCmd(args) {
147
+ if (!args.target) {
148
+ (0, ui_1.error)('Usage: gurulu alerts delete <id>');
149
+ process.exit(1);
150
+ }
151
+ if (args.dryRun) {
152
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/alerts/${encodeURIComponent(args.target)}?dryRun=1`, { profile: args.profile, method: 'DELETE' });
153
+ (0, dry_run_1.printDryRun)(body, args.json);
154
+ return;
155
+ }
156
+ const ok = await (0, confirm_1.promptConfirm)(`About to delete alert '${args.target}'. Continue?`, { yes: args.yes, defaultYes: false });
157
+ if (!ok)
158
+ return (0, ui_1.info)('Aborted.');
159
+ await (0, api_client_1.cliApiJson)(`/api/cli/alerts/${encodeURIComponent(args.target)}`, {
160
+ profile: args.profile,
161
+ method: 'DELETE',
162
+ });
163
+ (0, ui_1.success)(`Deleted alert ${args.target}`);
164
+ }
165
+ // ── channels ──────────────────────────────────────────────────────────────
166
+ async function channelsCmd(args) {
167
+ const sub = args.sub || 'list';
168
+ switch (sub) {
169
+ case 'list':
170
+ return channelsList(args);
171
+ case 'create':
172
+ return channelsCreate(args);
173
+ case 'update':
174
+ return channelsUpdate(args);
175
+ case 'delete':
176
+ return channelsDelete(args);
177
+ default:
178
+ (0, ui_1.error)(`Unknown alerts channels sub: ${sub}`);
179
+ process.exit(1);
180
+ }
181
+ }
182
+ async function channelsList(args) {
183
+ const body = await (0, api_client_1.cliApiJson)('/api/cli/alert-channels', {
184
+ profile: args.profile,
185
+ });
186
+ if (args.json) {
187
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
188
+ return;
189
+ }
190
+ const rows = body.channels || [];
191
+ if (rows.length === 0) {
192
+ (0, ui_1.info)('No alert channels configured.');
193
+ return;
194
+ }
195
+ process.stdout.write(['ID', 'NAME', 'TYPE', 'ACTIVE', 'LAST_DELIVERY'].join('\t') + '\n');
196
+ for (const c of rows) {
197
+ process.stdout.write([c.id, c.name, c.type, c.isActive ? 'yes' : 'no', String(c.lastDeliveryAt || '-')].join('\t') + '\n');
198
+ }
199
+ }
200
+ async function channelsCreate(args) {
201
+ let payload = {};
202
+ if (args.fromFile)
203
+ payload = (0, from_file_1.loadFromFile)(args.fromFile);
204
+ if (args.name)
205
+ payload.name = args.name;
206
+ if (args.type)
207
+ payload.type = args.type;
208
+ if (!payload.name || !payload.type) {
209
+ (0, ui_1.error)('name and type are required.');
210
+ process.exit(1);
211
+ }
212
+ if (args.dryRun) {
213
+ const body = await (0, api_client_1.cliApiJson)('/api/cli/alert-channels?dryRun=1', {
214
+ profile: args.profile,
215
+ method: 'POST',
216
+ json: payload,
217
+ });
218
+ (0, dry_run_1.printDryRun)(body, args.json);
219
+ return;
220
+ }
221
+ const ok = await (0, confirm_1.promptConfirm)(`Create alert channel '${payload.name}'?`, {
222
+ yes: args.yes,
223
+ defaultYes: true,
224
+ });
225
+ if (!ok)
226
+ return (0, ui_1.info)('Aborted.');
227
+ const body = await (0, api_client_1.cliApiJson)('/api/cli/alert-channels', {
228
+ profile: args.profile,
229
+ method: 'POST',
230
+ json: payload,
231
+ });
232
+ if (args.json) {
233
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
234
+ return;
235
+ }
236
+ (0, ui_1.success)(`Created channel ${body.channel?.id ?? ''}`);
237
+ }
238
+ async function channelsUpdate(args) {
239
+ if (!args.target) {
240
+ (0, ui_1.error)('Usage: gurulu alerts channels update <id>');
241
+ process.exit(1);
242
+ }
243
+ let payload = {};
244
+ if (args.fromFile)
245
+ payload = (0, from_file_1.loadFromFile)(args.fromFile);
246
+ if (args.name)
247
+ payload.name = args.name;
248
+ if (args.dryRun) {
249
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/alert-channels/${encodeURIComponent(args.target)}?dryRun=1`, { profile: args.profile, method: 'PATCH', json: payload });
250
+ (0, dry_run_1.printDryRun)(body, args.json);
251
+ return;
252
+ }
253
+ const ok = await (0, confirm_1.promptConfirm)(`Update channel '${args.target}'?`, {
254
+ yes: args.yes,
255
+ defaultYes: true,
256
+ });
257
+ if (!ok)
258
+ return (0, ui_1.info)('Aborted.');
259
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/alert-channels/${encodeURIComponent(args.target)}`, { profile: args.profile, method: 'PATCH', json: payload });
260
+ if (args.json) {
261
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
262
+ return;
263
+ }
264
+ (0, ui_1.success)(`Updated channel ${args.target}`);
265
+ }
266
+ async function channelsDelete(args) {
267
+ if (!args.target) {
268
+ (0, ui_1.error)('Usage: gurulu alerts channels delete <id>');
269
+ process.exit(1);
270
+ }
271
+ if (args.dryRun) {
272
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/alert-channels/${encodeURIComponent(args.target)}?dryRun=1`, { profile: args.profile, method: 'DELETE' });
273
+ (0, dry_run_1.printDryRun)(body, args.json);
274
+ return;
275
+ }
276
+ const ok = await (0, confirm_1.promptConfirm)(`About to delete channel '${args.target}'. Continue?`, { yes: args.yes, defaultYes: false });
277
+ if (!ok)
278
+ return (0, ui_1.info)('Aborted.');
279
+ await (0, api_client_1.cliApiJson)(`/api/cli/alert-channels/${encodeURIComponent(args.target)}`, { profile: args.profile, method: 'DELETE' });
280
+ (0, ui_1.success)(`Deleted channel ${args.target}`);
281
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Phase 18.5 W2 — `gurulu api-keys ...` subcommands.
3
+ *
4
+ * Thin wrappers around `/api/cli/api-keys` routes. TTY-aware output: when
5
+ * piped, `create` emits the raw key on stdout so scripts can consume it
6
+ * with `key=$(gurulu api-keys create ...)`.
7
+ */
8
+ export interface ApiKeysArgs {
9
+ action?: string;
10
+ id?: string;
11
+ name?: string;
12
+ mode?: 'live' | 'test';
13
+ scopes?: string;
14
+ filterMode?: 'live' | 'test';
15
+ json?: boolean;
16
+ format?: string;
17
+ yes?: boolean;
18
+ profile?: string;
19
+ }
20
+ export declare function apiKeysCommand(args: ApiKeysArgs): Promise<void>;
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 18.5 W2 — `gurulu api-keys ...` subcommands.
4
+ *
5
+ * Thin wrappers around `/api/cli/api-keys` routes. TTY-aware output: when
6
+ * piped, `create` emits the raw key on stdout so scripts can consume it
7
+ * with `key=$(gurulu api-keys create ...)`.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.apiKeysCommand = apiKeysCommand;
11
+ const api_client_1 = require("../api-client");
12
+ const ui_1 = require("../utils/ui");
13
+ function timeAgo(iso) {
14
+ if (!iso)
15
+ return '-';
16
+ const then = new Date(iso).getTime();
17
+ if (Number.isNaN(then))
18
+ return '-';
19
+ const s = Math.floor((Date.now() - then) / 1000);
20
+ if (s < 60)
21
+ return `${s}s ago`;
22
+ if (s < 3600)
23
+ return `${Math.floor(s / 60)}m ago`;
24
+ if (s < 86400)
25
+ return `${Math.floor(s / 3600)}h ago`;
26
+ return `${Math.floor(s / 86400)}d ago`;
27
+ }
28
+ async function apiKeysCommand(args) {
29
+ const action = args.action || 'list';
30
+ switch (action) {
31
+ case 'list':
32
+ return listKeys(args);
33
+ case 'create':
34
+ return createKey(args);
35
+ case 'revoke':
36
+ return revokeKey(args);
37
+ case 'rotate':
38
+ return rotateKey(args);
39
+ default:
40
+ (0, ui_1.error)(`Unknown api-keys action: ${action}`);
41
+ (0, ui_1.info)('Usage: gurulu api-keys [list|create|revoke|rotate]');
42
+ process.exit(1);
43
+ }
44
+ }
45
+ async function listKeys(args) {
46
+ const body = await (0, api_client_1.cliApiJson)('/api/cli/api-keys', { profile: args.profile });
47
+ let keys = body.apiKeys || [];
48
+ if (args.filterMode)
49
+ keys = keys.filter((k) => k.mode === args.filterMode);
50
+ if (args.json) {
51
+ process.stdout.write(JSON.stringify(keys, null, 2) + '\n');
52
+ return;
53
+ }
54
+ if (keys.length === 0) {
55
+ (0, ui_1.info)('No API keys yet. Run `gurulu api-keys create --name ...` to add one.');
56
+ return;
57
+ }
58
+ process.stdout.write(['NAME', 'PREFIX', 'MODE', 'SCOPES', 'LAST USED', 'CREATED', 'STATUS'].join('\t') + '\n');
59
+ for (const k of keys) {
60
+ const scopes = Array.isArray(k.scopes) ? k.scopes.join(',') : '';
61
+ process.stdout.write([
62
+ k.name,
63
+ k.prefix,
64
+ k.mode,
65
+ scopes,
66
+ timeAgo(k.lastUsedAt),
67
+ new Date(k.createdAt).toISOString().slice(0, 10),
68
+ k.revokedAt ? 'revoked' : 'active',
69
+ ].join('\t') + '\n');
70
+ }
71
+ }
72
+ async function createKey(args) {
73
+ if (!args.name) {
74
+ (0, ui_1.error)('--name is required.');
75
+ process.exit(1);
76
+ }
77
+ const mode = args.mode || 'live';
78
+ const scopes = (args.scopes || 'admin:*').split(',').map((s) => s.trim()).filter(Boolean);
79
+ const body = await (0, api_client_1.cliApiJson)('/api/cli/api-keys', {
80
+ method: 'POST',
81
+ profile: args.profile,
82
+ json: { name: args.name, mode, scopes },
83
+ });
84
+ const fullKey = body.secretKey || body.full_key || '';
85
+ const isTty = !!process.stdout.isTTY;
86
+ if (args.json || args.format === 'json') {
87
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
88
+ return;
89
+ }
90
+ if (!isTty) {
91
+ // Pipe-friendly: just the raw key so scripts can capture it.
92
+ process.stdout.write(fullKey + '\n');
93
+ return;
94
+ }
95
+ (0, ui_1.success)('Created new API key');
96
+ process.stdout.write('\n ' + fullKey + '\n\n');
97
+ (0, ui_1.warn)('This key will NOT be shown again. Copy it now.');
98
+ (0, ui_1.info)('Add to your CI environment:');
99
+ process.stdout.write(` GURULU_SECRET_KEY=${fullKey.slice(0, 12)}...\n`);
100
+ }
101
+ async function revokeKey(args) {
102
+ if (!args.id) {
103
+ (0, ui_1.error)('Usage: gurulu api-keys revoke <id>');
104
+ process.exit(1);
105
+ }
106
+ if (!args.yes) {
107
+ const ans = await (0, ui_1.prompt)(`? Revoke API key '${args.id}' permanently? [y/N] `);
108
+ if (!ans.toLowerCase().startsWith('y')) {
109
+ (0, ui_1.info)('Aborted.');
110
+ return;
111
+ }
112
+ }
113
+ await (0, api_client_1.cliApiJson)(`/api/cli/api-keys/${encodeURIComponent(args.id)}`, {
114
+ method: 'DELETE',
115
+ profile: args.profile,
116
+ });
117
+ (0, ui_1.success)(`Revoked ${args.id}.`);
118
+ }
119
+ async function rotateKey(args) {
120
+ if (!args.id) {
121
+ (0, ui_1.error)('Usage: gurulu api-keys rotate <id>');
122
+ process.exit(1);
123
+ }
124
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/api-keys/${encodeURIComponent(args.id)}/rotate`, { method: 'POST', profile: args.profile, json: {} });
125
+ const fullKey = body.secretKey || '';
126
+ (0, ui_1.warn)(`Rotating key ${args.id}`);
127
+ (0, ui_1.info)(` Old key: valid for 24 more hours`);
128
+ process.stdout.write(` New key: ${(0, ui_1.dim)(fullKey)}\n`);
129
+ (0, ui_1.warn)('Update your CI/production environments before the 24h grace window expires.');
130
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Phase 19.5 W2 B2 — `gurulu audiences list|show`.
3
+ * Phase 20 W2 B1 — `create|update|delete` (with `--dry-run`, `--yes`).
4
+ */
5
+ export interface AudiencesArgs {
6
+ action?: string;
7
+ target?: string;
8
+ json?: boolean;
9
+ profile?: string;
10
+ fromFile?: string;
11
+ name?: string;
12
+ description?: string;
13
+ yes?: boolean;
14
+ dryRun?: boolean;
15
+ }
16
+ export declare function audiencesCommand(args: AudiencesArgs): Promise<void>;
@@ -0,0 +1,180 @@
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 api_client_1 = require("../api-client");
9
+ const ui_1 = require("../utils/ui");
10
+ const confirm_1 = require("../utils/confirm");
11
+ const dry_run_1 = require("../utils/dry-run");
12
+ const from_file_1 = require("../utils/from-file");
13
+ async function audiencesCommand(args) {
14
+ const action = args.action || 'list';
15
+ switch (action) {
16
+ case 'list':
17
+ return listCmd(args);
18
+ case 'show':
19
+ return showCmd(args);
20
+ case 'create':
21
+ return createCmd(args);
22
+ case 'update':
23
+ return updateCmd(args);
24
+ case 'delete':
25
+ return deleteCmd(args);
26
+ default:
27
+ (0, ui_1.error)(`Unknown audiences action: ${action}`);
28
+ (0, ui_1.info)('Usage: gurulu audiences [list|show|create|update|delete]');
29
+ process.exit(1);
30
+ }
31
+ }
32
+ async function listCmd(args) {
33
+ const body = await (0, api_client_1.cliApiJson)('/api/cli/audiences', {
34
+ profile: args.profile,
35
+ });
36
+ const audiences = body.audiences || [];
37
+ if (args.json) {
38
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
39
+ return;
40
+ }
41
+ if (audiences.length === 0) {
42
+ (0, ui_1.info)('No audiences yet.');
43
+ return;
44
+ }
45
+ process.stdout.write(['NAME', 'ID', 'SIZE', 'DESTINATIONS', 'UPDATED'].join('\t') + '\n');
46
+ for (const a of audiences) {
47
+ process.stdout.write([
48
+ a.name,
49
+ a.id,
50
+ String(a.estimatedSize ?? '-'),
51
+ String(a.destinationCount ?? 0),
52
+ String(a.updatedAt || '-').slice(0, 19),
53
+ ].join('\t') + '\n');
54
+ }
55
+ }
56
+ async function resolveId(target, profile) {
57
+ const body = await (0, api_client_1.cliApiJson)('/api/cli/audiences', { profile });
58
+ const found = (body.audiences || []).find((a) => a.id === target) ||
59
+ (body.audiences || []).find((a) => a.name === target);
60
+ if (!found)
61
+ throw new Error(`Audience '${target}' not found`);
62
+ return found.id;
63
+ }
64
+ async function showCmd(args) {
65
+ if (!args.target) {
66
+ (0, ui_1.error)('Usage: gurulu audiences show <name-or-id>');
67
+ process.exit(1);
68
+ }
69
+ const id = await resolveId(args.target, args.profile);
70
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences/${encodeURIComponent(id)}`, { profile: args.profile });
71
+ if (args.json) {
72
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
73
+ return;
74
+ }
75
+ const a = body.audience;
76
+ process.stdout.write(`Name: ${a.name}\n`);
77
+ process.stdout.write(`ID: ${a.id}\n`);
78
+ process.stdout.write(`Description: ${a.description || '-'}\n`);
79
+ process.stdout.write(`Estimated: ${a.estimatedSize ?? '-'}\n`);
80
+ process.stdout.write(`Destinations: ${(a.destinations || []).length}\n`);
81
+ process.stdout.write(`Triggers: ${(body.triggers || []).length}\n`);
82
+ process.stdout.write(`Updated: ${a.updatedAt}\n`);
83
+ }
84
+ async function createCmd(args) {
85
+ let payload = {};
86
+ if (args.fromFile) {
87
+ payload = (0, from_file_1.loadFromFile)(args.fromFile);
88
+ }
89
+ if (args.name)
90
+ payload.name = args.name;
91
+ if (args.description !== undefined)
92
+ payload.description = args.description;
93
+ if (!payload.name) {
94
+ (0, ui_1.error)('name is required (pass --name or --from-file)');
95
+ process.exit(1);
96
+ }
97
+ const qs = args.dryRun ? '?dryRun=1' : '';
98
+ if (args.dryRun) {
99
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences${qs}`, {
100
+ profile: args.profile,
101
+ method: 'POST',
102
+ json: payload,
103
+ });
104
+ (0, dry_run_1.printDryRun)(body, args.json);
105
+ return;
106
+ }
107
+ const ok = await (0, confirm_1.promptConfirm)(`Create audience '${payload.name}'?`, { yes: args.yes, defaultYes: true });
108
+ if (!ok) {
109
+ (0, ui_1.info)('Aborted.');
110
+ return;
111
+ }
112
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences`, {
113
+ profile: args.profile,
114
+ method: 'POST',
115
+ json: payload,
116
+ });
117
+ if (args.json) {
118
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
119
+ return;
120
+ }
121
+ (0, ui_1.success)(`Created audience ${body.audience?.id ?? ''}`);
122
+ }
123
+ async function updateCmd(args) {
124
+ if (!args.target) {
125
+ (0, ui_1.error)('Usage: gurulu audiences update <name-or-id> [--from-file <path>] [--name ...]');
126
+ process.exit(1);
127
+ }
128
+ const id = await resolveId(args.target, args.profile);
129
+ let payload = {};
130
+ if (args.fromFile) {
131
+ payload = (0, from_file_1.loadFromFile)(args.fromFile);
132
+ }
133
+ if (args.name)
134
+ payload.name = args.name;
135
+ if (args.description !== undefined)
136
+ payload.description = args.description;
137
+ const qs = args.dryRun ? '?dryRun=1' : '';
138
+ if (args.dryRun) {
139
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences/${encodeURIComponent(id)}${qs}`, { profile: args.profile, method: 'PATCH', json: payload });
140
+ (0, dry_run_1.printDryRun)(body, args.json);
141
+ return;
142
+ }
143
+ const ok = await (0, confirm_1.promptConfirm)(`Update audience '${args.target}'?`, {
144
+ yes: args.yes,
145
+ defaultYes: true,
146
+ });
147
+ if (!ok) {
148
+ (0, ui_1.info)('Aborted.');
149
+ return;
150
+ }
151
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences/${encodeURIComponent(id)}`, { profile: args.profile, method: 'PATCH', json: payload });
152
+ if (args.json) {
153
+ process.stdout.write(JSON.stringify(body, null, 2) + '\n');
154
+ return;
155
+ }
156
+ (0, ui_1.success)(`Updated audience ${id}`);
157
+ }
158
+ async function deleteCmd(args) {
159
+ if (!args.target) {
160
+ (0, ui_1.error)('Usage: gurulu audiences delete <name-or-id>');
161
+ process.exit(1);
162
+ }
163
+ const id = await resolveId(args.target, args.profile);
164
+ const qs = args.dryRun ? '?dryRun=1' : '';
165
+ if (args.dryRun) {
166
+ const body = await (0, api_client_1.cliApiJson)(`/api/cli/audiences/${encodeURIComponent(id)}${qs}`, { profile: args.profile, method: 'DELETE' });
167
+ (0, dry_run_1.printDryRun)(body, args.json);
168
+ return;
169
+ }
170
+ const ok = await (0, confirm_1.promptConfirm)(`About to delete audience '${args.target}'. Continue?`, { yes: args.yes, defaultYes: false });
171
+ if (!ok) {
172
+ (0, ui_1.info)('Aborted.');
173
+ return;
174
+ }
175
+ await (0, api_client_1.cliApiJson)(`/api/cli/audiences/${encodeURIComponent(id)}`, {
176
+ profile: args.profile,
177
+ method: 'DELETE',
178
+ });
179
+ (0, ui_1.success)(`Deleted audience ${id}`);
180
+ }
@@ -0,0 +1,20 @@
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;