@cocorograph/hub-agent 0.7.48 → 0.7.49

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocorograph/hub-agent",
3
- "version": "0.7.48",
3
+ "version": "0.7.49",
4
4
  "description": "Hub Hosted Cockpit のローカル常駐 agent。Hub と outbound WSS で接続し、ローカルの tmux/pty を中継する。",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -20,12 +20,31 @@
20
20
  * もともと config ディレクトリ単位で分離済み (profiles.mjs の hasFileCredentials を使う)。
21
21
  */
22
22
  import { promises as fs } from "node:fs"
23
+ import os from "node:os"
24
+ import path from "node:path"
23
25
  import { createHash } from "node:crypto"
24
26
  import { execFile } from "node:child_process"
25
27
  import { promisify } from "node:util"
26
28
 
27
29
  const execFileP = promisify(execFile)
28
30
 
31
+ /**
32
+ * 既定 (プライマリ) プロファイルで CLAUDE_CONFIG_DIR が未設定のとき、Claude Code は
33
+ * ハッシュ無しの素のサービス名 `Claude Code-credentials` に OAuth トークンを保存する
34
+ * (ハッシュ付き `-<sha256>` を使うのは CLAUDE_CONFIG_DIR を明示した非既定プロファイルだけ)。
35
+ *
36
+ * ⚠️ 2026-07 実バグ: 既定プロファイル (`~/.claude`) を「ハッシュ付き名だけ」で在否
37
+ * チェックしていたため、ログイン済みで実際に使用中でも常に空振りし、Cockpit の
38
+ * アカウント切替 UI に「要ログイン」が出っぱなしになっていた。既定プロファイルは
39
+ * 素のサービス名も候補に含めて判定する。
40
+ */
41
+ export const PLAIN_CREDENTIAL_SERVICE = "Claude Code-credentials"
42
+
43
+ /** 既定プロファイル (CLAUDE_CONFIG_DIR 未設定時) の config ディレクトリ (`~/.claude`)。 */
44
+ function defaultConfigDir() {
45
+ return path.join(os.homedir(), ".claude")
46
+ }
47
+
29
48
  /** Keychain を使うプラットフォームか (認証分離が Keychain 依存なのは darwin のみ)。 */
30
49
  export function isKeychainPlatform(platform = process.platform) {
31
50
  return platform === "darwin"
@@ -76,6 +95,11 @@ export async function existsGenericPassword(service, opts = {}) {
76
95
  * symlink パスと realpath のどちらでハッシュを計算するかに依存しないよう、両方の
77
96
  * ハッシュ候補を在否チェックし、いずれか存在すればログイン済みとみなす。
78
97
  *
98
+ * さらに既定プロファイル (~/.claude、CLAUDE_CONFIG_DIR 未設定) のときは、Claude Code が
99
+ * ハッシュ無しの素の `Claude Code-credentials` に保存するため、そのサービス名も候補に
100
+ * 含める (PLAIN_CREDENTIAL_SERVICE のコメント参照)。含めないと既定プロファイルが常に
101
+ * 空振りして「要ログイン」が出っぱなしになる。
102
+ *
79
103
  * @param {string} configDir
80
104
  * @param {{execFileP?: Function}} [opts]
81
105
  * @returns {Promise<boolean>}
@@ -87,9 +111,28 @@ export async function profileHasKeychainCredentials(configDir, opts = {}) {
87
111
  } catch {
88
112
  /* realpath 不能 (ディレクトリ未作成等) は無視して literal パスのみ評価 */
89
113
  }
90
- for (const dir of candidates) {
114
+
115
+ // チェックするサービス名を組み立てる。各 configDir 候補のハッシュ付き名に加え、
116
+ // 既定プロファイル (~/.claude、CLAUDE_CONFIG_DIR 未設定) のときだけ素のサービス名を足す。
117
+ const services = new Set()
118
+ for (const dir of candidates) services.add(credentialService(dir))
119
+
120
+ // 既定プロファイル判定: configDir 候補 (literal / realpath) のいずれかが
121
+ // ~/.claude (literal / realpath) に一致するか。iCloud 等への symlink でも拾える。
122
+ const defaultCandidates = new Set([defaultConfigDir()])
123
+ try {
124
+ defaultCandidates.add(await fs.realpath(defaultConfigDir()))
125
+ } catch {
126
+ /* ~/.claude 未作成なら literal のみで判定 */
127
+ }
128
+ const isDefaultProfile = [...candidates].some((dir) =>
129
+ defaultCandidates.has(dir),
130
+ )
131
+ if (isDefaultProfile) services.add(PLAIN_CREDENTIAL_SERVICE)
132
+
133
+ for (const service of services) {
91
134
  try {
92
- if (await existsGenericPassword(credentialService(dir), opts)) return true
135
+ if (await existsGenericPassword(service, opts)) return true
93
136
  } catch {
94
137
  /* security 実行エラーは「不明」→ 存在しない寄せ */
95
138
  }