@saluzi/saluzi-edu 0.3.3 → 0.3.5

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.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Saluzi CLI - interactive AI coding assistant in the terminal",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -105,13 +105,11 @@ describe('Security: /setup concurrent protection', () => {
105
105
  app = createApp()
106
106
  })
107
107
 
108
- // Under bun --isolate, each test file gets its own module graph, so the
109
- // SQLite singleton in db/sqlite.ts drifts between the test's freshDb() and
110
- // the route handler's getDb() — the DELETE runs on a different _db than
111
- // the INSERT, so both concurrent requests see stale rows and return 409.
112
- // The production code (atomic INSERT ... WHERE NOT EXISTS) is race-free;
113
- // this test only validates under non-isolate mode.
114
- test.skip('concurrent setup requests only create one admin', async () => {
108
+ // freshDb() resets the db/sqlite.ts singleton and points it at a unique
109
+ // temp file per test, so the route handler's getDb() resolves to the same
110
+ // connection as this test. The production INSERT ... WHERE NOT EXISTS is
111
+ // atomic, so exactly one of two concurrent setup requests succeeds.
112
+ test('concurrent setup requests only create one admin', async () => {
115
113
  // Fire two setup requests concurrently
116
114
  const [res1, res2] = await Promise.all([
117
115
  app.request('/web/auth/setup', {
@@ -1,5 +1,7 @@
1
1
  import { describe, test, expect, beforeEach, mock } from 'bun:test'
2
2
  import { randomUUID } from 'node:crypto'
3
+ import { tmpdir } from 'node:os'
4
+ import { join } from 'node:path'
3
5
  import { mockConfigModule } from './helpers/mock-config'
4
6
 
5
7
  mock.module('../config', () => mockConfigModule())
@@ -37,7 +39,10 @@ function freshDb(): Database {
37
39
  try {
38
40
  resetDbSingleton()
39
41
  } catch {}
40
- const uniquePath = `/tmp/rcs-design-gap-${randomUUID().replace(/-/g, '').slice(0, 8)}.db`
42
+ const uniquePath = join(
43
+ tmpdir(),
44
+ `rcs-design-gap-${randomUUID().replace(/-/g, '').slice(0, 8)}.db`,
45
+ )
41
46
  return initDatabase(uniquePath)
42
47
  }
43
48
 
@@ -330,7 +335,10 @@ describe('Gap 2: resolveOwnedWebSessionId removes auto-bind', () => {
330
335
  resetDbSingleton()
331
336
  } catch {}
332
337
  initDatabase(
333
- `/tmp/rcs-gap2-${randomUUID().replace(/-/g, '').slice(0, 8)}.db`,
338
+ join(
339
+ tmpdir(),
340
+ `rcs-gap2-${randomUUID().replace(/-/g, '').slice(0, 8)}.db`,
341
+ ),
334
342
  )
335
343
  })
336
344
 
@@ -1,6 +1,7 @@
1
1
  import { describe, test, expect, beforeEach, mock } from 'bun:test'
2
2
  import { readFileSync } from 'node:fs'
3
- import { resolve } from 'node:path'
3
+ import { tmpdir } from 'node:os'
4
+ import { join, resolve } from 'node:path'
4
5
  import { randomUUID } from 'node:crypto'
5
6
  import { mockConfigModule } from './helpers/mock-config'
6
7
 
@@ -34,7 +35,10 @@ function freshDb() {
34
35
  } catch {
35
36
  // ignore
36
37
  }
37
- const uniquePath = `/tmp/rcs-low-fixes-${randomUUID().replace(/-/g, '').slice(0, 8)}.db`
38
+ const uniquePath = join(
39
+ tmpdir(),
40
+ `rcs-low-fixes-${randomUUID().replace(/-/g, '').slice(0, 8)}.db`,
41
+ )
38
42
  return initDatabase(uniquePath)
39
43
  }
40
44
 
@@ -162,6 +162,21 @@ export async function startRcsServer(
162
162
  // eslint-disable-next-line custom-rules/no-process-exit
163
163
  process.exit(1)
164
164
  }
165
+ if (opts.singleUser && err.code === 'EADDRINUSE') {
166
+ // Single-user embedded mode (`slz rcs one` / `/rcs one`) breaks
167
+ // silently when another RCS instance owns the port: the one-time
168
+ // login codes and the worker-session poll live in THIS process's
169
+ // memory, while the browser talks to the OTHER server — its /otp/
170
+ // route doesn't know the code and bounces the user to the login
171
+ // page, defeating the auto-open-into-session flow. Fail loudly.
172
+ console.error(
173
+ '[RCS] Single-user mode needs this process to own the port ' +
174
+ '(one-time login codes are in-memory). ' +
175
+ 'Stop the other RCS instance or set RCS_PORT.',
176
+ )
177
+ // eslint-disable-next-line custom-rules/no-process-exit
178
+ process.exit(1)
179
+ }
165
180
  })
166
181
 
167
182
  // Inject WebSocket support for Node.js