peertable 0.8.1 → 0.8.3

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.1",
3
+ "version": "0.8.3",
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.1'
16
+ const MCP_VERSION = '0.8.3'
17
17
  const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
18
18
 
19
19
  const USAGE = `usage:
@@ -0,0 +1,28 @@
1
+ import { execFileSync } from 'node:child_process'
2
+
3
+ export function parseWindowsCreationDate(value) {
4
+ if (value instanceof Date) {
5
+ if (Number.isNaN(value.getTime())) throw new Error('pid の CreationDate を解釈できない')
6
+ return value.toISOString()
7
+ }
8
+ if (typeof value === 'number' && Number.isFinite(value)) return new Date(value).toISOString()
9
+ const text = String(value ?? '').trim()
10
+ const microsoft = /^\/Date\((-?\d+)(?:[+-]\d+)?\)\/$/u.exec(text)
11
+ if (microsoft) return new Date(Number(microsoft[1])).toISOString()
12
+ const normalized = text.replace(/(\.\d{3})\d+(?=[+-]\d{2}:\d{2}$)/u, '$1')
13
+ const parsed = new Date(normalized)
14
+ if (Number.isNaN(parsed.getTime())) throw new Error(`pid の CreationDate を解釈できない: ${text}`)
15
+ return parsed.toISOString()
16
+ }
17
+
18
+ export function observeWindowsPidCommand(pid, hashArgv) {
19
+ const filter = `ProcessId=${pid}`
20
+ const script = `Get-CimInstance Win32_Process -Filter ${JSON.stringify(filter)} | Select-Object ProcessId,CreationDate,CommandLine | ConvertTo-Json -Compress`
21
+ const output = execFileSync('powershell.exe', ['-NoProfile', '-Command', script], { encoding: 'utf8' }).trim()
22
+ if (!output) throw new Error('pid のWin32_Processを観測できない')
23
+ const row = JSON.parse(output)
24
+ const started = parseWindowsCreationDate(row.CreationDate)
25
+ const argv = String(row.CommandLine || '').trim()
26
+ if (!started || !argv) throw new Error('pid のCreationDate/CommandLineを観測できない')
27
+ return { pid: Number(row.ProcessId), started_identity: started, argv, argv_digest: hashArgv(argv) }
28
+ }
@@ -0,0 +1,9 @@
1
+ export function resolveWindowsLatticeCommand(cli, exists) {
2
+ const lower = cli.toLowerCase()
3
+ const shim = lower.endsWith('.cmd') || lower.endsWith('.bat') || lower.endsWith('.exe')
4
+ ? cli
5
+ : exists(`${cli}.cmd`)
6
+ ? `${cli}.cmd`
7
+ : cli
8
+ return { command: process.env.ComSpec || 'cmd.exe', argv: ['/d', '/c', shim, 'todo', 'status', '--json'] }
9
+ }
@@ -9,6 +9,7 @@ import { createHash } from 'node:crypto'
9
9
  import { execFileSync } from 'node:child_process'
10
10
  import { resolve } from 'node:path'
11
11
  import { fileURLToPath } from 'node:url'
12
+ import { observeWindowsPidCommand, parseWindowsCreationDate } from './platform/windows/observe-pid-command.mjs'
12
13
 
13
14
  export function hashArgv(argv) {
14
15
  return createHash('sha256').update(String(argv), 'utf8').digest('hex')
@@ -22,6 +23,7 @@ export function observePidCommand(pid) {
22
23
  error.code = 'SEAT_IDENTITY_NO_PID'
23
24
  throw error
24
25
  }
26
+ if (process.platform === 'win32') return observeWindowsPidCommand(pid, hashArgv)
25
27
  const started = execFileSync('/bin/ps', ['-o', 'lstart=', '-p', String(pid)], { encoding: 'utf8', env: psEnv() }).trim()
26
28
  const argv = execFileSync('/bin/ps', ['-o', 'command=', '-p', String(pid)], { encoding: 'utf8', env: psEnv() }).trim()
27
29
  if (!started || !argv) {
@@ -34,23 +36,7 @@ export function observePidCommand(pid) {
34
36
 
35
37
  const AGENT = /(?:claude|codex|grok|composer)(?:\.cmd|\.exe)?(?:\s|$)/iu
36
38
 
37
- export function parseWinCreationDate(value) {
38
- if (value instanceof Date) {
39
- if (Number.isNaN(value.getTime())) throw new Error('pid の CreationDate を解釈できない')
40
- return value.toISOString()
41
- }
42
- if (typeof value === 'number' && Number.isFinite(value)) {
43
- const parsed = new Date(value)
44
- if (Number.isNaN(parsed.getTime())) throw new Error('pid の CreationDate を解釈できない')
45
- return parsed.toISOString()
46
- }
47
- const text = String(value ?? '').trim()
48
- const microsoft = /^\/Date\((-?\d+)(?:[+-]\d+)?\)\/$/u.exec(text)
49
- if (microsoft) return new Date(Number(microsoft[1])).toISOString()
50
- const parsed = new Date(text)
51
- if (Number.isNaN(parsed.getTime())) throw new Error(`pid の CreationDate を解釈できない: ${text}`)
52
- return parsed.toISOString()
53
- }
39
+ export const parseWinCreationDate = parseWindowsCreationDate
54
40
 
55
41
  // ps の lstart 書式(LC_TIME)と非ASCII argv のエスケープ(LC_CTYPE)は locale で変わり、
56
42
  // 観測者ごとに digest が割れる(2026-08-22 実測)。Lattice 側の観測も LC_ALL=C に固定している。
@@ -27,7 +27,7 @@ import { execFileSync } from 'node:child_process'
27
27
  import { existsSync, readFileSync, renameSync, writeFileSync, unlinkSync } from 'node:fs'
28
28
  import { join } from 'node:path'
29
29
 
30
- import { STOP_DECLARATION, combineSeatLamp, hasActiveDescendant, patrolTargets, classifyPaneTail, decideBridgeContinuation, deriveMissingSession, isPaneProcessStopped, parsePaneTokenHint, resolvePostToken, resolveSeatObservation, resolveTmuxSocket, supportsMemberObservation, tmuxArgv, tmuxPanePid } from './seat-usage.mjs'
30
+ import { STOP_DECLARATION, combineSeatLamp, hasActiveDescendant, patrolTargets, classifyPaneTail, decideBridgeContinuation, deriveMissingSession, isPaneProcessStopped, parsePaneTokenHint, resolveLatticeExecutable, resolvePostToken, resolveSeatObservation, resolveTmuxSocket, supportsMemberObservation, tmuxArgv, tmuxPanePid } from './seat-usage.mjs'
31
31
 
32
32
  const args = process.argv.slice(2)
33
33
  const proj = args[0]
@@ -232,10 +232,8 @@ async function patrolClaims() {
232
232
  try {
233
233
  // doctor.sh と同じ解決規則(LATTICE_CLI env → setup記録 → 既定 'lattice'、Windowsは .cmd 解決)
234
234
  const latticeCli = process.env.LATTICE_CLI || (typeof setup.lattice_cli === 'string' && setup.lattice_cli) || 'lattice'
235
- const latticeCommand = process.platform === 'win32' && !/\.(cmd|bat|exe)$/i.test(latticeCli) && existsSync(`${latticeCli}.cmd`)
236
- ? `${latticeCli}.cmd`
237
- : latticeCli
238
- const out = execFileSync(latticeCommand, ['todo', 'status', '--json'], { cwd: proj, encoding: 'utf8' })
235
+ const lattice = resolveLatticeExecutable(latticeCli)
236
+ const out = execFileSync(lattice.command, lattice.argv, { cwd: proj, encoding: 'utf8' })
239
237
  activeTasks = (JSON.parse(out).active_set ?? []).map(t => t.task_id)
240
238
  } catch (e) {
241
239
  console.error(`seat-status-bridge: claim巡回がLatticeを読めない(今周期は検査しない): ${e.message.split('\n')[0]}`)
@@ -3,6 +3,7 @@ import { existsSync, readFileSync, readdirSync } from 'node:fs'
3
3
  import { execFileSync } from 'node:child_process'
4
4
  import { homedir, tmpdir } from 'node:os'
5
5
  import path, { join } from 'node:path'
6
+ import { resolveWindowsLatticeCommand } from './platform/windows/resolve-lattice-command.mjs'
6
7
 
7
8
  const TOKEN_HINT = /[↓↑]\s*([0-9]+(?:\.[0-9]+)?)\s*([kKmM]?)\s*tokens\b/gu
8
9
 
@@ -105,13 +106,7 @@ export function tmuxArgv(extraArgs = [], { socket, env = process.env, platform =
105
106
  export function resolveLatticeExecutable(cli, { platform = process.platform, exists = existsSync } = {}) {
106
107
  if (typeof cli !== 'string' || !cli) return { command: cli, argv: ['todo', 'status', '--json'] }
107
108
  if (platform !== 'win32') return { command: cli, argv: ['todo', 'status', '--json'] }
108
- const lower = cli.toLowerCase()
109
- const cmd = lower.endsWith('.cmd') || lower.endsWith('.bat') || lower.endsWith('.exe')
110
- ? cli
111
- : exists(`${cli}.cmd`)
112
- ? `${cli}.cmd`
113
- : cli
114
- return { command: process.env.ComSpec || 'cmd.exe', argv: ['/d', '/c', cmd, 'todo', 'status', '--json'] }
109
+ return resolveWindowsLatticeCommand(cli, exists)
115
110
  }
116
111
 
117
112
  /** member の自己申告を優先し、無い既存 member だけ旧 session 名へ互換フォールバックする。 */