@clubs.place/cli 0.1.25 → 0.1.26
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/.env.example +8 -2
- package/PROVENANCE.json +28 -13
- package/bin/clubs.mjs +196 -16
- package/package.json +1 -1
- package/src/config.mjs +34 -11
- package/src/config.test.mjs +83 -0
- package/src/generator-env.mjs +42 -0
- package/src/generator-env.test.mjs +66 -0
- package/src/generators/hermes.mjs +188 -0
- package/src/generators/hermes.test.mjs +287 -0
- package/src/hermes-cron-script.test.mjs +105 -0
- package/src/hermes-cron.live.test.mjs +97 -0
- package/src/hermes-cron.mjs +421 -0
- package/src/hermes-cron.test.mjs +430 -0
- package/src/init.pipeline.test.mjs +130 -0
- package/src/narrative.hermes.test.mjs +74 -0
- package/src/narrative.mjs +30 -5
- package/src/narrative.targets.test.mjs +26 -0
- package/src/pack.test.mjs +23 -0
- package/src/profiles.mjs +25 -1
- package/src/profiles.test.mjs +15 -0
- package/src/prompt.mjs +1 -0
- package/src/prompt.test.mjs +5 -0
- package/src/sources/hermes.mjs +18 -9
- package/src/sources/hermes.test.mjs +129 -0
- package/src/tick.pipeline.test.mjs +166 -0
- package/src/tick.profiles.test.mjs +131 -0
package/.env.example
CHANGED
|
@@ -12,7 +12,8 @@ EMAIL_TO=mayumi@frame00.com
|
|
|
12
12
|
|
|
13
13
|
# ── 収集元(エージェントごとに1つ) ──────────────────────────
|
|
14
14
|
# claude-code = この端末の Claude Code セッション記録(既定)。名義は必ず Claude
|
|
15
|
-
#
|
|
15
|
+
# hermes = Hermes Agent のセッション記録(~/.hermes/sessions/)。名義は必ず Hermes
|
|
16
|
+
# events-file = エージェントが書き出した JSONL(その他)。docs/events-schema.md
|
|
16
17
|
SOURCE=claude-code,cowork
|
|
17
18
|
# EVENTS_FILE=/path/to/hermes-events.jsonl
|
|
18
19
|
|
|
@@ -43,9 +44,14 @@ TZ_OFFSET_MIN=540
|
|
|
43
44
|
# Gmail は SVG を描画しないので PNG。scripts/gen-email-logo.mjs で書き出す。
|
|
44
45
|
|
|
45
46
|
# ── 本文生成 ─────────────────────────────────────────────
|
|
47
|
+
# 既定は収集元で決まる(hermes を読む設定は hermes、それ以外は claude)。
|
|
48
|
+
# 明示の指定があればそれが勝つ。
|
|
49
|
+
# hermes = `hermes chat`(Hermes 既定モデル・pin なし・追加の API キーなし)
|
|
46
50
|
# claude(既定)= claude -p / openrouter = OpenRouter API /
|
|
47
51
|
# openai-compat = OpenAI互換カスタム(Sakana Fugu 等)/ none = LLM を使わない
|
|
48
|
-
GENERATOR=
|
|
52
|
+
# GENERATOR=hermes
|
|
53
|
+
# hermes が PATH に無い環境(cron/launchd)向け。フルパスで上書き。
|
|
54
|
+
# HERMES_BIN=/Users/you/.local/bin/hermes
|
|
49
55
|
# claude/codex 等の CLI が PATH に無い環境(cron/launchd)向け。フルパスで上書き。
|
|
50
56
|
# CLAUDE_BIN=/Users/you/.local/bin/claude
|
|
51
57
|
# 生成の上限時間(ミリ秒・既定 240000)。大きい日に timeout で fallback に落ちるなら伸ばす。
|
package/PROVENANCE.json
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "@clubs.place/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"repository": "https://github.com/frame00/@clubs.place/cli",
|
|
5
|
-
"commit": "
|
|
5
|
+
"commit": "ae832668418d671d3ce73c963ad1e8491b30f66b",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"generated_at": "2026-10-
|
|
7
|
+
"generated_at": "2026-10-30T01:32:18.659Z",
|
|
8
8
|
"files": {
|
|
9
|
-
".env.example": "sha256-
|
|
10
|
-
"PROVENANCE.json": "sha256-82a47fe5806c0f66050a9b9d8cba39135311988e25b2e0c5c4e56b1252f3778d",
|
|
9
|
+
".env.example": "sha256-09e1f40044c664c2c7f08df245eec356652fa3ba8ddbbbb8966e2307d7a268f9",
|
|
11
10
|
"bin/clubs-nippo": "sha256-03868da2fca1cbd333f2260d08bcb0b14a258a7e7df0da8bfce3e2ed7bee2c62",
|
|
12
11
|
"bin/clubs-nippo-web.mjs": "sha256-23734c47f22cd686c30fd3425b904174ee265ca113a66c34b45916905ff06b4a",
|
|
13
12
|
"bin/clubs-nippo.mjs": "sha256-51102bf7467c7794770c6046305384f2209c450136af3dced39b6cc2a974cff7",
|
|
14
|
-
"bin/clubs.mjs": "sha256-
|
|
13
|
+
"bin/clubs.mjs": "sha256-c32905377a5a4ee39a64726645947291340160576e72dbe90ccb82b7ae5e0561",
|
|
15
14
|
"docs/events-schema.md": "sha256-6be7a614351c2a3dbf15c5201dbf5d466649e4b91d17206c603f0c26b0290256",
|
|
16
15
|
"examples/hermes-events.sample.jsonl": "sha256-bb895a3d5abeeab10a46835af2771d077b71564286a816caf8d73682dbfec9cf",
|
|
17
16
|
"src/aggregate.mjs": "sha256-2b8c9185ee133a115e98e2ed6d05c536e4d9f6cc71e390ceab2fca89f7dc6512",
|
|
@@ -20,25 +19,38 @@
|
|
|
20
19
|
"src/autorun.test.mjs": "sha256-868260443886a4ee29ce259f890f04615d0ce69d651ff9960441a5255f2358cd",
|
|
21
20
|
"src/cards.mjs": "sha256-e301d3039eccbab6efad4a98679de898be75f95dc3542aaab15a1d629191eca5",
|
|
22
21
|
"src/collect.mjs": "sha256-60f9d89d373fd8392008aaf6055815248e76485a61d72afcc6aa0b2b6d84a343",
|
|
23
|
-
"src/config.mjs": "sha256-
|
|
22
|
+
"src/config.mjs": "sha256-eaa186201b3427614865b56705b68fb832cef105ef41b3e574619f27d6445472",
|
|
23
|
+
"src/config.test.mjs": "sha256-267e667da09c6c5b11974d744b782f7fd4f184ec38da557b13abdee77a0bb1fe",
|
|
24
|
+
"src/generator-env.mjs": "sha256-96e4191592f33467c0880395476956d2bd8f23fc2002e49a95783e1e948f12c1",
|
|
25
|
+
"src/generator-env.test.mjs": "sha256-d28b6c3ea900f7910850087ace971ecfb1b8027fe21d7e82981c24ff97f12701",
|
|
24
26
|
"src/generators/claude.mjs": "sha256-8bec1a232bc9c3a04031ce131da0e8aa078c55d640ed0bdb690047b75700004b",
|
|
25
27
|
"src/generators/cli.mjs": "sha256-b7ac838a562d831e08afb96b40878566adbb4207036a4c1b051e386e43524368",
|
|
26
28
|
"src/generators/codex.mjs": "sha256-918d35bb8f07ef89a8796ac156da0c1fb4b376049ae01540369932f171c8860b",
|
|
27
29
|
"src/generators/copilot.mjs": "sha256-236ff8b4aec02499206f53a90eb96358edf8df75d4730c5ea155848ec434baec",
|
|
28
30
|
"src/generators/gemini.mjs": "sha256-edc88b56bf6d9b611f434e591d8f67348f7ec01c40ee7fbeb897fcb3ccf87d16",
|
|
31
|
+
"src/generators/hermes.mjs": "sha256-19317a1eed2b5dca229186b40811867180e79fb4a6265ee30d139e5ff9461c46",
|
|
32
|
+
"src/generators/hermes.test.mjs": "sha256-41a49f22cf9817279f7eeadd86a73fb17705f5730ae018242133e252b5916286",
|
|
29
33
|
"src/generators/openai-compat.mjs": "sha256-cbc896583c6dafd5f59ae3de0af40163d4cdcd5abd11954a02c8bb60277ce6ad",
|
|
30
34
|
"src/generators/openrouter.mjs": "sha256-ab8b0a6398832405fd8e2274148b84500c263162e93106b007e9b4f22a5bcd32",
|
|
35
|
+
"src/hermes-cron-script.test.mjs": "sha256-41e6a73e47e58565c246dd9b32dd76d447842ed17119814e60304ff2b21df89c",
|
|
36
|
+
"src/hermes-cron.live.test.mjs": "sha256-a51eea3aa52127df1584c31c7421d36dcaa44e2be2d101083ed074ba154c2f0e",
|
|
37
|
+
"src/hermes-cron.mjs": "sha256-2c6f6107c5a6d9d3c559abda962d2c128a7376c5ecf0bf5acc8baff0b67601cd",
|
|
38
|
+
"src/hermes-cron.test.mjs": "sha256-504989ef349a4997a4cf6dbc16129eddec32a58286fabaab31effa0f69191eab",
|
|
31
39
|
"src/history.mjs": "sha256-3dd234f2f9cd426f1637e267df0a8151f58bf6be39fdb8d2c6e5af31465b3eb2",
|
|
32
40
|
"src/ingest.mjs": "sha256-737a45e0c1b7f5e93c006efe6f72b47d82836f74527bc6e68d4718c8fa6ca56a",
|
|
41
|
+
"src/init.pipeline.test.mjs": "sha256-8bf7c30d765b895776ddc23f3b306fbfeb233ac6c69432da5ac9079e5e9d05f0",
|
|
33
42
|
"src/lemma.docHash.test.mjs": "sha256-7446ac23bdfbe7e92490134cbe74470266afc1efd176c3e40078d70bd1d1a141",
|
|
34
43
|
"src/lemma.mjs": "sha256-48fdf89267bf09c90eafde180f0fe00fb443ea0dbf87b7dd970689d1fc192675",
|
|
35
44
|
"src/mask.mjs": "sha256-324c47cf78db4596965f0e0f39ff8f63ed794baf8b9f1796de5feb0e69891471",
|
|
36
45
|
"src/narrative.blockRepos.test.mjs": "sha256-0be41c34bf5a2848667dbca9456cd7eac250f19deaec5bd2ba36ae572baf7ad3",
|
|
37
|
-
"src/narrative.mjs": "sha256-
|
|
38
|
-
"src/
|
|
39
|
-
"src/
|
|
40
|
-
"src/
|
|
41
|
-
"src/
|
|
46
|
+
"src/narrative.hermes.test.mjs": "sha256-4025003b26e8d5355edc700d36f6de38104db0b2cb0e7d293659564a44f60f3b",
|
|
47
|
+
"src/narrative.mjs": "sha256-d261977b2ce18a78a481eeb60dbbad1fa19bd74a7384a04d442e439f461f7dcc",
|
|
48
|
+
"src/narrative.targets.test.mjs": "sha256-b64abaa5d1416120e51dc857f6204f95e6198314b65bfcb9de638564b46c057f",
|
|
49
|
+
"src/pack.test.mjs": "sha256-87a269452a346b4e6d48d883685510489de01acadbecc6fa502aa659e6eb870d",
|
|
50
|
+
"src/profiles.mjs": "sha256-a31a60e3c83d97a7809aa60a88a12c224aca9f1e739d81c86a55c363258688be",
|
|
51
|
+
"src/profiles.test.mjs": "sha256-939115f77f403f963de6106716167a58ad37e211bc920ab76e919b93d2d9677b",
|
|
52
|
+
"src/prompt.mjs": "sha256-6c26a9bbb34ed28a3554f5a7c21794c8f2ea1848a2cf056fe76d6780ff0a843c",
|
|
53
|
+
"src/prompt.test.mjs": "sha256-14b08fc0f0deab948e4991478d5433abb364eccb9a6ee6b4215f4e3008c812db",
|
|
42
54
|
"src/remote-config.mjs": "sha256-8988c6dac675775e6e33fd3aeda71d335eb2880bcfe9b5266f78e8a023fe74d6",
|
|
43
55
|
"src/render.mjs": "sha256-ad4e0394a32cea46aa148ef52000c2663c242ef4eb8d2abaf901e589d4c4057b",
|
|
44
56
|
"src/routines.mjs": "sha256-712fd2dc793e7354dd024d0378932301feca4d8df60b95fbaceb2aeaa0e87923",
|
|
@@ -51,9 +63,12 @@
|
|
|
51
63
|
"src/sources/cowork.mjs": "sha256-889d6262101e610e7865f3784a436e40b0698f1d7791170f0bdd01dc26e1458b",
|
|
52
64
|
"src/sources/events-file.mjs": "sha256-d2b51be3d2f29431cfe6521a971cf8445026a1dcf5067bb301813686e3aa08ad",
|
|
53
65
|
"src/sources/gemini-cli.mjs": "sha256-b10bc3813604e5eac5fb80f6173e9d0b00ffde41135512b0f7fd3edc687030da",
|
|
54
|
-
"src/sources/hermes.mjs": "sha256-
|
|
66
|
+
"src/sources/hermes.mjs": "sha256-01ddda06e1ecdcc3184df147e97cd9f3302fb80ba3dd6cce1e39da6eafbe75ce",
|
|
67
|
+
"src/sources/hermes.test.mjs": "sha256-0931afb39b2b571e6cb2f4c7bf8177973de467cb792e4ffa858cf008f7430d9a",
|
|
55
68
|
"src/store.mjs": "sha256-f1f9bd277f3c7de839d364a84c1becb18aba4400e1c600721ac4a478a956997d",
|
|
56
69
|
"src/tasks.mjs": "sha256-cf3f2bdda59de95a296ac3d570b038d1621ed11ace951737c6c161e453f127af",
|
|
70
|
+
"src/tick.pipeline.test.mjs": "sha256-91c349241d79eee5a783a1428d09a137d32cd7fbe9ef570b0bea7f7c02859cb8",
|
|
71
|
+
"src/tick.profiles.test.mjs": "sha256-797e08be2a2af0636658c200fa6f0f4c41d2f79d5354fbe2b4a916f775cc1849",
|
|
57
72
|
"src/tool-name.mjs": "sha256-b012399e2a3d2c937c2142ac7cd5f1bc35e1c947f6381c80a9cffe21be8cd765",
|
|
58
73
|
"templates/report-v3.reference.html": "sha256-c96e03cfac104a5c13c9f38e60c0ff3b027f12f93b1b0de43b6e8be85ef97e09",
|
|
59
74
|
"web/assets/logo-clubs-white.png": "sha256-96a96b6a94443c3407b5ea7def5a1e990da9c206c628125574652c1d35a35aa3",
|
package/bin/clubs.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { join, dirname } from "node:path";
|
|
|
4
4
|
import { platform, homedir } from "node:os";
|
|
5
5
|
import { execSync } from "node:child_process";
|
|
6
6
|
import { createInterface } from "node:readline";
|
|
7
|
-
import { loadConfig, APP_HOME, LOADED_ENV_KEYS } from "../src/config.mjs";
|
|
7
|
+
import { loadConfig, defaultGeneratorForSource, APP_HOME, LOADED_ENV_KEYS } from "../src/config.mjs";
|
|
8
8
|
import {
|
|
9
9
|
CHILD_FLAG,
|
|
10
10
|
chooseInitHome,
|
|
@@ -25,6 +25,17 @@ import { appendHistory, readHistory } from "../src/history.mjs";
|
|
|
25
25
|
import { evaluateCards } from "../src/cards.mjs";
|
|
26
26
|
import { fetchCollectorConfig } from "../src/remote-config.mjs";
|
|
27
27
|
import { tickCommand } from "../src/autorun.mjs";
|
|
28
|
+
import { collectorGeneratorEnv } from "../src/generator-env.mjs";
|
|
29
|
+
import {
|
|
30
|
+
SCHEDULER_ENV,
|
|
31
|
+
installHermesCron,
|
|
32
|
+
removeHermesCron,
|
|
33
|
+
statusHermesCron,
|
|
34
|
+
shouldSkipTick,
|
|
35
|
+
planScheduler,
|
|
36
|
+
markHermesManagedHomes,
|
|
37
|
+
clearHermesManagedHomes,
|
|
38
|
+
} from "../src/hermes-cron.mjs";
|
|
28
39
|
import { localRoutines } from "../src/routines.mjs";
|
|
29
40
|
|
|
30
41
|
/** [Y/n] を聞く。非対話(パイプ)では既定 Yes(自動化を止めない)。 */
|
|
@@ -38,6 +49,16 @@ const promptYesNo = (question) =>
|
|
|
38
49
|
});
|
|
39
50
|
});
|
|
40
51
|
|
|
52
|
+
/** PATH 上のバイナリのフルパス。無ければ null(cron の細い PATH 対策の材料)。 */
|
|
53
|
+
const findBin = (name) => {
|
|
54
|
+
try {
|
|
55
|
+
const out = execSync(`command -v ${name} 2>/dev/null`).toString().trim();
|
|
56
|
+
return out || null;
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
41
62
|
/**
|
|
42
63
|
* 毎朝の自動実行を登録する。macOS=launchd / Windows=schtasks / それ以外=crontab。
|
|
43
64
|
* cron は PATH が細いので npx はフルパスで渡す。既存の clubs-nippo 登録は入れ替える(冪等)。
|
|
@@ -97,6 +118,7 @@ const parseArgs = (argv) => {
|
|
|
97
118
|
else if (arg.startsWith("--agent-name=")) out.agentName = arg.slice(13);
|
|
98
119
|
else if (arg.startsWith("--owner-name=")) out.ownerName = arg.slice(13);
|
|
99
120
|
else if (arg.startsWith("--only=")) out.only = arg.slice(7);
|
|
121
|
+
else if (arg.startsWith("--scheduler=")) out.scheduler = arg.slice(12);
|
|
100
122
|
else if (arg === "--help" || arg === "-h") out.help = true;
|
|
101
123
|
else out._.push(arg);
|
|
102
124
|
}
|
|
@@ -128,6 +150,12 @@ const HELP = `clubs-nippo — Claude Code の実行ログから前日ぶんの A
|
|
|
128
150
|
収集元・生成器)を読み、時刻到達&本日未配信の
|
|
129
151
|
ときだけ配信する。設定はアプリ側が正——
|
|
130
152
|
crontab 行は init が表示する(npx のフルパス入り)
|
|
153
|
+
clubs-nippo hermes-cron install
|
|
154
|
+
Hermes 標準のスケジューラに毎時の実行を登録
|
|
155
|
+
(script-only tick・生成は Hermes 既定モデル)。
|
|
156
|
+
hermes の設定だけ担い、他 AI には触らない
|
|
157
|
+
clubs-nippo hermes-cron status hermes-cron の印とジョブの状態を見る
|
|
158
|
+
clubs-nippo hermes-cron remove hermes-cron を外す(OS 側の行は残る)
|
|
131
159
|
clubs-nippo collect 集計結果を JSON で表示(送信しない)
|
|
132
160
|
clubs-nippo backfill [日数] 実ログから過去ぶんの履歴を作る(既定14日)
|
|
133
161
|
clubs-nippo cards カードを評価して発行(送信はしない)
|
|
@@ -136,6 +164,8 @@ const HELP = `clubs-nippo — Claude Code の実行ログから前日ぶんの A
|
|
|
136
164
|
|
|
137
165
|
収集元:
|
|
138
166
|
--source=claude-code この端末の Claude Code セッション記録(既定)
|
|
167
|
+
--source=hermes Hermes Agent のセッション記録(~/.hermes/sessions/)
|
|
168
|
+
本文生成の既定も hermes(Hermes 既定モデル・pin なし)
|
|
139
169
|
--source=cowork Cowork(Claude のもう1つの面。claude-code と合算可)
|
|
140
170
|
--source=codex-cli Codex CLI(~/.codex/sessions)
|
|
141
171
|
--source=gemini-cli Gemini CLI(指示は logs.json、ツール実行は telemetry
|
|
@@ -294,12 +324,66 @@ const main = async () => {
|
|
|
294
324
|
const npx = join(dirname(process.execPath), "npx");
|
|
295
325
|
const hourlyLine = `0 * * * * ${tickCommand(npx).join(" ")} >> ${join(APP_HOME, "out", "cron.log")} 2>&1`;
|
|
296
326
|
console.log("");
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
327
|
+
// この設定の収集元。init 直後の updates が無ければ書いた .env を読む。
|
|
328
|
+
const initSources = String(
|
|
329
|
+
updates.SOURCE ?? readEnvFile(envPath).SOURCE ?? "claude-code,cowork",
|
|
330
|
+
)
|
|
331
|
+
.split(",")
|
|
332
|
+
.map((s) => s.trim())
|
|
333
|
+
.filter(Boolean);
|
|
334
|
+
const hermesPath = findBin("hermes");
|
|
335
|
+
const plan = argv.scheduler
|
|
336
|
+
? { os: argv.scheduler !== "hermes-cron", hermesCron: argv.scheduler === "hermes-cron" }
|
|
337
|
+
: planScheduler({ sources: initSources, hermesAvailable: Boolean(hermesPath) });
|
|
338
|
+
if (plan.hermesCron) {
|
|
339
|
+
if (!hermesPath) {
|
|
340
|
+
console.log("hermes コマンドが見つからないため、Hermes 標準のスケジューラには登録しません。");
|
|
341
|
+
plan.os = true;
|
|
342
|
+
} else if (await promptYesNo("Hermes 標準のスケジューラ(hermes cron・毎時)に登録しますか? [Y/n]")) {
|
|
343
|
+
try {
|
|
344
|
+
const r = installHermesCron({
|
|
345
|
+
home,
|
|
346
|
+
npxPath: npx,
|
|
347
|
+
hermesBinDir: dirname(hermesPath),
|
|
348
|
+
});
|
|
349
|
+
if (r.ok) {
|
|
350
|
+
const marked = markHermesManagedHomes(APP_HOME, r.job);
|
|
351
|
+
console.log(
|
|
352
|
+
`✓ hermes cron に登録しました(job ${r.job}・${r.created ? "新規" : "既存"})。` +
|
|
353
|
+
`hermes の設定 ${marked.length} 件がこちらで動きます。`,
|
|
354
|
+
);
|
|
355
|
+
} else {
|
|
356
|
+
console.log(`Hermes 側の登録に失敗しました(${r.reason})。OS 側の登録に倒します。`);
|
|
357
|
+
plan.os = true;
|
|
358
|
+
}
|
|
359
|
+
} catch (e) {
|
|
360
|
+
console.log(`Hermes 側の登録に失敗しました(${e?.message ?? e})。OS 側の登録に倒します。`);
|
|
361
|
+
plan.os = true;
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
console.log("Hermes 側の登録はスキップしました。あとで登録するには: npx @clubs.place/cli@latest hermes-cron install");
|
|
365
|
+
}
|
|
366
|
+
} else if (initSources.includes("hermes") && !hermesPath) {
|
|
367
|
+
console.log("注意: hermes の収集元ですが hermes コマンドが見つかりません。本文生成は外れて集計値だけになります(`hermes model` で既定モデルを確認してください)。");
|
|
368
|
+
}
|
|
369
|
+
if (!plan.os) {
|
|
370
|
+
// hermes だけの設定は hermes-cron に一本化。OS 側には重ねない(二重 tick 防止)。
|
|
371
|
+
console.log("OS 側(crontab/launchd)には登録しません(hermes-cron が担います)。");
|
|
372
|
+
console.log(`今すぐ試す(前日分を発行): ${home !== APP_HOME ? `CLUBS_NIPPO_HOME=${home} ` : ""}npx @clubs.place/cli@latest`);
|
|
301
373
|
return;
|
|
302
374
|
}
|
|
375
|
+
if (home !== APP_HOME) {
|
|
376
|
+
// 並べた側。hermes-cron だけの計画なら印+fanout で動くので OS 側は要らない。
|
|
377
|
+
if (!plan.os) {
|
|
378
|
+
console.log("OS 側(crontab/launchd)には登録しません(hermes-cron が担います)。");
|
|
379
|
+
console.log(`今すぐ試す(前日分を発行): CLUBS_NIPPO_HOME=${home} npx @clubs.place/cli@latest`);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
// OS 側が必要な計画なのに「今のものが動かす」とは限らない。hermes だけの
|
|
383
|
+
// 家には OS 登録が無いので、あとから Claude 等を足しても既存登録があるとは
|
|
384
|
+
// 限らない。冪等に入れ替えるので、そのまま OS 側の登録に進む。
|
|
385
|
+
console.log("並べた AI の分も動くよう、OS 側の自動実行を確認・登録します(既存の clubs-nippo 行は入れ替えます)。");
|
|
386
|
+
}
|
|
303
387
|
const yes = await promptYesNo("毎朝の自動実行を登録しますか? [Y/n]");
|
|
304
388
|
if (yes) {
|
|
305
389
|
try {
|
|
@@ -318,6 +402,69 @@ const main = async () => {
|
|
|
318
402
|
return;
|
|
319
403
|
}
|
|
320
404
|
|
|
405
|
+
// ── hermes-cron: Hermes 標準のスケジューラの管理 ────────────────
|
|
406
|
+
// 毎時 script-only tick(`tick --only=hermes`)を回す。生成は中の
|
|
407
|
+
// `hermes chat`(既定モデル・pin なし)が1回だけ行う。実登録は公式
|
|
408
|
+
// `hermes cron` CLI 経由。jobs.json は直接触らない。
|
|
409
|
+
if (argv._[0] === "hermes-cron") {
|
|
410
|
+
const sub = argv._[1] ?? "status";
|
|
411
|
+
if (sub === "install") {
|
|
412
|
+
const hermesPath = findBin("hermes");
|
|
413
|
+
if (!hermesPath) {
|
|
414
|
+
console.log("hermes コマンドが見つかりません。Hermes の入っている端末で実行してください。");
|
|
415
|
+
process.exitCode = 1;
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const r = installHermesCron({
|
|
419
|
+
home: APP_HOME,
|
|
420
|
+
npxPath: join(dirname(process.execPath), "npx"),
|
|
421
|
+
hermesBinDir: dirname(hermesPath),
|
|
422
|
+
});
|
|
423
|
+
if (!r.ok) {
|
|
424
|
+
console.log(`登録できませんでした: ${r.reason}`);
|
|
425
|
+
process.exitCode = 1;
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
const marked = markHermesManagedHomes(APP_HOME, r.job);
|
|
429
|
+
console.log(`hermes cron に登録しました(job ${r.job}・${r.created ? "新規" : "既存"})。`);
|
|
430
|
+
console.log(`hermes の設定 ${marked.length} 件がこちらで動きます:`);
|
|
431
|
+
for (const m of marked) console.log(` ${m}`);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (sub === "remove") {
|
|
435
|
+
const r = removeHermesCron({ home: APP_HOME });
|
|
436
|
+
if (!r.ok) {
|
|
437
|
+
console.log(`外せませんでした: ${r.reason}`);
|
|
438
|
+
process.exitCode = 1;
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
clearHermesManagedHomes(APP_HOME);
|
|
442
|
+
console.log("hermes cron から外し、印を消しました。OS 側(crontab/launchd)の行は残っています。");
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (sub === "status") {
|
|
446
|
+
const homes = [APP_HOME, ...listProfiles(APP_HOME)];
|
|
447
|
+
let bad = 0;
|
|
448
|
+
let found = 0;
|
|
449
|
+
for (const h of homes) {
|
|
450
|
+
const st = statusHermesCron({ home: h });
|
|
451
|
+
if (st.state === "unmanaged") continue;
|
|
452
|
+
found += 1;
|
|
453
|
+
console.log(`${h}: ${st.state}${st.job ? `(job ${st.job})` : ""}`);
|
|
454
|
+
if (st.state === "drift") {
|
|
455
|
+
console.log(` ${st.hint}`);
|
|
456
|
+
bad += 1;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (bad > 0) process.exitCode = 1;
|
|
460
|
+
else if (found === 0) console.log("hermes-cron の印はありません(OS 側の tick が全部動かします)。");
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
console.log("使い方: clubs-nippo hermes-cron [install|remove|status]");
|
|
464
|
+
process.exitCode = 1;
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
|
|
321
468
|
/* 申し送り済みの用件(未完了・原文=同じものは書かせない)と、確定した代表表記。
|
|
322
469
|
どちらも CLUBS 側が持っているので collector-config から受け取り、生成
|
|
323
470
|
プロンプトに載せる。取れなくても生成は続ける(言い回しの揺れが残るだけで、
|
|
@@ -336,6 +483,27 @@ const main = async () => {
|
|
|
336
483
|
// 8時→9時に変えたら翌8時ティックはスキップ・9時ティックで発火。
|
|
337
484
|
// 取得できないティックは何もしない(次のティックで取り戻す)。
|
|
338
485
|
if (argv._[0] === "tick") {
|
|
486
|
+
// hermes-cron 管理の印がある設定は、OS 側(crontab/launchd)の tick では
|
|
487
|
+
// 飛ばす。同じ設定への二重 tick(OS+Hermes)を避けるためのもの。
|
|
488
|
+
// hermes-cron 経由(CLUBS_NIPPO_SCHEDULER=hermes-cron)と手動実行は通す。
|
|
489
|
+
const managed = shouldSkipTick(APP_HOME, { scheduler: process.env[SCHEDULER_ENV] });
|
|
490
|
+
if (managed.skip) {
|
|
491
|
+
console.log(`[tick] ${managed.reason}`);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
// --only は取りに行く前に絞る(ローカルの収集元だけ見る)。hermes-cron の
|
|
495
|
+
// `tick --only=hermes` が root の Claude 設定で fetch しないため。子の分は
|
|
496
|
+
// runAgentsToo の fanout が担うので、ここで止めても動く。
|
|
497
|
+
if (argv.only) {
|
|
498
|
+
const local = String(argv.source ?? process.env.SOURCE ?? "claude-code,cowork")
|
|
499
|
+
.split(",")
|
|
500
|
+
.map((s) => s.trim())
|
|
501
|
+
.filter(Boolean);
|
|
502
|
+
if (!local.includes(argv.only)) {
|
|
503
|
+
console.log(`[tick] --only=${argv.only} のため飛ばします(この設定の収集元: ${local.join(",") || "(未設定)"})`);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
339
507
|
const r = await fetchCollectorConfig();
|
|
340
508
|
if (!r.ok) {
|
|
341
509
|
console.log(`[tick] 設定を取得できませんでした(${r.reason})。次のティックで再試行します。`);
|
|
@@ -359,6 +527,9 @@ const main = async () => {
|
|
|
359
527
|
if (Array.isArray(c.cases)) cases = c.cases;
|
|
360
528
|
if (Array.isArray(c.decided)) decided = c.decided;
|
|
361
529
|
if (Array.isArray(c.routines)) clubRoutines = c.routines;
|
|
530
|
+
// 以降で argv.source をアプリの設定で上書きする前に、ローカルの収集元を
|
|
531
|
+
// 覚えておく(生成器の既定解決はローカルの収集元で決める)。
|
|
532
|
+
const localSource = argv.source ?? process.env.SOURCE ?? "claude-code,cowork";
|
|
362
533
|
const ADAPTERS = SOURCES;
|
|
363
534
|
const mapped = [
|
|
364
535
|
...new Set(
|
|
@@ -384,19 +555,19 @@ const main = async () => {
|
|
|
384
555
|
// claude-code 等のブランド収集元は帰属の規律で名義固定(上書きしない)。
|
|
385
556
|
process.env.AGENT_NAME = c.agentName;
|
|
386
557
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
process.env
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
558
|
+
// アプリの設定で実行内容を上書きする(アプリ=設定の正・秘密はローカルのまま)。
|
|
559
|
+
// モデル pin と API キーは渡さない。不明・旧来の汎用既定の backend は触らず、
|
|
560
|
+
// 収集元に応じた既定(hermes 収集元なら hermes 生成器)に任せる。ローカルで
|
|
561
|
+
// 明示した GENERATOR 系は上書きしない(意図した上書きを壊さない)。
|
|
562
|
+
for (const [key, value] of Object.entries(collectorGeneratorEnv(c.generator, { source: localSource }))) {
|
|
563
|
+
if (process.env[key] === undefined) {
|
|
564
|
+
process.env[key] = value;
|
|
565
|
+
// この tick だけの導出値。子の明示設定や収集元に持ち越さない。
|
|
566
|
+
LOADED_ENV_KEYS.push(key);
|
|
567
|
+
}
|
|
397
568
|
}
|
|
398
569
|
console.log(
|
|
399
|
-
`[tick] 発火: 対象日 ${c.reportDate} / source=${argv.source ?? "(ローカル設定)"} / generator=${process.env.GENERATOR ??
|
|
570
|
+
`[tick] 発火: 対象日 ${c.reportDate} / source=${argv.source ?? "(ローカル設定)"} / generator=${process.env.GENERATOR ?? defaultGeneratorForSource(argv.source)}`,
|
|
400
571
|
);
|
|
401
572
|
argv.tick = true; // 取込失敗時の早期打ち切り・メール未設定スキップに使う
|
|
402
573
|
argv._ = []; // 以降は通常パイプライン(収集→生成→登録→取込→送信→カード)
|
|
@@ -629,11 +800,20 @@ const runAgentsToo = async () => {
|
|
|
629
800
|
if (process.env[CHILD_FLAG]) return;
|
|
630
801
|
const argv = parseArgs(process.argv.slice(2));
|
|
631
802
|
if (argv.help || !(argv._[0] === undefined || argv._[0] === "tick")) return;
|
|
803
|
+
const scheduler = process.env[SCHEDULER_ENV];
|
|
632
804
|
await runProfiles({
|
|
633
805
|
home: APP_HOME,
|
|
634
806
|
args: process.argv.slice(2),
|
|
635
807
|
loadedKeys: LOADED_ENV_KEYS,
|
|
636
808
|
script: fileURLToPath(import.meta.url),
|
|
809
|
+
only: argv.only,
|
|
810
|
+
// hermes-cron 管理の印がある設定は OS 側 tick では回さない(二重 tick 防止)。
|
|
811
|
+
// hermes-cron 経由・手動実行は通す。子プロセス側でも印を見て止める二重化。
|
|
812
|
+
shouldSkip: (dir) => {
|
|
813
|
+
if (argv._[0] !== "tick") return null;
|
|
814
|
+
const s = shouldSkipTick(dir, { scheduler });
|
|
815
|
+
return s.skip ? s.reason : null;
|
|
816
|
+
},
|
|
637
817
|
});
|
|
638
818
|
};
|
|
639
819
|
|
package/package.json
CHANGED
package/src/config.mjs
CHANGED
|
@@ -20,7 +20,7 @@ export const APP_HOME =
|
|
|
20
20
|
: join(homedir(), ".clubs-nippo"));
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* .env
|
|
23
|
+
* .env から読み込んだキーと実行時の導出キー(子プロセスに渡さないため)。
|
|
24
24
|
* 並べた AI を動かすとき、APP_HOME の鍵を引き継がせない。
|
|
25
25
|
*/
|
|
26
26
|
export const LOADED_ENV_KEYS = [];
|
|
@@ -40,6 +40,14 @@ const loadDotEnv = () => {
|
|
|
40
40
|
}
|
|
41
41
|
};
|
|
42
42
|
loadDotEnv();
|
|
43
|
+
// native cron の所有者は既定の home にだけ使う。各 collector の明示パスが
|
|
44
|
+
// 優先し、この既定値も root の .env と同様に子へは持ち越さない。
|
|
45
|
+
if (process.env.HERMES_HOME === undefined &&
|
|
46
|
+
process.env.CLUBS_NIPPO_SCHEDULER === "hermes-cron" &&
|
|
47
|
+
process.env.CLUBS_NIPPO_SCHEDULER_HERMES_HOME) {
|
|
48
|
+
process.env.HERMES_HOME = process.env.CLUBS_NIPPO_SCHEDULER_HERMES_HOME;
|
|
49
|
+
LOADED_ENV_KEYS.push("HERMES_HOME");
|
|
50
|
+
}
|
|
43
51
|
|
|
44
52
|
/** JST 基準の「前日」。TZ_OFFSET は分単位のオフセット(既定 +540 = JST)。 */
|
|
45
53
|
const yesterdayLocal = (offsetMin) => {
|
|
@@ -68,19 +76,33 @@ const parseMaskPatterns = (raw) =>
|
|
|
68
76
|
})
|
|
69
77
|
.filter(Boolean);
|
|
70
78
|
|
|
79
|
+
/** 収集元から本文生成の既定を決める(hermes を読む設定は hermes 生成器)。 */
|
|
80
|
+
export const defaultGeneratorForSource = (source) =>
|
|
81
|
+
String(source ?? "")
|
|
82
|
+
.split(",")
|
|
83
|
+
.map((s) => s.trim())
|
|
84
|
+
.includes("hermes")
|
|
85
|
+
? "hermes"
|
|
86
|
+
: "claude";
|
|
87
|
+
|
|
71
88
|
export const loadConfig = (argv = {}) => {
|
|
72
89
|
const tzOffsetMin = num(process.env.TZ_OFFSET_MIN, 540);
|
|
90
|
+
// 収集元。カンマ区切りで複数指定できる(名義が同じものに限る)。
|
|
91
|
+
// claude-code = この端末の Claude Code セッション記録
|
|
92
|
+
// cowork = Claude デスクトップアプリ(Cowork)のセッション記録
|
|
93
|
+
// hermes = Hermes Agent のセッション記録(~/.hermes/sessions/)
|
|
94
|
+
// events-file = エージェントが書き出した JSONL(汎用)
|
|
95
|
+
// Claude は Code と Cowork の2面を持つ1体なので、既定で両方を合算する。
|
|
96
|
+
const source = argv.source ?? process.env.SOURCE ?? "claude-code,cowork";
|
|
97
|
+
// 本文生成の既定は収集元で決まる。hermes の記録を読む設定で既定 claude の
|
|
98
|
+
// ままにすると、Hermes しかいない端末で毎回 fallback に落ちる。明示の
|
|
99
|
+
// GENERATOR があればそれが常に勝つ(意図した上書きは壊さない)。
|
|
100
|
+
const defaultGenerator = defaultGeneratorForSource(source);
|
|
73
101
|
return {
|
|
74
102
|
date: argv.date ?? process.env.REPORT_DATE ?? yesterdayLocal(tzOffsetMin),
|
|
75
103
|
tzOffsetMin,
|
|
76
104
|
|
|
77
|
-
|
|
78
|
-
// claude-code = この端末の Claude Code セッション記録
|
|
79
|
-
// cowork = Claude デスクトップアプリ(Cowork)のセッション記録
|
|
80
|
-
// hermes = Hermes Agent のセッション記録(~/.hermes/sessions/)
|
|
81
|
-
// events-file = エージェントが書き出した JSONL(汎用)
|
|
82
|
-
// Claude は Code と Cowork の2面を持つ1体なので、既定で両方を合算する。
|
|
83
|
-
source: argv.source ?? process.env.SOURCE ?? "claude-code,cowork",
|
|
105
|
+
source,
|
|
84
106
|
eventsFile: argv.eventsFile ?? process.env.EVENTS_FILE ?? "",
|
|
85
107
|
coworkDir: process.env.COWORK_DIR ?? "",
|
|
86
108
|
|
|
@@ -119,12 +141,13 @@ export const loadConfig = (argv = {}) => {
|
|
|
119
141
|
maskPatterns: parseMaskPatterns(process.env.MASK_PATTERNS),
|
|
120
142
|
|
|
121
143
|
// 本文生成の担い手。収集元と同じサービスに揃えると、1つのサブスクだけで
|
|
122
|
-
// 実行ログ→日報まで完結する(claude-code なら claude、
|
|
123
|
-
// claude / codex / gemini / copilot … その端末の AI CLI をヘッドレスで叩く
|
|
144
|
+
// 実行ログ→日報まで完結する(claude-code なら claude、hermes なら hermes)。
|
|
145
|
+
// claude / codex / gemini / copilot / hermes … その端末の AI CLI をヘッドレスで叩く
|
|
124
146
|
// openrouter = OpenRouter API
|
|
125
147
|
// openai-compat = OpenAI互換カスタム(Sakana Fugu 等)
|
|
126
148
|
// none = LLM を使わない
|
|
127
|
-
|
|
149
|
+
// hermes は `hermes chat`(既定モデル・pin なし・追加の API キーなし)。
|
|
150
|
+
generator: process.env.GENERATOR ?? defaultGenerator,
|
|
128
151
|
openrouter: {
|
|
129
152
|
apiKey: process.env.OPENROUTER_API_KEY ?? "",
|
|
130
153
|
model: process.env.OPENROUTER_MODEL ?? "anthropic/claude-sonnet-4.5",
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { loadConfig } from "./config.mjs";
|
|
4
|
+
|
|
5
|
+
/* 生成器の既定は収集元で決まる。hermes の記録を読む設定なのに既定 claude の
|
|
6
|
+
ままでは、Hermes 端末で毎回 fallback に落ちる。明示の GENERATOR は常に勝つ。 */
|
|
7
|
+
|
|
8
|
+
const KEYS = ["SOURCE", "GENERATOR"];
|
|
9
|
+
const save = () => Object.fromEntries(KEYS.map((k) => [k, process.env[k]]));
|
|
10
|
+
const restore = (s) => {
|
|
11
|
+
for (const k of KEYS) {
|
|
12
|
+
if (s[k] === undefined) delete process.env[k];
|
|
13
|
+
else process.env[k] = s[k];
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
test("既定は claude(従来どおり・回帰防止)", () => {
|
|
18
|
+
const s = save();
|
|
19
|
+
delete process.env.SOURCE;
|
|
20
|
+
delete process.env.GENERATOR;
|
|
21
|
+
try {
|
|
22
|
+
assert.equal(loadConfig({}).generator, "claude");
|
|
23
|
+
assert.equal(loadConfig({ source: "claude-code,cowork" }).generator, "claude");
|
|
24
|
+
} finally {
|
|
25
|
+
restore(s);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("SOURCE=hermes なら既定は hermes(Hermes 既定モデルを使う)", () => {
|
|
30
|
+
const s = save();
|
|
31
|
+
delete process.env.GENERATOR;
|
|
32
|
+
try {
|
|
33
|
+
process.env.SOURCE = "hermes";
|
|
34
|
+
assert.equal(loadConfig({}).generator, "hermes");
|
|
35
|
+
assert.equal(loadConfig({ source: "hermes" }).generator, "hermes");
|
|
36
|
+
} finally {
|
|
37
|
+
restore(s);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("hermes を含む複数収集元でも hermes が既定", () => {
|
|
42
|
+
const s = save();
|
|
43
|
+
delete process.env.GENERATOR;
|
|
44
|
+
try {
|
|
45
|
+
assert.equal(loadConfig({ source: "hermes,events-file" }).generator, "hermes");
|
|
46
|
+
} finally {
|
|
47
|
+
restore(s);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("明示の GENERATOR は収集元より勝つ(意図した上書きを壊さない)", () => {
|
|
52
|
+
const s = save();
|
|
53
|
+
try {
|
|
54
|
+
process.env.SOURCE = "hermes";
|
|
55
|
+
process.env.GENERATOR = "openrouter";
|
|
56
|
+
assert.equal(loadConfig({}).generator, "openrouter");
|
|
57
|
+
process.env.GENERATOR = "none";
|
|
58
|
+
assert.equal(loadConfig({}).generator, "none");
|
|
59
|
+
delete process.env.SOURCE;
|
|
60
|
+
process.env.GENERATOR = "claude";
|
|
61
|
+
assert.equal(loadConfig({ source: "hermes" }).generator, "claude");
|
|
62
|
+
} finally {
|
|
63
|
+
restore(s);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("hermes 以外の収集元は従来の既定(codex-cli 等の既定は変えない)", () => {
|
|
68
|
+
const s = save();
|
|
69
|
+
delete process.env.GENERATOR;
|
|
70
|
+
try {
|
|
71
|
+
assert.equal(loadConfig({ source: "codex-cli" }).generator, "claude");
|
|
72
|
+
assert.equal(loadConfig({ source: "events-file" }).generator, "claude");
|
|
73
|
+
} finally {
|
|
74
|
+
restore(s);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("defaultGeneratorForSource は tick ログと既定を一致させる", async () => {
|
|
79
|
+
const { defaultGeneratorForSource } = await import("./config.mjs");
|
|
80
|
+
assert.equal(defaultGeneratorForSource("hermes"), "hermes");
|
|
81
|
+
assert.equal(defaultGeneratorForSource("claude-code,cowork"), "claude");
|
|
82
|
+
assert.equal(defaultGeneratorForSource(""), "claude");
|
|
83
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* collector-config の `generator.backend` を実行環境(GENERATOR 系 env)に
|
|
3
|
+
* 反映する対応表。アプリ=設定の正だが、秘密(API キー)とモデルの pin は
|
|
4
|
+
* ローカルに置かない——返すのは GENERATOR と、openrouter/openai-compat の
|
|
5
|
+
* モデル・baseUrl だけ。
|
|
6
|
+
*
|
|
7
|
+
* 第2引数の source は適用前のローカル収集元。旧来の汎用既定(claude-cli や
|
|
8
|
+
* モデルなし openrouter/openai-compat)は、hermes 収集元では {} を返す=
|
|
9
|
+
* 何も上書きせず、収集元に応じた既定(hermes 生成器)が効く。黙って claude
|
|
10
|
+
* に落とさない。モデル・baseUrl 付きの明示指定は生かす。
|
|
11
|
+
*
|
|
12
|
+
* 不明・未指定の backend は {} を返す=何も上書きせず、収集元に応じた既定
|
|
13
|
+
* に任せる。
|
|
14
|
+
*/
|
|
15
|
+
const sourceHasHermes = (source) =>
|
|
16
|
+
String(source ?? "")
|
|
17
|
+
.split(",")
|
|
18
|
+
.map((s) => s.trim())
|
|
19
|
+
.includes("hermes");
|
|
20
|
+
|
|
21
|
+
export const collectorGeneratorEnv = (generator, { source } = {}) => {
|
|
22
|
+
const backend = generator?.backend;
|
|
23
|
+
const hermesSource = sourceHasHermes(source);
|
|
24
|
+
if (backend === "hermes-cli") return { GENERATOR: "hermes" };
|
|
25
|
+
if (backend === "claude-cli") return hermesSource ? {} : { GENERATOR: "claude" };
|
|
26
|
+
if (backend === "openrouter") {
|
|
27
|
+
if (!generator.model) return hermesSource ? {} : { GENERATOR: "openrouter" };
|
|
28
|
+
return {
|
|
29
|
+
GENERATOR: "openrouter",
|
|
30
|
+
OPENROUTER_MODEL: generator.model,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (backend === "openai-compat") {
|
|
34
|
+
if (!generator.baseUrl && !generator.model) return hermesSource ? {} : { GENERATOR: "openai-compat" };
|
|
35
|
+
return {
|
|
36
|
+
GENERATOR: "openai-compat",
|
|
37
|
+
...(generator.baseUrl ? { BACKEND_BASE_URL: generator.baseUrl } : {}),
|
|
38
|
+
...(generator.model ? { BACKEND_MODEL: generator.model } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {};
|
|
42
|
+
};
|