@cocorograph/hub-agent 0.8.57 → 0.8.58
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/package.json +1 -1
- package/src/main.mjs +5 -3
- package/src/profiles.mjs +36 -36
package/package.json
CHANGED
package/src/main.mjs
CHANGED
|
@@ -520,9 +520,11 @@ export async function syncTmuxCodexEnv(profile, log) {
|
|
|
520
520
|
* bundle hooks (cockpit_permission_bridge 等) を反映させる。
|
|
521
521
|
*
|
|
522
522
|
* 新規プロファイル追加 (profile.add) 直後に呼ぶ。provisionProfile は
|
|
523
|
-
* `~/.claude/settings.json`
|
|
524
|
-
*
|
|
525
|
-
*
|
|
523
|
+
* `~/.claude/settings.json` への symlink を新プロファイルへ張る (コピーではない) ので、
|
|
524
|
+
* 正本が最新ならフックは自動的に揃う。ただし正本自体が古い / setup 未走のタイミングだと
|
|
525
|
+
* 新プロファイルの初回セッションでもフックが欠けうるため、1 回叩いて正本を最新化する。
|
|
526
|
+
* 旧版がコピーで置いた実体 settings.json が残るプロファイルにも、setup_hub_ai は
|
|
527
|
+
* 全プロファイル対応 (案1) なので同じ 1 回で反映される。
|
|
526
528
|
*
|
|
527
529
|
* 失敗・未 setup 環境 (スクリプト不在 / python3 無し) でも **例外を投げない**。detached +
|
|
528
530
|
* stdio ignore で daemon をブロックしない。session_start.py の auto-update と同じ
|
package/src/profiles.mjs
CHANGED
|
@@ -4,8 +4,12 @@
|
|
|
4
4
|
* 1 台のマシンで複数の Claude アカウント (例: 個人 Max / チーム) を `CLAUDE_CONFIG_DIR`
|
|
5
5
|
* で切り替えて使うための仕組み。各プロファイルは独立した config ディレクトリを持ち、
|
|
6
6
|
* 認証 (`.credentials.json` / Keychain) と `.claude.json` をアカウント別に保持する。一方で
|
|
7
|
-
* skills / agents / commands / scripts / hooks / CLAUDE.md は symlink
|
|
8
|
-
*
|
|
7
|
+
* skills / agents / commands / scripts / hooks / CLAUDE.md / settings.json は symlink で
|
|
8
|
+
* 共有する (provisionProfile が面倒を見る)。settings.json はプロファイル間で共有扱いとし、
|
|
9
|
+
* アカウント別の上書きはサポートしない — つまり forceLoginOrgUUID / apiKeyHelper /
|
|
10
|
+
* env.ANTHROPIC_* のようなアカウント固有の値を settings.json に置くと全プロファイルへ効く
|
|
11
|
+
* (経緯と却下案は Hub ナレッジ
|
|
12
|
+
* `ナレッジ/Cockpit/自動コンパクションとPreCompactフック` の「プロファイル間の設定ドリフト」節)。
|
|
9
13
|
*
|
|
10
14
|
* 会話履歴 (`projects/`) は **既定 `~/.claude/projects` に集約して全プロファイルで共有する**
|
|
11
15
|
* (ensureSharedProjects が `<configDir>/projects` を既定への symlink にする)。同じ作業
|
|
@@ -312,7 +316,7 @@ function nextNumericId(profiles) {
|
|
|
312
316
|
|
|
313
317
|
/**
|
|
314
318
|
* プロファイルを新規追加する (連番 id・中立パス `~/.claude-<n>`)。
|
|
315
|
-
* ディレクトリ整備 (symlink
|
|
319
|
+
* ディレクトリ整備 (共有資産の symlink) は provisionProfile が行う。
|
|
316
320
|
* 追加後もログインは未了 (ユーザーが端末で `/login` する必要がある)。
|
|
317
321
|
*
|
|
318
322
|
* @param {{label?: string}} [opts]
|
|
@@ -383,7 +387,8 @@ export async function hasFileCredentials(configDir) {
|
|
|
383
387
|
}
|
|
384
388
|
}
|
|
385
389
|
|
|
386
|
-
/** 既定プロファイルから共有 symlink
|
|
390
|
+
/** 既定プロファイルから共有 symlink する資産。settings.json の書き手 (Claude Code 本体 /
|
|
391
|
+
* setup_hub_ai.py の hooks auto-merge) は symlink 越しに正本 `~/.claude/settings.json` を更新する。 */
|
|
387
392
|
export const SHARED_SYMLINK_ITEMS = [
|
|
388
393
|
"agents",
|
|
389
394
|
"skills",
|
|
@@ -391,11 +396,9 @@ export const SHARED_SYMLINK_ITEMS = [
|
|
|
391
396
|
"scripts",
|
|
392
397
|
"hooks",
|
|
393
398
|
"CLAUDE.md",
|
|
399
|
+
"settings.json",
|
|
394
400
|
]
|
|
395
401
|
|
|
396
|
-
/** コピーで共有する資産 (アプリが書き換えるため symlink 不可)。 */
|
|
397
|
-
export const SHARED_COPY_ITEMS = ["settings.json"]
|
|
398
|
-
|
|
399
402
|
/**
|
|
400
403
|
* 会話履歴 (`projects/`) を既定 `~/.claude/projects` に集約・共有する。
|
|
401
404
|
*
|
|
@@ -459,18 +462,17 @@ export async function ensureSharedProjects(configDir) {
|
|
|
459
462
|
|
|
460
463
|
/**
|
|
461
464
|
* プロファイルディレクトリを整備する。
|
|
462
|
-
* - 共有 symlink 資産: 既定 `~/.claude/<item>` への絶対 symlink
|
|
463
|
-
*
|
|
465
|
+
* - 共有 symlink 資産: 既定 `~/.claude/<item>` への絶対 symlink を張る。既存 (symlink でも
|
|
466
|
+
* 実体でも) があれば触らない — 旧版がコピーで置いた settings.json も同じ規則で残る。
|
|
464
467
|
* 認証・履歴・`.claude.json` には一切触れない (アカウント固有のため)。
|
|
465
468
|
*
|
|
466
469
|
* @param {string} configDir - 対象プロファイルの config ディレクトリ絶対パス
|
|
467
|
-
* @returns {Promise<{linked: string[],
|
|
470
|
+
* @returns {Promise<{linked: string[], skipped: string[]}>}
|
|
468
471
|
*/
|
|
469
472
|
export async function provisionProfile(configDir) {
|
|
470
473
|
const base = defaultConfigDir()
|
|
471
474
|
await fs.mkdir(configDir, { recursive: true, mode: 0o700 })
|
|
472
475
|
const linked = []
|
|
473
|
-
const copied = []
|
|
474
476
|
const skipped = []
|
|
475
477
|
|
|
476
478
|
for (const item of SHARED_SYMLINK_ITEMS) {
|
|
@@ -479,8 +481,19 @@ export async function provisionProfile(configDir) {
|
|
|
479
481
|
try {
|
|
480
482
|
await fs.access(src)
|
|
481
483
|
} catch {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
+
// settings.json だけは正本を空 JSON で作ってから張る。ここで skip すると、直後の
|
|
485
|
+
// setup_hub_ai (hooks auto-merge) がプロファイル側に実体を作り、以後 provisionProfile が
|
|
486
|
+
// 「既存」として触らないため、コピー時代と同じドリフトが復活する。
|
|
487
|
+
// 既定側が壊れた symlink のときは書き込み先が分からないので触らない。
|
|
488
|
+
if (item !== "settings.json" || (await isBrokenSymlink(src))) {
|
|
489
|
+
skipped.push(`${item}(元無し)`)
|
|
490
|
+
continue
|
|
491
|
+
}
|
|
492
|
+
// 既定ディレクトリ自体が無い端末 (Claude Code 未起動) でも正本を作れるようにする。
|
|
493
|
+
await fs.mkdir(base, { recursive: true, mode: 0o700 })
|
|
494
|
+
await fs.writeFile(src, "{}\n", { flag: "wx" }).catch((err) => {
|
|
495
|
+
if (err?.code !== "EEXIST") throw err
|
|
496
|
+
})
|
|
484
497
|
}
|
|
485
498
|
// dst が既に存在 (symlink/実体) なら触らない。
|
|
486
499
|
let exists = false
|
|
@@ -498,28 +511,6 @@ export async function provisionProfile(configDir) {
|
|
|
498
511
|
linked.push(item)
|
|
499
512
|
}
|
|
500
513
|
|
|
501
|
-
for (const item of SHARED_COPY_ITEMS) {
|
|
502
|
-
const src = path.join(base, item)
|
|
503
|
-
const dst = path.join(configDir, item)
|
|
504
|
-
let dstExists = false
|
|
505
|
-
try {
|
|
506
|
-
await fs.lstat(dst)
|
|
507
|
-
dstExists = true
|
|
508
|
-
} catch {
|
|
509
|
-
/* not exists */
|
|
510
|
-
}
|
|
511
|
-
if (dstExists) {
|
|
512
|
-
skipped.push(`${item}(既存)`)
|
|
513
|
-
continue
|
|
514
|
-
}
|
|
515
|
-
try {
|
|
516
|
-
await fs.copyFile(src, dst)
|
|
517
|
-
copied.push(item)
|
|
518
|
-
} catch {
|
|
519
|
-
skipped.push(`${item}(元無し)`)
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
514
|
// 会話履歴は既定 ~/.claude/projects へ集約して共有する (アカウント間で会話
|
|
524
515
|
// コンテキストを引き継げるようにするため)。新規は素直に symlink、既存実体は
|
|
525
516
|
// 無損失マージしてから symlink 化する。
|
|
@@ -531,7 +522,16 @@ export async function provisionProfile(configDir) {
|
|
|
531
522
|
skipped.push(`projects(失敗:${err?.code || err?.message || "error"})`)
|
|
532
523
|
}
|
|
533
524
|
|
|
534
|
-
return { linked,
|
|
525
|
+
return { linked, skipped }
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/** `p` が lstat できるのに access できない (= リンク先を失った symlink) なら true。 */
|
|
529
|
+
async function isBrokenSymlink(p) {
|
|
530
|
+
try {
|
|
531
|
+
return (await fs.lstat(p)).isSymbolicLink()
|
|
532
|
+
} catch {
|
|
533
|
+
return false
|
|
534
|
+
}
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
/** テスト用: モジュールキャッシュを破棄する。 */
|