offwatch 0.5.12 → 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.
- package/README.md +132 -178
- package/bin/offwatch.js +6 -7
- package/lib/downloader.js +112 -0
- package/package.json +18 -11
- package/postinstall.js +21 -0
- package/src/__tests__/agent-jwt-env.test.ts +0 -79
- package/src/__tests__/allowed-hostname.test.ts +0 -80
- package/src/__tests__/auth-command-registration.test.ts +0 -16
- package/src/__tests__/board-auth.test.ts +0 -53
- package/src/__tests__/common.test.ts +0 -98
- package/src/__tests__/company-delete.test.ts +0 -95
- package/src/__tests__/company-import-export-e2e.test.ts +0 -502
- package/src/__tests__/company-import-url.test.ts +0 -74
- package/src/__tests__/company-import-zip.test.ts +0 -44
- package/src/__tests__/company.test.ts +0 -599
- package/src/__tests__/context.test.ts +0 -70
- package/src/__tests__/data-dir.test.ts +0 -79
- package/src/__tests__/doctor.test.ts +0 -102
- package/src/__tests__/feedback.test.ts +0 -177
- package/src/__tests__/helpers/embedded-postgres.ts +0 -6
- package/src/__tests__/helpers/zip.ts +0 -87
- package/src/__tests__/home-paths.test.ts +0 -44
- package/src/__tests__/http.test.ts +0 -106
- package/src/__tests__/network-bind.test.ts +0 -62
- package/src/__tests__/onboard.test.ts +0 -166
- package/src/__tests__/routines.test.ts +0 -249
- package/src/__tests__/telemetry.test.ts +0 -117
- package/src/__tests__/worktree-merge-history.test.ts +0 -492
- package/src/__tests__/worktree.test.ts +0 -982
- package/src/adapters/http/format-event.ts +0 -4
- package/src/adapters/http/index.ts +0 -7
- package/src/adapters/index.ts +0 -2
- package/src/adapters/process/format-event.ts +0 -4
- package/src/adapters/process/index.ts +0 -7
- package/src/adapters/registry.ts +0 -63
- package/src/checks/agent-jwt-secret-check.ts +0 -40
- package/src/checks/config-check.ts +0 -33
- package/src/checks/database-check.ts +0 -59
- package/src/checks/deployment-auth-check.ts +0 -88
- package/src/checks/index.ts +0 -18
- package/src/checks/llm-check.ts +0 -82
- package/src/checks/log-check.ts +0 -30
- package/src/checks/path-resolver.ts +0 -1
- package/src/checks/port-check.ts +0 -24
- package/src/checks/secrets-check.ts +0 -146
- package/src/checks/storage-check.ts +0 -51
- package/src/client/board-auth.ts +0 -282
- package/src/client/command-label.ts +0 -4
- package/src/client/context.ts +0 -175
- package/src/client/http.ts +0 -255
- package/src/commands/allowed-hostname.ts +0 -40
- package/src/commands/auth-bootstrap-ceo.ts +0 -138
- package/src/commands/client/activity.ts +0 -71
- package/src/commands/client/agent.ts +0 -315
- package/src/commands/client/approval.ts +0 -259
- package/src/commands/client/auth.ts +0 -113
- package/src/commands/client/common.ts +0 -221
- package/src/commands/client/company.ts +0 -1578
- package/src/commands/client/context.ts +0 -125
- package/src/commands/client/dashboard.ts +0 -34
- package/src/commands/client/feedback.ts +0 -645
- package/src/commands/client/issue.ts +0 -411
- package/src/commands/client/plugin.ts +0 -374
- package/src/commands/client/zip.ts +0 -129
- package/src/commands/configure.ts +0 -201
- package/src/commands/db-backup.ts +0 -102
- package/src/commands/doctor.ts +0 -203
- package/src/commands/env.ts +0 -411
- package/src/commands/heartbeat-run.ts +0 -344
- package/src/commands/onboard.ts +0 -692
- package/src/commands/routines.ts +0 -352
- package/src/commands/run.ts +0 -216
- package/src/commands/worktree-lib.ts +0 -279
- package/src/commands/worktree-merge-history-lib.ts +0 -764
- package/src/commands/worktree.ts +0 -2876
- package/src/config/data-dir.ts +0 -48
- package/src/config/env.ts +0 -125
- package/src/config/home.ts +0 -80
- package/src/config/hostnames.ts +0 -26
- package/src/config/schema.ts +0 -30
- package/src/config/secrets-key.ts +0 -48
- package/src/config/server-bind.ts +0 -183
- package/src/config/store.ts +0 -120
- package/src/index.ts +0 -182
- package/src/prompts/database.ts +0 -157
- package/src/prompts/llm.ts +0 -43
- package/src/prompts/logging.ts +0 -37
- package/src/prompts/secrets.ts +0 -99
- package/src/prompts/server.ts +0 -221
- package/src/prompts/storage.ts +0 -146
- package/src/telemetry.ts +0 -49
- package/src/utils/banner.ts +0 -24
- package/src/utils/net.ts +0 -18
- package/src/utils/path-resolver.ts +0 -25
- 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
|
-
}
|