@loicngr/kobo 2.1.26 → 2.1.29
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/AGENTS.md +7 -4
- package/CHANGELOG.md +15 -0
- package/CONFIGURATION.md +124 -71
- package/CONTRIBUTING.md +1 -1
- package/README.md +1 -1
- package/SECURITY.md +33 -6
- package/dist/mcp-server/kobo-tasks-server.js +36 -1021
- package/dist/mcp-server/managed-mcp-dispatcher.js +960 -0
- package/dist/mcp-server/workspace-dialogue-client.js +26 -1
- package/dist/server/index.js +52 -19
- package/dist/server/middleware/host-check-middleware.js +8 -5
- package/dist/server/middleware/network-auth-middleware.js +51 -29
- package/dist/server/middleware/security-headers-middleware.js +19 -0
- package/dist/server/middleware/traffic-limits-middleware.js +74 -0
- package/dist/server/routes/auth.js +73 -0
- package/dist/server/routes/internal-mcp.js +137 -0
- package/dist/server/routes/mcp-credentials.js +30 -0
- package/dist/server/routes/mcp.js +156 -76
- package/dist/server/routes/workspaces.js +19 -3
- package/dist/server/services/agent/engines/claude-code/resolve-binary.js +1 -1
- package/dist/server/services/agent/orchestrator.js +27 -17
- package/dist/server/services/dev-server-service.js +68 -4
- package/dist/server/services/environment-check-service.js +3 -2
- package/dist/server/services/external-mcp-credentials-service.js +110 -0
- package/dist/server/services/forge/github/provider.js +15 -3
- package/dist/server/services/forge/types.js +8 -0
- package/dist/server/services/internal-mcp-capability-service.js +98 -0
- package/dist/server/services/mcp-connection-info-service.js +1 -1
- package/dist/server/services/memory-mcp-service.js +9 -7
- package/dist/server/services/memory-service.js +15 -0
- package/dist/server/services/network-access-service.js +43 -73
- package/dist/server/services/network-session-service.js +182 -0
- package/dist/server/services/settings-service.js +29 -7
- package/dist/server/services/terminal-websocket-service.js +5 -0
- package/dist/server/services/websocket-service.js +5 -0
- package/dist/server/utils/paths.js +2 -1
- package/dist/server/utils/startup-display.js +1 -2
- package/dist/shared/models.js +6 -0
- package/docker-compose.vps.yml +6 -0
- package/docs/deployment-vps.md +24 -12
- package/docs/getting-started.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/package.json +14 -14
- package/src/client/dist/pwa/assets/ActivityFeed-CiTe2VUQ.js +2 -0
- package/src/client/dist/pwa/assets/{AutoLoopFinalReviewSettings-DOr41E2Y.js → AutoLoopFinalReviewSettings-v3jXoH_Q.js} +1 -1
- package/src/client/dist/pwa/assets/{ChangelogPage-DOCgZMpn.js → ChangelogPage-C5KjAOMG.js} +1 -1
- package/src/client/dist/pwa/assets/{ClosePopup-CRV2hxHS.js → ClosePopup-rU8Bm-mC.js} +1 -1
- package/src/client/dist/pwa/assets/ComparisonPanel-XXyzPEzW.js +2 -0
- package/src/client/dist/pwa/assets/CreatePage-MYCexRJu.js +2 -0
- package/src/client/dist/pwa/assets/{DashboardPage-dBpJaQSb.js → DashboardPage-DyhUu26C.js} +1 -1
- package/src/client/dist/pwa/assets/DiffViewer-CvsmC9d5.js +8 -0
- package/src/client/dist/pwa/assets/{DocumentsPanel-AqLx0MzB.js → DocumentsPanel-ItndGxeB.js} +1 -1
- package/src/client/dist/pwa/assets/{DrawerToggleButton-BLWaFgh0.js → DrawerToggleButton-DfzS_CD8.js} +1 -1
- package/src/client/dist/pwa/assets/GitPanel-C1ybi9bZ.js +3 -0
- package/src/client/dist/pwa/assets/{GitPanel-CRaLJzjc.css → GitPanel-yHvU38dp.css} +1 -1
- package/src/client/dist/pwa/assets/{HealthPage-DeryVLyz.js → HealthPage-C1u35ANL.js} +1 -1
- package/src/client/dist/pwa/assets/MainLayout-BEAGPqei.js +2 -0
- package/src/client/dist/pwa/assets/{MemoryPanel-B34DENtz.js → MemoryPanel-CZzI7_nY.js} +1 -1
- package/src/client/dist/pwa/assets/MemoryProposalList-DqNTycY1.js +1 -0
- package/src/client/dist/pwa/assets/{QBadge-CW3Lw-kL.js → QBadge-DiwbRpte.js} +1 -1
- package/src/client/dist/pwa/assets/{QBanner-BT2ZemYu.js → QBanner-Cw89HSjk.js} +1 -1
- package/src/client/dist/pwa/assets/QBtn-kfhHFy7Q.js +1 -0
- package/src/client/dist/pwa/assets/{QBtnDropdown-BPcK_rVs.js → QBtnDropdown-D7ml7zHo.js} +1 -1
- package/src/client/dist/pwa/assets/{QBtnGroup-CnM4vSJA.js → QBtnGroup-Bioxjp9q.js} +1 -1
- package/src/client/dist/pwa/assets/{QBtnToggle-DccOOtbh.js → QBtnToggle-Bod-x9im.js} +1 -1
- package/src/client/dist/pwa/assets/{QChip-Be16M4Ig.js → QChip-DvhtPv_L.js} +1 -1
- package/src/client/dist/pwa/assets/{QExpansionItem-CpwQ8L_F.js → QExpansionItem-BVb067oc.js} +1 -1
- package/src/client/dist/pwa/assets/{QItemSection-MdJ1_ZK7.js → QItemSection-ahqRK4JF.js} +1 -1
- package/src/client/dist/pwa/assets/{QList-Cx1UfaUm.js → QList-CAUl0v66.js} +1 -1
- package/src/client/dist/pwa/assets/{QMarkupTable-UhP7fVUp.js → QMarkupTable-B4LLAly-.js} +1 -1
- package/src/client/dist/pwa/assets/{QPage-CqkGHokx.js → QPage-DIy_mRoT.js} +1 -1
- package/src/client/dist/pwa/assets/{QScrollArea-BkNSVl7s.js → QScrollArea-CUDWkEHh.js} +1 -1
- package/src/client/dist/pwa/assets/QSelect-DOnv_ry0.js +1 -0
- package/src/client/dist/pwa/assets/{QSpinnerDots-xdWyaE1i.js → QSpinnerDots-Bea6hAuJ.js} +1 -1
- package/src/client/dist/pwa/assets/{QTooltip-Dp8fitHV.js → QTooltip-B5QJIU3p.js} +1 -1
- package/src/client/dist/pwa/assets/{SchedulePanel-Ck8LKDCa.js → SchedulePanel-IJOcrHgk.js} +1 -1
- package/src/client/dist/pwa/assets/{SearchPage-BAUZg1zq.js → SearchPage-BpTRWtne.js} +1 -1
- package/src/client/dist/pwa/assets/{SettingsPage-Dcq1Jeoo.css → SettingsPage-B4rmd2WX.css} +1 -1
- package/src/client/dist/pwa/assets/SettingsPage-CFaRN9Pk.js +9 -0
- package/src/client/dist/pwa/assets/{SplitWorkspacePage-CH8OHmtn.js → SplitWorkspacePage-BZ8wN4ii.js} +1 -1
- package/src/client/dist/pwa/assets/{TerminalPanel-Dz9EE-Ph.css → TerminalPanel-BcbwuFVR.css} +1 -1
- package/src/client/dist/pwa/assets/TerminalPanel-pY-RGBb6.js +16 -0
- package/src/client/dist/pwa/assets/{ToolCallItem-CLtGTV6w.js → ToolCallItem-C9ksVseJ.js} +4 -4
- package/src/client/dist/pwa/assets/{TouchPan-9SH2dx6m.js → TouchPan-DRynDIve.js} +1 -1
- package/src/client/dist/pwa/assets/{TourReplayButton-Dw4sGf9B.js → TourReplayButton-D4h1QR6C.js} +1 -1
- package/src/client/dist/pwa/assets/WorkspaceAttentionLabels-Bkj2DhOn.js +1 -0
- package/src/client/dist/pwa/assets/WorkspacePage-BTJnap9a.js +4 -0
- package/src/client/dist/pwa/assets/{browser-CC4W8r76.js → browser-BcOi2ES2.js} +2 -2
- package/src/client/dist/pwa/assets/build-path-tree-mDco3pi7.js +1 -0
- package/src/client/dist/pwa/assets/custom-sounds-T5HKzrWh.js +1 -0
- package/src/client/dist/pwa/assets/de-suqWUSXa.js +17 -0
- package/src/client/dist/pwa/assets/detached-fullscreen-CHqMxB-8.js +1 -0
- package/src/client/dist/pwa/assets/dom-CRPVbmiy.js +2 -0
- package/src/client/dist/pwa/assets/ecl-94qehhXe.js +1 -0
- package/src/client/dist/pwa/assets/{editor.api-BwfxvQ88.js → editor.api-CRrihXg2.js} +9 -9
- package/src/client/dist/pwa/assets/editor.worker-xrHNhPSq.js +33 -0
- package/src/client/dist/pwa/assets/{efforts-B3I7w5bs.js → efforts-C-p9DzkL.js} +2 -2
- package/src/client/dist/pwa/assets/es-Dcptbz5U.js +17 -0
- package/src/client/dist/pwa/assets/expand-template-gIwrPbeS.js +1 -0
- package/src/client/dist/pwa/assets/{format-CJIzi3Vf.js → format-DeBnwmgW.js} +48 -48
- package/src/client/dist/pwa/assets/{formatters-DQ8cP7nO.js → formatters-CxZZ_WTb.js} +1 -1
- package/src/client/dist/pwa/assets/fr-Db70bqVq.js +17 -0
- package/src/client/dist/pwa/assets/{freemarker2-DbpVO-cL.js → freemarker2-h7OLlqTn.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-BHve0PpA.js → handlebars-BCWmi1-q.js} +1 -1
- package/src/client/dist/pwa/assets/{html-BhUkdFPz.js → html-MUMpfUBd.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-DLprfNLs.js +1 -0
- package/src/client/dist/pwa/assets/index-C0MApoXN.css +1 -0
- package/src/client/dist/pwa/assets/index-DLoUeovT.js +20 -0
- package/src/client/dist/pwa/assets/it-Cloo58Nn.js +17 -0
- package/src/client/dist/pwa/assets/{javascript-BLAorPa3.js → javascript-CHDwijEy.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-CJGF-h-f.js → liquid-dFrnLevt.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-Cm0oNz_l.js → mdx-BHS7JVD4.js} +1 -1
- package/src/client/dist/pwa/assets/monaco-contributions-DCWZe7nX.js +53 -0
- package/src/client/dist/pwa/assets/{monaco.contribution-hr9s26Ud.js → monaco.contribution-Dx0gcAn4.js} +2 -2
- package/src/client/dist/pwa/assets/network-auth-DDMAzwYH.js +1 -0
- package/src/client/dist/pwa/assets/{notify-theme-EcbxuH_8.js → notify-theme-JuVT96rq.js} +1 -1
- package/src/client/dist/pwa/assets/{permissionModes-DLWiBfdb.js → permissionModes-D-Nexnoo.js} +1 -1
- package/src/client/dist/pwa/assets/{private.use-form-DY1BkePL.js → private.use-form-BcspuzM9.js} +1 -1
- package/src/client/dist/pwa/assets/{project-color-vDuTaTJn.js → project-color-DihsE4yN.js} +1 -1
- package/src/client/dist/pwa/assets/{python-BEjbzFAQ.js → python-BtjZnK9y.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-BJPT7Wxx.js → razor-DBcOGz0a.js} +1 -1
- package/src/client/dist/pwa/assets/reactivity.esm-bundler-Dplx1ILy.js +1 -0
- package/src/client/dist/pwa/assets/render-chat-markdown-C9ihAYBt.js +67 -0
- package/src/client/dist/pwa/assets/runtime-core.esm-bundler-BAUgNzMK.js +1 -0
- package/src/client/dist/pwa/assets/{typescript-DPqM46Ef.js → typescript-B320c7wU.js} +1 -1
- package/src/client/dist/pwa/assets/{use-checkbox-v4aAbE6K.js → use-checkbox-DUxgXR4e.js} +1 -1
- package/src/client/dist/pwa/assets/{use-id-BrdYXce0.js → use-id-B7ieAx2m.js} +1 -1
- package/src/client/dist/pwa/assets/use-is-mobile-CHmaewI1.js +1 -0
- package/src/client/dist/pwa/assets/{use-search-index-status-BwwrB410.js → use-search-index-status-B95GPaJP.js} +1 -1
- package/src/client/dist/pwa/assets/use-slide-transition-BJh9xGMa.js +1 -0
- package/src/client/dist/pwa/assets/use-tours-CU5oA7cz.css +1 -0
- package/src/client/dist/pwa/assets/use-tours-C_5R1Y5D.js +2 -0
- package/src/client/dist/pwa/assets/{use-worktree-restore-tzd2FESU.js → use-worktree-restore-Cj8eGE9s.js} +1 -1
- package/src/client/dist/pwa/assets/{workspace-templates-CubzsZUU.js → workspace-templates-B9UETKER.js} +1 -1
- package/src/client/dist/pwa/assets/{xml-C6SVzw5_.js → xml-uK8LK4OB.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-OpCqnv4g.js → yaml-B2D1l_tz.js} +1 -1
- package/src/client/dist/pwa/index.html +10 -10
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/mcp-server/README.md +13 -19
- package/src/mcp-server/kobo-tasks-server.ts +42 -1146
- package/src/mcp-server/managed-mcp-dispatcher.ts +1071 -0
- package/src/mcp-server/workspace-dialogue-client.ts +26 -1
- package/src/client/dist/pwa/assets/ActivityFeed-DfYrVNEh.js +0 -2
- package/src/client/dist/pwa/assets/ComparisonPanel-b5nTBjK6.js +0 -2
- package/src/client/dist/pwa/assets/CreatePage-B7VPHEqm.js +0 -2
- package/src/client/dist/pwa/assets/DiffViewer-BLQz2Qyx.js +0 -8
- package/src/client/dist/pwa/assets/GitPanel-Ba7ltCsM.js +0 -3
- package/src/client/dist/pwa/assets/MainLayout-B2XvjdWi.js +0 -2
- package/src/client/dist/pwa/assets/MemoryProposalList-BpN_OU8J.js +0 -1
- package/src/client/dist/pwa/assets/QBtn-BUSdPaf4.js +0 -1
- package/src/client/dist/pwa/assets/QSelect-DrLpbvcr.js +0 -1
- package/src/client/dist/pwa/assets/SettingsPage-Cj6kSVT5.js +0 -9
- package/src/client/dist/pwa/assets/TerminalPanel-CBmZ0IzQ.js +0 -16
- package/src/client/dist/pwa/assets/WorkspaceAttentionLabels-wd_sObJa.js +0 -1
- package/src/client/dist/pwa/assets/WorkspacePage-D4ODPOG-.js +0 -4
- package/src/client/dist/pwa/assets/build-path-tree-BBuyB-my.js +0 -1
- package/src/client/dist/pwa/assets/custom-sounds-BbPNRzAs.js +0 -1
- package/src/client/dist/pwa/assets/de-D9VzPDGF.js +0 -17
- package/src/client/dist/pwa/assets/detached-fullscreen-Du3TcYdX.js +0 -1
- package/src/client/dist/pwa/assets/dom-BtYTvLeN.js +0 -2
- package/src/client/dist/pwa/assets/ecl-D05T4iGw.js +0 -1
- package/src/client/dist/pwa/assets/editor.worker-BLJF_oQx.js +0 -33
- package/src/client/dist/pwa/assets/es-3R4MJuTt.js +0 -17
- package/src/client/dist/pwa/assets/expand-template-Ci9UjnxZ.js +0 -1
- package/src/client/dist/pwa/assets/fr-DXwDVnJ4.js +0 -17
- package/src/client/dist/pwa/assets/i18n-CGLQAADf.js +0 -1
- package/src/client/dist/pwa/assets/index-C8zAZ-k-.css +0 -1
- package/src/client/dist/pwa/assets/index-CNcXBpzz.js +0 -20
- package/src/client/dist/pwa/assets/it-T9AjnG42.js +0 -17
- package/src/client/dist/pwa/assets/monaco-contributions-nBg6wf05.js +0 -53
- package/src/client/dist/pwa/assets/network-auth-C850jGfI.js +0 -1
- package/src/client/dist/pwa/assets/reactivity.esm-bundler-DY1JkAEA.js +0 -1
- package/src/client/dist/pwa/assets/render-chat-markdown-CafzCcmu.js +0 -69
- package/src/client/dist/pwa/assets/runtime-core.esm-bundler-Bqg71Vll.js +0 -1
- package/src/client/dist/pwa/assets/use-is-mobile-CDB_rzM9.js +0 -1
- package/src/client/dist/pwa/assets/use-slide-transition-B_Nu6u-t.js +0 -1
- package/src/client/dist/pwa/assets/use-tours-B0KtCS9w.js +0 -2
- package/src/client/dist/pwa/assets/use-tours-CTZuBZOi.css +0 -1
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { isLoopbackAddress, resolveProxyOrigin } from './network-access-service.js';
|
|
3
|
+
const sessions = new Map();
|
|
4
|
+
const tickets = new Map();
|
|
5
|
+
const sockets = new Map();
|
|
6
|
+
const revokedSockets = new WeakSet();
|
|
7
|
+
const hash = (value) => createHash('sha256').update(value).digest('hex');
|
|
8
|
+
const fingerprint = (p) => hash(JSON.stringify([p.networkAccessEnabled, !!p.networkAccessBehindProxy, p.networkAccessToken]));
|
|
9
|
+
const lifetime = 12 * 60 * 60 * 1000;
|
|
10
|
+
export function isNetworkTransportAllowed(address, policy) {
|
|
11
|
+
return policy.networkAccessEnabled || (!policy.networkAccessBehindProxy && isLoopbackAddress(address));
|
|
12
|
+
}
|
|
13
|
+
function prune() {
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
for (const [id, session] of sessions)
|
|
16
|
+
if (session.expiresAt <= now)
|
|
17
|
+
sessions.delete(id);
|
|
18
|
+
for (const [id, ticket] of tickets)
|
|
19
|
+
if (ticket.expiresAt <= now)
|
|
20
|
+
tickets.delete(id);
|
|
21
|
+
}
|
|
22
|
+
export function createBrowserSession(policy) {
|
|
23
|
+
prune();
|
|
24
|
+
if (sessions.size >= 256)
|
|
25
|
+
throw new Error('Session capacity reached');
|
|
26
|
+
const session = {
|
|
27
|
+
token: randomBytes(32).toString('base64url'),
|
|
28
|
+
expiresAt: Date.now() + lifetime,
|
|
29
|
+
policy: fingerprint(policy),
|
|
30
|
+
};
|
|
31
|
+
const { token, ...record } = session;
|
|
32
|
+
sessions.set(hash(token), record);
|
|
33
|
+
return session;
|
|
34
|
+
}
|
|
35
|
+
export function getBrowserSession(token, policy) {
|
|
36
|
+
if (!token || token.length > 128)
|
|
37
|
+
return undefined;
|
|
38
|
+
const record = sessions.get(hash(token));
|
|
39
|
+
if (!record || record.expiresAt <= Date.now() || record.policy !== fingerprint(policy))
|
|
40
|
+
return undefined;
|
|
41
|
+
return { ...record, token };
|
|
42
|
+
}
|
|
43
|
+
function sessionValid(session, policy) {
|
|
44
|
+
return !session || !!getBrowserSession(session.token, policy);
|
|
45
|
+
}
|
|
46
|
+
export function createWebSocketTicket(path, policy, session) {
|
|
47
|
+
if (!/^\/ws(?:\/terminal\/[A-Za-z0-9_-]{1,200})?$/.test(path))
|
|
48
|
+
throw new Error('Invalid WebSocket path');
|
|
49
|
+
prune();
|
|
50
|
+
if (tickets.size >= 1024)
|
|
51
|
+
throw new Error('Ticket capacity reached');
|
|
52
|
+
if (!sessionValid(session, policy))
|
|
53
|
+
throw new Error('Session expired');
|
|
54
|
+
const token = randomBytes(32).toString('base64url');
|
|
55
|
+
tickets.set(hash(token), { path, policy: fingerprint(policy), expiresAt: Date.now() + 60_000, session });
|
|
56
|
+
return token;
|
|
57
|
+
}
|
|
58
|
+
export function consumeWebSocketTicket(token, path, policy) {
|
|
59
|
+
if (!token || token.length > 128)
|
|
60
|
+
return undefined;
|
|
61
|
+
const id = hash(token);
|
|
62
|
+
const ticket = tickets.get(id);
|
|
63
|
+
tickets.delete(id);
|
|
64
|
+
if (!ticket ||
|
|
65
|
+
ticket.path !== path ||
|
|
66
|
+
ticket.expiresAt <= Date.now() ||
|
|
67
|
+
ticket.policy !== fingerprint(policy) ||
|
|
68
|
+
!sessionValid(ticket.session, policy))
|
|
69
|
+
return undefined;
|
|
70
|
+
return ticket;
|
|
71
|
+
}
|
|
72
|
+
function terminate(ws) {
|
|
73
|
+
const grant = sockets.get(ws);
|
|
74
|
+
if (grant) {
|
|
75
|
+
grant.revoked = true;
|
|
76
|
+
clearTimeout(grant.timer);
|
|
77
|
+
sockets.delete(ws);
|
|
78
|
+
}
|
|
79
|
+
if (!revokedSockets.has(ws)) {
|
|
80
|
+
revokedSockets.add(ws);
|
|
81
|
+
ws.terminate();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export function registerNetworkSocket(ws, address, policy, current, session) {
|
|
85
|
+
let currentPolicy;
|
|
86
|
+
try {
|
|
87
|
+
currentPolicy = current();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
terminate(ws);
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
if (!isNetworkTransportAllowed(address, currentPolicy) ||
|
|
94
|
+
sockets.size >= 256 ||
|
|
95
|
+
[...sockets.values()].filter((g) => g.address === address).length >= 64 ||
|
|
96
|
+
!sessionValid(session, currentPolicy) ||
|
|
97
|
+
fingerprint(policy) !== fingerprint(currentPolicy)) {
|
|
98
|
+
terminate(ws);
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
const expiresAt = session?.expiresAt ?? Date.now() + lifetime;
|
|
102
|
+
const timer = setTimeout(() => terminate(ws), Math.max(0, expiresAt - Date.now()));
|
|
103
|
+
timer.unref?.();
|
|
104
|
+
sockets.set(ws, {
|
|
105
|
+
address,
|
|
106
|
+
policy: fingerprint(policy),
|
|
107
|
+
current,
|
|
108
|
+
session,
|
|
109
|
+
expiresAt,
|
|
110
|
+
revoked: false,
|
|
111
|
+
timer,
|
|
112
|
+
count: 0,
|
|
113
|
+
window: Date.now(),
|
|
114
|
+
});
|
|
115
|
+
ws.once('close', () => {
|
|
116
|
+
clearTimeout(timer);
|
|
117
|
+
sockets.delete(ws);
|
|
118
|
+
revokedSockets.add(ws);
|
|
119
|
+
});
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
export function isNetworkSocketAuthorized(ws) {
|
|
123
|
+
if (revokedSockets.has(ws))
|
|
124
|
+
return false;
|
|
125
|
+
const grant = sockets.get(ws);
|
|
126
|
+
if (!grant)
|
|
127
|
+
return true; // Unregistered service-level sockets do not participate in network authentication.
|
|
128
|
+
let policy;
|
|
129
|
+
try {
|
|
130
|
+
policy = grant.current();
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
terminate(ws);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
if (grant.revoked ||
|
|
137
|
+
grant.expiresAt <= Date.now() ||
|
|
138
|
+
!isNetworkTransportAllowed(grant.address, policy) ||
|
|
139
|
+
grant.policy !== fingerprint(policy) ||
|
|
140
|
+
!sessionValid(grant.session, policy)) {
|
|
141
|
+
terminate(ws);
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
export function acceptNetworkSocketMessage(ws) {
|
|
147
|
+
if (!isNetworkSocketAuthorized(ws))
|
|
148
|
+
return false;
|
|
149
|
+
const grant = sockets.get(ws);
|
|
150
|
+
if (!grant)
|
|
151
|
+
return true;
|
|
152
|
+
if (Date.now() - grant.window >= 1000) {
|
|
153
|
+
grant.window = Date.now();
|
|
154
|
+
grant.count = 0;
|
|
155
|
+
}
|
|
156
|
+
if (++grant.count > 240) {
|
|
157
|
+
terminate(ws);
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
export function revokeBrowserSession(token) {
|
|
163
|
+
sessions.delete(hash(token));
|
|
164
|
+
for (const [id, ticket] of tickets)
|
|
165
|
+
if (ticket.session?.token === token)
|
|
166
|
+
tickets.delete(id);
|
|
167
|
+
for (const [ws, grant] of sockets)
|
|
168
|
+
if (grant.session?.token === token)
|
|
169
|
+
terminate(ws);
|
|
170
|
+
}
|
|
171
|
+
export function revokeNetworkSessions() {
|
|
172
|
+
sessions.clear();
|
|
173
|
+
tickets.clear();
|
|
174
|
+
for (const ws of sockets.keys())
|
|
175
|
+
terminate(ws);
|
|
176
|
+
}
|
|
177
|
+
export function browserCookieName(policy) {
|
|
178
|
+
return secureBrowserCookie(policy) ? '__Host-kobo-session' : 'kobo-session';
|
|
179
|
+
}
|
|
180
|
+
export function secureBrowserCookie(policy) {
|
|
181
|
+
return !!policy.networkAccessBehindProxy && !resolveProxyOrigin()?.startsWith('http:');
|
|
182
|
+
}
|
|
@@ -12,6 +12,7 @@ import { getSettingsPath } from '../utils/paths.js';
|
|
|
12
12
|
import { InvalidWorktreesPathError, resolveGlobalWorktreesRoot, sanitizeWorktreesPath, validateWorktreesPath, } from '../utils/worktree-paths.js';
|
|
13
13
|
import { DEFAULT_NOTION_INITIAL_PROMPT, DEFAULT_SENTRY_INITIAL_PROMPT } from './initial-prompt-template-service.js';
|
|
14
14
|
import { LEGACY_AGNOSTIC_AUTO_LOOP_REVIEW_GATE, LEGACY_AGNOSTIC_REVIEW_TEMPLATE, LEGACY_CI_FIX_PROMPT_TEMPLATE, LEGACY_FINALIZATION_PROMPT, LEGACY_NOTION_INITIAL_PROMPT, LEGACY_PR_PROMPT_TEMPLATE, LEGACY_SENTRY_INITIAL_PROMPT, } from './legacy-public-prompts.js';
|
|
15
|
+
import { revokeNetworkSessions } from './network-session-service.js';
|
|
15
16
|
import { DEFAULT_REVIEW_PROMPT_TEMPLATE } from './review-template-service.js';
|
|
16
17
|
import { DEFAULT_CHANGE_SOURCE_BRANCH_SCRIPT, DEFAULT_MODEL_BY_ENGINE, FRESH_MODEL_BY_ENGINE, } from './settings-defaults.js';
|
|
17
18
|
import { AGNOSTIC_PROMPTS } from './skill-suite-prompts.js';
|
|
@@ -1293,6 +1294,7 @@ function readSettings() {
|
|
|
1293
1294
|
}
|
|
1294
1295
|
function readSettingsFromDisk() {
|
|
1295
1296
|
ensureSettingsPathInitialized();
|
|
1297
|
+
protectSettingsFiles();
|
|
1296
1298
|
if (!fs.existsSync(settingsFilePath)) {
|
|
1297
1299
|
const defaults = defaultSettings();
|
|
1298
1300
|
writeSettings(defaults);
|
|
@@ -1334,6 +1336,22 @@ function readSettingsFromDisk() {
|
|
|
1334
1336
|
}
|
|
1335
1337
|
/** Number of `settings.json.backup-*` files kept; older ones are rotated out. */
|
|
1336
1338
|
const SETTINGS_BACKUP_KEEP = 5;
|
|
1339
|
+
/** Repair legacy permissions before reading secrets, without following file symlinks. */
|
|
1340
|
+
function protectSettingsFiles() {
|
|
1341
|
+
const dir = path.dirname(settingsFilePath);
|
|
1342
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
1343
|
+
fs.chmodSync(dir, 0o700);
|
|
1344
|
+
const basename = path.basename(settingsFilePath);
|
|
1345
|
+
for (const name of fs.readdirSync(dir)) {
|
|
1346
|
+
if (name !== basename && name !== `${basename}.tmp` && !name.startsWith(`${basename}.backup-`))
|
|
1347
|
+
continue;
|
|
1348
|
+
const file = path.join(dir, name);
|
|
1349
|
+
const stat = fs.lstatSync(file);
|
|
1350
|
+
if (!stat.isFile() || stat.nlink !== 1)
|
|
1351
|
+
throw new Error('[settings] Refusing unsafe settings file');
|
|
1352
|
+
fs.chmodSync(file, 0o600);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1337
1355
|
/**
|
|
1338
1356
|
* Keep only the `SETTINGS_BACKUP_KEEP` most recent `settings.json.backup-*`
|
|
1339
1357
|
* files in `dir`, deleting the rest. Best-effort — never throws.
|
|
@@ -1368,7 +1386,7 @@ function createSettingsBackupIfPresent() {
|
|
|
1368
1386
|
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
1369
1387
|
settingsBackupSequence += 1;
|
|
1370
1388
|
const backupPath = path.join(dir, `settings.json.backup-${stamp}-${settingsBackupSequence}`);
|
|
1371
|
-
fs.
|
|
1389
|
+
fs.writeFileSync(backupPath, fs.readFileSync(settingsFilePath), { mode: 0o600, flag: 'wx' });
|
|
1372
1390
|
pruneSettingsBackups(dir);
|
|
1373
1391
|
}
|
|
1374
1392
|
function writeSettings(settings, options) {
|
|
@@ -1376,14 +1394,11 @@ function writeSettings(settings, options) {
|
|
|
1376
1394
|
invalidateSettingsCache();
|
|
1377
1395
|
ensureSettingsPathInitialized();
|
|
1378
1396
|
const tmpPath = `${settingsFilePath}.tmp`;
|
|
1379
|
-
|
|
1380
|
-
if (!fs.existsSync(dir)) {
|
|
1381
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
1382
|
-
}
|
|
1397
|
+
protectSettingsFiles();
|
|
1383
1398
|
if (options?.backup) {
|
|
1384
1399
|
createSettingsBackupIfPresent();
|
|
1385
1400
|
}
|
|
1386
|
-
fs.writeFileSync(tmpPath, JSON.stringify(settings, null, 2), 'utf-8');
|
|
1401
|
+
fs.writeFileSync(tmpPath, JSON.stringify(settings, null, 2), { encoding: 'utf-8', mode: 0o600 });
|
|
1387
1402
|
fs.renameSync(tmpPath, settingsFilePath);
|
|
1388
1403
|
}
|
|
1389
1404
|
/** Read and return the full settings object, creating defaults if missing. */
|
|
@@ -1823,7 +1838,9 @@ export function updateGlobalSettings(input) {
|
|
|
1823
1838
|
* (which would leave the server bound wide but unauthenticatable after a restart).
|
|
1824
1839
|
*/
|
|
1825
1840
|
export function updateNetworkAccessSettings(patch) {
|
|
1826
|
-
const
|
|
1841
|
+
const current = readSettings();
|
|
1842
|
+
// Keep cached policy intact until persistence succeeds.
|
|
1843
|
+
const settings = { ...current, global: { ...current.global } };
|
|
1827
1844
|
if (typeof patch.networkAccessEnabled === 'boolean') {
|
|
1828
1845
|
settings.global.networkAccessEnabled = patch.networkAccessEnabled;
|
|
1829
1846
|
}
|
|
@@ -1834,6 +1851,11 @@ export function updateNetworkAccessSettings(patch) {
|
|
|
1834
1851
|
settings.global.networkAccessBehindProxy = patch.networkAccessBehindProxy;
|
|
1835
1852
|
}
|
|
1836
1853
|
writeSettings(settings, { backup: true });
|
|
1854
|
+
if (current.global.networkAccessEnabled !== settings.global.networkAccessEnabled ||
|
|
1855
|
+
current.global.networkAccessToken !== settings.global.networkAccessToken ||
|
|
1856
|
+
current.global.networkAccessBehindProxy !== settings.global.networkAccessBehindProxy) {
|
|
1857
|
+
revokeNetworkSessions();
|
|
1858
|
+
}
|
|
1837
1859
|
return settings.global;
|
|
1838
1860
|
}
|
|
1839
1861
|
function ensureGlobalWorktreesRootExists(worktreesPath) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assertWorkspaceLifecycleAvailable, isWorkspaceLifecycleBusy } from '../utils/workspace-lifecycle-guard.js';
|
|
2
|
+
import { acceptNetworkSocketMessage, isNetworkSocketAuthorized } from './network-session-service.js';
|
|
2
3
|
import { createTerminal, getTerminal } from './terminal-service.js';
|
|
3
4
|
import { getWorkspace } from './workspace-service.js';
|
|
4
5
|
const MAX_BUFFERED_BYTES = 1024 * 1024;
|
|
@@ -31,6 +32,8 @@ export function handleTerminalConnection(ws, workspaceId, isShuttingDown = () =>
|
|
|
31
32
|
ws.terminate();
|
|
32
33
|
}
|
|
33
34
|
function send(data) {
|
|
35
|
+
if (!isNetworkSocketAuthorized(ws))
|
|
36
|
+
return false;
|
|
34
37
|
if (disposed || ws.readyState !== 1)
|
|
35
38
|
return false;
|
|
36
39
|
if (ws.bufferedAmount + Buffer.byteLength(data) > MAX_BUFFERED_BYTES) {
|
|
@@ -72,6 +75,8 @@ export function handleTerminalConnection(ws, workspaceId, isShuttingDown = () =>
|
|
|
72
75
|
ws.on('close', dispose);
|
|
73
76
|
ws.on('error', disconnect);
|
|
74
77
|
ws.on('message', (data, isBinary) => {
|
|
78
|
+
if (!acceptNetworkSocketMessage(ws))
|
|
79
|
+
return;
|
|
75
80
|
if (disposed || ws.readyState !== 1 || isShuttingDown())
|
|
76
81
|
return;
|
|
77
82
|
if (isBinary) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { nanoid } from 'nanoid';
|
|
2
2
|
import { getDb } from '../db/index.js';
|
|
3
3
|
import { recordActivity } from './activity-service.js';
|
|
4
|
+
import { acceptNetworkSocketMessage, isNetworkSocketAuthorized } from './network-session-service.js';
|
|
4
5
|
import { getAllPersistedSnapshots } from './usage/db.js';
|
|
5
6
|
// ── State ──────────────────────────────────────────────────────────────────────
|
|
6
7
|
/** Maps each WS client to the set of workspaceIds they are subscribed to */
|
|
@@ -11,6 +12,8 @@ const clients = new Map();
|
|
|
11
12
|
const MAX_BUFFERED_BYTES = 1024 * 1024;
|
|
12
13
|
/** A dropped frame must end the connection so its replay cursor cannot skip it. */
|
|
13
14
|
function sendFrame(ws, message) {
|
|
15
|
+
if (!isNetworkSocketAuthorized(ws))
|
|
16
|
+
return;
|
|
14
17
|
if (ws.readyState !== 1)
|
|
15
18
|
return;
|
|
16
19
|
if (ws.bufferedAmount > MAX_BUFFERED_BYTES) {
|
|
@@ -92,6 +95,8 @@ export function handleConnection(ws) {
|
|
|
92
95
|
console.error('[ws] usage hydration failed:', err);
|
|
93
96
|
}
|
|
94
97
|
ws.on('message', (data) => {
|
|
98
|
+
if (!acceptNetworkSocketMessage(ws))
|
|
99
|
+
return;
|
|
95
100
|
let msg;
|
|
96
101
|
try {
|
|
97
102
|
const parsed = JSON.parse(data.toString());
|
|
@@ -67,8 +67,9 @@ export function getKoboHome() {
|
|
|
67
67
|
export function ensureKoboHome() {
|
|
68
68
|
const dir = getKoboHome();
|
|
69
69
|
if (!fs.existsSync(dir)) {
|
|
70
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
70
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
71
71
|
}
|
|
72
|
+
fs.chmodSync(dir, 0o700);
|
|
72
73
|
return dir;
|
|
73
74
|
}
|
|
74
75
|
/**
|
|
@@ -32,8 +32,7 @@ export function formatStartupBanner(options) {
|
|
|
32
32
|
if (options.networkEnabled) {
|
|
33
33
|
for (const lanUrl of options.lanUrls ?? [])
|
|
34
34
|
lines.push(` LAN: ${lanUrl}`);
|
|
35
|
-
|
|
36
|
-
lines.push(` Token: ${options.token}`);
|
|
35
|
+
lines.push(' Authentication required; retrieve the token from local settings.');
|
|
37
36
|
}
|
|
38
37
|
const release = parseChangelog(options.changelog ?? '').find((entry) => entry.version === options.version);
|
|
39
38
|
const highlights = (release?.notes ?? '')
|
package/dist/shared/models.js
CHANGED
|
@@ -35,6 +35,12 @@ export const CLAUDE_MODELS = [
|
|
|
35
35
|
i18nLabelKey: 'model.sonnet55',
|
|
36
36
|
i18nDescriptionKey: 'model.sonnet55Description',
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
id: 'claude-haiku-5-5',
|
|
40
|
+
label: 'Haiku 5.5',
|
|
41
|
+
i18nLabelKey: 'model.haiku55',
|
|
42
|
+
i18nDescriptionKey: 'model.haiku55Description',
|
|
43
|
+
},
|
|
38
44
|
{
|
|
39
45
|
id: 'claude-haiku-4-5-20251001',
|
|
40
46
|
label: 'Haiku 4.5',
|
package/docker-compose.vps.yml
CHANGED
|
@@ -46,6 +46,12 @@ services:
|
|
|
46
46
|
traefik.http.routers.kobo.entrypoints: ${TRAEFIK_ENTRYPOINT:-websecure}
|
|
47
47
|
traefik.http.routers.kobo.tls: "true"
|
|
48
48
|
traefik.http.routers.kobo.tls.certresolver: ${TRAEFIK_CERTRESOLVER:-letsencrypt}
|
|
49
|
+
traefik.http.routers.kobo.middlewares: kobo-security
|
|
50
|
+
traefik.http.middlewares.kobo-security.headers.stsseconds: "31536000"
|
|
51
|
+
traefik.http.middlewares.kobo-security.headers.contenttypenosniff: "true"
|
|
52
|
+
traefik.http.middlewares.kobo-security.headers.referrerpolicy: no-referrer
|
|
53
|
+
traefik.http.middlewares.kobo-security.headers.customframeoptionsvalue: SAMEORIGIN
|
|
54
|
+
traefik.http.middlewares.kobo-security.headers.contentsecuritypolicy: "frame-ancestors 'self'"
|
|
49
55
|
traefik.http.services.kobo.loadbalancer.server.port: "3000"
|
|
50
56
|
# No published HTTP or SSH ports. Traefik handles HTTP and WebSockets.
|
|
51
57
|
stop_grace_period: 180s
|
package/docs/deployment-vps.md
CHANGED
|
@@ -191,13 +191,21 @@ From `/opt/kobo`:
|
|
|
191
191
|
docker network inspect web # use your TRAEFIK_NETWORK value
|
|
192
192
|
docker compose config --quiet
|
|
193
193
|
docker compose up -d --wait --wait-timeout 180 kobo
|
|
194
|
-
docker compose
|
|
194
|
+
docker compose exec kobo node -e 'const fs = require("node:fs"); console.log(JSON.parse(fs.readFileSync(process.env.KOBO_HOME + "/settings.json", "utf8")).global.networkAccessToken)'
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
-
Open `https://kobo.example.com
|
|
198
|
-
|
|
197
|
+
Open `https://kobo.example.com/` on your PC and paste the token into the login
|
|
198
|
+
dialog. The retrieval command deliberately displays a secret in your local
|
|
199
|
+
terminal; do not copy its output into logs or shared transcripts. Startup logs
|
|
200
|
+
never contain the token. After upgrading from a version that logged it, rotate
|
|
201
|
+
the token and apply your existing log-retention policy to old copies.
|
|
199
202
|
`KOBO_NETWORK_ACCESS_PROXY_HOST` is populated from `KOBO_DOMAIN` and must match
|
|
200
203
|
the public hostname. Both network access and behind-proxy mode are enabled.
|
|
204
|
+
Missing or invalid proxy hostname configuration rejects public traffic. Browser
|
|
205
|
+
origins must match the configured scheme and port (HTTPS is the default).
|
|
206
|
+
The Compose example attaches HSTS and browser security headers at the TLS router;
|
|
207
|
+
same-origin split panes remain supported. Keep the Docker socket unmounted unless
|
|
208
|
+
project workflows need its root-equivalent host access.
|
|
201
209
|
|
|
202
210
|
This example publishes no HTTP or SSH ports. Use SSH to the VPS followed by
|
|
203
211
|
`docker compose exec kobo kobo-vps-entrypoint --exec bash` for a container shell. The VPS launcher prepares the home permissions, then drops root before starting Node. It does not start the image's optional SSH daemon. `--exec` runs administrative shell/CLI commands as the same non-root user without repeating home migration.
|
|
@@ -211,15 +219,19 @@ curl -s -o /dev/null -w '%{http_code}\n' https://kobo.example.com/api/workspaces
|
|
|
211
219
|
|
|
212
220
|
Expect health JSON with `status: "ok"` and the installed version, then `401`
|
|
213
221
|
without a token. `/api/health` is intentionally public. Ordinary authenticated
|
|
214
|
-
REST
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
222
|
+
REST scripts use the operator token in `X-Kobo-Token`; browser login creates a
|
|
223
|
+
12-hour HttpOnly session cookie. External MCP clients require a dedicated key,
|
|
224
|
+
with explicitly granted tools/workspaces, even on loopback. See
|
|
225
|
+
[external MCP credentials](../CONFIGURATION.md#external-llm-access-through-mcp).
|
|
226
|
+
No additional Traefik route is needed for WebSocket upgrades: the browser obtains
|
|
227
|
+
a 60-second, single-use ticket instead of placing the operator token in the URL.
|
|
228
|
+
|
|
229
|
+
Rotating the operator token, disabling network access or changing proxy mode
|
|
230
|
+
revokes browser sessions and existing WebSockets, including terminal connections.
|
|
231
|
+
Managed agent MCP processes use a revocable per-launch capability through a
|
|
232
|
+
loopback-only internal endpoint, not the operator token. Plan/read-only launches
|
|
233
|
+
cannot mutate workspace state; only their narrow progress/report exceptions
|
|
234
|
+
remain. Agent processes are not stopped by browser credential revocation.
|
|
223
235
|
|
|
224
236
|
## 3. Authenticate and choose a project
|
|
225
237
|
|
package/docs/getting-started.md
CHANGED
|
@@ -4,7 +4,7 @@ Kōbō is a local, single-user application that runs Claude Code or OpenAI Codex
|
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
7
|
-
- Node.js
|
|
7
|
+
- Node.js 26.12.1 or later, Git and Bash, with a writable local Git repository containing at least one commit.
|
|
8
8
|
- A working login or API credential for the engine you choose. Kōbō ships neither credentials nor a provider subscription. Provider usage can incur charges.
|
|
9
9
|
- Linux is the locally exercised platform for this readiness change. CI is configured to exercise Linux and macOS; a configured job is not a completed compatibility test. Windows users need a Linux environment such as WSL; native Windows and WSL have not been validated in this release rehearsal.
|
|
10
10
|
|
package/docs/troubleshooting.md
CHANGED
|
@@ -6,7 +6,7 @@ Open the first-run setup from Help and run the check again after changing the se
|
|
|
6
6
|
|
|
7
7
|
| Check | Action |
|
|
8
8
|
|---|---|
|
|
9
|
-
| Node | Use Node
|
|
9
|
+
| Node | Use Node 26.12.1 or later; contributors should use `.nvmrc`. |
|
|
10
10
|
| Git / Bash | Install the missing executable and make it available on the server's `PATH`. |
|
|
11
11
|
| Project | Select a writable Git repository containing at least one commit. |
|
|
12
12
|
| Storage / worktrees | Check permissions on `KOBO_HOME`, the project and configured worktree root. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loicngr/kobo",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.29",
|
|
4
4
|
"description": "Kōbō — local multi-workspace orchestrator for Claude Code and OpenAI Codex, with isolated Git worktrees, live review and optional automation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"quasar"
|
|
62
62
|
],
|
|
63
63
|
"engines": {
|
|
64
|
-
"node": ">=
|
|
64
|
+
"node": ">=26.12.1"
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"dev": "KOBO_ENFORCE_LOCAL_HOME=1 KOBO_HOME=./data PORT=3300 KOBO_DEV_CLIENT_ORIGIN=http://localhost:8080 tsx watch src/server/index.ts",
|
|
@@ -88,32 +88,32 @@
|
|
|
88
88
|
"security:scan": "node scripts/scan-secrets.mjs"
|
|
89
89
|
},
|
|
90
90
|
"dependencies": {
|
|
91
|
-
"@anthropic-ai/claude-agent-sdk": "^0.3.
|
|
91
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.308",
|
|
92
92
|
"@emnapi/core": "^1.11.3",
|
|
93
93
|
"@emnapi/runtime": "^1.11.3",
|
|
94
|
-
"@hono/node-server": "^2.1.
|
|
95
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
96
|
-
"@openai/codex": "^0.
|
|
94
|
+
"@hono/node-server": "^2.1.5",
|
|
95
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
96
|
+
"@openai/codex": "^0.163.0",
|
|
97
97
|
"better-sqlite3": "^13.0.3",
|
|
98
|
-
"cron-parser": "^5.10.
|
|
99
|
-
"hono": "^4.13.
|
|
100
|
-
"nanoid": "^6.0.
|
|
98
|
+
"cron-parser": "^5.10.2",
|
|
99
|
+
"hono": "^4.13.13",
|
|
100
|
+
"nanoid": "^6.0.2",
|
|
101
101
|
"node-pty": "^1.1.0",
|
|
102
102
|
"ws": "^8.22.0"
|
|
103
103
|
},
|
|
104
104
|
"devDependencies": {
|
|
105
105
|
"@biomejs/biome": "2.5.14",
|
|
106
106
|
"@types/better-sqlite3": "^9.6.0",
|
|
107
|
-
"@types/node": "^26.6.
|
|
107
|
+
"@types/node": "^26.6.5",
|
|
108
108
|
"@types/ws": "^8.18.2",
|
|
109
109
|
"@vitest/runner": "^4.1.11",
|
|
110
|
-
"concurrently": "^10.0.
|
|
110
|
+
"concurrently": "^10.0.6",
|
|
111
111
|
"tsx": "^4.23.15",
|
|
112
112
|
"typescript": "^7.0.2",
|
|
113
|
-
"vitest": "^5.0.
|
|
113
|
+
"vitest": "^5.0.5"
|
|
114
114
|
},
|
|
115
115
|
"overrides": {
|
|
116
|
-
"@hono/node-server": "^2.1.
|
|
117
|
-
"shell-quote": "^1.
|
|
116
|
+
"@hono/node-server": "^2.1.5",
|
|
117
|
+
"shell-quote": "^1.12.0"
|
|
118
118
|
}
|
|
119
119
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{N as e,S as t,ft as n,mt as r,p as i,ut as a,v as o}from"./reactivity.esm-bundler-Dplx1ILy.js";import{C as s,F as c,J as l,L as u,N as d,O as f,P as p,T as m,Y as h,_ as g,d as _,f as v,g as y,j as ee,k as b,l as x,p as S,r as C,u as w,y as T,z as te}from"./runtime-core.esm-bundler-BAUgNzMK.js";import{$ as ne,K as re,M as E,f as ie,z as ae}from"./dom-CRPVbmiy.js";import{l as oe,t as D,u as se}from"./QBtn-kfhHFy7Q.js";import{Gt as ce,Jt as O,O as le,_t as ue,at as de,en as fe,j as pe,m as me,mt as he,pn as ge,pt as _e,rn as k,st as ve,un as A,v as ye}from"./index-DLoUeovT.js";import{t as be}from"./QSpinnerDots-Bea6hAuJ.js";import{t as xe}from"./QList-CAUl0v66.js";import{t as Se}from"./QMarkupTable-B4LLAly-.js";import{c as j,l as M,s as N}from"./QSelect-DOnv_ry0.js";import{t as P}from"./QItemSection-ahqRK4JF.js";import{t as Ce}from"./QExpansionItem-BVb067oc.js";import{a as F,c as we,r as I,t as L}from"./ToolCallItem-C9ksVseJ.js";import{t as Te}from"./QScrollArea-CUDWkEHh.js";import{a as R}from"./use-tours-C_5R1Y5D.js";import{f as Ee,g as z,m as De,p as Oe,u as ke}from"./use-worktree-restore-Cj8eGE9s.js";import{t as B}from"./QTooltip-B5QJIU3p.js";import{n as V}from"./render-chat-markdown-C9ihAYBt.js";import{n as H}from"./WorkspacePage-BTJnap9a.js";function Ae(e,t){return s(`div`,e,[s(`table`,{class:`q-table`},t)])}var je={list:xe,table:Se},U=[`list`,`table`,`__qtable`],Me=ae({name:`QVirtualScroll`,props:{...M,type:{type:String,default:`list`,validator:e=>U.includes(e)},items:{type:Array,default:()=>[]},itemsFn:Function,itemsSize:Number,scrollTarget:ge},setup(e,{slots:n,attrs:r}){let i,a=t(null),o=x(()=>e.itemsSize>=0&&e.itemsFn!==void 0?Number.parseInt(e.itemsSize,10):Array.isArray(e.items)?e.items.length:0),{virtualScrollSliceRange:c,localResetVirtualScroll:u,padVirtualScroll:f,onVirtualScrollEvt:m}=j({virtualScrollLength:o,getVirtualScrollTarget:y,getVirtualScrollEl:v}),h=x(()=>{if(o.value===0)return[];let t=(e,t)=>({index:c.value.from+t,item:e});return e.itemsFn===void 0?e.items.slice(c.value.from,c.value.to).map(t):e.itemsFn(c.value.from,c.value.to-c.value.from).map(t)}),g=x(()=>`q-virtual-scroll q-virtual-scroll`+(e.virtualScrollHorizontal?`--horizontal`:`--vertical`)+(e.scrollTarget===void 0?` scroll`:``)),_=x(()=>e.scrollTarget===void 0?{tabindex:0}:{});l(o,()=>{u()}),l(()=>e.scrollTarget,()=>{C(),S()});function v(){let e=a.value;return e===null?null:e.$el||e}function y(){return i}function S(){let t=v();t!==null&&(i=A(t,e.scrollTarget),i.addEventListener(`scroll`,m,E.passive))}function C(){i!==void 0&&(i.removeEventListener(`scroll`,m,E.passive),i=void 0)}function w(){let t=f(e.type===`list`?`div`:`tbody`,h.value.map(n.default));return n.before!==void 0&&(t=n.before().concat(t)),ie(n.after,t)}return u(),p(()=>{S()}),b(()=>{S()}),d(()=>{C()}),ee(()=>{C()}),()=>{if(n.default===void 0)console.error(`QVirtualScroll: default scoped slot is required for rendering`);else return e.type===`__qtable`?Ae({ref:a,class:`q-table__middle `+g.value,tabindex:0},w()):s(je[e.type],{...r,ref:a,class:[r.class,g.value],..._.value},w)}}}),Ne=new Set([`setup`,`cleanup`,`archive`]);function Pe(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:Ne.has(e.sender)||le(e.sender)?`script`:`user`;case`session`:return`session`;default:return`agent`}}function Fe(e){let t=[],n=null,r=null;for(let i of e){let e=Pe(i),a=e===`session`||e===`system-prompt`,o=e===`script`&&i.type===`user`?`script:${i.sender}`:e===`user`&&i.type===`user`&&i.source?JSON.stringify([`mcp`,i.source.transport,i.source.clientName]):e;!n||r!==o||a?(n={speaker:e,ts:i.ts,items:[i]},r=o,t.push(n),a&&(n=null)):n.items.push(i)}return t}function W(e){switch(e.type){case`text`:return`text:${e.messageId}`;case`thinking`:return`thinking:${e.messageId}:${e.eventIds?.[0]??e.ts??``}`;case`tool`:return`tool:${e.toolCallId}`;case`session`:return`session:${e.kind}:${e.eventIds?.[0]??e.ts??``}`;case`user`:return`user:${e.eventIds?.[0]??`${e.sender}:${e.ts??``}`}`;case`error`:return`error:${e.eventIds?.[0]??`${e.category}:${e.ts??``}`}`}}function Ie(e){let t=e.items[0];return`${e.speaker}:${t?W(t):`empty`}`}function Le(e,t){let n=Math.min(t,e.length)-1;for(let t=n;t>=0;t--)if(e[t]?.speaker===`user`)return t;return-1}function Re(e,t,n){return new Promise(r=>{let i=!1,a=null,o=null,s=e=>{i||(i=!0,a!==null&&clearTimeout(a),a=null,o?.(),r(e))};o=l(e,e=>{t(e)&&s(!0)},{immediate:!0}),i?o():a=setTimeout(()=>s(!1),n)})}var ze={class:`agent-error-item`},Be={class:`agent-error-item__body`},Ve={class:`agent-error-item__title`},G={class:`agent-error-item__message`},He=R(T({__name:`AgentErrorItem`,props:{item:{}},setup(e){let t=e,{te:n}=fe(),i=x(()=>{let e=`agent.error.${t.item.category}`;return n(e)?e:`agent.error.other`});return(t,n)=>(u(),S(`div`,ze,[g(oe,{name:`error_outline`,size:`16px`,class:`agent-error-item__icon`}),w(`div`,Be,[w(`div`,Ve,r(t.$t(i.value)),1),w(`div`,G,r(e.item.message),1)])]))}}),[[`__scopeId`,`data-v-e98c3c02`]]),Ue={class:`text-caption text-kobo-3`},We=T({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,n=x(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return z(t.item.detail);case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(u(),S(`span`,Ue,r(e.$t(n.value)),1))}}),K=new Map;function q(){for(let e of K.values())e.controller.abort(),e.blobUrl&&URL.revokeObjectURL(e.blobUrl);K.clear()}typeof window<`u`&&(window.addEventListener(`kobo:auth-token-changed`,q),window.addEventListener(`storage`,e=>{(e.key===`kobo:network-token`||e.key===null)&&q()}));function J(e){if(!/^\/api\/workspaces\/[^/]+\/images\/file\?path=/.test(e))throw Error(`Invalid workspace image URL`);let t=K.get(e);if(!t){let n=new AbortController,r={references:0,controller:n,ready:Promise.resolve(``)};r.ready=ce(e,{signal:n.signal}).then(async e=>{if(!e.ok)throw await O(e);let t=await e.blob();if(n.signal.aborted)throw new DOMException(`Aborted`,`AbortError`);return r.blobUrl=URL.createObjectURL(t),r.blobUrl}),t=r,K.set(e,t)}t.references++;let n=!1,r=t;return{ready:r.ready,release(){n||(n=!0,!(--r.references>0)&&(r.controller.abort(),r.blobUrl&&URL.revokeObjectURL(r.blobUrl),K.get(e)===r&&K.delete(e)))}}}function Ge(e,t){let n=o(``),r=o(0),a=()=>{r.value++},s=e=>{(e.key===`kobo:network-token`||e.key===null)&&a()};return window.addEventListener(`kobo:auth-token-changed`,a),window.addEventListener(`storage`,s),i(()=>{window.removeEventListener(`kobo:auth-token-changed`,a),window.removeEventListener(`storage`,s)}),l([e,r],([e],r,i)=>{let a=!0,o=[],s=document.createElement(`template`);s.innerHTML=e;for(let e of s.content.querySelectorAll(`img`)){let r=e.getAttribute(`src`)??``;if(e.removeAttribute(`data-kobo-image-url`),!/^\/api\/workspaces\/[^/]+\/images\/file\?path=/.test(r))continue;e.removeAttribute(`src`);let i=J(r);o.push(i),i.ready.then(t=>{a&&(e.setAttribute(`src`,t),e.setAttribute(`data-kobo-image-url`,r),n.value=s.innerHTML)}).catch(()=>{a&&(e.setAttribute(`title`,t()),n.value=s.innerHTML)})}n.value=s.innerHTML,i(()=>{a=!1;for(let e of o)e.release()})},{immediate:!0}),n}function Ke(e,t){return t?e.replace(/\[image:\s+([^\]]+)\]/g,(e,n)=>{let r=String(n).trim();return/^(\.ai\/images\/|images\/)/.test(r)?`}/images/file?path=${encodeURIComponent(r)}`})`:e}):e}var qe=[`innerHTML`],Je=[`innerHTML`],Ye=[`src`],Y=R(T({__name:`UserMessageItem`,props:{item:{},workspaceId:{}},setup(t){let n=t,{t:r}=fe(),a=x(()=>n.item.sender===`system-prompt`),s=Ge(x(()=>{let e=Ke(n.item.content,n.workspaceId);return V(e)}),()=>r(`network.login.unreachable`)),c=o(null),d=o(!1),f;function p(){d.value=!1,c.value=null,f?.release(),f=void 0}l(d,e=>{e||p()}),l(()=>n.workspaceId,p);let m=e=>{(e.key===`kobo:network-token`||e.key===null)&&p()};window.addEventListener(`storage`,m),window.addEventListener(`kobo:auth-token-changed`,p),i(()=>{p(),window.removeEventListener(`kobo:auth-token-changed`,p),window.removeEventListener(`storage`,m)});function y(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(p(),n.dataset.koboImageUrl&&(f=J(n.dataset.koboImageUrl),f.ready.catch(p)),c.value=n.src,d.value=!0)}return(t,n)=>(u(),S(C,null,[a.value?(u(),_(Ce,{key:0,dense:``,"dense-toggle":``,label:t.$t(`chat.systemPrompt`),"header-class":`text-kobo-2 text-caption`},{default:h(()=>[w(`div`,{class:`q-py-xs markdown-message markdown-user-prompt`,innerHTML:e(s)},null,8,qe)]),_:1},8,[`label`])):(u(),S(`div`,{key:1,class:`markdown-message`,onClick:y},[w(`div`,{innerHTML:e(s)},null,8,Je)])),g(k,{modelValue:d.value,"onUpdate:modelValue":n[1]||=e=>d.value=e},{default:h(()=>[c.value?(u(),S(`img`,{key:0,src:c.value,alt:``,class:`image-lightbox-img`,onClick:n[0]||=e=>d.value=!1},null,8,Ye)):v(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-965e6297`]]),Xe=[`data-event-ids`],X={class:`turn-header`},Ze={key:0,class:`turn-time`},Qe={class:`turn-time turn-time-updated`},Z={key:2,class:`turn-actions`},$e={class:`turn-body`},et={class:`ellipsis`},tt={class:`q-px-sm q-pb-sm`},nt={key:0,class:`turn-scroll-top`},rt=R(T({__name:`TurnCard`,props:{turn:{},workspaceId:{},highlighted:{type:Boolean}},emits:[`scrollTo`],setup(t,{emit:i}){let s=t,c=i,{t:l}=fe(),d=de(),f=m(F),p=o(null);function ee(){let e=p.value;if(!e)return;let t=e.closest(`.q-scrollarea`)?.querySelector(`.q-scrollarea__content`);if(!t){e.scrollIntoView({behavior:`smooth`,block:`start`});return}let n=e.getBoundingClientRect().top-t.getBoundingClientRect().top;c(`scrollTo`,Math.max(0,n-8))}let b=x(()=>{switch(s.turn.speaker){case`user`:{let e=s.turn.items[0],t=e?.type===`user`?e.source:void 0;return{label:t?l(`chat.externalLlm`,{name:t.clientName}):l(`chat.you`),accent:`var(--kobo-turn-user)`,badgeClass:`turn-badge-user`}}case`agent`:return{label:l(`chat.agent`),accent:`var(--kobo-turn-agent)`,badgeClass:`turn-badge-agent`};case`system-prompt`:return{label:l(`chat.systemPrompt`),accent:`var(--kobo-text-3)`,badgeClass:`turn-badge-system`};case`session`:return{label:l(`chat.session`),accent:`var(--kobo-text-3)`,badgeClass:`turn-badge-session`};case`script`:{let e=s.turn.items[0],t=e?.type===`user`?e.sender:``;return{label:le(t)?l(`chat.hookScript`,{event:t.slice(5)}):l(t===`archive`?`chat.archiveScript`:t===`setup`?`chat.setupScript`:`chat.cleanupScript`),accent:`var(--kobo-success)`,badgeClass:`turn-badge-script`}}}});function T(e,t=!1){if(!e)return``;let n=new Date(e);return Number.isNaN(n.getTime())?``:n.toLocaleTimeString(void 0,t?{hour:`2-digit`,minute:`2-digit`,second:`2-digit`}:{hour:`2-digit`,minute:`2-digit`})}let re=x(()=>T(s.turn.ts)),E=x(()=>s.turn.items.flatMap(e=>e.eventIds??[]).join(` `)),ie=x(()=>{let e=s.turn.items;if(e.length===0)return null;for(let t=e.length-1;t>=0;t--){let n=e[t].ts;if(n)return n}return null}),ae=x(()=>{let e=s.turn.ts,t=ie.value;if(!t||!e||t===e)return``;let n=new Date(e).getTime(),r=new Date(t).getTime();return Number.isNaN(n)||Number.isNaN(r)||r<=n?``:T(t,r-n<6e4)}),se=x(()=>ae.value!==``);function ce(e){return we(e.origin,Object.values(d.subagents[s.workspaceId]??{}),e.type===`tool`?e.toolCallId:void 0)}function O(e){return e||l(`chat.subagentActivity`)}function ue(e){return(e.type===`text`||e.type===`thinking`||e.type===`tool`)&&e.origin?.kind===`subagent`}let pe=x(()=>{let e=[],t=new Map;for(let n of s.turn.items){if(!ue(n)){e.push({type:`item`,key:W(n),item:n});continue}let r=ce(n),i=r?.toolUseId??n.origin?.toolCallId??n.origin?.threadId??W(n),a=t.get(i);if(a){a.items.push(n);continue}let o={type:`subagent`,key:`subagent:${W(n)}`,items:[n],subagentKey:i,subagentName:r?.description||null};t.set(i,o),e.push(o)}return e});function me(e){e.origin&&f?.({origin:e.origin})}let he=x(()=>s.turn.items.filter(e=>e.type===`tool`).length);return(i,o)=>(u(),S(`div`,{ref_key:`cardEl`,ref:p,class:a([`turn-card`,{"turn-card--user":t.turn.speaker===`user`,"turn-card--highlighted":t.highlighted}]),"data-event-ids":E.value,style:n({"--turn-accent":b.value.accent})},[w(`div`,X,[w(`span`,{class:a([`turn-badge`,b.value.badgeClass])},r(b.value.label),3),re.value?(u(),S(`span`,Ze,r(re.value),1)):v(``,!0),se.value?(u(),S(C,{key:1},[g(oe,{name:`arrow_forward`,size:`10px`,color:`kobo-3`,class:`turn-time-arrow`}),w(`span`,Qe,[y(r(ae.value)+` `,1),g(B,null,{default:h(()=>[y(r(e(l)(`chat.lastUpdatedAt`,{time:ae.value})),1)]),_:1})])],64)):v(``,!0),he.value>0?(u(),S(`span`,Z,` · `+r(e(l)(`chat.nActions`,{n:he.value})),1)):v(``,!0)]),w(`div`,$e,[(u(!0),S(C,null,te(pe.value,n=>(u(),S(C,{key:n.key},[n.type===`subagent`?(u(),_(Ce,{key:0,class:`turn-subagent-activity`,dense:``,label:O(n.subagentName),caption:e(l)(`chat.nActions`,{n:n.items.length}),"default-opened":t.highlighted&&n.items.some(e=>e.eventIds?.some(e=>E.value.includes(e)))},{header:h(()=>[g(P,null,{default:h(()=>[g(N,{class:`row items-center no-wrap`,title:n.subagentName??void 0},{default:h(()=>[g(oe,{name:`hub`,size:`16px`,class:`q-mr-xs`},{default:h(()=>[g(B,null,{default:h(()=>[y(r(e(l)(`chat.subagentActivity`)),1)]),_:1})]),_:1}),w(`span`,et,r(O(n.subagentName)),1)]),_:2},1032,[`title`]),g(N,{caption:``},{default:h(()=>[y(r(e(l)(`chat.nActions`,{n:n.items.length})),1)]),_:2},1024)]),_:2},1024),g(P,{side:``},{default:h(()=>[g(D,{flat:``,round:``,dense:``,size:`sm`,icon:`open_in_new`,"aria-label":e(l)(`chat.openSubagentActivity`),onClick:ne(e=>me(n.items[0]),[`stop`])},{default:h(()=>[g(B,null,{default:h(()=>[y(r(e(l)(`chat.openSubagentActivity`)),1)]),_:1})]),_:1},8,[`aria-label`,`onClick`])]),_:2},1024)]),default:h(()=>[w(`div`,tt,[(u(!0),S(C,null,te(n.items,t=>(u(),S(C,{key:e(W)(t)},[t.type===`text`?(u(),_(I,{key:0,item:t},null,8,[`item`])):t.type===`thinking`?(u(),_(H,{key:1,item:t},null,8,[`item`])):t.type===`tool`?(u(),_(L,{key:2,item:t},null,8,[`item`])):v(``,!0)],64))),128))])]),_:2},1032,[`label`,`caption`,`default-opened`])):(u(),S(C,{key:1},[n.item.type===`text`?(u(),_(I,{key:0,item:n.item},null,8,[`item`])):n.item.type===`thinking`?(u(),_(H,{key:1,item:n.item},null,8,[`item`])):n.item.type===`tool`?(u(),_(L,{key:2,item:n.item},null,8,[`item`])):n.item.type===`user`?(u(),_(Y,{key:3,item:n.item,"workspace-id":t.workspaceId},null,8,[`item`,`workspace-id`])):n.item.type===`session`?(u(),_(We,{key:4,item:n.item},null,8,[`item`])):n.item.type===`error`?(u(),_(He,{key:5,item:n.item},null,8,[`item`])):v(``,!0)],64))],64))),128))]),t.turn.items.length>4?(u(),S(`div`,nt,[g(D,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`kobo-3`,class:`turn-scroll-top-btn`,onClick:ee},{default:h(()=>[g(B,null,{default:h(()=>[y(r(e(l)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):v(``,!0)],14,Xe))}}),[[`__scopeId`,`data-v-13bebb3d`]]),it={key:0,class:`activity-feed-switching`},at={key:1,class:`activity-feed-wrap`,"data-tour":`ws-chat`},ot={key:0,role:`alert`,class:`q-pa-sm text-negative`},st={key:1,"data-testid":`history-load-error`,role:`alert`,class:`q-pa-sm text-negative`},ct=[`aria-label`],lt={key:0,class:`activity-feed-compacting`},ut={class:`text-caption text-kobo-2`},dt={key:0,class:`text-center q-py-sm text-caption text-kobo-3`},ft={key:1,class:`activity-feed-empty q-pa-md`},pt={class:`activity-feed-empty__title`},mt={class:`activity-feed-empty__hint`},ht={key:2,class:`q-px-md q-pb-md`},gt=[`title`],_t={class:`ellipsis`},vt={class:`activity-feed-nav-cluster`},yt=300,bt=60,xt=200,St=180,Ct=255,wt=3,Tt=60,Et=R(T({__name:`ActivityFeed`,props:{workspaceId:{}},setup(t){let{t:n}=fe(),i=t,a=ue(),s=pe(),d=de(),m=me(),ee=x(()=>m.isCompacting(i.workspaceId)),b=x(()=>d.selectedSessionId),T=x(()=>d.sessions.find(e=>e.id===b.value)?.engineSessionId??null),ne=x(()=>{let e=d.sessions;return e.length!==0&&b.value===e[e.length-1].id});function E(e){return b.value?e?e===b.value||e===T.value:ne.value:!0}let ie=x(()=>(d.activityFeeds[i.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&E(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,source:ye(e.meta?.source),ts:e.timestamp,sessionId:e.sessionId,eventIds:[e.id]}))),ae=x(()=>{let e=d.workspaces.find(e=>e.id===i.workspaceId);return ve(e?.status)}),le=ke(),ge=``,k=x(()=>{let e=a.eventsFor(i.workspaceId),t=a.timestampsFor(i.workspaceId),n=a.sessionIdsFor(i.workspaceId),r=a.eventIdsFor(i.workspaceId),o=[],c=[],l=[];for(let i=0;i<e.length;i++)E(n[i])&&(o.push(e[i]),c.push(t[i]),l.push(r[i]??null));let u=`${i.workspaceId}::${b.value??`*`}`;u!==ge&&(le=ke(),ge=u);let d=Ee(le,o,c,ae.value,l);return Fe(De(d,ie.value).filter(e=>e.type===`thinking`?!1:e.type===`session`?e.kind===`ended`?s.showVerboseSystemMessages||!Oe(e.detail):s.showVerboseSystemMessages:!0))}),A=x(()=>s.showVerboseSystemMessages?a.eventsFor(i.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),xe=x(()=>{for(let e=k.value.length-1;e>=0;e--){let t=k.value[e]?.items??[];for(let e=t.length-1;e>=0;e--){let r=t[e];if(r){if(r.type===`text`&&!r.streaming)return{key:`text:${r.messageId}`,text:r.text};if(r.type===`tool`)return r.result?{key:`tool:${r.toolCallId}:end`,text:r.result.isError?n(`activity.a11y.toolFailed`,{name:r.name}):n(`activity.a11y.toolFinished`,{name:r.name})}:{key:`tool:${r.toolCallId}:start`,text:n(`activity.a11y.toolStarted`,{name:r.name})}}}}return null}),Se=o(``);l(xe,(e,t)=>{if(!e||e.key===t?.key)return;let r=e.key.startsWith(`text:`)?n(`activity.a11y.agentSaid`,{text:e.text.slice(0,yt)}):e.text;Se.value=r});let j=o(null),M=o(!0),N=o(!1),P=o(``),F=_e(),we=_e(),I=_e(),L=o(null),R=!1,z=!0,B=null,V=o(new Set),H=new Map,Ae=x(()=>{if(k.value.length>0||A.value.length>0)return!1;let e=i.workspaceId,t=b.value;return!!d.loadingSessions[e]||m.isSyncPending(e)||t!==null&&V.value.has(W(e,t))}),je=o(new Map),U=x(()=>k.value.length>0||A.value.length>0),Ne=x(()=>!!d.loadingSessions[i.workspaceId]||m.isSyncPending(i.workspaceId)||b.value!==null&&V.value.has(W(i.workspaceId,b.value)));function Pe(e){et();let t=B;if(B=e.verticalSize,t!==null&&e.verticalSize>t&&M.value&&!N.value){q(0);return}let n=e.verticalSize-e.verticalPosition-e.verticalContainerSize;M.value=n<=bt,R&&e.verticalPosition<=xt&&!N.value&&!P.value&&ze()&&He()}function W(e,t){return`${e}:${t}`}function ze(){let e=b.value;return e?je.value.get(W(i.workspaceId,e))??!0:a.hasMoreOlderFor(i.workspaceId)}function Be(e,t,n){je.value.set(W(e,t),n)}function Ve(e){if(!b.value)return a.oldestIdFor(e);let t=a.eventIdsFor(e),n=a.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!E(n[e]))continue;let r=t[e];if(r)return r}}let G=null;function He(){if(G)return G;let e=K().finally(()=>{G===e&&(G=null)});return G=e,G}function Ue(){F.abort(),we.abort(),I.abort(),N.value=!1,G=null;for(let e of V.value)Ft.delete(e);H.clear(),V.value.clear(),P.value=``}function We(){P.value=``,Mt.value===0&&b.value?It():He()}l(()=>[i.workspaceId,b.value],Ue,{flush:`sync`});async function K(){let e=i.workspaceId,t=b.value,n=Ve(e);if(!n)return;let r=F.begin();P.value=``,N.value=!0;try{let i=j.value;if(await f(),!F.isCurrent(r))return;let o=i?.getScroll().verticalSize??0,s=i?.getScroll().verticalPosition??0,c=new URLSearchParams({before:n,limit:`200`});t&&c.set(`session`,t);let l=await ce(`/api/workspaces/${e}/events?${c.toString()}`,{signal:r});if(!l.ok)throw await O(l);let u=await l.json();if(!F.isCurrent(r))return;let p=u.events??[],m=p.filter(t=>t.type===`agent:event`&&t.workspaceId===e),h=p.filter(t=>t.type===`user:message`&&t.workspaceId===e),g=m.map(e=>e.payload),_=m.map(e=>e.createdAt),v=m.map(e=>e.sessionId??null),y=m.map(e=>e.id),ee=p.length>0?p[0].id:n;t&&Be(e,t,u.hasMore),a.prepend(e,g,_,{oldestId:ee,hasMoreOlder:t?a.hasMoreOlderFor(e):u.hasMore,sessionIds:v,eventIds:y});for(let t of h){let n=t.payload;typeof n.content==`string`&&d.addActivityItem(e,{id:t.id,type:`text`,content:n.content,timestamp:t.createdAt,sessionId:t.sessionId??void 0,meta:{sender:n.sender??`user`,source:ye(n.source)}})}if(await f(),i&&F.isCurrent(r)){let e=i.getScroll().verticalSize-o;if(e>0){let t=Math.max(s+e,250);i.setScrollPosition(`vertical`,t,0)}}}catch(e){if(he(e)||!F.isCurrent(r))return;P.value=e instanceof Error?e.message:String(e),console.error(`[ActivityFeed] failed to load older events:`,e)}finally{F.isCurrent(r)&&(N.value=!1)}}async function q(e=0){let t=Pt();if(await f(),t!==Pt())return;let n=j.value;if(!n)return;let r=n.getScroll();n.setScrollPosition(`vertical`,r.verticalSize,e)}let J=null,Ge=0;function Ke(){if(J!=null)return;let e=performance.now()-Ge<St,t=Pt();J=requestAnimationFrame(()=>{J=null,t===Pt()&&(Ge=performance.now(),q(e?0:180))})}function qe(e){$(),M.value=!1;let t=j.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}function Je(){let e=window.location.hash,t=e.indexOf(`?`);if(t===-1||!new URLSearchParams(e.slice(t+1)).has(`eventId`))return;let n=e.slice(0,t);window.history.replaceState(window.history.state,``,`${window.location.pathname}${window.location.search}${n}`)}async function Ye(e){let t=e.detail;if(t?.workspaceId!==i.workspaceId||!t.eventId)return;$(),M.value=!1;let n=I.begin(),r=i.workspaceId;try{let e=new URLSearchParams({around:t.eventId,limit:`200`});t.sessionId&&e.set(`session`,t.sessionId);let o=await ce(`/api/workspaces/${r}/events?${e}`,{signal:n});if(!o.ok)throw await O(o);let s=await o.json();if(!I.isCurrent(n))return;let c=s.events??[],l=c.filter(e=>e.type===`agent:event`&&e.workspaceId===i.workspaceId),u=c.filter(e=>e.type===`user:message`&&e.workspaceId===i.workspaceId);a.reset(i.workspaceId,l.map(e=>e.payload),l.map(e=>e.createdAt),{oldestId:c[0]?.id,hasMoreOlder:s.hasMore??!1,sessionIds:l.map(e=>e.sessionId),eventIds:l.map(e=>e.id)});for(let e of u){let t=e.payload;typeof t.content==`string`&&d.addActivityItem(i.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`,source:ye(t.source)}})}if(await Re(Ae,e=>!e,5e3),await f(),await new Promise(e=>requestAnimationFrame(()=>requestAnimationFrame(()=>e()))),!I.isCurrent(n))return;let p=k.value.findIndex(e=>e.items.some(e=>e.eventIds?.includes(t.eventId)??!1));if(p<0)return;L.value=t.eventId,Y.value?.scrollTo(p,`center`),X.value=p,window.setTimeout(()=>{L.value===t.eventId&&(L.value=null)},1800)}catch(e){if(he(e)||!I.isCurrent(n))return;P.value=e instanceof Error?e.message:String(e),console.error(`[ActivityFeed] failed to focus history event:`,e)}}let Y=o(null),Xe=o(null),X=o(0),Ze=o(!1),Qe=x(()=>{for(let e=k.value.length-1;e>=0;e--)if(k.value[e]?.speaker===`user`)return e;return-1}),Z=null;function $e(){let e=Qe.value,t=j.value?.getScrollTarget(),n=t?.querySelector(`[data-turn-index="${e}"]`);if(!n||!t){Ze.value=!1;return}let r=t.getBoundingClientRect(),i=n.getBoundingClientRect();Ze.value=i.bottom>r.top&&i.top<r.bottom}function et(){Z===null&&(Z=requestAnimationFrame(()=>{Z=null,$e()}))}function tt(e){return e.length<=Ct?e:`${e.slice(0,252)}...`}let nt=x(()=>{let e=Qe.value,t=k.value[e];if(!t||X.value<=e||Ze.value)return null;let n=t.items.filter(e=>e.type===`user`).map(e=>e.content).join(`
|
|
2
|
+
`).trim();return n?{index:e,content:tt(n)}:null});function Et(e){X.value=e.index,et()}function Dt(){let e=nt.value;e&&($(),M.value=!1,Y.value?.scrollTo(e.index,`start`),X.value=e.index)}let Ot=o(!1);async function kt(){if(Ot.value)return;$(),M.value=!1,Ot.value=!0;let e=i.workspaceId;try{let t=Le(k.value,X.value);if(t<0)for(let n=0;n<15;n++){if(e!==i.workspaceId)return;if(!ze())break;let n=k.value.length;if(await He(),await f(),e!==i.workspaceId)return;if(X.value+=k.value.length-n,t=Le(k.value,X.value),t>=0)break}t>=0&&e===i.workspaceId&&(Y.value?.scrollTo(t,`start`),X.value=t)}finally{Ot.value=!1}}let Q=0;function At(){return new Promise(e=>requestAnimationFrame(()=>e()))}async function jt(){if(!z||!U.value||Ae.value)return;R=!1;let e=++Q;if(await f(),e!==Q||!z||(Xe.value=j.value?.getScrollTarget()??null,await f(),e!==Q||!z))return;k.value.length>0&&Y.value?.scrollTo(k.value.length-1,`end-force`),await At();let t=-1,n=0;for(let r=0;r<Tt&&n<wt;r++){if(e!==Q||!z)return;await q(0),await At();let r=j.value?.getScroll().verticalSize??0;B=r,n=r===t?n+1:0,t=r}e===Q&&!Ne.value&&(R=!0,z=!1)}function $(){z=!1,!R&&(Q++,R=!0)}let Mt=x(()=>{let e=a.sessionIdsFor(i.workspaceId);if(!b.value)return e.length;let t=0;for(let n of e)E(n)&&t++;return t});l(Ae,async e=>{!e&&U.value&&await jt(),e||f(()=>{Xe.value=j.value?.getScrollTarget()??null,et()})}),l(Ne,(e,t)=>{!e&&t&&z&&U.value&&jt()}),p(()=>{window.addEventListener(`kobo:focus-history-event`,Ye),f(()=>{Xe.value=j.value?.getScrollTarget()??null,et()}),U.value&&jt(),b.value&&It();let e=new URLSearchParams(window.location.hash.split(`?`)[1]??``).get(`eventId`);e&&(Ye(new CustomEvent(`kobo:focus-history-event`,{detail:{workspaceId:i.workspaceId,sessionId:b.value,eventId:e}})),Je())}),l([Mt,()=>ie.value.length,()=>A.value.length],async([e,t,n])=>{N.value||z&&e+t+n>0&&await jt()}),l([Qe,()=>k.value.length],()=>{f(et)});let Nt=x(()=>a.liveAppendCountFor(i.workspaceId,E)),Pt=()=>`${i.workspaceId}\u0000${d.selectedSessionId??``}`;l([Nt,Pt],([e,t],[n,r])=>{t===r&&e>n&&M.value&&!N.value&&Ke()}),c(()=>{Q++,z=!1,Ue(),window.removeEventListener(`kobo:focus-history-event`,Ye),J!=null&&(cancelAnimationFrame(J),J=null),Z!==null&&cancelAnimationFrame(Z)}),l([()=>i.workspaceId,()=>d.selectedSessionId],()=>{Q++,z=!0,B=null,M.value=!0,R=!1,It(),U.value&&jt()});let Ft=new Set;async function It(){let e=i.workspaceId,t=b.value;if(!t||Mt.value>0)return;let n=W(e,t);if(Ft.has(n))return;Ft.add(n),V.value.add(n);let r=we.begin();H.set(n,r),P.value=``;try{let n=await ce(`/api/workspaces/${e}/events?session=${encodeURIComponent(t)}&limit=500`,{signal:r});if(!n.ok)throw await O(n);let i=await n.json();if(!we.isCurrent(r))return;let o=i.events??[];if(o.length===0)return;let s=o.filter(t=>t.type===`agent:event`&&t.workspaceId===e),c=o.filter(t=>t.type===`user:message`&&t.workspaceId===e),l=s.map(e=>e.payload),u=s.map(e=>e.createdAt),f=s.map(e=>e.sessionId??null),p=s.map(e=>e.id);Be(e,t,i.hasMore),l.length>0&&a.prepend(e,l,u,{oldestId:o[0].id,hasMoreOlder:a.hasMoreOlderFor(e),sessionIds:f,eventIds:p});for(let t of c){let n=t.payload;typeof n.content==`string`&&d.addActivityItem(e,{id:t.id,type:`text`,content:n.content,timestamp:t.createdAt,sessionId:t.sessionId??void 0,meta:{sender:n.sender??`user`,source:ye(n.source)}})}}catch(e){if(he(e)||!we.isCurrent(r)){H.has(n)&&H.get(n)!==r||Ft.delete(n);return}P.value=e instanceof Error?e.message:String(e),console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),Ft.delete(n)}finally{H.get(n)===r&&(H.delete(n),V.value.delete(n))}}let Lt=x(()=>ie.value.filter(e=>e.sender===`user`).length),Rt=x(()=>(d.activityFeeds[i.workspaceId]??[]).filter(e=>e.meta?.sender===`user`&&e.meta.pending&&E(e.sessionId)).length);l([Lt,Rt,Pt],async([e,t,n],[r,i,a])=>{let o=t>i;n===a&&(o||e>r&&M.value&&!N.value&&!Ne.value)&&(M.value=!0,await q(o?180:0))});async function zt(){M.value=!0,await q(250)}return(t,n)=>Ae.value?(u(),S(`div`,it,[g(be,{size:`40px`,color:`primary`})])):(u(),S(`div`,at,[e(d).sessionsErrors[i.workspaceId]?(u(),S(`div`,ot,[y(r(t.$t(`activity.sessionsError`,{message:e(d).sessionsErrors[i.workspaceId]}))+` `,1),g(D,{flat:``,dense:``,label:t.$t(`common.retry`),onClick:n[0]||=t=>e(d).fetchSessions(i.workspaceId)},null,8,[`label`])])):v(``,!0),P.value?(u(),S(`div`,st,[y(r(t.$t(`activity.historyError`,{message:P.value}))+` `,1),g(D,{flat:``,dense:``,label:t.$t(`common.retry`),onClick:We},null,8,[`label`])])):v(``,!0),w(`div`,{"data-testid":`activity-live-region`,class:`sr-only`,role:`log`,"aria-live":`polite`,"aria-atomic":`true`,"aria-label":t.$t(`activity.a11y.region`)},r(Se.value),9,ct),g(re,{name:`fade`},{default:h(()=>[ee.value?(u(),S(`div`,lt,[g(be,{size:`18px`,color:`primary`}),w(`span`,ut,r(t.$t(`activity.compacting`)),1)])):v(``,!0)]),_:1}),g(Te,{ref_key:`scrollRef`,ref:j,class:`activity-feed-scroll`,onScroll:Pe,onWheelPassive:$,onTouchstartPassive:$,onKeydown:$},{default:h(()=>[N.value?(u(),S(`div`,dt,[g(se,{size:`sm`}),y(` `+r(t.$t(`activity.loading_older`)),1)])):v(``,!0),g(Me,{ref_key:`virtualScrollRef`,ref:Y,class:`q-pa-md`,items:k.value,"scroll-target":Xe.value??void 0,"virtual-scroll-item-size":160,"virtual-scroll-slice-size":20,onVirtualScroll:Et},{default:h(({item:t,index:n})=>[(u(),_(rt,{key:e(Ie)(t),turn:t,"workspace-id":i.workspaceId,"data-turn-index":n,highlighted:t.items.some(e=>e.eventIds?.includes(L.value??``)??!1),onScrollTo:qe},null,8,[`turn`,`workspace-id`,`data-turn-index`,`highlighted`]))]),_:1},8,[`items`,`scroll-target`]),k.value.length===0&&A.value.length===0&&!N.value?(u(),S(`div`,ft,[g(oe,{name:`forum`,size:`28px`}),w(`div`,pt,r(t.$t(`activity.empty`)),1),w(`div`,mt,r(t.$t(`activity.emptyHint`)),1)])):v(``,!0),A.value.length?(u(),S(`div`,ht,[g(Ce,{label:t.$t(`activity.raw_lines`,{n:A.value.length}),dense:``},{default:h(()=>[(u(!0),S(C,null,te(A.value,(e,t)=>(u(),S(`div`,{key:t,class:`text-caption text-kobo-3 q-pa-xs`},r(e),1))),128))]),_:1},8,[`label`])])):v(``,!0)]),_:1},512),nt.value?(u(),S(`button`,{key:2,type:`button`,class:`activity-feed-last-user`,"data-testid":`latest-user-message`,title:nt.value.content,onClick:Dt},[g(oe,{name:`person`,size:`16px`}),w(`span`,_t,r(nt.value.content),1)],8,gt)):v(``,!0),w(`div`,vt,[M.value?v(``,!0):(u(),_(D,{key:0,round:``,dense:``,unelevated:``,color:`kobo-surface-2`,"text-color":`kobo-1`,icon:`arrow_downward`,size:`sm`,class:`activity-feed-nav-btn`,title:t.$t(`activity.scroll_to_bottom`),"aria-label":t.$t(`activity.scroll_to_bottom`),onClick:zt},null,8,[`title`,`aria-label`])),g(D,{round:``,dense:``,unelevated:``,color:`kobo-surface-2`,"text-color":`kobo-1`,icon:`arrow_upward`,size:`sm`,class:`activity-feed-nav-btn`,title:t.$t(`activity.prev_user_message`),"aria-label":t.$t(`activity.prev_user_message`),loading:Ot.value,disable:Ot.value,onClick:kt},null,8,[`title`,`aria-label`,`loading`,`disable`])])]))}}),[[`__scopeId`,`data-v-58324159`]]);export{Et as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{N as e,mt as t,v as n}from"./reactivity.esm-bundler-Dplx1ILy.js";import{J as r,L as i,Y as a,_ as o,d as s,f as c,l,p as u,r as d,u as f,y as p}from"./runtime-core.esm-bundler-BAUgNzMK.js";import{t as m}from"./QBtn-kfhHFy7Q.js";import{Nt as h,Pt as g,bn as _,en as v,f as y,j as b,l as x,rn as S,vn as C}from"./index-DLoUeovT.js";import{t as w}from"./QSelect-DOnv_ry0.js";import{a as T}from"./use-tours-C_5R1Y5D.js";import{i as E,t as D}from"./permissionModes-D-Nexnoo.js";import{t as O}from"./efforts-C-p9DzkL.js";function k(e,t){return e.engine!==t.engine||e.model!==t.model||e.reasoningEffort!==t.reasoningEffort||e.agentPermissionMode!==t.agentPermissionMode}var A={class:`text-h6`},j={class:`text-body2 text-kobo-2 q-mt-xs`},ee={class:`row q-col-gutter-sm q-mb-md`},te={class:`col-12 col-sm-6`},ne={class:`col-12 col-sm-6`},M={class:`col-12 col-sm-6`},N={class:`col-12 col-sm-6`},P={key:0,class:`text-caption text-kobo-3 q-mt-xs`},F={class:`text-caption text-kobo-3 q-mt-xs`},I={class:`text-caption text-kobo-3 q-mt-xs`},L=T(p({__name:`StartReviewDialog`,props:{modelValue:{type:Boolean},loading:{type:Boolean},workspace:{},canReturnToSession:{type:Boolean},currentSession:{},mode:{},scheduledConfiguration:{}},emits:[`update:modelValue`,`submit`],setup(e,{emit:p}){let T=e,L=p,{t:R}=v(),z=b(),B=l(()=>T.mode===`auto-loop`),V=n(``),H=n(!1),U=n(!1),W=n(`claude-code`),G=n(`auto`),K=n(`auto`),q=n(`bypass`),J=l(()=>B.value||U.value?`plan`:q.value),Y=l(()=>({engine:W.value,model:G.value,reasoningEffort:K.value,agentPermissionMode:J.value})),X=l(()=>{let e=T.currentSession;return B.value||U.value||!!T.workspace&&k(Y.value,T.workspace)||!!e?.engine&&e.engine!==W.value||G.value!==`auto`&&!!e?.model&&e.model!==G.value}),Z=l({get:()=>H.value||X.value,set:e=>{H.value=e}}),re=l(()=>[{value:`claude-code`,label:R(`workspacePage.engineClaude`)},{value:`codex`,label:R(`workspacePage.engineCodex`)}]),Q=l(()=>{let e=(E[W.value]??[]).map(e=>({value:e.value,label:R(e.i18nLabelKey)}));T.workspace?.engine===W.value&&!e.some(e=>e.value===T.workspace?.model)&&e.push({value:T.workspace.model,label:T.workspace.model});let t=T.scheduledConfiguration;return t?.engine===W.value&&!e.some(e=>e.value===t.model)&&e.push({value:t.model,label:t.model}),e}),ie=l(()=>(O[W.value]??[]).map(e=>({value:e.value,label:R(e.i18nLabelKey)}))),$=l(()=>(D[W.value]??[]).map(e=>({value:e,label:R(`agentPermissionMode.${e}`)})));r(W,()=>{let e=T.workspace,t=z.global.defaultModelByEngine[W.value];G.value=e?.engine===W.value?e.model:Q.value.some(e=>e.value===t)?t:`auto`,K.value=e?.engine===W.value?e.reasoningEffort:`auto`,$.value.some(e=>e.value===q.value)||(q.value=`plan`)},{flush:`sync`}),r([()=>T.modelValue,()=>T.workspace?.id],([e])=>{if(!e)return;let t=B.value?T.scheduledConfiguration:null;V.value=t?.additionalInstructions??``,H.value=!1,U.value=B.value,T.workspace&&(W.value=T.workspace.engine,G.value=T.workspace.model,K.value=T.workspace.reasoningEffort,q.value=T.workspace.agentPermissionMode),t&&(W.value=t.engine,G.value=t.model,K.value=t.reasoningEffort)},{immediate:!0}),r(()=>T.canReturnToSession,e=>{!e&&!B.value&&(U.value=!1)});function ae(){T.loading||L(`update:modelValue`,!1)}function oe(){!T.loading&&T.workspace&&L(`submit`,{...Y.value,additionalInstructions:V.value,newSession:Z.value,returnToSession:B.value||U.value&&T.canReturnToSession})}return(n,r)=>(i(),s(S,{"model-value":e.modelValue,persistent:e.loading,"onUpdate:modelValue":r[7]||=e=>L(`update:modelValue`,e)},{default:a(()=>[o(C,{class:`review-card text-kobo-1`},{default:a(()=>[o(_,null,{default:a(()=>[f(`div`,A,t(n.$t(B.value?`autoLoop.finalReview.title`:`review.title`)),1),f(`div`,j,t(n.$t(B.value?`autoLoop.finalReview.description`:`review.subtitle`)),1)]),_:1}),o(y,{dark:``}),o(_,{class:`q-pt-md`},{default:a(()=>[f(`div`,ee,[f(`div`,te,[o(w,{modelValue:W.value,"onUpdate:modelValue":r[0]||=e=>W.value=e,options:re.value,label:n.$t(`engine.select`),disable:e.loading,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``},null,8,[`modelValue`,`options`,`label`,`disable`])]),f(`div`,ne,[o(w,{modelValue:G.value,"onUpdate:modelValue":r[1]||=e=>G.value=e,options:Q.value,label:n.$t(`engine.model`),disable:e.loading,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``},null,8,[`modelValue`,`options`,`label`,`disable`])]),f(`div`,M,[o(w,{modelValue:K.value,"onUpdate:modelValue":r[2]||=e=>K.value=e,options:ie.value,label:n.$t(`engine.effort`),disable:e.loading,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``},null,8,[`modelValue`,`options`,`label`,`disable`])]),f(`div`,N,[o(w,{"model-value":J.value,options:$.value,label:n.$t(`agentPermissionMode.label`),disable:e.loading||U.value,"emit-value":``,"map-options":``,outlined:``,dark:``,dense:``,"onUpdate:modelValue":r[3]||=e=>q.value=e},null,8,[`model-value`,`options`,`label`,`disable`]),U.value?(i(),u(`div`,P,t(n.$t(`review.readOnlyForced`)),1)):c(``,!0)])]),o(g,{modelValue:V.value,"onUpdate:modelValue":r[4]||=e=>V.value=e,type:`textarea`,disable:e.loading,label:n.$t(`review.additionalInstructions`),placeholder:n.$t(`review.additionalInstructionsPlaceholder`),rows:4,outlined:``,dark:``,color:`primary`},null,8,[`modelValue`,`disable`,`label`,`placeholder`])]),_:1}),B.value?c(``,!0):(i(),s(_,{key:0,class:`q-pt-none`},{default:a(()=>[o(x,{modelValue:Z.value,"onUpdate:modelValue":r[5]||=e=>Z.value=e,disable:e.loading||X.value,label:n.$t(`review.newSession`),color:`primary`,dark:``},null,8,[`modelValue`,`disable`,`label`]),f(`div`,F,t(n.$t(X.value?`review.newSessionRequired`:`review.newSessionHint`)),1),Z.value?(i(),u(d,{key:0},[o(x,{modelValue:U.value,"onUpdate:modelValue":r[6]||=e=>U.value=e,disable:e.loading||!e.canReturnToSession,label:n.$t(`review.returnToSession`),color:`primary`,dark:``,class:`q-mt-md`},null,8,[`modelValue`,`disable`,`label`]),f(`div`,I,t(n.$t(e.canReturnToSession?`review.returnToSessionHint`:`review.returnUnavailable`)),1)],64)):c(``,!0)]),_:1})),o(h,{align:`right`,class:`q-pa-md`},{default:a(()=>[o(m,{flat:``,"no-caps":``,label:n.$t(`review.cancel`),color:`kobo-2`,disable:e.loading,onClick:ae},null,8,[`label`,`disable`]),o(m,{"no-caps":``,color:`primary`,label:n.$t(B.value?`autoLoop.finalReview.save`:`review.start`),loading:e.loading,disable:!e.workspace||e.loading,onClick:oe},null,8,[`label`,`loading`,`disable`])]),_:1})]),_:1})]),_:1},8,[`model-value`,`persistent`]))}}),[[`__scopeId`,`data-v-a4419350`]]),R={class:`final-review-settings`},z={class:`text-caption text-kobo-3`},B=T(p({__name:`AutoLoopFinalReviewSettings`,props:{modelValue:{},workspace:{},disabled:{type:Boolean},loading:{type:Boolean}},emits:[`update:modelValue`],setup(a,{emit:l}){let d=a,p=l,{t:h}=v(),g=n(!1),_=n(null);r(()=>d.modelValue,e=>{JSON.stringify(e)===JSON.stringify(_.value)&&(_.value=null)}),r(()=>d.workspace.id,()=>{g.value=!1,_.value=null});function y(e){d.disabled||d.loading||(e?g.value=!0:p(`update:modelValue`,null))}function b(e){!d.disabled&&!d.loading&&e.engine&&e.model&&e.reasoningEffort&&(_.value={engine:e.engine,model:e.model,reasoningEffort:e.reasoningEffort,additionalInstructions:e.additionalInstructions??``},p(`update:modelValue`,_.value),g.value=!1)}return(n,r)=>(i(),u(`div`,R,[o(x,{"model-value":!!a.modelValue,disable:a.disabled||a.loading,color:`primary`,label:e(h)(`autoLoop.finalReview.enabled`),"onUpdate:modelValue":y},null,8,[`model-value`,`disable`,`label`]),f(`p`,z,t(e(h)(`autoLoop.finalReview.hint`)),1),a.modelValue?(i(),s(m,{key:0,flat:``,dense:``,"no-caps":``,disable:a.disabled||a.loading,label:e(h)(`autoLoop.finalReview.configure`),onClick:r[0]||=e=>g.value=!0},null,8,[`disable`,`label`])):c(``,!0),o(L,{modelValue:g.value,"onUpdate:modelValue":r[1]||=e=>g.value=e,mode:`auto-loop`,loading:a.loading??!1,workspace:a.workspace,"scheduled-configuration":_.value??a.modelValue,"can-return-to-session":!0,onSubmit:b},null,8,[`modelValue`,`loading`,`workspace`,`scheduled-configuration`])]))}}),[[`__scopeId`,`data-v-b7f36f83`]]);export{L as n,B as t};
|