@saluzi/saluzi-edu 0.2.30 → 0.2.32
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": "@saluzi/saluzi-edu",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.32",
|
|
4
4
|
"description": "Saluzi CLI - interactive AI coding assistant in the terminal",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@agentclientprotocol/sdk": "^0.19.0",
|
|
69
|
-
"@saluzi/acp-link": "^0.1.
|
|
69
|
+
"@saluzi/acp-link": "^0.1.5",
|
|
70
70
|
"@saluzi/codegraph": "^0.2.4",
|
|
71
71
|
"@saluzi/sa-agent-tools": "^1.0.0",
|
|
72
72
|
"@saluzi/sa-builtin-tools": "^1.0.1",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Hono } from 'hono'
|
|
2
|
+
import { getCookie } from 'hono/cookie'
|
|
2
3
|
import { randomUUID } from 'node:crypto'
|
|
3
4
|
import type { Context } from 'hono'
|
|
4
5
|
import type { UpgradeWebSocket } from 'hono/ws'
|
|
@@ -14,6 +15,8 @@ import {
|
|
|
14
15
|
} from '../../auth/middleware'
|
|
15
16
|
import { validateApiKey } from '../../auth/api-key'
|
|
16
17
|
import { resolveToken } from '../../auth/token'
|
|
18
|
+
import { resolveSessionToken } from '../../auth/session'
|
|
19
|
+
import { getDb } from '../../db/sqlite'
|
|
17
20
|
import {
|
|
18
21
|
handleAcpWsOpen,
|
|
19
22
|
handleAcpWsMessage,
|
|
@@ -55,9 +58,40 @@ function toAcpAgentResponse(env: ReturnType<typeof storeGetEnvironment> & {}) {
|
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Resolve a Web UI user's token (SQLite session token from cookie or
|
|
63
|
+
* Bearer) to a userId. Returns null if the token is invalid or absent.
|
|
64
|
+
*
|
|
65
|
+
* Web UI users authenticate via rct_* SQLite session tokens stored in the
|
|
66
|
+
* rcs_access cookie (or Bearer for API calls). ACP read/relay endpoints
|
|
67
|
+
* must accept these in addition to admin API keys, otherwise non-admin
|
|
68
|
+
* users can list ACP sessions (via /web/sessions with sessionAuth) but
|
|
69
|
+
* can't open them (ACP endpoints reject with 401 "unauthorized").
|
|
70
|
+
*/
|
|
71
|
+
function resolveWebUserToken(c: Context): string | null {
|
|
72
|
+
// Cookie (rcs_access) — primary Web UI auth path for WebSocket upgrades
|
|
73
|
+
const cookieToken = getCookie(c, 'rcs_access')
|
|
74
|
+
// Bearer — for REST calls from Web UI (Authorization header)
|
|
75
|
+
const bearerToken = extractBearerToken(c)
|
|
76
|
+
const token = cookieToken || bearerToken
|
|
77
|
+
if (!token) return null
|
|
78
|
+
// Try in-memory token (legacy issueToken flow)
|
|
79
|
+
const username = resolveToken(token)
|
|
80
|
+
if (username) return username
|
|
81
|
+
// Try SQLite session token
|
|
82
|
+
try {
|
|
83
|
+
return resolveSessionToken(token, getDb())
|
|
84
|
+
} catch {
|
|
85
|
+
return null
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
58
89
|
function hasAcpReadAuth(c: Context): boolean {
|
|
59
|
-
|
|
60
|
-
|
|
90
|
+
// Admin API key (RCS_API_KEYS)
|
|
91
|
+
const bearer = extractBearerToken(c)
|
|
92
|
+
if (bearer && validateApiKey(bearer)) return true
|
|
93
|
+
// Web UI user (SQLite session token or in-memory token)
|
|
94
|
+
return !!resolveWebUserToken(c)
|
|
61
95
|
}
|
|
62
96
|
|
|
63
97
|
/**
|
|
@@ -68,10 +102,30 @@ function hasAcpReadAuth(c: Context): boolean {
|
|
|
68
102
|
*/
|
|
69
103
|
export function hasAcpRelayAuth(c: Context): boolean {
|
|
70
104
|
const token = extractWebSocketAuthToken(c)
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
105
|
+
if (token) {
|
|
106
|
+
if (validateApiKey(token)) return true
|
|
107
|
+
// Try in-memory token resolution (existing issueToken flow)
|
|
108
|
+
if (resolveToken(token)) return true
|
|
109
|
+
// Try SQLite session token (rct_* from Bearer or Sec-WebSocket-Protocol)
|
|
110
|
+
try {
|
|
111
|
+
if (resolveSessionToken(token, getDb())) return true
|
|
112
|
+
} catch {
|
|
113
|
+
// DB unavailable — fall through to cookie check
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Cookie-based session token (rcs_access) — browser WebSocket upgrades
|
|
117
|
+
// automatically send cookies, so this works without client-side token
|
|
118
|
+
// injection.
|
|
119
|
+
const cookieToken = getCookie(c, 'rcs_access')
|
|
120
|
+
if (cookieToken) {
|
|
121
|
+
if (resolveToken(cookieToken)) return true
|
|
122
|
+
try {
|
|
123
|
+
if (resolveSessionToken(cookieToken, getDb())) return true
|
|
124
|
+
} catch {
|
|
125
|
+
return false
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return false
|
|
75
129
|
}
|
|
76
130
|
|
|
77
131
|
function acpReadUnauthorized(c: Context) {
|