@evandu/super-pi 0.1.21 → 0.1.23

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.
Files changed (51) hide show
  1. package/README.md +58 -1
  2. package/agents/super-pi-memory/index.ts +34 -0
  3. package/dist/client/assets/index-BKKZnCis.css +1 -0
  4. package/dist/client/assets/index-LWO_0omC.js +50 -0
  5. package/dist/client/favicon.ico +0 -0
  6. package/dist/client/favicon.png +0 -0
  7. package/dist/client/favicon.svg +10 -0
  8. package/dist/client/index.html +5 -2
  9. package/dist/server/active-memory.d.ts +118 -0
  10. package/dist/server/active-memory.js +214 -0
  11. package/dist/server/application/conversation-service.js +1 -0
  12. package/dist/server/application/run-service.d.ts +12 -0
  13. package/dist/server/application/run-service.js +48 -4
  14. package/dist/server/command-handler.d.ts +16 -2
  15. package/dist/server/command-handler.js +24 -2
  16. package/dist/server/files.d.ts +1 -0
  17. package/dist/server/files.js +77 -46
  18. package/dist/server/http-server.d.ts +17 -1
  19. package/dist/server/http-server.js +185 -20
  20. package/dist/server/index.js +97 -29
  21. package/dist/server/memory.d.ts +25 -0
  22. package/dist/server/memory.js +88 -0
  23. package/dist/server/persistence/app-state-repository.d.ts +27 -0
  24. package/dist/server/persistence/app-state-repository.js +1 -0
  25. package/dist/server/persistence/json-state-repository.d.ts +12 -0
  26. package/dist/server/persistence/json-state-repository.js +24 -0
  27. package/dist/server/persistence/open-state-store.d.ts +3 -0
  28. package/dist/server/persistence/open-state-store.js +35 -0
  29. package/dist/server/persistence/rename.d.ts +22 -0
  30. package/dist/server/persistence/rename.js +44 -0
  31. package/dist/server/persistence/sqlite-state-repository.d.ts +13 -0
  32. package/dist/server/persistence/sqlite-state-repository.js +114 -0
  33. package/dist/server/runtime.d.ts +7 -1
  34. package/dist/server/runtime.js +23 -10
  35. package/dist/server/server-context.d.ts +12 -0
  36. package/dist/server/store.d.ts +12 -3
  37. package/dist/server/store.js +55 -82
  38. package/dist/server/subagent-runs.d.ts +2 -0
  39. package/dist/server/subagent-runs.js +6 -0
  40. package/dist/server/subagents.d.ts +2 -0
  41. package/dist/server/subagents.js +11 -3
  42. package/dist/server/terminal.d.ts +139 -0
  43. package/dist/server/terminal.js +366 -0
  44. package/dist/server/types.d.ts +69 -2
  45. package/dist/server/workflow-engine.d.ts +2 -0
  46. package/dist/server/workflow-engine.js +6 -0
  47. package/dist/server/ws-server.d.ts +11 -0
  48. package/dist/server/ws-server.js +42 -2
  49. package/package.json +2 -129
  50. package/dist/client/assets/index-B43FWHQ6.js +0 -43
  51. package/dist/client/assets/index-BY__2nV9.css +0 -1
@@ -45,12 +45,14 @@ export class WsServer {
45
45
  catalogService;
46
46
  accessToken;
47
47
  rootDir;
48
+ onSocketClose;
48
49
  constructor(options) {
49
50
  this.broadcaster = options.broadcaster;
50
51
  this.commandHandler = options.commandHandler;
51
52
  this.catalogService = options.catalogService;
52
53
  this.accessToken = options.accessToken;
53
54
  this.rootDir = options.rootDir;
55
+ this.onSocketClose = options.onSocketClose;
54
56
  this.websocketServer = new WebSocketServer({ noServer: true });
55
57
  this.setupUpgrade(options.httpServer);
56
58
  this.setupConnectionHandling();
@@ -61,14 +63,45 @@ export class WsServer {
61
63
  socket.destroy();
62
64
  return;
63
65
  }
66
+ const origin = request.headers.origin;
67
+ if (origin) {
68
+ try {
69
+ const parsed = new URL(origin);
70
+ const host = request.headers.host;
71
+ if (!host || parsed.host !== host || !["http:", "https:"].includes(parsed.protocol)) {
72
+ socket.destroy();
73
+ return;
74
+ }
75
+ }
76
+ catch {
77
+ socket.destroy();
78
+ return;
79
+ }
80
+ }
64
81
  this.websocketServer.handleUpgrade(request, socket, head, (ws) => {
65
82
  this.websocketServer.emit("connection", ws, request);
66
83
  });
67
84
  });
68
85
  }
69
86
  setupConnectionHandling() {
70
- this.websocketServer.on("connection", (socket) => {
87
+ this.websocketServer.on("connection", (socket, request) => {
71
88
  this.broadcaster.register(socket);
89
+ /**
90
+ * The peer address, captured ONCE per connection rather than read per message.
91
+ *
92
+ * `socket._socket.remoteAddress` is the real TCP peer, but the terminal re-checks it on every
93
+ * command (`TerminalService.assertUsable`), so it has to be read where the underlying socket is
94
+ * still reachable. Reading it inside the message handler would reach for a private field on a
95
+ * `WebSocket` that may already be torn down, and the terminal is exactly the command family where
96
+ * a missing address must fail CLOSED rather than fall back to "allowed".
97
+ */
98
+ const remoteAddress = request.socket.remoteAddress;
99
+ /**
100
+ * Terminal session ids this socket has opened. Recorded on `terminal.open` rather than inferred
101
+ * from the service on close, because the service is shared across sockets and a close must kill
102
+ * only the shells belonging to the socket that went away.
103
+ */
104
+ const terminalSessionIds = new Set();
72
105
  socket.send(JSON.stringify({ type: "state", state: this.broadcaster.getStateSnapshot() }));
73
106
  if (this.rootDir) {
74
107
  socket.send(JSON.stringify({ type: "server.version", version: checkDiskVersion(this.rootDir) }));
@@ -97,16 +130,23 @@ export class WsServer {
97
130
  socket.on("close", () => {
98
131
  clearInterval(heartbeat);
99
132
  this.broadcaster.unregister(socket);
133
+ // Kill the shells this socket started BEFORE clearing the set, so a close that races a final
134
+ // `terminal.open` cannot leak one. The set is per-connection, so another tab's terminal survives.
135
+ if (terminalSessionIds.size)
136
+ this.onSocketClose?.([...terminalSessionIds]);
137
+ terminalSessionIds.clear();
100
138
  });
101
139
  socket.on("message", async (raw) => {
102
140
  let command;
103
141
  try {
104
142
  command = JSON.parse(raw.toString());
143
+ if (command.type === "terminal.open")
144
+ terminalSessionIds.add(command.sessionId);
105
145
  const entityId = await this.commandHandler.handleCommand(command, (event) => {
106
146
  if (socket.readyState === WebSocket.OPEN) {
107
147
  socket.send(JSON.stringify(event));
108
148
  }
109
- });
149
+ }, remoteAddress);
110
150
  if (command.operationRequestId)
111
151
  socket.send(JSON.stringify({ type: "operation.result", requestId: command.operationRequestId, ok: true, entityId }));
112
152
  }
package/package.json CHANGED
@@ -1,129 +1,2 @@
1
- {
2
- "name": "@evandu/super-pi",
3
- "version": "0.1.21",
4
- "description": "Web control plane and companion for Pi",
5
- "license": "MIT",
6
- "type": "module",
7
- "bin": {
8
- "super-pi": "bin/super-pi.js"
9
- },
10
- "pi": {
11
- "extensions": [
12
- "./dist/extension.js"
13
- ]
14
- },
15
- "files": [
16
- "dist",
17
- "agents",
18
- "bin",
19
- "README.md"
20
- ],
21
- "engines": {
22
- "node": ">=22.19.0"
23
- },
24
- "scripts": {
25
- "prepack": "npm run build",
26
- "dev": "concurrently --kill-others-on-fail \"npm:dev:server\" \"npm:dev:client\"",
27
- "dev:client": "vite --host 0.0.0.0",
28
- "dev:server": "tsx watch src/server/index.ts",
29
- "build": "tsc -b && vite build",
30
- "start": "node dist/server/index.js",
31
- "restart:server": "node scripts/restart-server.mjs",
32
- "smoke": "npm run build && node scripts/smoke.mjs",
33
- "check": "node scripts/run-check.mjs"
34
- },
35
- "check:logic": [
36
- "scripts/dispatch-ancestry.test.mjs",
37
- "scripts/dispatch-endpoint.test.mjs",
38
- "scripts/dispatched-by.test.mjs",
39
- "scripts/verify-source-text.test.mjs",
40
- "scripts/runtime-extension-ui.test.mjs",
41
- "scripts/agent-mechanism.test.mjs",
42
- "scripts/agent-wiring.test.mjs",
43
- "scripts/message-timestamps.test.mjs",
44
- "scripts/pi-resolution.test.mjs",
45
- "scripts/agent-real-pi.test.mjs",
46
- "scripts/image-input-real-pi.test.mjs",
47
- "scripts/image-upload.test.mjs",
48
- "scripts/extension-ui-e2e.test.mjs",
49
- "scripts/session-picker.test.mjs",
50
- "scripts/workspace-cwd.test.mjs",
51
- "scripts/subagents.test.mjs",
52
- "scripts/subagent-noise.test.mjs",
53
- "scripts/subagent-runs.test.mjs",
54
- "scripts/conversation-reconcile.test.mjs",
55
- "scripts/subagent-dispatch.test.mjs",
56
- "scripts/workflow-plan.test.mjs",
57
- "scripts/workflow-engine.test.mjs",
58
- "scripts/workflow-endpoint.test.mjs",
59
- "scripts/workflow-ui.test.mjs",
60
- "scripts/agent-vis-records.test.mjs",
61
- "scripts/queue-store.test.mjs",
62
- "scripts/followup.test.mjs",
63
- "scripts/md-render.test.mjs",
64
- "scripts/revert-session-format.test.mjs",
65
- "scripts/revert-session-file.test.mjs",
66
- "scripts/revert-real-pi.test.mjs",
67
- "scripts/revert-browser.test.mjs",
68
- "scripts/bg-task.test.mjs",
69
- "scripts/todo-tool.test.mjs",
70
- "scripts/tool-calls.test.mjs",
71
- "scripts/files-api.test.mjs",
72
- "scripts/files-missing-path.test.mjs",
73
- "scripts/task-tool.test.mjs",
74
- "scripts/fork-server.test.mjs",
75
- "scripts/styles-no-dangling.test.mjs",
76
- "scripts/theme-no-hardcoded-colours.test.mjs",
77
- "scripts/agent-config-regression.test.mjs",
78
- "scripts/model-filter.test.mjs"
79
- ],
80
- "check:browser": [
81
- "scripts/extension-ui-browser.test.mjs",
82
- "scripts/inline-rename-blur.test.mjs",
83
- "scripts/message-timestamps-browser.test.mjs",
84
- "scripts/session-picker-browser.test.mjs",
85
- "scripts/workspace-settings-browser.test.mjs",
86
- "scripts/subagent-card-browser.test.mjs",
87
- "scripts/workflow-browser.test.mjs",
88
- "scripts/conversation-reconcile-browser.test.mjs",
89
- "scripts/visual-defects-browser.test.mjs",
90
- "scripts/agent-vis-card-browser.test.mjs",
91
- "scripts/queue-dock.test.mjs",
92
- "scripts/md-render-browser.test.mjs",
93
- "scripts/right-sidebar-browser.test.mjs",
94
- "scripts/sidebar-tools-browser.test.mjs",
95
- "scripts/tool-call-blocks-browser.test.mjs",
96
- "scripts/files-tab-browser.test.mjs",
97
- "scripts/msg-tabs-browser.test.mjs",
98
- "scripts/tabs-on-top-browser.test.mjs",
99
- "scripts/subagent-source.test.mjs",
100
- "scripts/fork-resize-browser.test.mjs",
101
- "scripts/msg-bubbles.test.mjs",
102
- "scripts/narrow-layout.test.mjs",
103
- "scripts/scrollbar-style.test.mjs",
104
- "scripts/theme-contrast.test.mjs",
105
- "scripts/theme-switch-state.test.mjs",
106
- "scripts/settings-window-structure.test.mjs",
107
- "scripts/file-tab-selected-contrast.test.mjs",
108
- "scripts/help-window.test.mjs",
109
- "scripts/composer-regression-browser.test.mjs",
110
- "scripts/workspace-picker-browser.test.mjs",
111
- "scripts/optimistic-browser.test.mjs"
112
- ],
113
- "dependencies": {
114
- "ws": "latest"
115
- },
116
- "devDependencies": {
117
- "@types/node": "latest",
118
- "@types/react": "latest",
119
- "@types/react-dom": "latest",
120
- "@types/ws": "latest",
121
- "@vitejs/plugin-react": "latest",
122
- "concurrently": "latest",
123
- "react": "latest",
124
- "react-dom": "latest",
125
- "tsx": "latest",
126
- "typescript": "latest",
127
- "vite": "latest"
128
- }
129
- }
1
+ { "name": "@evandu/super-pi", "version": "0.1.23", "description": "Web control plane and companion for Pi", "license": "MIT", "type": "module", "bin": { "super-pi": "bin/super-pi.js" }, "pi": { "extensions": [ "./dist/extension.js" ] }, "files": [ "dist", "agents", "bin", "README.md" ], "engines": { "node": ">=22.19.0" }, "scripts": { "prepack": "npm run build", "dev": "concurrently --kill-others-on-fail \"npm:dev:server\" \"npm:dev:client\"", "dev:client": "vite --host 0.0.0.0", "dev:server": "tsx watch src/server/index.ts", "build": "tsc -b && vite build", "start": "node dist/server/index.js", "restart:server": "node scripts/restart-server.mjs", "smoke": "npm run build && node scripts/smoke.mjs", "check": "node scripts/run-check.mjs" }, "check:logic": [ "scripts/lan-access.test.mjs", "scripts/terminal-pty.test.mjs", "scripts/persistence-repository.test.mjs", "scripts/sqlite-repository.test.mjs", "scripts/dispatch-ancestry.test.mjs", "scripts/dispatch-endpoint.test.mjs", "scripts/dispatched-by.test.mjs", "scripts/verify-source-text.test.mjs", "scripts/runtime-extension-ui.test.mjs", "scripts/agent-mechanism.test.mjs", "scripts/agent-wiring.test.mjs", "scripts/message-timestamps.test.mjs", "scripts/pi-resolution.test.mjs", "scripts/agent-real-pi.test.mjs", "scripts/image-input-real-pi.test.mjs", "scripts/image-upload.test.mjs", "scripts/extension-ui-e2e.test.mjs", "scripts/session-picker.test.mjs", "scripts/workspace-cwd.test.mjs", "scripts/subagents.test.mjs", "scripts/subagent-noise.test.mjs", "scripts/subagent-runs.test.mjs", "scripts/conversation-reconcile.test.mjs", "scripts/subagent-dispatch.test.mjs", "scripts/workflow-plan.test.mjs", "scripts/workflow-engine.test.mjs", "scripts/workflow-endpoint.test.mjs", "scripts/workflow-ui.test.mjs", "scripts/agent-vis-records.test.mjs", "scripts/queue-store.test.mjs", "scripts/followup.test.mjs", "scripts/md-render.test.mjs", "scripts/revert-session-format.test.mjs", "scripts/revert-session-file.test.mjs", "scripts/revert-real-pi.test.mjs", "scripts/revert-browser.test.mjs", "scripts/bg-task.test.mjs", "scripts/todo-tool.test.mjs", "scripts/tool-calls.test.mjs", "scripts/files-api.test.mjs", "scripts/files-missing-path.test.mjs", "scripts/task-tool.test.mjs", "scripts/fork-server.test.mjs", "scripts/styles-no-dangling.test.mjs", "scripts/theme-no-hardcoded-colours.test.mjs", "scripts/agent-config-regression.test.mjs", "scripts/model-filter.test.mjs", "scripts/active-memory.test.mjs" ], "check:browser": [ "scripts/extension-ui-browser.test.mjs", "scripts/inline-rename-blur.test.mjs", "scripts/message-timestamps-browser.test.mjs", "scripts/session-picker-browser.test.mjs", "scripts/workspace-settings-browser.test.mjs", "scripts/subagent-card-browser.test.mjs", "scripts/workflow-browser.test.mjs", "scripts/conversation-reconcile-browser.test.mjs", "scripts/visual-defects-browser.test.mjs", "scripts/agent-vis-card-browser.test.mjs", "scripts/queue-dock.test.mjs", "scripts/md-render-browser.test.mjs", "scripts/right-sidebar-browser.test.mjs", "scripts/sidebar-tools-browser.test.mjs", "scripts/tool-call-blocks-browser.test.mjs", "scripts/files-tab-browser.test.mjs", "scripts/msg-tabs-browser.test.mjs", "scripts/tabs-on-top-browser.test.mjs", "scripts/subagent-source.test.mjs", "scripts/fork-resize-browser.test.mjs", "scripts/msg-bubbles.test.mjs", "scripts/narrow-layout.test.mjs", "scripts/scrollbar-style.test.mjs",
2
+ "scripts/stream-tail-browser.test.mjs", "scripts/theme-contrast.test.mjs", "scripts/theme-switch-state.test.mjs", "scripts/settings-window-structure.test.mjs", "scripts/file-tab-selected-contrast.test.mjs", "scripts/help-window.test.mjs", "scripts/composer-regression-browser.test.mjs", "scripts/workspace-picker-browser.test.mjs", "scripts/optimistic-browser.test.mjs", "scripts/active-memory-browser.test.mjs" ], "dependencies": { "ws": "latest" }, "optionalDependencies": { "node-pty": "^1.2.0-beta.15" }, "devDependencies": { "@types/node": "latest", "@types/react": "latest", "@types/react-dom": "latest", "@types/ws": "latest", "@vitejs/plugin-react": "latest", "concurrently": "latest", "react": "latest", "react-dom": "latest", "tsx": "latest", "typescript": "latest", "vite": "latest" } }