@openchambery/web 2.0.0-beta.33 → 2.0.0-beta.35

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 (72) hide show
  1. package/dist/assets/{AgentAvatar-BSjLSc09.js → AgentAvatar-C1MGZ7qC.js} +1 -1
  2. package/dist/assets/{AssistantView-hn5vNfE6.js → AssistantView-DHzU2pdG.js} +3 -3
  3. package/dist/assets/{DiagramView-CaluLhXD.js → DiagramView-CcIHkVqJ.js} +1 -1
  4. package/dist/assets/{FadeInOnReveal-CMBd5BTd.js → FadeInOnReveal-BMSk_tY-.js} +1 -1
  5. package/dist/assets/{Icon-wxnMdUn7.js → Icon-BdzPRCSy.js} +1 -1
  6. package/dist/assets/{MarkdownRenderer-Ds1RopyO.js → MarkdownRenderer-dBTm_1uN.js} +2 -2
  7. package/dist/assets/{MarkdownRendererImpl-DSUKad4N.js → MarkdownRendererImpl-SjwAdTqn.js} +1 -1
  8. package/dist/assets/{MarkstreamRendererImpl-C90FPIyE.js → MarkstreamRendererImpl-DB7Z6TRb.js} +1 -1
  9. package/dist/assets/{MobileDetailNavigation-DbXwF0GB.js → MobileDetailNavigation-CVOEiJ-b.js} +1 -1
  10. package/dist/assets/{MobileShareBridge-BLzEpw1h.js → MobileShareBridge-BOrXoA1Y.js} +1 -1
  11. package/dist/assets/{MobileSurface-CTEInNuH.js → MobileSurface-DY9Tixc9.js} +1 -1
  12. package/dist/assets/{MultiRunWindow-CFanJGf1.js → MultiRunWindow-DEC0Rwez.js} +1 -1
  13. package/dist/assets/{OnboardingScreen-C6FL1sb4.js → OnboardingScreen-yRdQTR1M.js} +1 -1
  14. package/dist/assets/{OverlayScrollbar-CpJqv3aB.js → OverlayScrollbar-BUjtqnxn.js} +1 -1
  15. package/dist/assets/{SettingsView-DjBe5-jZ.js → SettingsView-D-yNM-tL.js} +4 -4
  16. package/dist/assets/{SettingsWindow-Chr5MglP.js → SettingsWindow-BPAEa2vr.js} +1 -1
  17. package/dist/assets/{TerminalView-CdhxAEGl.js → TerminalView-BnSzxCVG.js} +1 -1
  18. package/dist/assets/{ToolOutputDialog-BurrONSg.js → ToolOutputDialog-D2IHi6iv.js} +1 -1
  19. package/dist/assets/{appThemeRegistry-aqR2xSHX.js → appThemeRegistry-B1WCRUcv.js} +1 -1
  20. package/dist/assets/{button-Ck8ipzBm.js → button-CUwjpv-i.js} +1 -1
  21. package/dist/assets/{collapsible-nU99prkD.js → collapsible-paItOH3A.js} +1 -1
  22. package/dist/assets/{context-menu-DMzZN1oi.js → context-menu-K6zNumqN.js} +1 -1
  23. package/dist/assets/{diagram-CvO4P8ds.js → diagram-D6nY5hbD.js} +1 -1
  24. package/dist/assets/{dialog-Ct9R_6CF.js → dialog-PjKWayIU.js} +1 -1
  25. package/dist/assets/{es-DCjl24dV.js → es-BBbon7SL.js} +1 -1
  26. package/dist/assets/{fileMentionTouchSelection-ysOMdR4r.js → fileMentionTouchSelection-DyeSefuV.js} +1 -1
  27. package/dist/assets/{fr-DfJ5j0t5.js → fr-BsKj2CTi.js} +1 -1
  28. package/dist/assets/{gitApi-HZvElUdi.js → gitApi-wbphvTYV.js} +1 -1
  29. package/dist/assets/{input-CXhFmHcq.js → input-BeVw4D0W.js} +1 -1
  30. package/dist/assets/{insertionBoundaries-DiPvqwfZ.js → insertionBoundaries-BMs6NYaW.js} +1 -1
  31. package/dist/assets/{ja-B_9_w2Xf.js → ja-1mgxunoM.js} +1 -1
  32. package/dist/assets/{ko-DlGbo3kV.js → ko-Ra9znPcL.js} +1 -1
  33. package/dist/assets/{main-BgMWhca7.js → main-Bz2VsaQi.js} +4 -4
  34. package/dist/assets/{main-BE6r4fZX.js → main-o0x9eFCS.js} +2 -2
  35. package/dist/assets/{markdownHeightCache-g_UsWzbA.js → markdownHeightCache-CGCWmHgq.js} +1 -1
  36. package/dist/assets/{mcpQueries-CugnB3dX.js → mcpQueries-wid17NzX.js} +1 -1
  37. package/dist/assets/{miniChat-C_AZKZq_.js → miniChat-C1p4S55l.js} +2 -2
  38. package/dist/assets/{mobile-D7Y3SkfR.js → mobile-Cq3fINOf.js} +2 -2
  39. package/dist/assets/{multirun-h8b8FBvd.js → multirun-CK2Y7hnG.js} +1 -1
  40. package/dist/assets/{pl-DjUsS8Ei.js → pl-B714sdql.js} +1 -1
  41. package/dist/assets/{projectMeta-_M4EjKmd.js → projectMeta-DI97ujNn.js} +1 -1
  42. package/dist/assets/{pt-BR-B8sV7qo1.js → pt-BR-DVoT_I7_.js} +1 -1
  43. package/dist/assets/{renderElectronMiniChatApp-wEKfP_xk.js → renderElectronMiniChatApp-BciIgKbB.js} +2 -2
  44. package/dist/assets/{renderMobileApp-CVgGoMeS.js → renderMobileApp-DJMhiRuA.js} +4 -4
  45. package/dist/assets/{runtimeConfig-DHtqWZcT.js → runtimeConfig-CfLVZirP.js} +1 -1
  46. package/dist/assets/{runtimeEndpointReset-7-gtLsRv.js → runtimeEndpointReset-C9cEbeNU.js} +1 -1
  47. package/dist/assets/{sessionChangeSummary-DRfp1bQ_.js → sessionChangeSummary-BV9ikhBP.js} +1 -1
  48. package/dist/assets/{sessionLookup-QLV0R5XN.js → sessionLookup-BwoTIs5P.js} +1 -1
  49. package/dist/assets/{sortable-tabs-strip-DYOlyP5y.js → sortable-tabs-strip-D611LPyd.js} +1 -1
  50. package/dist/assets/src-BZ0tISrf.css +2 -0
  51. package/dist/assets/{store-Cuktref-.js → store-l_X_jQNz.js} +2 -2
  52. package/dist/assets/{textarea-DyC2D4-b.js → textarea-QtZjf4L8.js} +1 -1
  53. package/dist/assets/{tooltip-BKL7J5hx.js → tooltip-DDNnmcXI.js} +1 -1
  54. package/dist/assets/{uk-Bh7HXyLp.js → uk-ChNnTgKP.js} +1 -1
  55. package/dist/assets/{useAppFontEffects-rTMQV8Pe.js → useAppFontEffects-sbHH-URm.js} +6 -6
  56. package/dist/assets/{useDesktopWindowControlsLayout-CmkZcpo8.js → useDesktopWindowControlsLayout-BZWyBljH.js} +1 -1
  57. package/dist/assets/{useEffectiveDirectory-DPsrHggY.js → useEffectiveDirectory-CeEvXzot.js} +1 -1
  58. package/dist/assets/{useMobileNavigationStore--Sh3fwTE.js → useMobileNavigationStore-OOsKcmIS.js} +6 -6
  59. package/dist/assets/{useSessionAutoCleanup-BOLbKDuh.js → useSessionAutoCleanup-Clo-7FPo.js} +1 -1
  60. package/dist/assets/{useWorkerHighlightedLines-DYrmnMn2.js → useWorkerHighlightedLines-BXqm9rJh.js} +1 -1
  61. package/dist/assets/{zh-CN-Uzia0gk4.js → zh-CN-DHEFf4u1.js} +1 -1
  62. package/dist/assets/{zh-TW-CiI7DLdB.js → zh-TW-rdSa8atW.js} +1 -1
  63. package/dist/index.html +3 -3
  64. package/dist/mini-chat.html +3 -3
  65. package/dist/mobile.html +3 -3
  66. package/package.json +1 -1
  67. package/server/lib/client-auth/remote-clients.js +15 -0
  68. package/server/lib/client-auth/remote-clients.test.js +42 -1
  69. package/server/lib/opencode/DOCUMENTATION.md +1 -0
  70. package/server/lib/opencode/core-routes.js +17 -0
  71. package/server/lib/opencode/core-routes.test.js +43 -0
  72. package/dist/assets/src-BmSX_CVj.css +0 -2
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'bun:test';
1
+ import { describe, expect, it } from 'vitest';
2
2
  import fs from 'node:fs/promises';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
@@ -17,6 +17,47 @@ const createRuntime = async () => {
17
17
  };
18
18
 
19
19
  describe('remote client auth runtime', () => {
20
+ it('persists renamed labels without changing credentials, metadata or revocation', async () => {
21
+ const { dir, runtime } = await createRuntime();
22
+ try {
23
+ const created = await runtime.createClient({ label: 'Phone', devicePlatform: 'iOS', usesRelay: true });
24
+ const renamed = await runtime.renameClient(created.client.id, ' Work phone ');
25
+ expect(renamed).toEqual({ ...created.client, label: 'Work phone' });
26
+ expect('tokenHash' in renamed).toBe(false);
27
+ const reopened = createRemoteClientAuthRuntime({ fsPromises: fs, path, crypto, storePath: path.join(dir, 'remote-clients.json') });
28
+ expect(await reopened.listClients()).toEqual([renamed]);
29
+ expect((await reopened.authenticateBearerToken(created.token))?.clientId).toBe(created.client.id);
30
+ await Promise.all([
31
+ runtime.renameClient(created.client.id, 'Archived phone'),
32
+ runtime.revokeClient(created.client.id),
33
+ ]);
34
+ expect(await runtime.authenticateBearerToken(created.token)).toBe(null);
35
+ expect((await runtime.listClients())[0]).toMatchObject({ label: 'Archived phone', revokedAt: expect.any(String) });
36
+ } finally {
37
+ await fs.rm(dir, { recursive: true, force: true });
38
+ }
39
+ });
40
+
41
+ it('rejects invalid labels and failed writes without committing a new name', async () => {
42
+ const { dir, runtime } = await createRuntime();
43
+ try {
44
+ const created = await runtime.createClient({ label: 'Phone' });
45
+ for (const label of [undefined, null, 42, '', ' ', 'x'.repeat(81)]) {
46
+ await expect(runtime.renameClient(created.client.id, label)).rejects.toMatchObject({ status: 400 });
47
+ }
48
+ expect(await runtime.renameClient('missing', 'Name')).toBe(null);
49
+ const failing = createRemoteClientAuthRuntime({
50
+ fsPromises: { ...fs, writeFile: async () => { throw new Error('Write failed'); } },
51
+ path, crypto, storePath: path.join(dir, 'remote-clients.json'),
52
+ });
53
+ await expect(failing.renameClient(created.client.id, 'New name')).rejects.toThrow('Write failed');
54
+ expect(await runtime.listClients()).toEqual([created.client]);
55
+ expect((await runtime.authenticateBearerToken(created.token))?.ok).toBe(true);
56
+ } finally {
57
+ await fs.rm(dir, { recursive: true, force: true });
58
+ }
59
+ });
60
+
20
61
  it('creates, authenticates, lists, and revokes client tokens', async () => {
21
62
  const { dir, runtime } = await createRuntime();
22
63
  try {
@@ -25,6 +25,7 @@ This module provides OpenCode server integration utilities for the web server ru
25
25
  - `packages/web/server/lib/opencode/provider-catalog.js`: fail-closed projection for the safe provider catalog response.
26
26
  - `packages/web/server/lib/opencode/cli-options.js`: CLI/environment option parsing for server startup arguments.
27
27
  - `packages/web/server/lib/opencode/core-routes.js`: server status/system routes, auth/access guard routes, and settings utility route registration.
28
+ - `PATCH /api/client-auth/clients/:id` edits the stored device label (trimmed, 1–80 characters), returning `{ client }` without credentials. Owner UI sessions and local/paired desktop clients can edit any device; other client tokens can edit only themselves. Missing devices return 404, invalid labels 400, unauthorized targets 403. Renaming shares the client store mutation queue and preserves token identity, metadata and revocation. Web, Electron and mobile use the web `ClientAuthAPI`; VS Code does not expose client management.
28
29
  - `packages/web/server/lib/opencode/shutdown-runtime.js`: graceful shutdown orchestration runtime for watcher/session/terminal/process/server teardown.
29
30
  - `packages/web/server/lib/opencode/server-startup-runtime.js`: server listen/startup flow and process/signal handler orchestration runtime.
30
31
  - `packages/web/server/lib/opencode/static-routes-runtime.js`: static asset/SPA fallback route registration and manifest route wiring. The SPA fallback sends `index.html` relative to `root`, so deep links keep working when the install lives under a dot directory.
@@ -822,6 +822,23 @@ export const registerAuthAndAccessRoutes = (app, dependencies) => {
822
822
  });
823
823
  });
824
824
 
825
+ app.patch('/api/client-auth/clients/:id', express.json({ limit: '64kb' }), async (req, res, next) => {
826
+ await runWithClientManagementAuth(req, res, next, async (authContext) => {
827
+ if (authContext.type === 'client') {
828
+ const actingClient = await clientRecordFromAuthContext(authContext);
829
+ if (!canManageRemoteClients(actingClient)) {
830
+ const clientId = clientIdFromAuthContext(authContext);
831
+ if (!clientId || clientId !== req.params?.id) {
832
+ return res.status(403).json({ error: 'Client tokens can only rename themselves' });
833
+ }
834
+ }
835
+ }
836
+ const client = await remoteClientAuthRuntime.renameClient(req.params?.id, req.body?.label);
837
+ if (!client) return res.status(404).json({ error: 'Client not found' });
838
+ res.json({ client });
839
+ });
840
+ });
841
+
825
842
  app.delete('/api/client-auth/clients/:id', async (req, res, next) => {
826
843
  await runWithClientManagementAuth(req, res, next, async (authContext) => {
827
844
  if (authContext.type === 'client') {
@@ -51,6 +51,12 @@ const createClientAuthDependencies = (options = {}) => {
51
51
  clients.push(client);
52
52
  return { client, token: 'oc_client_secret' };
53
53
  },
54
+ renameClient: vi.fn(async (id, label) => {
55
+ const client = clients.find((entry) => entry.id === id);
56
+ if (!client) return null;
57
+ client.label = label.trim();
58
+ return client;
59
+ }),
54
60
  revokeClient: async (id) => {
55
61
  const client = clients.find((entry) => entry.id === id);
56
62
  if (!client) return { revoked: false };
@@ -1334,6 +1340,43 @@ describe('core-routes', () => {
1334
1340
  describe('client auth routes', () => {
1335
1341
  const createDependencies = createClientAuthDependencies;
1336
1342
 
1343
+ it.each(['session', 'desktop-local', 'desktop', 'mobile'])('scopes device renaming for %s', async (kind) => {
1344
+ const app = express();
1345
+ let authContext = { type: 'session' };
1346
+ const dependencies = createDependencies({ resolveAuthContext: async () => authContext });
1347
+ registerAuthAndAccessRoutes(app, dependencies);
1348
+ const own = (await request(app).post('/api/client-auth/clients').send({ label: 'Own', clientKind: kind })).body.client;
1349
+ const other = (await request(app).post('/api/client-auth/clients').send({ label: 'Other' })).body.client;
1350
+ if (kind !== 'session') authContext = { type: 'client', clientId: own.id, client: own };
1351
+
1352
+ await request(app).patch(`/api/client-auth/clients/${own.id}`).send({ label: 'Renamed own' }).expect(200);
1353
+ dependencies.remoteClientAuthRuntime.renameClient.mockClear();
1354
+ const response = await request(app).patch(`/api/client-auth/clients/${other.id}`).send({ label: 'Renamed other', clientKind: 'desktop-local' });
1355
+ expect(response.status).toBe(kind === 'mobile' ? 403 : 200);
1356
+ if (kind === 'mobile') {
1357
+ expect(dependencies.remoteClientAuthRuntime.renameClient).not.toHaveBeenCalled();
1358
+ } else {
1359
+ expect(response.body.client).toEqual({ ...other, label: 'Renamed other' });
1360
+ expect(dependencies.remoteClientAuthRuntime.renameClient).toHaveBeenCalledWith(other.id, 'Renamed other');
1361
+ await request(app).patch('/api/client-auth/clients/missing').send({ label: 'Name' }).expect(404);
1362
+ }
1363
+ });
1364
+
1365
+ it('requires authentication and propagates rename failures', async () => {
1366
+ const app = express();
1367
+ const dependencies = createDependencies({ resolveAuthContext: async () => null });
1368
+ dependencies.uiAuthController.requireSessionAuth = (_req, res) => res.status(401).json({ error: 'Unauthorized' });
1369
+ registerAuthAndAccessRoutes(app, dependencies);
1370
+ await request(app).patch('/api/client-auth/clients/other').send({ label: 'Name' }).expect(401);
1371
+ expect(dependencies.remoteClientAuthRuntime.renameClient).not.toHaveBeenCalled();
1372
+
1373
+ dependencies.uiAuthController.resolveAuthContext.mockResolvedValue({ type: 'session' });
1374
+ dependencies.remoteClientAuthRuntime.renameClient.mockRejectedValue(Object.assign(new Error('Invalid label'), { status: 400 }));
1375
+ await request(app).patch('/api/client-auth/clients/other').send({ label: '' }).expect(400);
1376
+ dependencies.remoteClientAuthRuntime.renameClient.mockRejectedValue(new Error('Write failed'));
1377
+ await request(app).patch('/api/client-auth/clients/other').send({ label: 'Name' }).expect(500);
1378
+ });
1379
+
1337
1380
  it('creates, lists, and revokes remote client tokens', async () => {
1338
1381
  const app = express();
1339
1382
  const dependencies = createDependencies();