@threadbase-sh/streamer 1.36.3 → 1.37.0
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/dist/cli.cjs +26490 -24984
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +1648 -149
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +378 -4
- package/dist/index.d.ts +378 -4
- package/dist/index.js +1651 -153
- package/dist/index.js.map +1 -1
- package/dist/migrations/010_create_managed_sessions.sql +65 -0
- package/dist/migrations/011_create_devices.sql +39 -0
- package/dist/migrations/012_create_push_tokens.sql +49 -0
- package/package.json +1 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
-- Managed-session registry (durable session runtime, C1 Phase 2).
|
|
2
|
+
-- See docs/architecture/2026-07-24-durable-session-runtime.md.
|
|
3
|
+
--
|
|
4
|
+
-- Until now managed session state lived only in SessionStore's in-memory Maps,
|
|
5
|
+
-- so a streamer restart lost startedAt, promptCount, sessionName, projectId,
|
|
6
|
+
-- the Codex placeholder→rollout binding, and failureReason outright. Sessions
|
|
7
|
+
-- did not come back as "recoverable" — they ceased to exist, reappearing at
|
|
8
|
+
-- best as external discovered processes with no managed metadata.
|
|
9
|
+
--
|
|
10
|
+
-- This table stores identity and provenance so the boot reconciler can say what
|
|
11
|
+
-- happened to each session. It deliberately does NOT store the byte stream:
|
|
12
|
+
-- outputBuffer is 64KiB of raw ANSI rewritten on every PTY chunk, and its
|
|
13
|
+
-- authoritative copy is already the provider's JSONL. Persisting it would turn
|
|
14
|
+
-- every chunk into a DB write to duplicate data we can re-read. Post-restart
|
|
15
|
+
-- replay is therefore conversation-accurate, not byte-accurate.
|
|
16
|
+
CREATE TABLE IF NOT EXISTS managed_sessions (
|
|
17
|
+
-- Provider-native resume identifier: the JSONL UUID for Claude Code, the
|
|
18
|
+
-- rollout id for Codex. This is what --resume / `codex resume` consumes, so
|
|
19
|
+
-- the registry never invents an identifier of its own.
|
|
20
|
+
session_id TEXT PRIMARY KEY,
|
|
21
|
+
provider TEXT NOT NULL,
|
|
22
|
+
|
|
23
|
+
-- Liveness probing. pid alone is never treated as identity: PIDs get reused,
|
|
24
|
+
-- so the reconciler matches the recorded cmdline before claiming a live
|
|
25
|
+
-- process is ours, and reports `orphaned` on a mismatch rather than guessing.
|
|
26
|
+
pid INTEGER,
|
|
27
|
+
cmdline TEXT,
|
|
28
|
+
|
|
29
|
+
project_path TEXT NOT NULL,
|
|
30
|
+
project_name TEXT NOT NULL,
|
|
31
|
+
branch TEXT NOT NULL DEFAULT '',
|
|
32
|
+
|
|
33
|
+
-- Semantic status (running/waiting_input/idle) as last observed. The
|
|
34
|
+
-- reconciler never trusts this over a live PID probe — a SIGKILLed streamer
|
|
35
|
+
-- never ran its exit writes, so a stored 'running' can be arbitrarily stale.
|
|
36
|
+
-- status_source records how the value was obtained so that staleness is
|
|
37
|
+
-- visible instead of implied.
|
|
38
|
+
status TEXT NOT NULL,
|
|
39
|
+
status_source TEXT NOT NULL,
|
|
40
|
+
status_updated_at INTEGER NOT NULL,
|
|
41
|
+
|
|
42
|
+
started_at INTEGER NOT NULL,
|
|
43
|
+
completed_at INTEGER,
|
|
44
|
+
last_activity_at INTEGER,
|
|
45
|
+
prompt_count INTEGER NOT NULL DEFAULT 0,
|
|
46
|
+
|
|
47
|
+
-- User-visible identity that today vanishes silently on restart.
|
|
48
|
+
session_name TEXT,
|
|
49
|
+
project_id TEXT,
|
|
50
|
+
-- Codex two-id model: the placeholder id the session was created under vs the
|
|
51
|
+
-- rollout id its history is indexed by. Losing this on restart orphans the
|
|
52
|
+
-- conversation from the session (types.ts:36-44).
|
|
53
|
+
bound_conversation_id TEXT,
|
|
54
|
+
resumed_from_conversation_id TEXT,
|
|
55
|
+
failure_reason TEXT,
|
|
56
|
+
|
|
57
|
+
-- Which streamer run started this session. A row whose instance differs from
|
|
58
|
+
-- the current run is the orphan test: the process outlived its streamer.
|
|
59
|
+
streamer_instance_id TEXT NOT NULL
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
-- The reconciler's only read path on boot: every row not already in a terminal
|
|
63
|
+
-- state. Terminal rows are retained for history but never re-probed.
|
|
64
|
+
CREATE INDEX IF NOT EXISTS idx_managed_sessions_status
|
|
65
|
+
ON managed_sessions (status);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
-- Paired-device registry (C5 scoped device capabilities).
|
|
2
|
+
-- See docs/architecture/2026-07-24-device-identity-and-capabilities.md.
|
|
3
|
+
--
|
|
4
|
+
-- Pairing exchanged a short-lived token for the streamer's API key — the SAME
|
|
5
|
+
-- string for every device that ever paired. Nothing recorded that a device
|
|
6
|
+
-- existed, so there was no attribution, no per-device revocation (rotating the
|
|
7
|
+
-- key de-authenticated everyone at once), and no way to scope authority.
|
|
8
|
+
CREATE TABLE IF NOT EXISTS devices (
|
|
9
|
+
-- Minted server-side. A client-supplied id would let one device claim
|
|
10
|
+
-- another's identity.
|
|
11
|
+
device_id TEXT PRIMARY KEY,
|
|
12
|
+
|
|
13
|
+
-- The client public key already supplied at pairing, previously used once as
|
|
14
|
+
-- a sealing target and then discarded.
|
|
15
|
+
public_key TEXT NOT NULL,
|
|
16
|
+
|
|
17
|
+
-- SHA-256 of the device token, never the token itself: a read of this table
|
|
18
|
+
-- must not let anyone impersonate a device. Same reasoning as password hashes.
|
|
19
|
+
token_hash TEXT NOT NULL UNIQUE,
|
|
20
|
+
|
|
21
|
+
-- Client-supplied label ("Ronen's iPhone"). Display only, never trusted for
|
|
22
|
+
-- authorization.
|
|
23
|
+
name TEXT,
|
|
24
|
+
|
|
25
|
+
-- JSON array of capability strings. Unknown entries are dropped on read, so a
|
|
26
|
+
-- downgrade cannot silently grant a capability this build does not understand.
|
|
27
|
+
capabilities TEXT NOT NULL,
|
|
28
|
+
|
|
29
|
+
created_at INTEGER NOT NULL,
|
|
30
|
+
last_seen_at INTEGER,
|
|
31
|
+
|
|
32
|
+
-- Set to revoke. Checked per request rather than cached — a stale cache is
|
|
33
|
+
-- exactly the window that makes revocation useless.
|
|
34
|
+
revoked_at INTEGER
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
-- Authentication looks a device up by token hash on every request, so this is
|
|
38
|
+
-- the hot path.
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_devices_token_hash ON devices (token_hash);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
-- Push registration and delivery state (C7 notification reliability).
|
|
2
|
+
--
|
|
3
|
+
-- POST /api/push/register was a no-op returning { ok: true }: mobile registered,
|
|
4
|
+
-- received success, and nothing was stored. No token existed, so no notification
|
|
5
|
+
-- could ever be delivered, no failure could be observed, and the client had no
|
|
6
|
+
-- way to discover that its "successful" registration meant nothing.
|
|
7
|
+
CREATE TABLE IF NOT EXISTS push_tokens (
|
|
8
|
+
-- The provider push token (Expo). Natural key: re-registering the same token
|
|
9
|
+
-- must update the existing row rather than accumulate duplicates, which is
|
|
10
|
+
-- how the same device ends up receiving one notification several times.
|
|
11
|
+
token TEXT PRIMARY KEY,
|
|
12
|
+
platform TEXT NOT NULL,
|
|
13
|
+
|
|
14
|
+
-- Optional device attribution. Nullable because push registration predates
|
|
15
|
+
-- device identity (C5) and must keep working without it.
|
|
16
|
+
device_id TEXT,
|
|
17
|
+
|
|
18
|
+
registered_at INTEGER NOT NULL,
|
|
19
|
+
|
|
20
|
+
-- Delivery health. Distinguishing "we have never tried" from "we tried and it
|
|
21
|
+
-- failed" is the difference between a client showing "not yet delivered" and
|
|
22
|
+
-- "your notifications are broken" — the report the user actually needs.
|
|
23
|
+
last_success_at INTEGER,
|
|
24
|
+
last_failure_at INTEGER,
|
|
25
|
+
last_failure_code TEXT,
|
|
26
|
+
|
|
27
|
+
-- Consecutive failures. Reset on success. A token the provider has rejected
|
|
28
|
+
-- repeatedly is dead (app uninstalled, token rotated) and should stop being
|
|
29
|
+
-- retried rather than failing forever.
|
|
30
|
+
failure_streak INTEGER NOT NULL DEFAULT 0,
|
|
31
|
+
|
|
32
|
+
-- Set when the provider tells us the token is permanently invalid, or the
|
|
33
|
+
-- user unregisters. Retained rather than deleted so the health report can
|
|
34
|
+
-- explain why delivery stopped.
|
|
35
|
+
revoked_at INTEGER
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
-- Delivery attempts are keyed by event id so a retry, a reconnect
|
|
39
|
+
-- reconciliation, or a duplicate trigger cannot notify the user twice for the
|
|
40
|
+
-- same underlying event.
|
|
41
|
+
CREATE TABLE IF NOT EXISTS push_events (
|
|
42
|
+
event_id TEXT PRIMARY KEY,
|
|
43
|
+
session_id TEXT,
|
|
44
|
+
created_at INTEGER NOT NULL,
|
|
45
|
+
delivered_at INTEGER
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
CREATE INDEX IF NOT EXISTS idx_push_tokens_revoked ON push_tokens (revoked_at);
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_push_events_created ON push_events (created_at);
|
package/package.json
CHANGED