offwatch 0.5.11 → 0.5.13

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 (95) hide show
  1. package/README.md +132 -178
  2. package/bin/offwatch.js +6 -7
  3. package/lib/downloader.js +112 -0
  4. package/package.json +17 -7
  5. package/postinstall.js +21 -0
  6. package/src/__tests__/agent-jwt-env.test.ts +0 -79
  7. package/src/__tests__/allowed-hostname.test.ts +0 -80
  8. package/src/__tests__/auth-command-registration.test.ts +0 -16
  9. package/src/__tests__/board-auth.test.ts +0 -53
  10. package/src/__tests__/common.test.ts +0 -98
  11. package/src/__tests__/company-delete.test.ts +0 -95
  12. package/src/__tests__/company-import-export-e2e.test.ts +0 -502
  13. package/src/__tests__/company-import-url.test.ts +0 -74
  14. package/src/__tests__/company-import-zip.test.ts +0 -44
  15. package/src/__tests__/company.test.ts +0 -599
  16. package/src/__tests__/context.test.ts +0 -70
  17. package/src/__tests__/data-dir.test.ts +0 -79
  18. package/src/__tests__/doctor.test.ts +0 -102
  19. package/src/__tests__/feedback.test.ts +0 -177
  20. package/src/__tests__/helpers/embedded-postgres.ts +0 -6
  21. package/src/__tests__/helpers/zip.ts +0 -87
  22. package/src/__tests__/home-paths.test.ts +0 -44
  23. package/src/__tests__/http.test.ts +0 -106
  24. package/src/__tests__/network-bind.test.ts +0 -62
  25. package/src/__tests__/onboard.test.ts +0 -166
  26. package/src/__tests__/routines.test.ts +0 -249
  27. package/src/__tests__/telemetry.test.ts +0 -117
  28. package/src/__tests__/worktree-merge-history.test.ts +0 -492
  29. package/src/__tests__/worktree.test.ts +0 -982
  30. package/src/adapters/http/format-event.ts +0 -4
  31. package/src/adapters/http/index.ts +0 -7
  32. package/src/adapters/index.ts +0 -2
  33. package/src/adapters/process/format-event.ts +0 -4
  34. package/src/adapters/process/index.ts +0 -7
  35. package/src/adapters/registry.ts +0 -63
  36. package/src/checks/agent-jwt-secret-check.ts +0 -40
  37. package/src/checks/config-check.ts +0 -33
  38. package/src/checks/database-check.ts +0 -59
  39. package/src/checks/deployment-auth-check.ts +0 -88
  40. package/src/checks/index.ts +0 -18
  41. package/src/checks/llm-check.ts +0 -82
  42. package/src/checks/log-check.ts +0 -30
  43. package/src/checks/path-resolver.ts +0 -1
  44. package/src/checks/port-check.ts +0 -24
  45. package/src/checks/secrets-check.ts +0 -146
  46. package/src/checks/storage-check.ts +0 -51
  47. package/src/client/board-auth.ts +0 -282
  48. package/src/client/command-label.ts +0 -4
  49. package/src/client/context.ts +0 -175
  50. package/src/client/http.ts +0 -255
  51. package/src/commands/allowed-hostname.ts +0 -40
  52. package/src/commands/auth-bootstrap-ceo.ts +0 -138
  53. package/src/commands/client/activity.ts +0 -71
  54. package/src/commands/client/agent.ts +0 -315
  55. package/src/commands/client/approval.ts +0 -259
  56. package/src/commands/client/auth.ts +0 -113
  57. package/src/commands/client/common.ts +0 -221
  58. package/src/commands/client/company.ts +0 -1578
  59. package/src/commands/client/context.ts +0 -125
  60. package/src/commands/client/dashboard.ts +0 -34
  61. package/src/commands/client/feedback.ts +0 -645
  62. package/src/commands/client/issue.ts +0 -411
  63. package/src/commands/client/plugin.ts +0 -374
  64. package/src/commands/client/zip.ts +0 -129
  65. package/src/commands/configure.ts +0 -201
  66. package/src/commands/db-backup.ts +0 -102
  67. package/src/commands/doctor.ts +0 -203
  68. package/src/commands/env.ts +0 -411
  69. package/src/commands/heartbeat-run.ts +0 -344
  70. package/src/commands/onboard.ts +0 -692
  71. package/src/commands/routines.ts +0 -352
  72. package/src/commands/run.ts +0 -216
  73. package/src/commands/worktree-lib.ts +0 -279
  74. package/src/commands/worktree-merge-history-lib.ts +0 -764
  75. package/src/commands/worktree.ts +0 -2876
  76. package/src/config/data-dir.ts +0 -48
  77. package/src/config/env.ts +0 -125
  78. package/src/config/home.ts +0 -80
  79. package/src/config/hostnames.ts +0 -26
  80. package/src/config/schema.ts +0 -30
  81. package/src/config/secrets-key.ts +0 -48
  82. package/src/config/server-bind.ts +0 -183
  83. package/src/config/store.ts +0 -120
  84. package/src/index.ts +0 -182
  85. package/src/prompts/database.ts +0 -157
  86. package/src/prompts/llm.ts +0 -43
  87. package/src/prompts/logging.ts +0 -37
  88. package/src/prompts/secrets.ts +0 -99
  89. package/src/prompts/server.ts +0 -221
  90. package/src/prompts/storage.ts +0 -146
  91. package/src/telemetry.ts +0 -49
  92. package/src/utils/banner.ts +0 -24
  93. package/src/utils/net.ts +0 -18
  94. package/src/utils/path-resolver.ts +0 -25
  95. package/src/version.ts +0 -10
@@ -1,411 +0,0 @@
1
- import { Command } from "commander";
2
- import { writeFile } from "node:fs/promises";
3
- import {
4
- addIssueCommentSchema,
5
- checkoutIssueSchema,
6
- createIssueSchema,
7
- type FeedbackTrace,
8
- updateIssueSchema,
9
- type Issue,
10
- type IssueComment,
11
- } from "@paperclipai/shared";
12
- import {
13
- addCommonClientOptions,
14
- formatInlineRecord,
15
- handleCommandError,
16
- printOutput,
17
- resolveCommandContext,
18
- type BaseClientOptions,
19
- } from "./common.js";
20
- import {
21
- buildFeedbackTraceQuery,
22
- normalizeFeedbackTraceExportFormat,
23
- serializeFeedbackTraces,
24
- } from "./feedback.js";
25
-
26
- interface IssueBaseOptions extends BaseClientOptions {
27
- status?: string;
28
- assigneeAgentId?: string;
29
- projectId?: string;
30
- match?: string;
31
- }
32
-
33
- interface IssueCreateOptions extends BaseClientOptions {
34
- title: string;
35
- description?: string;
36
- status?: string;
37
- priority?: string;
38
- assigneeAgentId?: string;
39
- projectId?: string;
40
- goalId?: string;
41
- parentId?: string;
42
- requestDepth?: string;
43
- billingCode?: string;
44
- }
45
-
46
- interface IssueUpdateOptions extends BaseClientOptions {
47
- title?: string;
48
- description?: string;
49
- status?: string;
50
- priority?: string;
51
- assigneeAgentId?: string;
52
- projectId?: string;
53
- goalId?: string;
54
- parentId?: string;
55
- requestDepth?: string;
56
- billingCode?: string;
57
- comment?: string;
58
- hiddenAt?: string;
59
- }
60
-
61
- interface IssueCommentOptions extends BaseClientOptions {
62
- body: string;
63
- reopen?: boolean;
64
- }
65
-
66
- interface IssueCheckoutOptions extends BaseClientOptions {
67
- agentId: string;
68
- expectedStatuses?: string;
69
- }
70
-
71
- interface IssueFeedbackOptions extends BaseClientOptions {
72
- targetType?: string;
73
- vote?: string;
74
- status?: string;
75
- from?: string;
76
- to?: string;
77
- sharedOnly?: boolean;
78
- includePayload?: boolean;
79
- out?: string;
80
- format?: string;
81
- }
82
-
83
- export function registerIssueCommands(program: Command): void {
84
- const issue = program.command("issue").description("Issue operations");
85
-
86
- addCommonClientOptions(
87
- issue
88
- .command("list")
89
- .description("List issues for a company")
90
- .option("-C, --company-id <id>", "Company ID")
91
- .option("--status <csv>", "Comma-separated statuses")
92
- .option("--assignee-agent-id <id>", "Filter by assignee agent ID")
93
- .option("--project-id <id>", "Filter by project ID")
94
- .option("--match <text>", "Local text match on identifier/title/description")
95
- .action(async (opts: IssueBaseOptions) => {
96
- try {
97
- const ctx = resolveCommandContext(opts, { requireCompany: true });
98
- const params = new URLSearchParams();
99
- if (opts.status) params.set("status", opts.status);
100
- if (opts.assigneeAgentId) params.set("assigneeAgentId", opts.assigneeAgentId);
101
- if (opts.projectId) params.set("projectId", opts.projectId);
102
-
103
- const query = params.toString();
104
- const path = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
105
- const rows = (await ctx.api.get<Issue[]>(path)) ?? [];
106
-
107
- const filtered = filterIssueRows(rows, opts.match);
108
- if (ctx.json) {
109
- printOutput(filtered, { json: true });
110
- return;
111
- }
112
-
113
- if (filtered.length === 0) {
114
- printOutput([], { json: false });
115
- return;
116
- }
117
-
118
- for (const item of filtered) {
119
- console.log(
120
- formatInlineRecord({
121
- identifier: item.identifier,
122
- id: item.id,
123
- status: item.status,
124
- priority: item.priority,
125
- assigneeAgentId: item.assigneeAgentId,
126
- title: item.title,
127
- projectId: item.projectId,
128
- }),
129
- );
130
- }
131
- } catch (err) {
132
- handleCommandError(err);
133
- }
134
- }),
135
- { includeCompany: false },
136
- );
137
-
138
- addCommonClientOptions(
139
- issue
140
- .command("get")
141
- .description("Get an issue by UUID or identifier (e.g. PC-12)")
142
- .argument("<idOrIdentifier>", "Issue ID or identifier")
143
- .action(async (idOrIdentifier: string, opts: BaseClientOptions) => {
144
- try {
145
- const ctx = resolveCommandContext(opts);
146
- const row = await ctx.api.get<Issue>(`/api/issues/${idOrIdentifier}`);
147
- printOutput(row, { json: ctx.json });
148
- } catch (err) {
149
- handleCommandError(err);
150
- }
151
- }),
152
- );
153
-
154
- addCommonClientOptions(
155
- issue
156
- .command("create")
157
- .description("Create an issue")
158
- .requiredOption("-C, --company-id <id>", "Company ID")
159
- .requiredOption("--title <title>", "Issue title")
160
- .option("--description <text>", "Issue description")
161
- .option("--status <status>", "Issue status")
162
- .option("--priority <priority>", "Issue priority")
163
- .option("--assignee-agent-id <id>", "Assignee agent ID")
164
- .option("--project-id <id>", "Project ID")
165
- .option("--goal-id <id>", "Goal ID")
166
- .option("--parent-id <id>", "Parent issue ID")
167
- .option("--request-depth <n>", "Request depth integer")
168
- .option("--billing-code <code>", "Billing code")
169
- .action(async (opts: IssueCreateOptions) => {
170
- try {
171
- const ctx = resolveCommandContext(opts, { requireCompany: true });
172
- const payload = createIssueSchema.parse({
173
- title: opts.title,
174
- description: opts.description,
175
- status: opts.status,
176
- priority: opts.priority,
177
- assigneeAgentId: opts.assigneeAgentId,
178
- projectId: opts.projectId,
179
- goalId: opts.goalId,
180
- parentId: opts.parentId,
181
- requestDepth: parseOptionalInt(opts.requestDepth),
182
- billingCode: opts.billingCode,
183
- });
184
-
185
- const created = await ctx.api.post<Issue>(`/api/companies/${ctx.companyId}/issues`, payload);
186
- printOutput(created, { json: ctx.json });
187
- } catch (err) {
188
- handleCommandError(err);
189
- }
190
- }),
191
- { includeCompany: false },
192
- );
193
-
194
- addCommonClientOptions(
195
- issue
196
- .command("update")
197
- .description("Update an issue")
198
- .argument("<issueId>", "Issue ID")
199
- .option("--title <title>", "Issue title")
200
- .option("--description <text>", "Issue description")
201
- .option("--status <status>", "Issue status")
202
- .option("--priority <priority>", "Issue priority")
203
- .option("--assignee-agent-id <id>", "Assignee agent ID")
204
- .option("--project-id <id>", "Project ID")
205
- .option("--goal-id <id>", "Goal ID")
206
- .option("--parent-id <id>", "Parent issue ID")
207
- .option("--request-depth <n>", "Request depth integer")
208
- .option("--billing-code <code>", "Billing code")
209
- .option("--comment <text>", "Optional comment to add with update")
210
- .option("--hidden-at <iso8601|null>", "Set hiddenAt timestamp or literal 'null'")
211
- .action(async (issueId: string, opts: IssueUpdateOptions) => {
212
- try {
213
- const ctx = resolveCommandContext(opts);
214
- const payload = updateIssueSchema.parse({
215
- title: opts.title,
216
- description: opts.description,
217
- status: opts.status,
218
- priority: opts.priority,
219
- assigneeAgentId: opts.assigneeAgentId,
220
- projectId: opts.projectId,
221
- goalId: opts.goalId,
222
- parentId: opts.parentId,
223
- requestDepth: parseOptionalInt(opts.requestDepth),
224
- billingCode: opts.billingCode,
225
- comment: opts.comment,
226
- hiddenAt: parseHiddenAt(opts.hiddenAt),
227
- });
228
-
229
- const updated = await ctx.api.patch<Issue & { comment?: IssueComment | null }>(`/api/issues/${issueId}`, payload);
230
- printOutput(updated, { json: ctx.json });
231
- } catch (err) {
232
- handleCommandError(err);
233
- }
234
- }),
235
- );
236
-
237
- addCommonClientOptions(
238
- issue
239
- .command("comment")
240
- .description("Add comment to issue")
241
- .argument("<issueId>", "Issue ID")
242
- .requiredOption("--body <text>", "Comment body")
243
- .option("--reopen", "Reopen if issue is done/cancelled")
244
- .action(async (issueId: string, opts: IssueCommentOptions) => {
245
- try {
246
- const ctx = resolveCommandContext(opts);
247
- const payload = addIssueCommentSchema.parse({
248
- body: opts.body,
249
- reopen: opts.reopen,
250
- });
251
- const comment = await ctx.api.post<IssueComment>(`/api/issues/${issueId}/comments`, payload);
252
- printOutput(comment, { json: ctx.json });
253
- } catch (err) {
254
- handleCommandError(err);
255
- }
256
- }),
257
- );
258
-
259
- addCommonClientOptions(
260
- issue
261
- .command("feedback:list")
262
- .description("List feedback traces for an issue")
263
- .argument("<issueId>", "Issue ID")
264
- .option("--target-type <type>", "Filter by target type")
265
- .option("--vote <vote>", "Filter by vote value")
266
- .option("--status <status>", "Filter by trace status")
267
- .option("--from <iso8601>", "Only include traces created at or after this timestamp")
268
- .option("--to <iso8601>", "Only include traces created at or before this timestamp")
269
- .option("--shared-only", "Only include traces eligible for sharing/export")
270
- .option("--include-payload", "Include stored payload snapshots in the response")
271
- .action(async (issueId: string, opts: IssueFeedbackOptions) => {
272
- try {
273
- const ctx = resolveCommandContext(opts);
274
- const traces = (await ctx.api.get<FeedbackTrace[]>(
275
- `/api/issues/${issueId}/feedback-traces${buildFeedbackTraceQuery(opts)}`,
276
- )) ?? [];
277
- if (ctx.json) {
278
- printOutput(traces, { json: true });
279
- return;
280
- }
281
- printOutput(
282
- traces.map((trace) => ({
283
- id: trace.id,
284
- issue: trace.issueIdentifier ?? trace.issueId,
285
- vote: trace.vote,
286
- status: trace.status,
287
- targetType: trace.targetType,
288
- target: trace.targetSummary.label,
289
- })),
290
- { json: false },
291
- );
292
- } catch (err) {
293
- handleCommandError(err);
294
- }
295
- }),
296
- );
297
-
298
- addCommonClientOptions(
299
- issue
300
- .command("feedback:export")
301
- .description("Export feedback traces for an issue")
302
- .argument("<issueId>", "Issue ID")
303
- .option("--target-type <type>", "Filter by target type")
304
- .option("--vote <vote>", "Filter by vote value")
305
- .option("--status <status>", "Filter by trace status")
306
- .option("--from <iso8601>", "Only include traces created at or after this timestamp")
307
- .option("--to <iso8601>", "Only include traces created at or before this timestamp")
308
- .option("--shared-only", "Only include traces eligible for sharing/export")
309
- .option("--include-payload", "Include stored payload snapshots in the export")
310
- .option("--out <path>", "Write export to a file path instead of stdout")
311
- .option("--format <format>", "Export format: json or ndjson", "ndjson")
312
- .action(async (issueId: string, opts: IssueFeedbackOptions) => {
313
- try {
314
- const ctx = resolveCommandContext(opts);
315
- const traces = (await ctx.api.get<FeedbackTrace[]>(
316
- `/api/issues/${issueId}/feedback-traces${buildFeedbackTraceQuery(opts, opts.includePayload ?? true)}`,
317
- )) ?? [];
318
- const serialized = serializeFeedbackTraces(traces, opts.format);
319
- if (opts.out?.trim()) {
320
- await writeFile(opts.out, serialized, "utf8");
321
- if (ctx.json) {
322
- printOutput(
323
- { out: opts.out, count: traces.length, format: normalizeFeedbackTraceExportFormat(opts.format) },
324
- { json: true },
325
- );
326
- return;
327
- }
328
- console.log(`Wrote ${traces.length} feedback trace(s) to ${opts.out}`);
329
- return;
330
- }
331
- process.stdout.write(`${serialized}${serialized.endsWith("\n") ? "" : "\n"}`);
332
- } catch (err) {
333
- handleCommandError(err);
334
- }
335
- }),
336
- );
337
-
338
- addCommonClientOptions(
339
- issue
340
- .command("checkout")
341
- .description("Checkout issue for an agent")
342
- .argument("<issueId>", "Issue ID")
343
- .requiredOption("--agent-id <id>", "Agent ID")
344
- .option(
345
- "--expected-statuses <csv>",
346
- "Expected current statuses",
347
- "todo,backlog,blocked",
348
- )
349
- .action(async (issueId: string, opts: IssueCheckoutOptions) => {
350
- try {
351
- const ctx = resolveCommandContext(opts);
352
- const payload = checkoutIssueSchema.parse({
353
- agentId: opts.agentId,
354
- expectedStatuses: parseCsv(opts.expectedStatuses),
355
- });
356
- const updated = await ctx.api.post<Issue>(`/api/issues/${issueId}/checkout`, payload);
357
- printOutput(updated, { json: ctx.json });
358
- } catch (err) {
359
- handleCommandError(err);
360
- }
361
- }),
362
- );
363
-
364
- addCommonClientOptions(
365
- issue
366
- .command("release")
367
- .description("Release issue back to todo and clear assignee")
368
- .argument("<issueId>", "Issue ID")
369
- .action(async (issueId: string, opts: BaseClientOptions) => {
370
- try {
371
- const ctx = resolveCommandContext(opts);
372
- const updated = await ctx.api.post<Issue>(`/api/issues/${issueId}/release`, {});
373
- printOutput(updated, { json: ctx.json });
374
- } catch (err) {
375
- handleCommandError(err);
376
- }
377
- }),
378
- );
379
- }
380
-
381
- function parseCsv(value: string | undefined): string[] {
382
- if (!value) return [];
383
- return value.split(",").map((v) => v.trim()).filter(Boolean);
384
- }
385
-
386
- function parseOptionalInt(value: string | undefined): number | undefined {
387
- if (value === undefined) return undefined;
388
- const parsed = Number.parseInt(value, 10);
389
- if (!Number.isFinite(parsed)) {
390
- throw new Error(`Invalid integer value: ${value}`);
391
- }
392
- return parsed;
393
- }
394
-
395
- function parseHiddenAt(value: string | undefined): string | null | undefined {
396
- if (value === undefined) return undefined;
397
- if (value.trim().toLowerCase() === "null") return null;
398
- return value;
399
- }
400
-
401
- function filterIssueRows(rows: Issue[], match: string | undefined): Issue[] {
402
- if (!match?.trim()) return rows;
403
- const needle = match.trim().toLowerCase();
404
- return rows.filter((row) => {
405
- const text = [row.identifier, row.title, row.description]
406
- .filter((part): part is string => Boolean(part))
407
- .join("\n")
408
- .toLowerCase();
409
- return text.includes(needle);
410
- });
411
- }