@online5880/opensession 0.1.5 → 0.1.7

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": "@online5880/opensession",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Session continuity bridge CLI with Supabase backend",
5
5
  "type": "module",
6
6
  "bin": {
package/session.txt ADDED
@@ -0,0 +1,3 @@
1
+ Session started: 883d114c-ce11-4f6b-a36a-74fac5dc2bfc
2
+ Project: test-all
3
+ Actor: e2e-user
package/sql/schema.sql CHANGED
@@ -36,3 +36,15 @@ create index if not exists session_events_idempotency_idx on session_events(sess
36
36
  create unique index if not exists session_events_session_type_idempotency_key_uidx
37
37
  on session_events(session_id, type, idempotency_key)
38
38
  where idempotency_key is not null;
39
+
40
+ DO $$
41
+ BEGIN
42
+ IF EXISTS (
43
+ SELECT 1 FROM pg_publication WHERE pubname = 'supabase_realtime'
44
+ ) THEN
45
+ ALTER PUBLICATION supabase_realtime ADD TABLE session_events;
46
+ END IF;
47
+ EXCEPTION WHEN OTHERS THEN
48
+ -- Ignore if table is already in publication or other error
49
+ END;
50
+ $$;
package/src/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import { Command } from 'commander';
4
4
  import readline from 'node:readline';
@@ -559,26 +559,23 @@ async function runOpsConsole(options) {
559
559
  }
560
560
  input.on('keypress', onKeypress);
561
561
 
562
- intervalHandle = setInterval(async () => {
562
+ const unsubscribe = subscribeToSessionEvents(client, selectedSession.id, async (event) => {
563
563
  if (closed) {
564
564
  return;
565
565
  }
566
566
  await refresh();
567
567
  redraw();
568
- }, refreshMs);
568
+ });
569
569
 
570
570
  await new Promise((resolve) => {
571
571
  const done = () => {
572
+ unsubscribe();
572
573
  close();
573
574
  resolve();
574
575
  };
575
576
  input.once('end', done);
576
- const poll = setInterval(() => {
577
- if (closed) {
578
- clearInterval(poll);
579
- resolve();
580
- }
581
- }, 100);
577
+ // 폴링 제거
578
+ resolve();
582
579
  });
583
580
  }
584
581
 
package/src/supabase.js CHANGED
@@ -307,3 +307,26 @@ export async function listSessions(client, projectId, limit = 100) {
307
307
 
308
308
  return data;
309
309
  }
310
+
311
+ export function subscribeToSessionEvents(client, sessionId, onEvent) {
312
+ const channel = client.channel(`session-${sessionId}`);
313
+
314
+ channel
315
+ .on(
316
+ 'postgres_changes',
317
+ {
318
+ event: 'INSERT',
319
+ schema: 'public',
320
+ table: 'session_events',
321
+ filter: `session_id=eq.${sessionId}`
322
+ },
323
+ (payload) => {
324
+ onEvent(payload.new);
325
+ }
326
+ )
327
+ .subscribe();
328
+
329
+ return () => {
330
+ client.removeChannel(channel);
331
+ };
332
+ }
package/src/tui.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import blessed from 'blessed';
2
- import { listActiveSessions, getSessionEvents } from './supabase.js';
2
+ import { listActiveSessions, getSessionEvents, subscribeToSessionEvents } from './supabase.js';
3
3
 
4
4
  export async function startTui(client, options = {}) {
5
5
  const screen = blessed.screen({
@@ -89,14 +89,14 @@ export async function startTui(client, options = {}) {
89
89
 
90
90
  let sessions = [];
91
91
  let selectedSessionId = null;
92
- let refreshInterval = null;
92
+ let unsubscribeRealtime = null;
93
93
 
94
94
  async function refreshSessions() {
95
95
  try {
96
96
  header.setContent(` Loading sessions... `);
97
97
  screen.render();
98
98
 
99
- const activeSessions = await listActiveSessions(client);
99
+ const activeSessions = await listActiveSessions(client, options.projectKey ?? options.project);
100
100
  sessions = activeSessions || [];
101
101
 
102
102
  sessionList.setItems(sessions.map(s => `${s.actor} (${s.id.slice(0, 8)})`));
@@ -108,13 +108,11 @@ export async function startTui(client, options = {}) {
108
108
  }
109
109
  }
110
110
 
111
- async function refreshEvents() {
111
+ async function loadInitialEvents() {
112
112
  if (!selectedSessionId) return;
113
113
 
114
114
  try {
115
115
  const events = await getSessionEvents(client, selectedSessionId);
116
- const currentScroll = eventLog.childBase;
117
-
118
116
  eventLog.clear();
119
117
  if (events && events.length > 0) {
120
118
  events.forEach(e => {
@@ -123,12 +121,10 @@ export async function startTui(client, options = {}) {
123
121
  } else {
124
122
  eventLog.log(' No events found for this session.');
125
123
  }
126
-
127
- // Maintain scroll position if needed or scroll to bottom
128
- eventLog.setScroll(currentScroll);
129
124
  screen.render();
130
125
  } catch (error) {
131
- eventLog.log(` Auto-refresh error: ${error.message}`);
126
+ eventLog.log(` Error loading events: ${error.message}`);
127
+ screen.render();
132
128
  }
133
129
  }
134
130
 
@@ -140,16 +136,23 @@ export async function startTui(client, options = {}) {
140
136
  eventLog.setContent(` Loading events for ${session.id}... \n`);
141
137
  screen.render();
142
138
 
143
- if (refreshInterval) clearInterval(refreshInterval);
139
+ if (unsubscribeRealtime) {
140
+ unsubscribeRealtime();
141
+ unsubscribeRealtime = null;
142
+ }
144
143
 
145
- await refreshEvents();
144
+ await loadInitialEvents();
146
145
 
147
- // Set up auto-refresh every 5 seconds for the selected session
148
- refreshInterval = setInterval(() => refreshEvents(), 5000);
146
+ // Set up Realtime subscription for the selected session
147
+ unsubscribeRealtime = subscribeToSessionEvents(client, selectedSessionId, (newEvent) => {
148
+ // Append the new event to the log instantly
149
+ eventLog.log(`[${new Date(newEvent.created_at).toLocaleTimeString()}] ${newEvent.type}: ${JSON.stringify(newEvent.payload)}`);
150
+ screen.render();
151
+ });
149
152
  });
150
153
 
151
154
  screen.key(['escape', 'q', 'C-c'], () => {
152
- if (refreshInterval) clearInterval(refreshInterval);
155
+ if (unsubscribeRealtime) unsubscribeRealtime();
153
156
  process.exit(0);
154
157
  });
155
158
  screen.key(['r'], () => refreshSessions());