@floomhq/floom 5.0.6 → 5.0.8

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.
@@ -0,0 +1,391 @@
1
+ import { readFile, readdir, stat } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { createAuthenticatedClient, FloomApiError } from "../lib/api.js";
5
+ import { getCommandName } from "../lib/command-name.js";
6
+ import { log, printJson, renderTable } from "../lib/output.js";
7
+ // #806 — `floom support` ticket commands + `floom feedback`, 1:1 with the
8
+ // cloud member API (docs/SUPPORT-TICKETS.md, mounted at /api/support/*). The
9
+ // FloomApiClient prepends /api in hosted (cloud) mode, so paths here start at
10
+ // /support. Support is a cloud-only feature; against an OSS engine these routes
11
+ // 404 (handled below as a clear "cloud-only" message).
12
+ // Mirror the server-side caps (apps/api/support.py) so we fail fast and, for
13
+ // `feedback`, fit the appended transcript under the body limit.
14
+ const MAX_SUBJECT_LEN = 200;
15
+ const MAX_BODY_LEN = 20_000;
16
+ const VALID_SEVERITY = ["low", "normal", "high"];
17
+ const VALID_STATUS = ["open", "resolved"];
18
+ // The dashboard ticket URL printed on file. Cloud serves the dashboard under
19
+ // /app on floom.dev; allow an env override rather than baking the host in.
20
+ function deepLink(ticketId) {
21
+ const base = (process.env.WORKEROS_APP_BASE || "https://floom.dev/app").replace(/\/+$/, "");
22
+ return `${base}/support/${ticketId}`;
23
+ }
24
+ // Shared error mapping (mirrors runs.ts::handleAuthError). Returns an exit code
25
+ // when it recognises the error, or null to let the caller rethrow.
26
+ function handleAuthError(error) {
27
+ const message = error instanceof Error ? error.message : String(error);
28
+ if (message.includes("Not logged in")) {
29
+ log.err("Not authenticated.");
30
+ process.stderr.write(`Run: ${getCommandName()} login\n`);
31
+ return 1;
32
+ }
33
+ if (error instanceof FloomApiError && (error.status === 401 || error.status === 403)) {
34
+ log.err("Your session expired.");
35
+ process.stderr.write(`Re-run: ${getCommandName()} login\n`);
36
+ return 1;
37
+ }
38
+ if (error instanceof FloomApiError && error.status && error.status >= 500) {
39
+ log.err(`API error: ${message}`);
40
+ process.stderr.write("Check API status, then retry.\n");
41
+ return 1;
42
+ }
43
+ return null;
44
+ }
45
+ // Support has no OSS counterpart: a 404 on a *collection* route means the engine
46
+ // does not mount /api/support (self-hosted), not that a ticket is missing.
47
+ function noteCloudOnlyOn404(error) {
48
+ if (error instanceof FloomApiError && error.status === 404) {
49
+ log.err("Support is a cloud-only feature.");
50
+ process.stderr.write(`Log in to the hosted product: ${getCommandName()} login --cloud\n`);
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+ function validSeverity(value) {
56
+ return value !== undefined && VALID_SEVERITY.includes(value);
57
+ }
58
+ function renderThread(ticket) {
59
+ log.heading(ticket.subject);
60
+ log.kv("Ticket", ticket.id);
61
+ log.kv("Status", ticket.status);
62
+ log.kv("Severity", ticket.severity);
63
+ if (ticket.opened_via)
64
+ log.kv("Opened via", ticket.opened_via);
65
+ if (ticket.workspace_id)
66
+ log.kv("Workspace", ticket.workspace_id);
67
+ const messages = ticket.messages || [];
68
+ if (!messages.length) {
69
+ log.blank();
70
+ log.info("(no messages)");
71
+ return;
72
+ }
73
+ for (const m of messages) {
74
+ log.blank();
75
+ const who = m.author_kind === "staff" ? "Support" : "You";
76
+ log.info(`${who} · ${m.created_at}`);
77
+ process.stdout.write(`${m.body}\n`);
78
+ }
79
+ }
80
+ // --- support file -----------------------------------------------------------
81
+ export async function supportFileCommand(options) {
82
+ const subject = (options.subject || "").trim();
83
+ if (!subject) {
84
+ log.err("--subject is required.");
85
+ return 1;
86
+ }
87
+ if (subject.length > MAX_SUBJECT_LEN) {
88
+ log.err(`--subject must be <= ${MAX_SUBJECT_LEN} characters.`);
89
+ return 1;
90
+ }
91
+ if (options.severity !== undefined && !validSeverity(options.severity)) {
92
+ log.err(`--severity must be one of: ${VALID_SEVERITY.join(", ")}.`);
93
+ return 1;
94
+ }
95
+ // The first message body is --body plus any structured agent context. At least
96
+ // one of these must be present (the API requires a non-empty body).
97
+ const parts = [];
98
+ if (options.body && options.body.trim())
99
+ parts.push(options.body.trim());
100
+ if (options.operation && options.operation.trim())
101
+ parts.push(`Operation: ${options.operation.trim()}`);
102
+ if (options.errorCode && options.errorCode.trim())
103
+ parts.push(`Error code: ${options.errorCode.trim()}`);
104
+ const body = parts.join("\n\n");
105
+ if (!body) {
106
+ log.err("Provide --body (or --operation / --error-code for context).");
107
+ return 1;
108
+ }
109
+ if (body.length > MAX_BODY_LEN) {
110
+ log.err(`Message body must be <= ${MAX_BODY_LEN} characters.`);
111
+ return 1;
112
+ }
113
+ try {
114
+ const { client } = await createAuthenticatedClient();
115
+ const ticket = (await client.requestJson("POST", "/support/tickets", {
116
+ body: { subject, body, severity: options.severity || "normal", opened_via: "cli" },
117
+ }));
118
+ if (options.json) {
119
+ printJson(ticket);
120
+ return 0;
121
+ }
122
+ log.ok(`Filed ticket ${ticket.id}`);
123
+ log.info(deepLink(ticket.id));
124
+ log.info("You'll be notified by email and on your next session when support replies.");
125
+ return 0;
126
+ }
127
+ catch (error) {
128
+ if (noteCloudOnlyOn404(error))
129
+ return 1;
130
+ const handled = handleAuthError(error);
131
+ if (handled !== null)
132
+ return handled;
133
+ throw error;
134
+ }
135
+ }
136
+ // --- support list -----------------------------------------------------------
137
+ export async function supportListCommand(options) {
138
+ if (options.status !== undefined && !VALID_STATUS.includes(options.status)) {
139
+ log.err(`--status must be one of: ${VALID_STATUS.join(", ")}.`);
140
+ return 1;
141
+ }
142
+ if (options.limit !== undefined && (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 100)) {
143
+ log.err("--limit must be an integer between 1 and 100.");
144
+ return 1;
145
+ }
146
+ try {
147
+ const { client } = await createAuthenticatedClient();
148
+ const result = (await client.requestJson("GET", "/support/tickets", {
149
+ query: { status: options.status, limit: options.limit },
150
+ }));
151
+ if (options.json) {
152
+ printJson(result);
153
+ return 0;
154
+ }
155
+ const tickets = result.tickets || [];
156
+ if (!tickets.length) {
157
+ log.info("No tickets.");
158
+ return 0;
159
+ }
160
+ process.stdout.write(renderTable(tickets.map((t) => ({
161
+ id: t.id,
162
+ subject: t.subject.length > 48 ? `${t.subject.slice(0, 47)}…` : t.subject,
163
+ status: t.status,
164
+ severity: t.severity,
165
+ unread: t.unread_for_opener ? "•" : "",
166
+ updated: t.updated_at || "-",
167
+ })), [
168
+ { key: "id", label: "Ticket" },
169
+ { key: "subject", label: "Subject" },
170
+ { key: "status", label: "Status" },
171
+ { key: "severity", label: "Severity" },
172
+ { key: "unread", label: "Unread" },
173
+ { key: "updated", label: "Updated" },
174
+ ]) + "\n");
175
+ if (result.unread_count)
176
+ log.info(`${result.unread_count} ticket(s) with unread replies.`);
177
+ return 0;
178
+ }
179
+ catch (error) {
180
+ if (noteCloudOnlyOn404(error))
181
+ return 1;
182
+ const handled = handleAuthError(error);
183
+ if (handled !== null)
184
+ return handled;
185
+ throw error;
186
+ }
187
+ }
188
+ // --- support get ------------------------------------------------------------
189
+ export async function supportGetCommand(ticketId, options) {
190
+ try {
191
+ const { client } = await createAuthenticatedClient();
192
+ const ticket = (await client.requestJson("GET", `/support/tickets/${encodeURIComponent(ticketId)}`));
193
+ if (options.json) {
194
+ printJson(ticket);
195
+ return 0;
196
+ }
197
+ renderThread(ticket);
198
+ return 0;
199
+ }
200
+ catch (error) {
201
+ // A 404 on a single-ticket route is genuinely "ticket not found" (the route
202
+ // exists), so don't shadow it with the cloud-only hint.
203
+ if (error instanceof FloomApiError && error.status === 404) {
204
+ log.err(`Ticket '${ticketId}' not found.`);
205
+ log.info(`List tickets: ${getCommandName()} support list`);
206
+ return 1;
207
+ }
208
+ const handled = handleAuthError(error);
209
+ if (handled !== null)
210
+ return handled;
211
+ throw error;
212
+ }
213
+ }
214
+ // --- support reply ----------------------------------------------------------
215
+ export async function supportReplyCommand(ticketId, options) {
216
+ const body = (options.body || "").trim();
217
+ if (!body) {
218
+ log.err("--body is required.");
219
+ return 1;
220
+ }
221
+ if (body.length > MAX_BODY_LEN) {
222
+ log.err(`--body must be <= ${MAX_BODY_LEN} characters.`);
223
+ return 1;
224
+ }
225
+ try {
226
+ const { client } = await createAuthenticatedClient();
227
+ const ticket = (await client.requestJson("POST", `/support/tickets/${encodeURIComponent(ticketId)}/messages`, { body: { body } }));
228
+ if (options.json) {
229
+ printJson(ticket);
230
+ return 0;
231
+ }
232
+ log.ok(`Replied to ${ticketId}`);
233
+ return 0;
234
+ }
235
+ catch (error) {
236
+ if (error instanceof FloomApiError && error.status === 404) {
237
+ log.err(`Ticket '${ticketId}' not found.`);
238
+ return 1;
239
+ }
240
+ const handled = handleAuthError(error);
241
+ if (handled !== null)
242
+ return handled;
243
+ throw error;
244
+ }
245
+ }
246
+ // --- support ack ------------------------------------------------------------
247
+ export async function supportAckCommand(ticketId, options) {
248
+ try {
249
+ const { client } = await createAuthenticatedClient();
250
+ const ticket = (await client.requestJson("POST", `/support/tickets/${encodeURIComponent(ticketId)}/ack`));
251
+ if (options.json) {
252
+ printJson(ticket);
253
+ return 0;
254
+ }
255
+ log.ok(`Cleared unread flag on ${ticketId}`);
256
+ return 0;
257
+ }
258
+ catch (error) {
259
+ if (error instanceof FloomApiError && error.status === 404) {
260
+ log.err(`Ticket '${ticketId}' not found.`);
261
+ return 1;
262
+ }
263
+ const handled = handleAuthError(error);
264
+ if (handled !== null)
265
+ return handled;
266
+ throw error;
267
+ }
268
+ }
269
+ // --- feedback ---------------------------------------------------------------
270
+ // Locate the local session transcript to attach to `feedback`. Only the local
271
+ // CLI can read the on-disk transcript (the MCP server can't), which is why this
272
+ // lives here and not in the API. Resolution order: explicit path → env override
273
+ // → newest *.jsonl under ~/.claude/projects. Returns null when none is found.
274
+ async function findSessionTranscript(explicitPath) {
275
+ const direct = explicitPath || process.env.WORKEROS_SESSION_TRANSCRIPT || process.env.CLAUDE_SESSION_TRANSCRIPT;
276
+ if (direct) {
277
+ try {
278
+ await stat(direct);
279
+ return direct;
280
+ }
281
+ catch {
282
+ return null;
283
+ }
284
+ }
285
+ const root = join(homedir(), ".claude", "projects");
286
+ const candidates = [];
287
+ async function walk(dir, depth) {
288
+ if (depth > 4)
289
+ return;
290
+ let entries;
291
+ try {
292
+ entries = await readdir(dir, { withFileTypes: true });
293
+ }
294
+ catch {
295
+ return;
296
+ }
297
+ for (const entry of entries) {
298
+ const full = join(dir, entry.name);
299
+ if (entry.isDirectory()) {
300
+ await walk(full, depth + 1);
301
+ }
302
+ else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
303
+ try {
304
+ const s = await stat(full);
305
+ candidates.push({ path: full, mtime: s.mtimeMs });
306
+ }
307
+ catch {
308
+ /* skip unreadable */
309
+ }
310
+ }
311
+ }
312
+ }
313
+ await walk(root, 0);
314
+ if (!candidates.length)
315
+ return null;
316
+ return candidates.reduce((a, b) => (b.mtime > a.mtime ? b : a)).path;
317
+ }
318
+ // Append the transcript tail to `message`, clamped so the whole body fits under
319
+ // MAX_BODY_LEN (the API rejects longer). The tail is the most recent and most
320
+ // relevant part of a session.
321
+ function composeFeedbackBody(message, transcript) {
322
+ if (!transcript)
323
+ return message;
324
+ const header = "\n\n--- Session transcript (most recent, truncated) ---\n";
325
+ const budget = MAX_BODY_LEN - message.length - header.length;
326
+ if (budget <= 200)
327
+ return message; // no meaningful room left
328
+ const tail = transcript.length > budget ? transcript.slice(transcript.length - budget) : transcript;
329
+ return `${message}${header}${tail}`;
330
+ }
331
+ export async function feedbackCommand(options) {
332
+ const message = (options.message || "").trim();
333
+ if (!message) {
334
+ log.err("--message is required.");
335
+ return 1;
336
+ }
337
+ if (message.length > MAX_BODY_LEN) {
338
+ log.err(`--message must be <= ${MAX_BODY_LEN} characters.`);
339
+ return 1;
340
+ }
341
+ if (options.severity !== undefined && !validSeverity(options.severity)) {
342
+ log.err(`--severity must be one of: ${VALID_SEVERITY.join(", ")}.`);
343
+ return 1;
344
+ }
345
+ const explicitPath = typeof options.transcript === "string" ? options.transcript : undefined;
346
+ let transcript = null;
347
+ if (options.transcript !== false) {
348
+ const path = await findSessionTranscript(explicitPath);
349
+ if (path) {
350
+ try {
351
+ transcript = await readFile(path, "utf8");
352
+ }
353
+ catch {
354
+ transcript = null;
355
+ }
356
+ }
357
+ else if (explicitPath) {
358
+ log.warn(`Transcript not found at ${explicitPath}; filing feedback without it.`);
359
+ }
360
+ }
361
+ const subjectSource = message.split("\n")[0].trim() || "CLI feedback";
362
+ const subject = subjectSource.length > MAX_SUBJECT_LEN
363
+ ? subjectSource.slice(0, MAX_SUBJECT_LEN - 1) + "…"
364
+ : subjectSource;
365
+ const body = composeFeedbackBody(message, transcript);
366
+ try {
367
+ const { client } = await createAuthenticatedClient();
368
+ const ticket = (await client.requestJson("POST", "/support/tickets", {
369
+ body: { subject, body, severity: options.severity || "normal", opened_via: "cli" },
370
+ }));
371
+ if (options.json) {
372
+ printJson(ticket);
373
+ return 0;
374
+ }
375
+ log.ok(`Filed feedback ${ticket.id}`);
376
+ if (transcript)
377
+ log.step("Attached the local session transcript.");
378
+ log.info(deepLink(ticket.id));
379
+ log.info("You'll be notified by email and on your next session when support replies.");
380
+ return 0;
381
+ }
382
+ catch (error) {
383
+ if (noteCloudOnlyOn404(error))
384
+ return 1;
385
+ const handled = handleAuthError(error);
386
+ if (handled !== null)
387
+ return handled;
388
+ throw error;
389
+ }
390
+ }
391
+ //# sourceMappingURL=support.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"support.js","sourceRoot":"","sources":["../../src/commands/support.ts"],"names":[],"mappings":"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"}
@@ -16,7 +16,18 @@ export declare function loadWorkerSource(dirArg: string): Promise<{
16
16
  source?: WorkerSource;
17
17
  errors: string[];
18
18
  }>;
19
- export declare function workersValidateCommand(dir: string): Promise<number>;
19
+ export declare function workersContractCommand(options: {
20
+ json?: boolean;
21
+ }): Promise<number>;
22
+ export declare function workersTemplatesListCommand(options: {
23
+ json?: boolean;
24
+ }): Promise<number>;
25
+ export declare function workersTemplatesGetCommand(id: string, options: {
26
+ json?: boolean;
27
+ }): Promise<number>;
28
+ export declare function workersValidateCommand(dir: string, options?: {
29
+ json?: boolean;
30
+ }): Promise<number>;
20
31
  export declare function workersPushCommand(dir: string): Promise<number>;
21
32
  export declare function workersListCommand(options: {
22
33
  json?: boolean;
@@ -6,6 +6,7 @@ import { createAuthenticatedClient, FloomApiError, FloomConnectionError } from "
6
6
  import { getCommandName } from "../lib/command-name.js";
7
7
  import { log, printJson, renderTable } from "../lib/output.js";
8
8
  import { promptYesNo } from "../lib/prompt.js";
9
+ import { WORKER_AUTHORING_CONTRACT, getWorkerTemplate, listWorkerTemplates, } from "../lib/worker-authoring.js";
9
10
  const SERVER_WORKER_RUNTIMES = new Set(["python311", "node22", "bash", "skill", "none"]);
10
11
  function emitError(message, hint, json) {
11
12
  if (json) {
@@ -537,11 +538,90 @@ function emitApiError(error) {
537
538
  }
538
539
  throw error;
539
540
  }
540
- export async function workersValidateCommand(dir) {
541
+ export async function workersContractCommand(options) {
542
+ if (options.json) {
543
+ printJson(WORKER_AUTHORING_CONTRACT);
544
+ return 0;
545
+ }
546
+ log.heading("Floom worker authoring contract");
547
+ log.kv("Schema", WORKER_AUTHORING_CONTRACT.schema_version);
548
+ log.kv("Required files", WORKER_AUTHORING_CONTRACT.required_files.join(", "));
549
+ log.kv("Required fields", WORKER_AUTHORING_CONTRACT.required_top_level_fields.join(", "));
550
+ log.blank();
551
+ log.info("Recommended flow:");
552
+ for (const step of WORKER_AUTHORING_CONTRACT.validation_order) {
553
+ log.info(` - ${step}`);
554
+ }
555
+ log.blank();
556
+ log.info(`List templates: ${getCommandName()} workers templates list`);
557
+ return 0;
558
+ }
559
+ export async function workersTemplatesListCommand(options) {
560
+ const templates = listWorkerTemplates();
561
+ if (options.json) {
562
+ printJson({ templates });
563
+ return 0;
564
+ }
565
+ process.stdout.write(renderTable(templates.map((template) => ({
566
+ id: template.id,
567
+ mode: template.mode,
568
+ title: template.title,
569
+ description: template.description,
570
+ })), [
571
+ { key: "id", label: "ID" },
572
+ { key: "mode", label: "Mode" },
573
+ { key: "title", label: "Title" },
574
+ { key: "description", label: "Description" },
575
+ ]) + "\n");
576
+ return 0;
577
+ }
578
+ export async function workersTemplatesGetCommand(id, options) {
579
+ const template = getWorkerTemplate(id);
580
+ if (!template) {
581
+ return emitError(`Unknown worker template '${id}'.`, `List available templates: ${getCommandName()} workers templates list`, options.json);
582
+ }
583
+ if (options.json) {
584
+ printJson(template);
585
+ return 0;
586
+ }
587
+ log.heading(template.title);
588
+ log.kv("ID", template.id);
589
+ log.kv("Mode", template.mode);
590
+ log.info(template.description);
591
+ log.blank();
592
+ log.info("worker.yml:");
593
+ process.stdout.write(`${template.worker_yml}\n`);
594
+ if (template.run_py) {
595
+ log.info("run.py:");
596
+ process.stdout.write(`${template.run_py}\n`);
597
+ }
598
+ if (template.skill_md) {
599
+ log.info("SKILL.md:");
600
+ process.stdout.write(`${template.skill_md}\n`);
601
+ }
602
+ return 0;
603
+ }
604
+ export async function workersValidateCommand(dir, options = {}) {
541
605
  const result = await loadWorkerSource(dir);
542
606
  if (!result.source) {
607
+ if (options.json) {
608
+ printJson({ valid: false, errors: result.errors });
609
+ return 1;
610
+ }
543
611
  return emitValidationErrors(result.errors);
544
612
  }
613
+ if (options.json) {
614
+ printJson({
615
+ valid: true,
616
+ errors: [],
617
+ worker_id: result.source.workerId,
618
+ directory: result.source.dir,
619
+ name: result.source.displayName,
620
+ runtime: result.source.runtime,
621
+ source: result.source.entrypoint === "SKILL.md" ? "SKILL.md" : result.source.runPy?.trim() ? "run.py" : "SKILL.md",
622
+ });
623
+ return 0;
624
+ }
545
625
  log.ok(`Validated ${result.source.workerId}`);
546
626
  log.kv("Directory", result.source.dir);
547
627
  log.kv("Name", result.source.displayName);