claude-task-worker 0.123.0 → 0.124.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.
- package/dist/chunk-HFU27QHT.js +579 -0
- package/dist/{chunk-HZHIQMAQ.js → chunk-XJCZF5SA.js} +45 -372
- package/dist/{dispatcher-JKUMXTJ2.js → dispatcher-CIAW3Z6X.js} +1 -1
- package/dist/{herdr-runner-33D6C2KI.js → herdr-runner-TNX2W4G6.js} +1 -1
- package/dist/index.js +15 -15
- package/dist/lib.js +6 -5
- package/dist/{table-DWVMNDS3.js → table-OOKBQVW5.js} +7 -1
- package/dist/table.d.ts +7 -0
- package/dist/user-config.d.ts +1 -0
- package/package.json +1 -1
- package/dist/chunk-N42YU3GJ.js +0 -184
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
// src/table.ts
|
|
2
|
+
import { appendFileSync, chmodSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { dirname, join as join2, resolve as resolve3 } from "node:path";
|
|
4
|
+
import { format } from "node:util";
|
|
5
|
+
|
|
6
|
+
// src/dispatch-args.ts
|
|
7
|
+
import { resolve } from "node:path";
|
|
8
|
+
var FLAG_INCOMPATIBLE_COMMANDS = [
|
|
9
|
+
"init",
|
|
10
|
+
"apply-labels",
|
|
11
|
+
"install",
|
|
12
|
+
"update",
|
|
13
|
+
"cloud-setup",
|
|
14
|
+
"usage",
|
|
15
|
+
"version",
|
|
16
|
+
"list-workers"
|
|
17
|
+
];
|
|
18
|
+
function collectFlagValues(argv, flag) {
|
|
19
|
+
const values = [];
|
|
20
|
+
for (let i = 0; i < argv.length; i++) {
|
|
21
|
+
if (argv[i] !== flag) continue;
|
|
22
|
+
const raw = argv[i + 1];
|
|
23
|
+
if (!raw || raw.startsWith("--")) {
|
|
24
|
+
console.error(`[dispatcher] ${flag} requires a value`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
values.push(raw);
|
|
28
|
+
}
|
|
29
|
+
return values;
|
|
30
|
+
}
|
|
31
|
+
function parseProjectFilters() {
|
|
32
|
+
return collectFlagValues(process.argv, "--project");
|
|
33
|
+
}
|
|
34
|
+
function hasProjectFilter() {
|
|
35
|
+
return process.argv.includes("--project");
|
|
36
|
+
}
|
|
37
|
+
function assertProjectCompatibleCommand(command) {
|
|
38
|
+
if (FLAG_INCOMPATIBLE_COMMANDS.includes(command)) {
|
|
39
|
+
console.error(`[dispatcher] --project cannot be used with the "${command}" command`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
var cachedCloudFlag;
|
|
44
|
+
function hasCloudFlag() {
|
|
45
|
+
if (cachedCloudFlag === void 0) {
|
|
46
|
+
cachedCloudFlag = process.argv.includes("--cloud");
|
|
47
|
+
}
|
|
48
|
+
return cachedCloudFlag;
|
|
49
|
+
}
|
|
50
|
+
var cachedDebugFlag;
|
|
51
|
+
function hasDebugFlag() {
|
|
52
|
+
if (cachedDebugFlag === void 0) {
|
|
53
|
+
cachedDebugFlag = process.argv.includes("--debug");
|
|
54
|
+
}
|
|
55
|
+
return cachedDebugFlag;
|
|
56
|
+
}
|
|
57
|
+
var INHERIT_CONFIG_FLAG = "--inherit-config";
|
|
58
|
+
var cachedInheritConfigPath;
|
|
59
|
+
function getInheritConfigPath() {
|
|
60
|
+
if (cachedInheritConfigPath === void 0) {
|
|
61
|
+
const values = collectFlagValues(process.argv, INHERIT_CONFIG_FLAG);
|
|
62
|
+
cachedInheritConfigPath = values.length > 0 ? resolve(values[values.length - 1]) : null;
|
|
63
|
+
}
|
|
64
|
+
return cachedInheritConfigPath;
|
|
65
|
+
}
|
|
66
|
+
function assertCloudCompatibleCommand(command) {
|
|
67
|
+
if (FLAG_INCOMPATIBLE_COMMANDS.includes(command)) {
|
|
68
|
+
console.error(`[worker] --cloud cannot be used with the "${command}" command`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function shellQuote(value) {
|
|
73
|
+
if (value === "") return "''";
|
|
74
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
75
|
+
}
|
|
76
|
+
function buildForwardedCommand(argv) {
|
|
77
|
+
const tokens = [];
|
|
78
|
+
for (let i = 0; i < argv.length; i++) {
|
|
79
|
+
if (argv[i] === "--project") {
|
|
80
|
+
i++;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (argv[i] === INHERIT_CONFIG_FLAG && i + 1 < argv.length) {
|
|
84
|
+
tokens.push(argv[i], resolve(argv[i + 1]));
|
|
85
|
+
i++;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
tokens.push(argv[i]);
|
|
89
|
+
}
|
|
90
|
+
return ["claude-task-worker", ...tokens.map(shellQuote)].join(" ");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/user-config.ts
|
|
94
|
+
import { readFileSync, statSync } from "node:fs";
|
|
95
|
+
import { homedir } from "node:os";
|
|
96
|
+
import { isAbsolute, join, resolve as resolve2 } from "node:path";
|
|
97
|
+
var RESERVED_ALL = "all";
|
|
98
|
+
var DEFAULT_RUN_MODE = "default";
|
|
99
|
+
var DEFAULT_ADVISOR_ENABLED = false;
|
|
100
|
+
var PERMISSION_MODES = [
|
|
101
|
+
"manual",
|
|
102
|
+
"auto",
|
|
103
|
+
"acceptEdits",
|
|
104
|
+
"dontAsk",
|
|
105
|
+
"plan",
|
|
106
|
+
"bypassPermissions"
|
|
107
|
+
];
|
|
108
|
+
var DEFAULT_PERMISSION_MODE = "bypassPermissions";
|
|
109
|
+
var UserConfigError = class extends Error {
|
|
110
|
+
constructor(message) {
|
|
111
|
+
super(message);
|
|
112
|
+
this.name = "UserConfigError";
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
function getConfigDir() {
|
|
116
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
117
|
+
const configHome = xdg && xdg.length > 0 ? xdg : join(homedir(), ".config");
|
|
118
|
+
return join(configHome, "claude-task-worker");
|
|
119
|
+
}
|
|
120
|
+
function getUserConfigPath() {
|
|
121
|
+
return join(getConfigDir(), "config.json");
|
|
122
|
+
}
|
|
123
|
+
function isDirectory(path) {
|
|
124
|
+
try {
|
|
125
|
+
return statSync(path).isDirectory();
|
|
126
|
+
} catch {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function parseConfigFile(path) {
|
|
131
|
+
let content;
|
|
132
|
+
try {
|
|
133
|
+
content = readFileSync(path, "utf-8");
|
|
134
|
+
} catch (err) {
|
|
135
|
+
if (err.code === "ENOENT") return void 0;
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
return JSON.parse(content);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
if (err instanceof SyntaxError) {
|
|
142
|
+
throw new UserConfigError(`config file contains invalid JSON: ${path}: ${err.message}`);
|
|
143
|
+
}
|
|
144
|
+
throw err;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function readRawConfig() {
|
|
148
|
+
return parseConfigFile(getUserConfigPath());
|
|
149
|
+
}
|
|
150
|
+
function parseMode(raw, path) {
|
|
151
|
+
if (!("mode" in raw)) return DEFAULT_RUN_MODE;
|
|
152
|
+
const value = raw["mode"];
|
|
153
|
+
if (value === "default" || value === "herdr") return value;
|
|
154
|
+
console.warn(`[config] invalid mode: ${JSON.stringify(value)} in ${path}, using "${DEFAULT_RUN_MODE}"`);
|
|
155
|
+
return DEFAULT_RUN_MODE;
|
|
156
|
+
}
|
|
157
|
+
function parseAdvisor(raw, path) {
|
|
158
|
+
if (!("advisor" in raw)) return DEFAULT_ADVISOR_ENABLED;
|
|
159
|
+
const value = raw["advisor"];
|
|
160
|
+
if (typeof value === "boolean") return value;
|
|
161
|
+
console.warn(`[config] invalid advisor: ${JSON.stringify(value)} in ${path}, using ${DEFAULT_ADVISOR_ENABLED}`);
|
|
162
|
+
return DEFAULT_ADVISOR_ENABLED;
|
|
163
|
+
}
|
|
164
|
+
function parsePermission(raw, path) {
|
|
165
|
+
if (!("permission" in raw)) return DEFAULT_PERMISSION_MODE;
|
|
166
|
+
const value = raw["permission"];
|
|
167
|
+
if (typeof value === "string" && PERMISSION_MODES.includes(value)) return value;
|
|
168
|
+
console.warn(`[config] invalid permission: ${JSON.stringify(value)} in ${path}, using "${DEFAULT_PERMISSION_MODE}"`);
|
|
169
|
+
return DEFAULT_PERMISSION_MODE;
|
|
170
|
+
}
|
|
171
|
+
function loadUserConfig() {
|
|
172
|
+
const path = getUserConfigPath();
|
|
173
|
+
const raw = readRawConfig();
|
|
174
|
+
if (raw === void 0) {
|
|
175
|
+
throw new UserConfigError(`config.json not found: ${path}`);
|
|
176
|
+
}
|
|
177
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
178
|
+
throw new UserConfigError(`config.json must contain a JSON object: ${path}`);
|
|
179
|
+
}
|
|
180
|
+
if (!("projects" in raw) || typeof raw["projects"] !== "object" || raw["projects"] === null || Array.isArray(raw["projects"])) {
|
|
181
|
+
throw new UserConfigError(`config.json must contain a "projects" section as an object: ${path}`);
|
|
182
|
+
}
|
|
183
|
+
if ("projectGroups" in raw && (typeof raw["projectGroups"] !== "object" || raw["projectGroups"] === null || Array.isArray(raw["projectGroups"]))) {
|
|
184
|
+
throw new UserConfigError(`config.json "projectGroups" must be an object: ${path}`);
|
|
185
|
+
}
|
|
186
|
+
const mode = parseMode(raw, path);
|
|
187
|
+
const advisor = parseAdvisor(raw, path);
|
|
188
|
+
const permission = parsePermission(raw, path);
|
|
189
|
+
const rawProjects = raw["projects"];
|
|
190
|
+
const rawProjectGroups = "projectGroups" in raw ? raw["projectGroups"] : {};
|
|
191
|
+
const projectKeys = Object.keys(rawProjects);
|
|
192
|
+
const groupKeys = Object.keys(rawProjectGroups);
|
|
193
|
+
if (projectKeys.includes(RESERVED_ALL) || groupKeys.includes(RESERVED_ALL)) {
|
|
194
|
+
throw new UserConfigError(
|
|
195
|
+
`"${RESERVED_ALL}" is a reserved word and cannot be used as a key in "projects" or "projectGroups"`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
const groupKeySet = new Set(groupKeys);
|
|
199
|
+
const duplicateKeys = projectKeys.filter((key) => groupKeySet.has(key));
|
|
200
|
+
if (duplicateKeys.length > 0) {
|
|
201
|
+
throw new UserConfigError(
|
|
202
|
+
`"projects" and "projectGroups" share the same key namespace; duplicate keys are not allowed: ${duplicateKeys.join(", ")}`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
const projects = {};
|
|
206
|
+
for (const [name, value] of Object.entries(rawProjects)) {
|
|
207
|
+
if (name === "__proto__") {
|
|
208
|
+
throw new UserConfigError(`"__proto__" cannot be used as a key in "projects": ${path}`);
|
|
209
|
+
}
|
|
210
|
+
if (typeof value !== "string" || !isAbsolute(value)) {
|
|
211
|
+
console.warn(`[config] invalid projects.${name}: expected an absolute path, skipping`);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (!isDirectory(value)) {
|
|
215
|
+
console.warn(`[config] projects.${name} does not exist as a directory: ${value}, skipping`);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
projects[name] = value;
|
|
219
|
+
}
|
|
220
|
+
const projectGroups = {};
|
|
221
|
+
for (const [groupName, value] of Object.entries(rawProjectGroups)) {
|
|
222
|
+
if (groupName === "__proto__") {
|
|
223
|
+
throw new UserConfigError(`"__proto__" cannot be used as a key in "projectGroups": ${path}`);
|
|
224
|
+
}
|
|
225
|
+
if (!Array.isArray(value)) {
|
|
226
|
+
console.warn(`[config] invalid projectGroups.${groupName}: expected an array, skipping`);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
const members = [];
|
|
230
|
+
for (const member of value) {
|
|
231
|
+
if (typeof member !== "string" || !Object.prototype.hasOwnProperty.call(projects, member)) {
|
|
232
|
+
console.warn(`[config] projectGroups.${groupName} references unknown project "${String(member)}", skipping`);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
members.push(member);
|
|
236
|
+
}
|
|
237
|
+
projectGroups[groupName] = members;
|
|
238
|
+
}
|
|
239
|
+
return { mode, advisor, permission, projects, projectGroups };
|
|
240
|
+
}
|
|
241
|
+
var cachedRunMode;
|
|
242
|
+
function getRunMode() {
|
|
243
|
+
if (cachedRunMode === void 0) {
|
|
244
|
+
cachedRunMode = readRunMode();
|
|
245
|
+
}
|
|
246
|
+
return cachedRunMode;
|
|
247
|
+
}
|
|
248
|
+
var cachedAdvisorEnabled;
|
|
249
|
+
function isAdvisorEnabled() {
|
|
250
|
+
if (cachedAdvisorEnabled === void 0) {
|
|
251
|
+
cachedAdvisorEnabled = readAdvisorEnabled();
|
|
252
|
+
}
|
|
253
|
+
return cachedAdvisorEnabled;
|
|
254
|
+
}
|
|
255
|
+
var cachedPermissionMode;
|
|
256
|
+
function getPermissionMode() {
|
|
257
|
+
if (cachedPermissionMode === void 0) {
|
|
258
|
+
cachedPermissionMode = readTopLevel(parsePermission, DEFAULT_PERMISSION_MODE, "permission");
|
|
259
|
+
}
|
|
260
|
+
return cachedPermissionMode;
|
|
261
|
+
}
|
|
262
|
+
function readTopLevel(parse, fallback, label) {
|
|
263
|
+
let raw;
|
|
264
|
+
try {
|
|
265
|
+
raw = readRawConfig();
|
|
266
|
+
} catch (err) {
|
|
267
|
+
console.warn(`[config] failed to read config file, using default ${label}: ${err}`);
|
|
268
|
+
return fallback;
|
|
269
|
+
}
|
|
270
|
+
if (raw === void 0) return fallback;
|
|
271
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
272
|
+
return fallback;
|
|
273
|
+
}
|
|
274
|
+
return parse(raw, getUserConfigPath());
|
|
275
|
+
}
|
|
276
|
+
function readAdvisorEnabled() {
|
|
277
|
+
return readTopLevel(parseAdvisor, DEFAULT_ADVISOR_ENABLED, "advisor");
|
|
278
|
+
}
|
|
279
|
+
function readRunMode() {
|
|
280
|
+
return readTopLevel(parseMode, DEFAULT_RUN_MODE, "mode");
|
|
281
|
+
}
|
|
282
|
+
function findProjectNameByPath(path) {
|
|
283
|
+
let config;
|
|
284
|
+
try {
|
|
285
|
+
config = loadUserConfig();
|
|
286
|
+
} catch {
|
|
287
|
+
return void 0;
|
|
288
|
+
}
|
|
289
|
+
const target = resolve2(path);
|
|
290
|
+
for (const [name, projectPath] of Object.entries(config.projects)) {
|
|
291
|
+
if (resolve2(projectPath) === target) return name;
|
|
292
|
+
}
|
|
293
|
+
return void 0;
|
|
294
|
+
}
|
|
295
|
+
function resolveTargetProjects(requested, config) {
|
|
296
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
297
|
+
for (const name of requested) {
|
|
298
|
+
if (name === RESERVED_ALL) {
|
|
299
|
+
for (const [projectName, projectPath] of Object.entries(config.projects)) {
|
|
300
|
+
resolved.set(projectName, { name: projectName, path: projectPath });
|
|
301
|
+
}
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (Object.prototype.hasOwnProperty.call(config.projects, name)) {
|
|
305
|
+
resolved.set(name, { name, path: config.projects[name] });
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (Object.prototype.hasOwnProperty.call(config.projectGroups, name)) {
|
|
309
|
+
for (const projectName of config.projectGroups[name]) {
|
|
310
|
+
const projectPath = config.projects[projectName];
|
|
311
|
+
if (projectPath === void 0) continue;
|
|
312
|
+
resolved.set(projectName, { name: projectName, path: projectPath });
|
|
313
|
+
}
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const availableProjects = Object.keys(config.projects).join(", ") || "(none)";
|
|
317
|
+
const availableGroups = Object.keys(config.projectGroups).join(", ") || "(none)";
|
|
318
|
+
throw new UserConfigError(
|
|
319
|
+
`Unknown project or group: "${name}". Available projects: ${availableProjects}. Available groups: ${availableGroups}.`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
const resolvedProjects = Array.from(resolved.values());
|
|
323
|
+
if (resolvedProjects.length === 0) {
|
|
324
|
+
throw new UserConfigError(`No projects resolved from requested targets: ${requested.join(", ")}`);
|
|
325
|
+
}
|
|
326
|
+
return resolvedProjects;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/table.ts
|
|
330
|
+
function getDisplayWidth(str) {
|
|
331
|
+
let width = 0;
|
|
332
|
+
for (const char of str.replace(CONTROL_CHARS, "")) {
|
|
333
|
+
const code = char.codePointAt(0);
|
|
334
|
+
if (code >= 4352 && code <= 4447 || code >= 11904 && code <= 12350 || code >= 12352 && code <= 13247 || code >= 13312 && code <= 19903 || code >= 19968 && code <= 42191 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 131072 && code <= 196605 || code >= 196608 && code <= 262141) {
|
|
335
|
+
width += 2;
|
|
336
|
+
} else {
|
|
337
|
+
width += 1;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return width;
|
|
341
|
+
}
|
|
342
|
+
function truncateToWidth(str, maxWidth) {
|
|
343
|
+
let width = 0;
|
|
344
|
+
let i = 0;
|
|
345
|
+
const chars = [...str];
|
|
346
|
+
while (i < chars.length) {
|
|
347
|
+
const charWidth = getDisplayWidth(chars[i]);
|
|
348
|
+
if (width + charWidth > maxWidth - 3) {
|
|
349
|
+
return chars.slice(0, i).join("") + "...";
|
|
350
|
+
}
|
|
351
|
+
width += charWidth;
|
|
352
|
+
i++;
|
|
353
|
+
}
|
|
354
|
+
return str;
|
|
355
|
+
}
|
|
356
|
+
function wrapToWidth(str, maxWidth) {
|
|
357
|
+
const lines = [];
|
|
358
|
+
let current = "";
|
|
359
|
+
let width = 0;
|
|
360
|
+
for (const char of str) {
|
|
361
|
+
const charWidth = getDisplayWidth(char);
|
|
362
|
+
if (width + charWidth > maxWidth && current !== "") {
|
|
363
|
+
lines.push(current);
|
|
364
|
+
current = "";
|
|
365
|
+
width = 0;
|
|
366
|
+
}
|
|
367
|
+
current += char;
|
|
368
|
+
width += charWidth;
|
|
369
|
+
}
|
|
370
|
+
lines.push(current);
|
|
371
|
+
return lines;
|
|
372
|
+
}
|
|
373
|
+
function padToWidth(str, targetWidth) {
|
|
374
|
+
const currentWidth = getDisplayWidth(str);
|
|
375
|
+
const padding = targetWidth - currentWidth;
|
|
376
|
+
return padding > 0 ? str + " ".repeat(padding) : str;
|
|
377
|
+
}
|
|
378
|
+
var CLOUD_WORKTREE_LABEL = "\x1B[36m\u2601 running in cloud\x1B[0m";
|
|
379
|
+
function formatDuration(start, end = /* @__PURE__ */ new Date()) {
|
|
380
|
+
const diffMs = end.getTime() - start.getTime();
|
|
381
|
+
const totalSeconds = Math.floor(diffMs / 1e3);
|
|
382
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
383
|
+
const seconds = totalSeconds % 60;
|
|
384
|
+
return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
|
|
385
|
+
}
|
|
386
|
+
function formatTime(date) {
|
|
387
|
+
const h = String(date.getHours()).padStart(2, "0");
|
|
388
|
+
const m = String(date.getMinutes()).padStart(2, "0");
|
|
389
|
+
const s = String(date.getSeconds()).padStart(2, "0");
|
|
390
|
+
return `${h}:${m}:${s}`;
|
|
391
|
+
}
|
|
392
|
+
function renderBoxTable(headers, groups) {
|
|
393
|
+
const allRows = groups.flat();
|
|
394
|
+
const widths = headers.map(
|
|
395
|
+
(h, i) => Math.max(1, getDisplayWidth(h), ...allRows.map((r) => getDisplayWidth(r[i] ?? "")))
|
|
396
|
+
);
|
|
397
|
+
const border = (l, m, r) => `${l}${widths.map((w) => "\u2500".repeat(w + 2)).join(m)}${r}`;
|
|
398
|
+
const row = (cells) => `\u2502 ${cells.map((c, i) => padToWidth(c ?? "", widths[i])).join(" \u2502 ")} \u2502`;
|
|
399
|
+
const lines = [];
|
|
400
|
+
lines.push(border("\u250C", "\u252C", "\u2510"));
|
|
401
|
+
lines.push(row(headers));
|
|
402
|
+
lines.push(border("\u251C", "\u253C", "\u2524"));
|
|
403
|
+
const nonEmpty = groups.filter((g) => g.length > 0);
|
|
404
|
+
nonEmpty.forEach((group, gi) => {
|
|
405
|
+
if (gi > 0) lines.push(border("\u251C", "\u253C", "\u2524"));
|
|
406
|
+
for (const r of group) lines.push(row(r));
|
|
407
|
+
});
|
|
408
|
+
lines.push(border("\u2514", "\u2534", "\u2518"));
|
|
409
|
+
return lines;
|
|
410
|
+
}
|
|
411
|
+
var TASK_DISPLAY_LIMIT = 20;
|
|
412
|
+
var LOG_DISPLAY_LIMIT = 20;
|
|
413
|
+
function taskRecency(t) {
|
|
414
|
+
return (t.finishedAt ?? t.startedAt).getTime();
|
|
415
|
+
}
|
|
416
|
+
function selectRecentTasks(entries, limit = TASK_DISPLAY_LIMIT) {
|
|
417
|
+
const byId = /* @__PURE__ */ new Map();
|
|
418
|
+
for (const e of entries) byId.set(e.id, e);
|
|
419
|
+
const unique = [...byId.values()];
|
|
420
|
+
const running = unique.filter((t) => t.status === "running").sort((a, b) => taskRecency(b) - taskRecency(a));
|
|
421
|
+
const finished = unique.filter((t) => t.status !== "running").sort((a, b) => taskRecency(b) - taskRecency(a));
|
|
422
|
+
return [...running, ...finished].slice(0, limit);
|
|
423
|
+
}
|
|
424
|
+
function buildTaskTableLines(entries, now = /* @__PURE__ */ new Date()) {
|
|
425
|
+
if (entries.length === 0) return [];
|
|
426
|
+
const runningTasks = entries.filter((t) => t.status === "running");
|
|
427
|
+
const finishedTasks = entries.filter((t) => t.status !== "running");
|
|
428
|
+
const maxTitleWidth = 40;
|
|
429
|
+
const maxPathWidth = 40;
|
|
430
|
+
const truncTitle = (s) => getDisplayWidth(s) > maxTitleWidth ? truncateToWidth(s, maxTitleWidth) : s;
|
|
431
|
+
const truncPath = (p) => p ? getDisplayWidth(p) > maxPathWidth ? truncateToWidth(p, maxPathWidth) : p : "";
|
|
432
|
+
const hasPath = entries.some((t) => truncPath(t.path) !== "");
|
|
433
|
+
const runningRows = runningTasks.map(
|
|
434
|
+
(t) => taskRow(
|
|
435
|
+
t,
|
|
436
|
+
truncTitle(t.title),
|
|
437
|
+
truncPath(t.path),
|
|
438
|
+
hasPath,
|
|
439
|
+
t.agentStatus ? `${t.status}:${t.agentStatus}` : t.status,
|
|
440
|
+
formatTime(t.startedAt),
|
|
441
|
+
formatDuration(t.startedAt, now)
|
|
442
|
+
)
|
|
443
|
+
);
|
|
444
|
+
const finishedRows = finishedTasks.map(
|
|
445
|
+
(t) => taskRow(
|
|
446
|
+
t,
|
|
447
|
+
truncTitle(t.title),
|
|
448
|
+
truncPath(t.path),
|
|
449
|
+
hasPath,
|
|
450
|
+
t.status,
|
|
451
|
+
formatTime(t.finishedAt ?? t.startedAt),
|
|
452
|
+
formatDuration(t.startedAt, t.finishedAt ?? now)
|
|
453
|
+
)
|
|
454
|
+
);
|
|
455
|
+
const headers = hasPath ? ["#", "Title", "Worker", "Worktree", "Status", "Time", "Duration"] : ["#", "Title", "Worker", "Status", "Time", "Duration"];
|
|
456
|
+
return renderBoxTable(headers, [runningRows, finishedRows]);
|
|
457
|
+
}
|
|
458
|
+
function taskRow(t, title, path, hasPath, status, time, duration) {
|
|
459
|
+
return hasPath ? [`#${t.id}`, title, t.workerName, path, status, time, duration] : [`#${t.id}`, title, t.workerName, status, time, duration];
|
|
460
|
+
}
|
|
461
|
+
var logLines = [];
|
|
462
|
+
function pushLogLine(entry) {
|
|
463
|
+
if (entry.text.trim().length === 0) return;
|
|
464
|
+
if (logFileEnabled) appendLogFile(entry);
|
|
465
|
+
logLines.push(entry);
|
|
466
|
+
if (logLines.length > LOG_DISPLAY_LIMIT) {
|
|
467
|
+
logLines.splice(0, logLines.length - LOG_DISPLAY_LIMIT);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
var logFileEnabled = false;
|
|
471
|
+
function logFilePath(time) {
|
|
472
|
+
const y = time.getFullYear();
|
|
473
|
+
const m = String(time.getMonth() + 1).padStart(2, "0");
|
|
474
|
+
const d = String(time.getDate()).padStart(2, "0");
|
|
475
|
+
return join2(getConfigDir(), "logs", `${y}-${m}-${d}.log`);
|
|
476
|
+
}
|
|
477
|
+
function formatLogFileLine(entry) {
|
|
478
|
+
const id = entry.id === void 0 ? "-" : `#${entry.id}`;
|
|
479
|
+
return `${entry.time.toISOString()} ${resolve3(entry.path ?? ".")} ${id} ${entry.stream} ${entry.text.replace(CONTROL_CHARS, "")}
|
|
480
|
+
`;
|
|
481
|
+
}
|
|
482
|
+
var securedLogPath;
|
|
483
|
+
var logFileErrorReported = false;
|
|
484
|
+
function appendLogFile(entry) {
|
|
485
|
+
try {
|
|
486
|
+
const path = logFilePath(entry.time);
|
|
487
|
+
if (path !== securedLogPath) {
|
|
488
|
+
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
489
|
+
chmodSync(dirname(path), 448);
|
|
490
|
+
}
|
|
491
|
+
appendFileSync(path, formatLogFileLine(entry), { mode: 384 });
|
|
492
|
+
if (path !== securedLogPath) {
|
|
493
|
+
chmodSync(path, 384);
|
|
494
|
+
securedLogPath = path;
|
|
495
|
+
}
|
|
496
|
+
} catch (err) {
|
|
497
|
+
if (logFileErrorReported) return;
|
|
498
|
+
logFileErrorReported = true;
|
|
499
|
+
rawError(`[log-file] failed to write debug log: ${err instanceof Error ? err.message : String(err)}`);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
var rawLog = console.log.bind(console);
|
|
503
|
+
var rawClear = console.clear.bind(console);
|
|
504
|
+
var rawError = console.error.bind(console);
|
|
505
|
+
function writeScreen(lines) {
|
|
506
|
+
rawClear();
|
|
507
|
+
rawLog(lines.join("\n"));
|
|
508
|
+
}
|
|
509
|
+
var consoleCaptured = false;
|
|
510
|
+
function captureConsole() {
|
|
511
|
+
if (consoleCaptured) return;
|
|
512
|
+
consoleCaptured = true;
|
|
513
|
+
logFileEnabled = hasDebugFlag();
|
|
514
|
+
process.on("exit", () => {
|
|
515
|
+
if (logLines.length > 0) rawLog(buildLogTableLines(logLines).join("\n"));
|
|
516
|
+
});
|
|
517
|
+
const streams = { log: "stdout", info: "stdout", warn: "stderr", error: "stderr" };
|
|
518
|
+
for (const [method, stream] of Object.entries(streams)) {
|
|
519
|
+
console[method] = (...args) => {
|
|
520
|
+
for (const line of format(...args).split("\n")) {
|
|
521
|
+
pushLogLine({ stream, text: line, time: /* @__PURE__ */ new Date() });
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
var CONTROL_CHARS = (
|
|
527
|
+
// eslint-disable-next-line no-control-regex -- ANSI/制御文字を意図的に対象にする
|
|
528
|
+
/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[0-9;?]*[ -/]*[@-~]|[\x00-\x08\x0b-\x1f\x7f]/g
|
|
529
|
+
);
|
|
530
|
+
function sanitizeLogText(text) {
|
|
531
|
+
return text.replace(CONTROL_CHARS, " ").replace(/\t/g, " ");
|
|
532
|
+
}
|
|
533
|
+
function buildLogTableLines(entries) {
|
|
534
|
+
if (entries.length === 0) return [];
|
|
535
|
+
const maxTextWidth = 100;
|
|
536
|
+
const rows = [...entries].reverse().flatMap(
|
|
537
|
+
(e) => wrapToWidth(sanitizeLogText(e.text), maxTextWidth).map(
|
|
538
|
+
(chunk, i) => i === 0 ? [formatTime(e.time), e.id === void 0 ? "-" : `#${e.id}`, e.stream, chunk] : ["", "", "", chunk]
|
|
539
|
+
)
|
|
540
|
+
);
|
|
541
|
+
return renderBoxTable(["Time", "#", "Stream", "Log"], [rows]);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
export {
|
|
545
|
+
parseProjectFilters,
|
|
546
|
+
hasProjectFilter,
|
|
547
|
+
assertProjectCompatibleCommand,
|
|
548
|
+
hasCloudFlag,
|
|
549
|
+
hasDebugFlag,
|
|
550
|
+
INHERIT_CONFIG_FLAG,
|
|
551
|
+
getInheritConfigPath,
|
|
552
|
+
assertCloudCompatibleCommand,
|
|
553
|
+
buildForwardedCommand,
|
|
554
|
+
DEFAULT_PERMISSION_MODE,
|
|
555
|
+
UserConfigError,
|
|
556
|
+
loadUserConfig,
|
|
557
|
+
getRunMode,
|
|
558
|
+
isAdvisorEnabled,
|
|
559
|
+
getPermissionMode,
|
|
560
|
+
findProjectNameByPath,
|
|
561
|
+
resolveTargetProjects,
|
|
562
|
+
getDisplayWidth,
|
|
563
|
+
truncateToWidth,
|
|
564
|
+
wrapToWidth,
|
|
565
|
+
padToWidth,
|
|
566
|
+
CLOUD_WORKTREE_LABEL,
|
|
567
|
+
TASK_DISPLAY_LIMIT,
|
|
568
|
+
LOG_DISPLAY_LIMIT,
|
|
569
|
+
selectRecentTasks,
|
|
570
|
+
buildTaskTableLines,
|
|
571
|
+
logLines,
|
|
572
|
+
pushLogLine,
|
|
573
|
+
logFilePath,
|
|
574
|
+
formatLogFileLine,
|
|
575
|
+
writeScreen,
|
|
576
|
+
captureConsole,
|
|
577
|
+
CONTROL_CHARS,
|
|
578
|
+
buildLogTableLines
|
|
579
|
+
};
|