@tyhld/conductor 0.12.84 → 0.12.86

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 CHANGED
@@ -73,7 +73,17 @@ read -s -p 'tenant token: ' CONDUCTOR_TOKEN && export CONDUCTOR_TOKEN
73
73
  node dist/cli.js start # もしくは scripts/conductor-start.sh(後述)
74
74
  ```
75
75
 
76
- > 補足: `npm install -g` でのグローバル導入や GitHub Packages での publish 配布は現段階では行いません。clone したリポジトリ内で `node dist/cli.js start`(または補助スクリプト)を使う運用です。
76
+ > 補足: 配布は公開 npm(`@tyhld/conductor`・取得先 `https://registry.npmjs.org`)1本です。各PCの常駐は、入っている配布物(`npm install -g` で入れた物)だけを動かし、clone した作業ツリーは動かしません(`scripts/dist-source.sh`)。
77
+ > - **自動更新が入っているPC(自社PC)**: 版は更新係(`tyhld-autoupdate`)が、管制の決めた版へ段階的にそろえます(先行機 → 自己診断 PASS → 残りのPCへ1台ずつ)。人が `npm install -g` で入れると順番が崩れるので打ちません。入れ方は下の「自社PCの自動更新」。
78
+ > - **自動更新が入っていないPC(お客さまのPC・入れる前の機)**: 人が `npm install -g @tyhld/conductor --@tyhld:registry=https://registry.npmjs.org` で入れ直します。
79
+ >
80
+ > ### 自社PCの自動更新(★自社PCだけ・人が更新の窓で1回)
81
+ > ```bash
82
+ > bash "$(npm root -g)/@tyhld/conductor/scripts/autoupdate-install.sh" # まず確認だけ(書き込みなし)
83
+ > APPLY=1 bash "$(npm root -g)/@tyhld/conductor/scripts/autoupdate-install.sh" # 入れる
84
+ > ~/.tyhld/bin/tyhld-selfcheck --now # 自己診断を1回
85
+ > ```
86
+ > 鍵(`~/.tyhld/bin/autoupdate/enabled`)・更新係と自己診断のタイマー・Claude Code 自身の自動更新を止める設定(`DISABLE_AUTOUPDATER=1`)を置きます。Claude Code が公式推奨の1方式(`curl -fsSL https://claude.ai/install.sh | bash`)だけになっていなければ、人が打つ行を出して止まります。お客さまの導入台本(`sales-template/install.sh`・`setup.sh`)には入っていません。
77
87
 
78
88
  ---
79
89
 
@@ -0,0 +1,62 @@
1
+ /**
2
+ * 自動更新の土台(便 b7bd5e43・設計 3ed9e2b2 の A2)— 「入れ替え待ちの札」と「Claude Code の指名」。
3
+ *
4
+ * ★どちらも【置くのは更新係(次の便 A3)か人】。中継と自己診断はここを読むだけ。
5
+ *
6
+ * 【置き場は ~/.tyhld/bin/autoupdate/(職人が書けない場所)】
7
+ * 設計の案は札を ~/.local/state/conductor/drain、指名を ~/.tyhld/claude-pin に置く形だった。
8
+ * 実機と番人の判定で確かめると(2026-10-02・このPC):
9
+ * ・~/.local/state/conductor/ … 職人の Bash から書けて消せる。番人も止めない。
10
+ * ・~/.tyhld/ の直下 … Write・Edit の道具は番人が止めるが、Bash の touch・rm・
11
+ * リダイレクト・python などは番人が通す(守る場所の一覧
12
+ * GUARD_PLACES に入っているのは ~/.tyhld/hooks と ~/.tyhld/bin だけ)。
13
+ * ・~/.tyhld/bin/ の下 … どの書き方でも番人が止める(下の階層も含む)。
14
+ * 職人が札を【置ける】と、自分で全現場の便を止められる。【外せる】と、入れ替えの途中に
15
+ * 便を入れられ、起こし直しで作業中の職人ごと止まる(試行回数を1つ使う)。
16
+ * ★番人の判定は変えない(この便の決め)ので、もう守られている ~/.tyhld/bin の下に置く。
17
+ * 更新係は systemd の中で動き、番人を通らないので書ける(置き場は更新係と同じ ~/.tyhld/bin)。
18
+ * ★囲い(sandbox)の効いている現場では、どちらの置き場もそもそも書けない(自己診断④で確かめる)。
19
+ *
20
+ * 【試験用の入口】★安全境界ではない(ほかの CONDUCTOR_* と同じ)。
21
+ * CONDUCTOR_DRAIN_FILE … 札の場所を差し替える
22
+ * CONDUCTOR_CLAUDE_PIN_FILE … 指名の場所を差し替える
23
+ */
24
+ import { readFileSync } from 'node:fs';
25
+ import path from 'node:path';
26
+ import { autoupdateDir } from './relay.js';
27
+ // ─────────────────────────────────────────────────────────────────────────────
28
+ // 入れ替え待ちの札
29
+ // ─────────────────────────────────────────────────────────────────────────────
30
+ // ★札を読む本体は relay.ts に置く(relay.ts は試験が src から直接読み込むので、
31
+ // ほかの src を import できない=自分の中で完結させる)。ここは同じ物を見せるだけ。
32
+ export { autoupdateDir, drainFilePath, readDrain } from './relay.js';
33
+ /** 生存報告に載せる形(札が在るときだけ)。 */
34
+ export function drainReport(d, now = Date.now()) {
35
+ if (!d.active)
36
+ return null;
37
+ return {
38
+ drain: true,
39
+ drainSince: d.sinceMs === null ? null : new Date(d.sinceMs).toISOString(),
40
+ drainSec: d.sinceMs === null ? null : Math.max(0, Math.round((now - d.sinceMs) / 1000)),
41
+ };
42
+ }
43
+ // ─────────────────────────────────────────────────────────────────────────────
44
+ // Claude Code の指名
45
+ // ─────────────────────────────────────────────────────────────────────────────
46
+ /** 指名の場所。中身は1行目=claude の実行ファイルの絶対パス(それ以外の行は読まない)。 */
47
+ export function claudePinPath() {
48
+ return process.env.CONDUCTOR_CLAUDE_PIN_FILE || path.join(autoupdateDir(), 'claude-pin');
49
+ }
50
+ /** 指名のファイルの1行目を読む。無ければ undefined、読めなければ null。 */
51
+ export function readClaudePinLine(file = claudePinPath()) {
52
+ let raw;
53
+ try {
54
+ raw = readFileSync(file, 'utf8');
55
+ }
56
+ catch (e) {
57
+ if (e?.code === 'ENOENT')
58
+ return undefined;
59
+ return null;
60
+ }
61
+ return (raw.split('\n')[0] ?? '').trim();
62
+ }