claude-task-worker 0.136.0 → 0.138.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/README.md +1 -1
- package/dist/{chunk-NWYH24MB.js → chunk-3WIASXSK.js} +51 -32
- package/dist/gh.d.ts +5 -0
- package/dist/index.js +13 -9
- package/dist/lib.js +1 -1
- package/dist/runcat.d.ts +2 -2
- package/dist/slack.d.ts +20 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -139,7 +139,7 @@ claude-task-worker <command> [--epic <issue-number>]... [--label <label>]... [--
|
|
|
139
139
|
| `install` / `update` | 上記「セットアップ」を参照 |
|
|
140
140
|
| `cloud-setup [--force]` | クラウド VM 側の準備(下記「`--cloud`」を参照) |
|
|
141
141
|
| `list-workers` | プリセットとカスタムの全ワーカーを名前・種別・有効/無効・ロード元で表示(`workerFiles` のロード失敗時は exit 1) |
|
|
142
|
-
| `usage` | Claude API 使用状況(5時間/7日間の利用率とリセット時刻)を表示し、Slack
|
|
142
|
+
| `usage` | Claude API 使用状況(5時間/7日間の利用率とリセット時刻)を表示し、Slack にも通知。以降は360秒(6分)ごとに取得し直して表示・RunCat 用スナップショットを更新し続ける(Ctrl-C で終了。Slack 通知は起動時のみ)。プランによって返らない窓(Ultra の5時間枠など)は省いて表示する |
|
|
143
143
|
| `version` | CLI のバージョンを表示(`--version` / `-v` も可) |
|
|
144
144
|
|
|
145
145
|
### `--epic <issue-number>`
|
|
@@ -361,8 +361,11 @@ async function fetchPRChecks(prNumber) {
|
|
|
361
361
|
if (!output) return [];
|
|
362
362
|
return JSON.parse(output);
|
|
363
363
|
}
|
|
364
|
+
function excludePrsByLabels(prs, excludeLabels) {
|
|
365
|
+
return prs.filter((pr) => !pr.labels.some((l) => excludeLabels.includes(l.name)));
|
|
366
|
+
}
|
|
367
|
+
var PR_LIST_FETCH_LIMIT = 50;
|
|
364
368
|
async function listPullRequestsWithChecks(assignee, label, excludeLabels = [], limit = 5) {
|
|
365
|
-
const search = ["sort:created-asc", ...excludeLabels.map((l) => `-label:"${l}"`)].join(" ");
|
|
366
369
|
const args = [
|
|
367
370
|
"pr",
|
|
368
371
|
"list",
|
|
@@ -371,9 +374,9 @@ async function listPullRequestsWithChecks(assignee, label, excludeLabels = [], l
|
|
|
371
374
|
"--json",
|
|
372
375
|
"number,title,labels,headRefName",
|
|
373
376
|
"--search",
|
|
374
|
-
|
|
377
|
+
"sort:created-asc",
|
|
375
378
|
"--limit",
|
|
376
|
-
String(
|
|
379
|
+
String(PR_LIST_FETCH_LIMIT)
|
|
377
380
|
];
|
|
378
381
|
if (assignee) {
|
|
379
382
|
args.push("--assignee", assignee);
|
|
@@ -382,7 +385,7 @@ async function listPullRequestsWithChecks(assignee, label, excludeLabels = [], l
|
|
|
382
385
|
args.push("--label", label);
|
|
383
386
|
}
|
|
384
387
|
const output = await execGh(args);
|
|
385
|
-
const prs = JSON.parse(output);
|
|
388
|
+
const prs = excludePrsByLabels(JSON.parse(output), excludeLabels).slice(0, limit);
|
|
386
389
|
const withChecks = await Promise.all(prs.map(async (pr) => ({ ...pr, checks: await fetchPRChecks(pr.number) })));
|
|
387
390
|
return withChecks.filter((pr) => isCICompleted(pr.checks));
|
|
388
391
|
}
|
|
@@ -2164,6 +2167,7 @@ function ceilToMinute(date) {
|
|
|
2164
2167
|
return remainder === 0 ? date : new Date(date.getTime() + (MINUTE_MS - remainder));
|
|
2165
2168
|
}
|
|
2166
2169
|
function parseResetsAt(isoString) {
|
|
2170
|
+
if (!isoString) return null;
|
|
2167
2171
|
const date = new Date(isoString);
|
|
2168
2172
|
return Number.isNaN(date.getTime()) ? null : ceilToMinute(date);
|
|
2169
2173
|
}
|
|
@@ -2189,15 +2193,17 @@ function metric(title, pct, resetsAt, now) {
|
|
|
2189
2193
|
};
|
|
2190
2194
|
}
|
|
2191
2195
|
function buildRuncatSnapshot(usage, now = /* @__PURE__ */ new Date()) {
|
|
2192
|
-
const
|
|
2193
|
-
|
|
2196
|
+
const windows = [
|
|
2197
|
+
["5h", usage.fiveHourUtilization, usage.fiveHourResetsAt],
|
|
2198
|
+
["7d", usage.sevenDayUtilization, usage.sevenDayResetsAt]
|
|
2199
|
+
];
|
|
2200
|
+
const present = windows.filter((w) => w[1] !== null);
|
|
2201
|
+
const bar = present.map(([, pct]) => formatG(pct)).join("/");
|
|
2202
|
+
const stamp = present.length > 0 ? resetHour(present[0][2]) : "";
|
|
2194
2203
|
return {
|
|
2195
2204
|
title: "Claude Code",
|
|
2196
2205
|
symbol: "staroflife",
|
|
2197
|
-
metrics: [
|
|
2198
|
-
metric("5h", usage.fiveHourUtilization, usage.fiveHourResetsAt, now),
|
|
2199
|
-
metric("7d", usage.sevenDayUtilization, usage.sevenDayResetsAt, now)
|
|
2200
|
-
],
|
|
2206
|
+
metrics: present.map(([title, pct, resetsAt]) => metric(title, pct, resetsAt, now)),
|
|
2201
2207
|
metricsBarValue: stamp ? `${bar} \u21BB${stamp}` : bar,
|
|
2202
2208
|
lastUpdatedDate: now.toISOString().replace(/\.\d{3}Z$/, "Z")
|
|
2203
2209
|
};
|
|
@@ -2211,7 +2217,10 @@ function writeRuncatUsage(usage, outFile = RUNCAT_OUT_FILE) {
|
|
|
2211
2217
|
writeFileSync3(tmp, JSON.stringify(snapshot), "utf-8");
|
|
2212
2218
|
renameSync2(tmp, outFile);
|
|
2213
2219
|
} catch (err) {
|
|
2214
|
-
|
|
2220
|
+
try {
|
|
2221
|
+
rmSync(tmp, { force: true });
|
|
2222
|
+
} catch {
|
|
2223
|
+
}
|
|
2215
2224
|
console.error(`[runcat] Failed to write ${outFile}: ${err}`);
|
|
2216
2225
|
}
|
|
2217
2226
|
}
|
|
@@ -2220,6 +2229,7 @@ function writeRuncatUsage(usage, outFile = RUNCAT_OUT_FILE) {
|
|
|
2220
2229
|
var execAsync = promisify2(exec);
|
|
2221
2230
|
var WEBHOOK_URL = process.env.CLAUDE_TASK_WORKER_SLACK_WEBHOOK_URL;
|
|
2222
2231
|
var USAGE_CACHE_PATH = "/tmp/claude-usage-cache.json";
|
|
2232
|
+
var USAGE_POLL_INTERVAL_SECONDS = 360;
|
|
2223
2233
|
var USAGE_CACHE_TTL_SECONDS = 360;
|
|
2224
2234
|
async function send(payload) {
|
|
2225
2235
|
if (!WEBHOOK_URL) return;
|
|
@@ -2233,6 +2243,14 @@ async function send(payload) {
|
|
|
2233
2243
|
console.error(`[slack] Failed to send notification: ${err}`);
|
|
2234
2244
|
}
|
|
2235
2245
|
}
|
|
2246
|
+
function parseUsageResponse(body) {
|
|
2247
|
+
return {
|
|
2248
|
+
fiveHourUtilization: body.five_hour?.utilization ?? null,
|
|
2249
|
+
fiveHourResetsAt: body.five_hour?.resets_at ?? null,
|
|
2250
|
+
sevenDayUtilization: body.seven_day?.utilization ?? null,
|
|
2251
|
+
sevenDayResetsAt: body.seven_day?.resets_at ?? null
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2236
2254
|
function extractToken(credentials) {
|
|
2237
2255
|
if (typeof credentials === "string") return credentials;
|
|
2238
2256
|
return credentials.claudeAiOauth?.accessToken ?? credentials.oauth_token ?? credentials.access_token ?? "";
|
|
@@ -2263,8 +2281,8 @@ function writeUsageCache(data) {
|
|
|
2263
2281
|
} catch {
|
|
2264
2282
|
}
|
|
2265
2283
|
}
|
|
2266
|
-
async function fetchUsageInfo() {
|
|
2267
|
-
const cached = readUsageCache();
|
|
2284
|
+
async function fetchUsageInfo(fresh) {
|
|
2285
|
+
const cached = fresh ? null : readUsageCache();
|
|
2268
2286
|
if (cached) return cached;
|
|
2269
2287
|
try {
|
|
2270
2288
|
const token = await getOAuthToken();
|
|
@@ -2278,13 +2296,7 @@ async function fetchUsageInfo() {
|
|
|
2278
2296
|
console.error(`[slack] Usage API returned ${res.status}`);
|
|
2279
2297
|
return null;
|
|
2280
2298
|
}
|
|
2281
|
-
const
|
|
2282
|
-
const data = {
|
|
2283
|
-
fiveHourUtilization: body.five_hour.utilization,
|
|
2284
|
-
fiveHourResetsAt: body.five_hour.resets_at,
|
|
2285
|
-
sevenDayUtilization: body.seven_day.utilization,
|
|
2286
|
-
sevenDayResetsAt: body.seven_day.resets_at
|
|
2287
|
-
};
|
|
2299
|
+
const data = parseUsageResponse(await res.json());
|
|
2288
2300
|
writeUsageCache(data);
|
|
2289
2301
|
return data;
|
|
2290
2302
|
} catch (err) {
|
|
@@ -2307,17 +2319,23 @@ function formatResetTimeJST(isoString) {
|
|
|
2307
2319
|
minute: "2-digit"
|
|
2308
2320
|
});
|
|
2309
2321
|
}
|
|
2322
|
+
function formatWindow(label, utilization, resetsAt) {
|
|
2323
|
+
if (utilization === null) return null;
|
|
2324
|
+
const reset = resetsAt ? ` (reset: ${formatResetTimeJST(resetsAt)})` : "";
|
|
2325
|
+
return `${label}: ${utilization.toFixed(1)}%${reset}`;
|
|
2326
|
+
}
|
|
2310
2327
|
function formatTokenLimitText(usage) {
|
|
2311
|
-
const
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
}
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2328
|
+
const parts = [
|
|
2329
|
+
formatWindow("5h", usage.fiveHourUtilization, usage.fiveHourResetsAt),
|
|
2330
|
+
formatWindow("7d", usage.sevenDayUtilization, usage.sevenDayResetsAt)
|
|
2331
|
+
].filter((p) => p !== null);
|
|
2332
|
+
if (parts.length === 0) return "";
|
|
2333
|
+
const values = [usage.fiveHourUtilization, usage.sevenDayUtilization].filter((v) => v !== null);
|
|
2334
|
+
return ` | ${utilizationEmoji(Math.max(...values))} ${parts.join(" / ")}`;
|
|
2335
|
+
}
|
|
2336
|
+
async function buildTokenLimitText(fresh = false) {
|
|
2337
|
+
const usage = await fetchUsageInfo(fresh);
|
|
2338
|
+
if (!usage) return null;
|
|
2321
2339
|
writeRuncatUsage(usage);
|
|
2322
2340
|
return formatTokenLimitText(usage);
|
|
2323
2341
|
}
|
|
@@ -2345,7 +2363,7 @@ function buildTaskNotificationText(params) {
|
|
|
2345
2363
|
return `${sessionUrlPrefix}${body}`;
|
|
2346
2364
|
}
|
|
2347
2365
|
async function notifyTaskCompleted(workerName, repoName, id, title, url, output, cloud) {
|
|
2348
|
-
const tokenText = await buildTokenLimitText();
|
|
2366
|
+
const tokenText = await buildTokenLimitText() ?? "";
|
|
2349
2367
|
await send({
|
|
2350
2368
|
text: buildTaskNotificationText({
|
|
2351
2369
|
status: "completed",
|
|
@@ -2361,7 +2379,7 @@ async function notifyTaskCompleted(workerName, repoName, id, title, url, output,
|
|
|
2361
2379
|
});
|
|
2362
2380
|
}
|
|
2363
2381
|
async function notifyTaskFailed(workerName, repoName, id, title, url, output, cloud) {
|
|
2364
|
-
const tokenText = await buildTokenLimitText();
|
|
2382
|
+
const tokenText = await buildTokenLimitText() ?? "";
|
|
2365
2383
|
await send({
|
|
2366
2384
|
text: buildTaskNotificationText({
|
|
2367
2385
|
status: "failed",
|
|
@@ -3176,6 +3194,7 @@ export {
|
|
|
3176
3194
|
waitForAllProcesses,
|
|
3177
3195
|
shutdown,
|
|
3178
3196
|
generateWorktreeName,
|
|
3197
|
+
USAGE_POLL_INTERVAL_SECONDS,
|
|
3179
3198
|
send,
|
|
3180
3199
|
buildTokenLimitText,
|
|
3181
3200
|
notifyTaskCompleted,
|
package/dist/gh.d.ts
CHANGED
|
@@ -85,6 +85,11 @@ export interface PullRequestWithChecks extends PullRequest {
|
|
|
85
85
|
checks: PRCheck[];
|
|
86
86
|
}
|
|
87
87
|
export declare function isCICompleted(checks: PRCheck[]): boolean;
|
|
88
|
+
export declare function excludePrsByLabels<T extends {
|
|
89
|
+
labels: {
|
|
90
|
+
name: string;
|
|
91
|
+
}[];
|
|
92
|
+
}>(prs: T[], excludeLabels: string[]): T[];
|
|
88
93
|
export declare function listPullRequestsWithChecks(assignee?: string, label?: string, excludeLabels?: string[], limit?: number): Promise<PullRequestWithChecks[]>;
|
|
89
94
|
export declare function addLabel(type: "issue" | "pr", number: number, label: string): Promise<void>;
|
|
90
95
|
export declare function addAssignee(type: "issue" | "pr", number: number, login: string): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
INHERIT_CONFIG_KEY,
|
|
8
8
|
LOCAL_CONFIG_PATH,
|
|
9
9
|
SCHEDULED_WORKER_NAMES,
|
|
10
|
+
USAGE_POLL_INTERVAL_SECONDS,
|
|
10
11
|
addAssignee,
|
|
11
12
|
addLabel,
|
|
12
13
|
bodyMentionsIssue,
|
|
@@ -64,7 +65,7 @@ import {
|
|
|
64
65
|
shutdown,
|
|
65
66
|
waitForAllProcesses,
|
|
66
67
|
writeLastRun
|
|
67
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-3WIASXSK.js";
|
|
68
69
|
import {
|
|
69
70
|
UserConfigError,
|
|
70
71
|
assertCloudCompatibleCommand,
|
|
@@ -1822,15 +1823,18 @@ if (hasProjectFilter()) {
|
|
|
1822
1823
|
await cloudSetup({ force: process.argv.slice(3).includes("--force") });
|
|
1823
1824
|
})();
|
|
1824
1825
|
} else if (workerType === "usage") {
|
|
1825
|
-
|
|
1826
|
-
const text = await buildTokenLimitText();
|
|
1827
|
-
if (
|
|
1828
|
-
console.error(
|
|
1829
|
-
|
|
1826
|
+
const pollUsage = async (notify) => {
|
|
1827
|
+
const text = await buildTokenLimitText(true);
|
|
1828
|
+
if (text === null) {
|
|
1829
|
+
console.error(`[usage] ${(/* @__PURE__ */ new Date()).toISOString()} Failed to fetch usage info`);
|
|
1830
|
+
return;
|
|
1830
1831
|
}
|
|
1831
|
-
console.log(
|
|
1832
|
-
await send({ text: `\u{1F4CA} Usage${text}` });
|
|
1833
|
-
}
|
|
1832
|
+
console.log(`[usage] ${(/* @__PURE__ */ new Date()).toISOString()}${text}`);
|
|
1833
|
+
if (notify) await send({ text: `\u{1F4CA} Usage${text}` });
|
|
1834
|
+
};
|
|
1835
|
+
void pollUsage(true);
|
|
1836
|
+
setInterval(() => void pollUsage(false), USAGE_POLL_INTERVAL_SECONDS * 1e3);
|
|
1837
|
+
console.log(`[usage] polling every ${USAGE_POLL_INTERVAL_SECONDS}s (Ctrl-C to stop)`);
|
|
1834
1838
|
} else if (workerType === "list-workers") {
|
|
1835
1839
|
for (const e of PRESET_WORKERS) printWorkerRow(e.definition.name, "preset", "preset");
|
|
1836
1840
|
for (const c of customWorkers) printWorkerRow(c.definition.name, "custom", c.source);
|
package/dist/lib.js
CHANGED
package/dist/runcat.d.ts
CHANGED
|
@@ -12,9 +12,9 @@ export interface RuncatSnapshot {
|
|
|
12
12
|
lastUpdatedDate: string;
|
|
13
13
|
}
|
|
14
14
|
/** 'HH:MM' 形式。日を跨ぐ場合は 'MM/DD HH:MM' として日付も付ける。不正値は空文字。 */
|
|
15
|
-
export declare function resetStamp(isoString: string, now?: Date): string;
|
|
15
|
+
export declare function resetStamp(isoString: string | null, now?: Date): string;
|
|
16
16
|
/** 'HH' のみ。バーのように幅が限られる用途向け。不正値は空文字。 */
|
|
17
|
-
export declare function resetHour(isoString: string): string;
|
|
17
|
+
export declare function resetHour(isoString: string | null): string;
|
|
18
18
|
export declare function buildRuncatSnapshot(usage: UsageInfo, now?: Date): RuncatSnapshot;
|
|
19
19
|
/**
|
|
20
20
|
* RunCat Neo 用のスナップショットを書き出す。
|
package/dist/slack.d.ts
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
|
+
/** `usage` コマンドが利用状況をポーリングする間隔 */
|
|
2
|
+
export declare const USAGE_POLL_INTERVAL_SECONDS = 360;
|
|
1
3
|
export declare function send(payload: Record<string, unknown>): Promise<void>;
|
|
2
4
|
export interface UsageInfo {
|
|
3
|
-
fiveHourUtilization: number;
|
|
4
|
-
fiveHourResetsAt: string;
|
|
5
|
-
sevenDayUtilization: number;
|
|
6
|
-
sevenDayResetsAt: string;
|
|
5
|
+
fiveHourUtilization: number | null;
|
|
6
|
+
fiveHourResetsAt: string | null;
|
|
7
|
+
sevenDayUtilization: number | null;
|
|
8
|
+
sevenDayResetsAt: string | null;
|
|
7
9
|
}
|
|
10
|
+
interface UsageWindow {
|
|
11
|
+
utilization?: number | null;
|
|
12
|
+
resets_at?: string | null;
|
|
13
|
+
}
|
|
14
|
+
/** usage API のレスポンスを UsageInfo へ。欠けている窓は null にする(throw しない) */
|
|
15
|
+
export declare function parseUsageResponse(body: {
|
|
16
|
+
five_hour?: UsageWindow | null;
|
|
17
|
+
seven_day?: UsageWindow | null;
|
|
18
|
+
}): UsageInfo;
|
|
19
|
+
/** 返ってきた窓だけを並べる。どちらも無ければ空文字 */
|
|
20
|
+
export declare function formatTokenLimitText(usage: UsageInfo): string;
|
|
8
21
|
/**
|
|
9
22
|
* 現在の利用状況を取得し、RunCat Neo 用スナップショットを更新したうえで通知に添えるテキストを返す。
|
|
10
23
|
* ワーカーの完了/失敗通知はすべてここを通るため、タスクが終わるたびに RunCat 側も最新化される
|
|
11
24
|
* (Slack webhook 未設定でも通知が no-op になるだけで、スナップショットの更新は行われる)。
|
|
25
|
+
* 取得に失敗したときだけ null を返す(どの枠も返らない正常応答は空文字)。
|
|
12
26
|
*/
|
|
13
|
-
export declare function buildTokenLimitText(): Promise<string>;
|
|
27
|
+
export declare function buildTokenLimitText(fresh?: boolean): Promise<string | null>;
|
|
14
28
|
/**
|
|
15
29
|
* 通知本文へ載せる output を末尾1000文字に切り詰める。
|
|
16
30
|
* 案内文(CLOUD_FAILURE_GUIDANCE)は appendCloudFailureGuidance が output の先頭に
|
|
@@ -44,3 +58,4 @@ export declare function notifyTaskFailed(workerName: string, repoName: string, i
|
|
|
44
58
|
sessionId?: string;
|
|
45
59
|
}): Promise<void>;
|
|
46
60
|
export declare function notifyError(workerName: string, repoName: string, error: unknown): Promise<void>;
|
|
61
|
+
export {};
|