peertable 0.8.18 → 0.8.19
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": "peertable",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.19",
|
|
4
4
|
"description": "A round table of peer agents. No orchestrator at the head. Turn Claude Code, Codex, and Grok sessions into a team of equal, long-lived peers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/room/client.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import { findModelsDoc, resolveSeatIdentity } from '../skill/scripts/resolve-sea
|
|
|
13
13
|
|
|
14
14
|
// client.mjs 側のハードコード版数。package.json の version と一致していることを
|
|
15
15
|
// diagnostics の version_consistency が見る(2 つの版数源の drift 検出。決定45)
|
|
16
|
-
const MCP_VERSION = '0.8.
|
|
16
|
+
const MCP_VERSION = '0.8.19'
|
|
17
17
|
const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
18
18
|
|
|
19
19
|
const USAGE = `usage:
|
|
@@ -22,6 +22,7 @@ import { promisify } from 'node:util'
|
|
|
22
22
|
|
|
23
23
|
import { resolvePostToken } from './seat-usage.mjs'
|
|
24
24
|
import { observePidCommand } from './seat-identity.mjs'
|
|
25
|
+
import { updateBridgeProgress } from './bridge-record-live.mjs'
|
|
25
26
|
|
|
26
27
|
const run = promisify(execFile)
|
|
27
28
|
const [proj, flag] = process.argv.slice(2)
|
|
@@ -57,9 +58,10 @@ if (!token) { console.error('ALARM_BRIDGE_TOKEN_MISSING: 書込トークンが
|
|
|
57
58
|
mkdirSync(alarmsDir, { recursive: true })
|
|
58
59
|
|
|
59
60
|
const self = observePidCommand(process.pid)
|
|
61
|
+
const startedAt = new Date().toISOString()
|
|
60
62
|
writeFileSync(record, JSON.stringify({
|
|
61
63
|
pid: process.pid, start_identity: self.started_identity, room, server_url: url,
|
|
62
|
-
started_at:
|
|
64
|
+
started_at: startedAt, ready_at: startedAt, last_progress_at: startedAt,
|
|
63
65
|
}) + '\n')
|
|
64
66
|
const cleanup = () => { try { unlinkSync(record) } catch {} process.exit(0) }
|
|
65
67
|
process.on('SIGTERM', cleanup)
|
|
@@ -117,5 +119,6 @@ async function tick() {
|
|
|
117
119
|
log(`alarm-bridge start: room=${room} project=${proj} pid=${process.pid}`)
|
|
118
120
|
for (;;) {
|
|
119
121
|
try { await tick() } catch (e) { log(`tick 失敗(続行): ${e.message}`) }
|
|
122
|
+
updateBridgeProgress(record)
|
|
120
123
|
await sleep(2000)
|
|
121
124
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Windows は pid を再利用する。alive な pid だけでは本人ではない
|
|
4
4
|
// (2026-08-21 実測: wakeup-bridge.json の pid が oracle-mcp に再利用され、
|
|
5
5
|
// 本物の配達は死んだまま ensure-bridge が起動済みと誤認した)。
|
|
6
|
-
import { readFileSync } from 'node:fs'
|
|
6
|
+
import { readFileSync, renameSync, writeFileSync } from 'node:fs'
|
|
7
7
|
import { resolve } from 'node:path'
|
|
8
8
|
import { fileURLToPath } from 'node:url'
|
|
9
9
|
|
|
@@ -21,6 +21,13 @@ export function bridgeRecordLive(record, now = Date.now()) {
|
|
|
21
21
|
return (now - at) <= STALE_MS
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export function updateBridgeProgress(path, now = new Date()) {
|
|
25
|
+
const record = JSON.parse(readFileSync(path, 'utf8'))
|
|
26
|
+
const temporary = `${path}.${process.pid}.tmp`
|
|
27
|
+
writeFileSync(temporary, JSON.stringify({ ...record, last_progress_at: now.toISOString() }) + '\n')
|
|
28
|
+
renameSync(temporary, path)
|
|
29
|
+
}
|
|
30
|
+
|
|
24
31
|
const invokedDirectly = Boolean(process.argv[1])
|
|
25
32
|
&& resolve(fileURLToPath(import.meta.url)).toLowerCase() === resolve(process.argv[1]).toLowerCase()
|
|
26
33
|
if (invokedDirectly) {
|
|
@@ -25,6 +25,10 @@ fi
|
|
|
25
25
|
sock=$(node "$(dirname "$0")/tmux-socket.mjs")
|
|
26
26
|
room=$(node -e 'process.stdout.write(require(process.argv[1]).room)' "$team/setup-state.json")
|
|
27
27
|
session="peertable-${name}-${room}"
|
|
28
|
+
# **session が在るだけでは常駐が生きている証拠にならない**(中の node だけ死んで殻が残る)。
|
|
29
|
+
# pid/進捗の生存確認を通れずここへ来たsessionは、Windowsでlogを開いたままの可能性がある。
|
|
30
|
+
# log切詰めより先に畳み、EBUSYを発生させない。
|
|
31
|
+
tmux_at kill-session -t "$session" 2>/dev/null || true
|
|
28
32
|
# **死んだ記録をここで消す。** 残すと下の loop が前回の `ready_at` を読んで、
|
|
29
33
|
# 新しい bridge が1バイトも動いていない段階で success を返す——「起動していないのに
|
|
30
34
|
# 起動したと言う」=この supervisor が塞ぐはずの穴そのものになる。
|
|
@@ -41,9 +45,6 @@ for v in PEERTABLE_TMUX_SOCKET PEERTABLE_POST_TOKEN PEERTABLE_CREDENTIAL_FILE PE
|
|
|
41
45
|
eval "val=\${$v:-}"
|
|
42
46
|
[ -n "$val" ] && env_prefix="$env_prefix $v=$(printf '%q' "$val")"
|
|
43
47
|
done
|
|
44
|
-
# **session が在るだけでは常駐が生きている証拠にならない**(中の node だけ死んで殻が残る)。
|
|
45
|
-
# 上で pid 生存を確かめて here まで来た=生きていないので、殻は畳んでから立て直す。
|
|
46
|
-
tmux_at kill-session -t "$session" 2>/dev/null || true
|
|
47
48
|
if [ "$(node -p 'process.platform')" = "win32" ]; then
|
|
48
49
|
# psmuxが起動する正規shellはPowerShell 7。POSIXの`env`とMSYS pathを文字列のまま
|
|
49
50
|
# 渡すとnodeへ到達する前に終了するため、Windows nodeに各pathをargvとして変換させ、
|
|
@@ -28,6 +28,7 @@ import { existsSync, readFileSync, renameSync, writeFileSync, unlinkSync } from
|
|
|
28
28
|
import { join } from 'node:path'
|
|
29
29
|
|
|
30
30
|
import { STOP_DECLARATION, attributeJobSession, combineSeatLamp, isPaneHarnessMissing, hasActiveDescendant, subtreeCpuSeconds, patrolTargets, classifyPaneTail, decideBridgeContinuation, deriveMissingSession, isPaneProcessStopped, parsePaneTokenHint, resolveLatticeExecutable, resolvePostToken, resolveSeatObservation, resolveTmuxSocket, supportsMemberObservation, tmuxArgv, tmuxPanePid } from './seat-usage.mjs'
|
|
31
|
+
import { updateBridgeProgress } from './bridge-record-live.mjs'
|
|
31
32
|
|
|
32
33
|
const args = process.argv.slice(2)
|
|
33
34
|
const proj = args[0]
|
|
@@ -381,7 +382,8 @@ function recordReady() {
|
|
|
381
382
|
readyRecorded = true
|
|
382
383
|
const record = JSON.parse(readFileSync(pidPath, 'utf8'))
|
|
383
384
|
const temporary = `${pidPath}.${process.pid}.tmp`
|
|
384
|
-
|
|
385
|
+
const readyAt = new Date().toISOString()
|
|
386
|
+
writeFileSync(temporary, JSON.stringify({ ...record, ready_at: readyAt, last_progress_at: readyAt }) + '\n')
|
|
385
387
|
renameSync(temporary, pidPath)
|
|
386
388
|
}
|
|
387
389
|
|
|
@@ -484,6 +486,7 @@ let provenWritable = false
|
|
|
484
486
|
async function guardedTick() {
|
|
485
487
|
await beatBridge()
|
|
486
488
|
const { attempted, failed } = await tick() ?? NOTHING_ATTEMPTED
|
|
489
|
+
updateBridgeProgress(pidPath)
|
|
487
490
|
const decided = decideBridgeContinuation({ attempted, failed, provenWritable, failedTicks, limit: FAILED_TICK_LIMIT })
|
|
488
491
|
provenWritable = decided.provenWritable
|
|
489
492
|
failedTicks = decided.failedTicks
|