@loicngr/kobo 1.11.40 → 1.11.42
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 +110 -32
- package/CHANGELOG.md +8 -0
- package/README.md +6 -4
- package/dist/server/routes/images.js +65 -5
- package/dist/server/routes/workspaces.js +29 -10
- package/dist/server/services/activity-service.js +3 -0
- package/dist/server/services/agent/engines/claude-code/engine.js +1 -0
- package/dist/server/services/attachment-service.js +122 -0
- package/dist/server/services/image-service.js +16 -7
- package/dist/server/services/settings-service.js +13 -0
- package/dist/shared/attachments.js +58 -0
- package/package.json +1 -1
- package/src/client/dist/pwa/assets/{ActivityFeed-MqsC-95i.js → ActivityFeed-CyCGH0Zr.js} +1 -1
- package/src/client/dist/pwa/assets/{ChangelogPage-BxURWYxa.js → ChangelogPage-B6xjI-I4.js} +1 -1
- package/src/client/dist/pwa/assets/{ComparisonPanel-DjiMDAUi.js → ComparisonPanel-BmRzCu1c.js} +1 -1
- package/src/client/dist/pwa/assets/CreatePage-DHe0x63K.js +2 -0
- package/src/client/dist/pwa/assets/CreatePage-TY9g3hZb.css +1 -0
- package/src/client/dist/pwa/assets/{DashboardPage-mA5RZf1t.js → DashboardPage-CRTkx9P-.js} +1 -1
- package/src/client/dist/pwa/assets/{DiffViewer-Ci7ucmy3.js → DiffViewer-BJfLiYGs.js} +3 -3
- package/src/client/dist/pwa/assets/{DocumentsPanel-RZTvcJkT.js → DocumentsPanel-B9jjzoqY.js} +1 -1
- package/src/client/dist/pwa/assets/{DrawerToggleButton-CTOc3yA0.js → DrawerToggleButton-CBZpPa30.js} +1 -1
- package/src/client/dist/pwa/assets/GitPanel-gqySqt7G.js +3 -0
- package/src/client/dist/pwa/assets/{HealthPage-D3__hKJ5.js → HealthPage-9VEvoR_4.js} +1 -1
- package/src/client/dist/pwa/assets/MainLayout-BM14JTBB.js +2 -0
- package/src/client/dist/pwa/assets/{MainLayout-DxMVVA6f.css → MainLayout-Dhj28mOe.css} +1 -1
- package/src/client/dist/pwa/assets/{QBtnDropdown-td0RAVPx.js → QBtnDropdown-Cnkq_5eu.js} +1 -1
- package/src/client/dist/pwa/assets/{QDrawer-BkyFPV7a.js → QDrawer-C3b-lbmv.js} +1 -1
- package/src/client/dist/pwa/assets/{QSelect-WspK3KC7.js → QSelect-DJL6yy1E.js} +1 -1
- package/src/client/dist/pwa/assets/{QTooltip-7pw7iuV0.js → QTooltip-WIoDeLoZ.js} +1 -1
- package/src/client/dist/pwa/assets/{SchedulePanel-CLNM13oo.js → SchedulePanel-tVggDx-C.js} +1 -1
- package/src/client/dist/pwa/assets/{SearchPage-CmRgIvu4.js → SearchPage-l4m1qxKs.js} +1 -1
- package/src/client/dist/pwa/assets/SettingsPage-W6N01tGz.css +1 -0
- package/src/client/dist/pwa/assets/SettingsPage-msHMBS7q.js +9 -0
- package/src/client/dist/pwa/assets/{SplitWorkspacePage-CCHGEsKf.js → SplitWorkspacePage-AXMYd-9b.js} +1 -1
- package/src/client/dist/pwa/assets/{TerminalPanel-ijmsYk-2.js → TerminalPanel-CENXl8po.js} +1 -1
- package/src/client/dist/pwa/assets/{TouchPan-DuQFjlQF.js → TouchPan-DaEpS0Vf.js} +1 -1
- package/src/client/dist/pwa/assets/{TourReplayButton-C5RF0lOK.js → TourReplayButton-U3mwT0fV.js} +1 -1
- package/src/client/dist/pwa/assets/{WorkspaceAttentionLabels-BR_XElog.js → WorkspaceAttentionLabels-CpTA6VGb.js} +1 -1
- package/src/client/dist/pwa/assets/WorkspacePage-DCg9zSxz.css +1 -0
- package/src/client/dist/pwa/assets/WorkspacePage-DE6-wlf7.js +4 -0
- package/src/client/dist/pwa/assets/{browser-DUqzdkbg.js → browser-B4pgwnXH.js} +1 -1
- package/src/client/dist/pwa/assets/{build-path-tree-ImiSpzfI.js → build-path-tree-6ENXqh7O.js} +1 -1
- package/src/client/dist/pwa/assets/{de-CaHID9mW.js → de-D9gPXN_a.js} +2 -2
- package/src/client/dist/pwa/assets/{documents-2C1hn1pw.js → documents-D88lOIxR.js} +1 -1
- package/src/client/dist/pwa/assets/{editor.api-Bm1vVaBr.js → editor.api-qQNqM6Y6.js} +1 -1
- package/src/client/dist/pwa/assets/{efforts-_D8DFYXo.js → efforts-CMQi0Rp2.js} +1 -1
- package/src/client/dist/pwa/assets/{engineFeatures-CZ9qfBYq.js → engineFeatures-BPcXoXFd.js} +1 -1
- package/src/client/dist/pwa/assets/{es-ZhzlCmer.js → es-B4Q1ncyP.js} +2 -2
- package/src/client/dist/pwa/assets/expand-template-DuMdsD-k.js +1 -0
- package/src/client/dist/pwa/assets/{format-pPImdFUy.js → format-BVgznNN8.js} +1 -1
- package/src/client/dist/pwa/assets/{formatters-dTX38ZsV.js → formatters-DV1VAk5B.js} +1 -1
- package/src/client/dist/pwa/assets/{fr-BfXw7L7J.js → fr-Qv-edVJn.js} +2 -2
- package/src/client/dist/pwa/assets/{freemarker2-DGluDnAm.js → freemarker2-Nt_Z9A9p.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-BgviTnpD.js → handlebars-sJjXCA98.js} +1 -1
- package/src/client/dist/pwa/assets/{html-CV-MrunL.js → html-B2wF0NKO.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-eiyc92m_.js +1 -0
- package/src/client/dist/pwa/assets/{index-9tiw3NjK.js → index-DtsrXdzx.js} +3 -3
- package/src/client/dist/pwa/assets/{it-CHAurcTF.js → it-D-qzfo9T.js} +2 -2
- package/src/client/dist/pwa/assets/{javascript-C2VFcHf2.js → javascript-BPbCoYVZ.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-Dg9ZhbP5.js → liquid-DBwLDEnw.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-ImgrPYNl.js → mdx-D_Sphj-P.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco-contributions-waWZXS1T.js → monaco-contributions-BBjimLYs.js} +1 -1
- package/src/client/dist/pwa/assets/monaco.contribution-DNP7HNKq.js +2 -0
- package/src/client/dist/pwa/assets/{network-auth-h6O0qCMA.js → network-auth-BLGlHVxR.js} +1 -1
- package/src/client/dist/pwa/assets/{notify-theme-Dyg-FGI6.js → notify-theme-tvaTlaFw.js} +1 -1
- package/src/client/dist/pwa/assets/{pending-diff-open-Dfpg92li.js → pending-diff-open-CxtcrwVN.js} +1 -1
- package/src/client/dist/pwa/assets/{permissionModes-DDWmNBW1.js → permissionModes-CESk18Vk.js} +1 -1
- package/src/client/dist/pwa/assets/{project-color-XUfY_zbw.js → project-color-D75XmBNA.js} +1 -1
- package/src/client/dist/pwa/assets/{python-DVLs-DBA.js → python-BdBTal4f.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-D34BqMwB.js → razor-gJNlRRte.js} +1 -1
- package/src/client/dist/pwa/assets/{render-chat-markdown-DwUkPy-O.js → render-chat-markdown-K3tUW_1p.js} +1 -1
- package/src/client/dist/pwa/assets/{typescript-C7-UpNXx.js → typescript-DAGixRVW.js} +1 -1
- package/src/client/dist/pwa/assets/{use-tours-DfE-kwrX.js → use-tours-CS5oBFk6.js} +1 -1
- package/src/client/dist/pwa/assets/{workspace-templates-DwwdOEAq.js → workspace-templates-BoGUaV1C.js} +1 -1
- package/src/client/dist/pwa/assets/{xml-Cx1oQhEr.js → xml-BC9pt2ZZ.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-BQOiJsdx.js → yaml-CQpOdW-l.js} +1 -1
- package/src/client/dist/pwa/index.html +1 -1
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/mcp-server/README.md +51 -7
- package/src/client/dist/pwa/assets/CreatePage-A3pVradx.css +0 -1
- package/src/client/dist/pwa/assets/CreatePage-B-rhBmqv.js +0 -2
- package/src/client/dist/pwa/assets/GitPanel-_kL2gMoy.js +0 -3
- package/src/client/dist/pwa/assets/MainLayout-B3JKUm3n.js +0 -2
- package/src/client/dist/pwa/assets/SettingsPage-Cj6Y_TlN.js +0 -9
- package/src/client/dist/pwa/assets/SettingsPage-sVlsJQ0b.css +0 -1
- package/src/client/dist/pwa/assets/WorkspacePage-BGCildyP.js +0 -4
- package/src/client/dist/pwa/assets/WorkspacePage-CqcMweNL.css +0 -1
- package/src/client/dist/pwa/assets/expand-template-CbDNnTPP.js +0 -1
- package/src/client/dist/pwa/assets/i18n-CmXBenUp.js +0 -1
- package/src/client/dist/pwa/assets/monaco.contribution-BB2rtCHM.js +0 -2
|
@@ -7,12 +7,15 @@ const execFileAsync = promisify(execFileCb);
|
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { Hono } from 'hono';
|
|
10
|
+
import { bodyLimit } from 'hono/body-limit';
|
|
11
|
+
import { MAX_ATTACHMENT_REQUEST_BYTES } from '../../shared/attachments.js';
|
|
10
12
|
import { AUTO_LOOP_HARD_RULES, buildAutoLoopGroomingSteps, buildGroomingIntro } from '../../shared/auto-loop-prompts.js';
|
|
11
13
|
import { getDb } from '../db/index.js';
|
|
12
14
|
import { migrationGuard } from '../middleware/migration-guard.js';
|
|
13
15
|
import { listEngines } from '../services/agent/engines/registry.js';
|
|
14
16
|
import * as agentManager from '../services/agent/orchestrator.js';
|
|
15
17
|
import * as archiveScriptService from '../services/archive-script-service.js';
|
|
18
|
+
import { AttachmentRequestError, attachmentReference, readWorkspaceCreationRequest, removeAttachments, saveAttachments, } from '../services/attachment-service.js';
|
|
16
19
|
import * as autoLoopService from '../services/auto-loop-service.js';
|
|
17
20
|
import { changeSourceBranch } from '../services/change-source-branch-service.js';
|
|
18
21
|
import { listChatHistory, pushChatHistory } from '../services/chat-history-service.js';
|
|
@@ -183,12 +186,16 @@ function emitCreateFailed(creationId, step, message) {
|
|
|
183
186
|
*/
|
|
184
187
|
async function rollbackFailedCreation(workspace, opts) {
|
|
185
188
|
try {
|
|
186
|
-
|
|
189
|
+
const warnings = await deleteWorkspaceWithSideEffects(workspace, {
|
|
187
190
|
deleteLocalBranch: opts.deleteLocalBranch,
|
|
188
191
|
removeWorktree: opts.removeWorktree,
|
|
189
192
|
// Nothing was ever pushed at this point in the creation flow.
|
|
190
193
|
deleteRemoteBranch: false,
|
|
191
194
|
});
|
|
195
|
+
if (!opts.removeWorktree && opts.attachments?.length && fs.existsSync(workspace.worktreePath)) {
|
|
196
|
+
warnings.push(...(await removeAttachments(workspace.worktreePath, opts.attachments)));
|
|
197
|
+
}
|
|
198
|
+
return warnings;
|
|
192
199
|
}
|
|
193
200
|
catch (err) {
|
|
194
201
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -353,8 +360,11 @@ app.post('/:id/switch-engine', migrationGuard, async (c) => {
|
|
|
353
360
|
return c.json({ error: err instanceof Error ? err.message : 'Failed to switch engine' }, 500);
|
|
354
361
|
}
|
|
355
362
|
});
|
|
356
|
-
|
|
357
|
-
|
|
363
|
+
const creationBodyLimit = bodyLimit({
|
|
364
|
+
maxSize: MAX_ATTACHMENT_REQUEST_BYTES,
|
|
365
|
+
onError: (c) => c.json({ error: 'Workspace creation request is too large', step: 'validate' }, 413),
|
|
366
|
+
});
|
|
367
|
+
app.post('/', migrationGuard, creationBodyLimit, async (c) => {
|
|
358
368
|
// Declared outside the try so the outermost catch (any unforeseen throw,
|
|
359
369
|
// including a malformed request body) can still name a step instead of
|
|
360
370
|
// leaving the create-progress channel silent. Updated in lockstep with
|
|
@@ -371,14 +381,13 @@ app.post('/', migrationGuard, async (c) => {
|
|
|
371
381
|
// branch. A failure before that point must not delete a branch we never made.
|
|
372
382
|
let createdBranch = false;
|
|
373
383
|
let createdWorktree = false;
|
|
384
|
+
let creationAttachments = [];
|
|
374
385
|
// Hoisted out of the `build-prompt` block (declared with `const` there) so
|
|
375
386
|
// both rollback paths — the `start-agent` branch and the outermost catch —
|
|
376
387
|
// can read it to decide whether the setup-script caveat applies.
|
|
377
388
|
let setupScriptConfigured = false;
|
|
378
389
|
try {
|
|
379
|
-
const body = await c.req
|
|
380
|
-
.json()
|
|
381
|
-
.catch(() => ({}));
|
|
390
|
+
const { body, attachments } = await readWorkspaceCreationRequest(c.req.raw);
|
|
382
391
|
// workingBranch is derived from git when worktreePath is provided, so
|
|
383
392
|
// it's not required in that flow. The other 3 fields stay mandatory.
|
|
384
393
|
if (!body.name || !body.projectPath || !body.sourceBranch) {
|
|
@@ -890,6 +899,7 @@ app.post('/', migrationGuard, async (c) => {
|
|
|
890
899
|
['.ai/.git-conventions.md', '.git-conventions.md'],
|
|
891
900
|
['.ai/thoughts/', 'thoughts/'],
|
|
892
901
|
['.ai/images/', 'images/'],
|
|
902
|
+
['.ai/attachments/', 'attachments/'],
|
|
893
903
|
['.ai/.setup-script.tmp', '.setup-script.tmp'],
|
|
894
904
|
['.ai/.cleanup-script.tmp', '.cleanup-script.tmp'],
|
|
895
905
|
['.ai/.archive-script.tmp', '.archive-script.tmp'],
|
|
@@ -943,6 +953,12 @@ app.post('/', migrationGuard, async (c) => {
|
|
|
943
953
|
let agentStartError = null;
|
|
944
954
|
currentStep = 'write-context-files';
|
|
945
955
|
emitCreateProgress(creationId, currentStep);
|
|
956
|
+
creationAttachments = await saveAttachments(worktreePath, attachments);
|
|
957
|
+
if (creationAttachments.length > 0) {
|
|
958
|
+
body.description = [body.description?.trim(), ...creationAttachments.map(attachmentReference)]
|
|
959
|
+
.filter(Boolean)
|
|
960
|
+
.join('\n\n');
|
|
961
|
+
}
|
|
946
962
|
// Save Notion content as markdown in worktree
|
|
947
963
|
let notionFilePath = null;
|
|
948
964
|
if (notionContent && body.notionUrl) {
|
|
@@ -1249,9 +1265,10 @@ Once the brainstorming + planning steps above are complete and you have a saved
|
|
|
1249
1265
|
}
|
|
1250
1266
|
// Setup script — runs AFTER the prompt is persisted so a crash here
|
|
1251
1267
|
// leaves the workspace in `error` state with `initial_prompt` ready for
|
|
1252
|
-
// a retry via POST /:id/start.
|
|
1253
|
-
//
|
|
1254
|
-
|
|
1268
|
+
// a retry via POST /:id/start. PR imports require an explicit false to
|
|
1269
|
+
// run setup; ordinary worktree reuse continues to skip it unconditionally.
|
|
1270
|
+
const allowSetupScript = prContent ? body.skipSetupScript === false : !useReusedWorktree && !body.skipSetupScript;
|
|
1271
|
+
setupScriptConfigured = Boolean(effectiveSettings.setupScript) && allowSetupScript;
|
|
1255
1272
|
if (setupScriptConfigured) {
|
|
1256
1273
|
currentStep = 'setup-script';
|
|
1257
1274
|
emitCreateProgress(creationId, currentStep);
|
|
@@ -1343,6 +1360,7 @@ Once the brainstorming + planning steps above are complete and you have a saved
|
|
|
1343
1360
|
const rollbackWarnings = await rollbackFailedCreation(workspace, {
|
|
1344
1361
|
deleteLocalBranch: createdBranch,
|
|
1345
1362
|
removeWorktree: createdWorktree,
|
|
1363
|
+
attachments: creationAttachments,
|
|
1346
1364
|
});
|
|
1347
1365
|
// The setup script has already run by this point — see the caveat's own
|
|
1348
1366
|
// comment for what that rollback cannot reach.
|
|
@@ -1400,6 +1418,7 @@ Once the brainstorming + planning steps above are complete and you have a saved
|
|
|
1400
1418
|
const rollbackWarnings = await rollbackFailedCreation(createdWorkspace, {
|
|
1401
1419
|
deleteLocalBranch: createdBranch,
|
|
1402
1420
|
removeWorktree: createdWorktree,
|
|
1421
|
+
attachments: creationAttachments,
|
|
1403
1422
|
});
|
|
1404
1423
|
logError('workspaces', 'Rolled back a creation that failed outside any per-step handler', {
|
|
1405
1424
|
workspaceId: createdWorkspace.id,
|
|
@@ -1418,7 +1437,7 @@ Once the brainstorming + planning steps above are complete and you have a saved
|
|
|
1418
1437
|
return c.json({ error: failure, step: currentStep, rollback: { done: true, warnings: rollbackWarnings } }, 500);
|
|
1419
1438
|
}
|
|
1420
1439
|
emitCreateFailed(creationId, currentStep, message);
|
|
1421
|
-
return c.json({ error: message, step: currentStep }, 500);
|
|
1440
|
+
return c.json({ error: message, step: currentStep }, err instanceof AttachmentRequestError ? 400 : 500);
|
|
1422
1441
|
}
|
|
1423
1442
|
});
|
|
1424
1443
|
// POST /api/workspaces/:id/sessions — create a new idle agent session
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getDb } from '../db/index.js';
|
|
2
|
+
import { getGlobalSettings } from './settings-service.js';
|
|
2
3
|
export function classifyActivity(type, payload) {
|
|
3
4
|
const event = payload && typeof payload === 'object' ? payload : {};
|
|
4
5
|
if (type === 'agent:event') {
|
|
@@ -33,6 +34,8 @@ export function recordActivity(workspaceId, type, payload, sessionId, db) {
|
|
|
33
34
|
if (!kind)
|
|
34
35
|
return;
|
|
35
36
|
try {
|
|
37
|
+
if (getGlobalSettings().activityDigestEnabled === false)
|
|
38
|
+
return;
|
|
36
39
|
const database = db ?? getDb();
|
|
37
40
|
database
|
|
38
41
|
.prepare('INSERT INTO workspace_activity (workspace_id, kind, session_id, created_at) VALUES (?, ?, ?, ?)')
|
|
@@ -377,6 +377,7 @@ export function createClaudeCodeEngine() {
|
|
|
377
377
|
kind: 'error',
|
|
378
378
|
category: 'other',
|
|
379
379
|
message: 'Session force-ended: the SDK generator stayed open after its final result (drain watchdog).',
|
|
380
|
+
code: 'result_drain_timeout',
|
|
380
381
|
});
|
|
381
382
|
emitSessionEnded('watchdog', null);
|
|
382
383
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { nanoid } from 'nanoid';
|
|
4
|
+
import { attachmentFormat, validateAttachments } from '../../shared/attachments.js';
|
|
5
|
+
import { ensureDirectoryInside, resolvePathInside } from '../utils/safe-path.js';
|
|
6
|
+
import { deleteImage, saveImage } from './image-service.js';
|
|
7
|
+
export class AttachmentRequestError extends Error {
|
|
8
|
+
}
|
|
9
|
+
/** Existing JSON clients stay supported; browser attachments travel with the creation request. */
|
|
10
|
+
export async function readWorkspaceCreationRequest(request) {
|
|
11
|
+
let body;
|
|
12
|
+
let attachments = [];
|
|
13
|
+
try {
|
|
14
|
+
if (request.headers.get('content-type')?.toLowerCase().startsWith('multipart/form-data')) {
|
|
15
|
+
const form = await request.formData();
|
|
16
|
+
const metadata = form.get('workspace');
|
|
17
|
+
if (typeof metadata !== 'string' || form.getAll('workspace').length !== 1) {
|
|
18
|
+
throw new AttachmentRequestError('Expected one JSON workspace field');
|
|
19
|
+
}
|
|
20
|
+
body = JSON.parse(metadata);
|
|
21
|
+
const files = [...form.getAll('attachments'), ...form.getAll('images')];
|
|
22
|
+
if (files.some((file) => !(file instanceof File))) {
|
|
23
|
+
throw new AttachmentRequestError('Each attachment must be a file');
|
|
24
|
+
}
|
|
25
|
+
attachments = files;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
body = await request.json();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (error instanceof AttachmentRequestError)
|
|
33
|
+
throw error;
|
|
34
|
+
throw new AttachmentRequestError('Invalid workspace creation body');
|
|
35
|
+
}
|
|
36
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
|
37
|
+
throw new AttachmentRequestError('Workspace must be a JSON object');
|
|
38
|
+
}
|
|
39
|
+
if ('description' in body && body.description !== undefined && typeof body.description !== 'string') {
|
|
40
|
+
throw new AttachmentRequestError('description must be a string');
|
|
41
|
+
}
|
|
42
|
+
const error = validateAttachments(attachments);
|
|
43
|
+
if (error) {
|
|
44
|
+
const messages = {
|
|
45
|
+
type: 'Unsupported attachment type. Use PNG, JPEG, GIF, WebP, PDF, Markdown or text documents',
|
|
46
|
+
size: 'Each attachment must be non-empty and no larger than 50 MB',
|
|
47
|
+
count: 'A workspace can be created with at most 10 attachments',
|
|
48
|
+
total: 'Creation attachments must total no more than 50 MB',
|
|
49
|
+
};
|
|
50
|
+
throw new AttachmentRequestError(messages[error]);
|
|
51
|
+
}
|
|
52
|
+
return { body: body, attachments };
|
|
53
|
+
}
|
|
54
|
+
/** Remove only this creation's files, preserving existing worktree content. */
|
|
55
|
+
export async function removeAttachments(worktreePath, attachments) {
|
|
56
|
+
const warnings = [];
|
|
57
|
+
for (const attachment of attachments) {
|
|
58
|
+
try {
|
|
59
|
+
if (attachment.kind === 'image') {
|
|
60
|
+
await deleteImage(worktreePath, attachment.uid);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const directory = ensureDirectoryInside(worktreePath, '.ai/attachments');
|
|
64
|
+
// Paths come exclusively from saveAttachments; never use the original upload name.
|
|
65
|
+
fs.unlinkSync(path.join(directory, path.basename(attachment.relativePath)));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
warnings.push(`Could not remove creation attachment '${attachment.uid}': ${error instanceof Error ? error.message : error}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return warnings;
|
|
73
|
+
}
|
|
74
|
+
export async function saveAttachments(worktreePath, files) {
|
|
75
|
+
const saved = [];
|
|
76
|
+
try {
|
|
77
|
+
for (const file of files) {
|
|
78
|
+
const format = attachmentFormat(file);
|
|
79
|
+
if (!format)
|
|
80
|
+
throw new AttachmentRequestError('Unsupported attachment type');
|
|
81
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
82
|
+
if (format.kind === 'image') {
|
|
83
|
+
const name = path.extname(file.name).toLowerCase() === format.extension
|
|
84
|
+
? file.name
|
|
85
|
+
: `${file.name || 'image'}${format.extension}`;
|
|
86
|
+
saved.push({ ...(await saveImage(worktreePath, buffer, name)), kind: 'image', originalName: file.name });
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
const directory = ensureDirectoryInside(worktreePath, '.ai/attachments');
|
|
90
|
+
const ignorePath = resolvePathInside(worktreePath, '.gitignore');
|
|
91
|
+
const ignore = fs.existsSync(ignorePath) ? fs.readFileSync(ignorePath, 'utf8') : '';
|
|
92
|
+
if (!ignore.split('\n').some((line) => line.trim() === '.ai/attachments/')) {
|
|
93
|
+
fs.appendFileSync(ignorePath, `${ignore && !ignore.endsWith('\n') ? '\n' : ''}.ai/attachments/\n`);
|
|
94
|
+
}
|
|
95
|
+
const uid = nanoid(10);
|
|
96
|
+
const filename = `${uid}${format.extension}`;
|
|
97
|
+
const descriptor = fs.openSync(path.join(directory, filename), 'wx', 0o600);
|
|
98
|
+
// Track ownership as soon as exclusive creation succeeds: a failed
|
|
99
|
+
// write can leave bytes on disk and must participate in rollback.
|
|
100
|
+
saved.push({ uid, relativePath: `.ai/attachments/${filename}`, kind: 'file', originalName: file.name });
|
|
101
|
+
try {
|
|
102
|
+
fs.writeFileSync(descriptor, buffer);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
fs.closeSync(descriptor);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return saved;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
const warnings = await removeAttachments(worktreePath, saved);
|
|
113
|
+
for (const warning of warnings)
|
|
114
|
+
console.error(`[creation-attachments] ${warning}`);
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export function attachmentReference(attachment) {
|
|
119
|
+
return attachment.kind === 'image'
|
|
120
|
+
? `[image: ${attachment.relativePath}]`
|
|
121
|
+
: `Attached document ${JSON.stringify(attachment.originalName)}: [file: ${attachment.relativePath}]`;
|
|
122
|
+
}
|
|
@@ -39,14 +39,18 @@ function writeIndex(imagesDir, entries) {
|
|
|
39
39
|
throw new Error('Symbolic links are not allowed for image index');
|
|
40
40
|
}
|
|
41
41
|
const tempPath = path.join(imagesDir, `.index-${randomUUID()}.tmp`);
|
|
42
|
-
|
|
42
|
+
const descriptor = fs.openSync(tempPath, 'wx', 0o600);
|
|
43
43
|
try {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
try {
|
|
45
|
+
fs.writeFileSync(descriptor, JSON.stringify(entries, null, 2));
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
fs.closeSync(descriptor);
|
|
49
|
+
}
|
|
46
50
|
fs.renameSync(tempPath, indexPath);
|
|
47
51
|
}
|
|
48
52
|
finally {
|
|
49
|
-
if (
|
|
53
|
+
if (fs.existsSync(tempPath))
|
|
50
54
|
fs.unlinkSync(tempPath);
|
|
51
55
|
}
|
|
52
56
|
}
|
|
@@ -63,11 +67,16 @@ export async function saveImage(worktreePath, fileBuffer, originalName) {
|
|
|
63
67
|
const imagesDir = ensureRealImagesDirectory(worktreePath);
|
|
64
68
|
const filename = `${uid}.${ext}`;
|
|
65
69
|
await withLock(worktreePath, () => {
|
|
66
|
-
//
|
|
67
|
-
// update happen atomically — avoids orphan files on crash between the two.
|
|
70
|
+
// Serialize image/index writes and clean up incomplete writes on failure.
|
|
68
71
|
const imagePath = path.join(imagesDir, filename);
|
|
69
|
-
fs.
|
|
72
|
+
const descriptor = fs.openSync(imagePath, 'wx', 0o600);
|
|
70
73
|
try {
|
|
74
|
+
try {
|
|
75
|
+
fs.writeFileSync(descriptor, fileBuffer);
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
fs.closeSync(descriptor);
|
|
79
|
+
}
|
|
71
80
|
const entries = readIndex(imagesDir);
|
|
72
81
|
entries.push({ uid, originalName, createdAt: new Date().toISOString() });
|
|
73
82
|
writeIndex(imagesDir, entries);
|
|
@@ -901,6 +901,14 @@ const settingsMigrations = [
|
|
|
901
901
|
global.defaultModelByEngine = map;
|
|
902
902
|
},
|
|
903
903
|
},
|
|
904
|
+
{
|
|
905
|
+
version: 58,
|
|
906
|
+
name: 'add-activity-digest-toggle',
|
|
907
|
+
migrate: ({ global }) => {
|
|
908
|
+
if (typeof global.activityDigestEnabled !== 'boolean')
|
|
909
|
+
global.activityDigestEnabled = true;
|
|
910
|
+
},
|
|
911
|
+
},
|
|
904
912
|
];
|
|
905
913
|
/** Current settings schema version — always equals the highest migration version. */
|
|
906
914
|
export const SETTINGS_SCHEMA_VERSION = settingsMigrations.length > 0 ? settingsMigrations[settingsMigrations.length - 1].version : 0;
|
|
@@ -954,6 +962,7 @@ function defaultSettings() {
|
|
|
954
962
|
autoPurgeOnPrMerged: false,
|
|
955
963
|
autoLoopMaxRetries: 5,
|
|
956
964
|
awaitingUserReminderMinutes: 0,
|
|
965
|
+
activityDigestEnabled: true,
|
|
957
966
|
maxConcurrentAgents: 0,
|
|
958
967
|
wsEventsRetentionDays: 0,
|
|
959
968
|
wsEventsKeepPerWorkspace: 0,
|
|
@@ -1460,6 +1469,9 @@ export function updateGlobalSettings(input) {
|
|
|
1460
1469
|
}
|
|
1461
1470
|
}
|
|
1462
1471
|
}
|
|
1472
|
+
if ('activityDigestEnabled' in data && typeof data.activityDigestEnabled !== 'boolean') {
|
|
1473
|
+
delete data.activityDigestEnabled;
|
|
1474
|
+
}
|
|
1463
1475
|
const allowedGlobalKeys = [
|
|
1464
1476
|
'defaultModelByEngine',
|
|
1465
1477
|
'dangerouslySkipPermissions',
|
|
@@ -1485,6 +1497,7 @@ export function updateGlobalSettings(input) {
|
|
|
1485
1497
|
'autoPurgeOnPrMerged',
|
|
1486
1498
|
'autoLoopMaxRetries',
|
|
1487
1499
|
'awaitingUserReminderMinutes',
|
|
1500
|
+
'activityDigestEnabled',
|
|
1488
1501
|
'maxConcurrentAgents',
|
|
1489
1502
|
'wsEventsRetentionDays',
|
|
1490
1503
|
'wsEventsKeepPerWorkspace',
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** Bounds and formats shared by the create form and multipart endpoint. */
|
|
2
|
+
const IMAGE_EXTENSIONS = {
|
|
3
|
+
'image/png': ['.png'],
|
|
4
|
+
'image/jpeg': ['.jpg', '.jpeg'],
|
|
5
|
+
'image/gif': ['.gif'],
|
|
6
|
+
'image/webp': ['.webp'],
|
|
7
|
+
};
|
|
8
|
+
export const DOCUMENT_EXTENSIONS = [
|
|
9
|
+
'.md',
|
|
10
|
+
'.markdown',
|
|
11
|
+
'.txt',
|
|
12
|
+
'.text',
|
|
13
|
+
'.pdf',
|
|
14
|
+
'.csv',
|
|
15
|
+
'.tsv',
|
|
16
|
+
'.json',
|
|
17
|
+
'.jsonl',
|
|
18
|
+
'.yaml',
|
|
19
|
+
'.yml',
|
|
20
|
+
'.xml',
|
|
21
|
+
'.log',
|
|
22
|
+
'.rst',
|
|
23
|
+
'.toml',
|
|
24
|
+
];
|
|
25
|
+
export const ATTACHMENT_ACCEPT = [
|
|
26
|
+
...Object.keys(IMAGE_EXTENSIONS),
|
|
27
|
+
...Object.values(IMAGE_EXTENSIONS).flat(),
|
|
28
|
+
...DOCUMENT_EXTENSIONS,
|
|
29
|
+
].join(',');
|
|
30
|
+
export const MAX_ATTACHMENTS = 10;
|
|
31
|
+
export const MAX_ATTACHMENT_BYTES = 50 * 1024 * 1024;
|
|
32
|
+
export const MAX_ATTACHMENTS_BYTES = 50 * 1024 * 1024;
|
|
33
|
+
export const MAX_ATTACHMENT_REQUEST_BYTES = MAX_ATTACHMENTS_BYTES + 1024 * 1024;
|
|
34
|
+
export function attachmentFormat(file) {
|
|
35
|
+
const extension = /\.[^.\\/]+$/.exec(file.name ?? '')?.[0]?.toLowerCase() ?? '';
|
|
36
|
+
const imageExtensions = IMAGE_EXTENSIONS[file.type];
|
|
37
|
+
if (imageExtensions)
|
|
38
|
+
return { kind: 'image', extension: imageExtensions.includes(extension) ? extension : imageExtensions[0] };
|
|
39
|
+
// Browsers vary in their MIME labels for text documents, especially Markdown.
|
|
40
|
+
if (DOCUMENT_EXTENSIONS.includes(extension))
|
|
41
|
+
return { kind: 'file', extension };
|
|
42
|
+
if ((!file.type || file.type === 'application/octet-stream') &&
|
|
43
|
+
Object.values(IMAGE_EXTENSIONS).some((values) => values.includes(extension))) {
|
|
44
|
+
return { kind: 'image', extension };
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
export function validateAttachments(files) {
|
|
49
|
+
if (files.length > MAX_ATTACHMENTS)
|
|
50
|
+
return 'count';
|
|
51
|
+
if (files.some((file) => !attachmentFormat(file)))
|
|
52
|
+
return 'type';
|
|
53
|
+
if (files.some((file) => file.size === 0 || file.size > MAX_ATTACHMENT_BYTES))
|
|
54
|
+
return 'size';
|
|
55
|
+
if (files.reduce((sum, file) => sum + file.size, 0) > MAX_ATTACHMENTS_BYTES)
|
|
56
|
+
return 'total';
|
|
57
|
+
return null;
|
|
58
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loicngr/kobo",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.42",
|
|
4
4
|
"description": "Kōbō — multi-workspace agent manager for Claude Code. Orchestrates isolated git worktrees with dev servers, Notion integration, and MCP tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{S as e,ct as t,j as n,ot as r,p as i,ut as a,v as o}from"./reactivity.esm-bundler-DEJj9cFv.js";import{C as s,F as c,J as l,L as u,N as d,O as f,P as p,Y as m,_ as h,d as g,f as _,g as v,j as y,k as b,l as x,p as S,r as C,u as w,y as T,z as ee}from"./runtime-core.esm-bundler-CDnVUfeZ.js";import{J as E,b as D,ct as te,l as O,pt as k,t as ne,tt as A,w as re,y as ie}from"./QBtn-CZaBclNw.js";import{A as ae,Dt as oe,Lt as j,N as se,P as M,Pt as ce,Tt as N,Z as le,d as ue,m as de,p as fe,x as P}from"./index-
|
|
1
|
+
import{S as e,ct as t,j as n,ot as r,p as i,ut as a,v as o}from"./reactivity.esm-bundler-DEJj9cFv.js";import{C as s,F as c,J as l,L as u,N as d,O as f,P as p,Y as m,_ as h,d as g,f as _,g as v,j as y,k as b,l as x,p as S,r as C,u as w,y as T,z as ee}from"./runtime-core.esm-bundler-CDnVUfeZ.js";import{J as E,b as D,ct as te,l as O,pt as k,t as ne,tt as A,w as re,y as ie}from"./QBtn-CZaBclNw.js";import{A as ae,Dt as oe,Lt as j,N as se,P as M,Pt as ce,Tt as N,Z as le,d as ue,m as de,p as fe,x as P}from"./index-DtsrXdzx.js";import{t as pe}from"./QSpinnerDots-DnCV3VzI.js";import{t as me}from"./QList-qupzuEqO.js";import{t as F}from"./QMarkupTable-B9QuKtK_.js";import{c as I,l as L}from"./QSelect-DJL6yy1E.js";import{d as he}from"./permissionModes-CESk18Vk.js";import{r as ge,t as R}from"./pending-diff-open-CxtcrwVN.js";import{t as z}from"./documents-D88lOIxR.js";import{a as B}from"./use-tours-CS5oBFk6.js";import{t as V}from"./QTooltip-WIoDeLoZ.js";import{i as _e,n as ve,o as H,r as ye,t as U}from"./render-chat-markdown-K3tUW_1p.js";import{a as be,i as xe,n as Se,o as Ce,r as we,s as W}from"./WorkspacePage-DE6-wlf7.js";function Te(e,t){return s(`div`,e,[s(`table`,{class:`q-table`},t)])}var Ee={list:me,table:F},G=[`list`,`table`,`__qtable`],De=A({name:`QVirtualScroll`,props:{...L,type:{type:String,default:`list`,validator:e=>G.includes(e)},items:{type:Array,default:()=>[]},itemsFn:Function,itemsSize:Number,scrollTarget:j},setup(t,{slots:n,attrs:r}){let i,a=e(null),o=x(()=>t.itemsSize>=0&&t.itemsFn!==void 0?Number.parseInt(t.itemsSize,10):Array.isArray(t.items)?t.items.length:0),{virtualScrollSliceRange:c,localResetVirtualScroll:u,padVirtualScroll:f,onVirtualScrollEvt:m}=I({virtualScrollLength:o,getVirtualScrollTarget:S,getVirtualScrollEl:v}),h=x(()=>{if(o.value===0)return[];let e=(e,t)=>({index:c.value.from+t,item:e});return t.itemsFn===void 0?t.items.slice(c.value.from,c.value.to).map(e):t.itemsFn(c.value.from,c.value.to-c.value.from).map(e)}),g=x(()=>`q-virtual-scroll q-virtual-scroll`+(t.virtualScrollHorizontal?`--horizontal`:`--vertical`)+(t.scrollTarget===void 0?` scroll`:``)),_=x(()=>t.scrollTarget===void 0?{tabindex:0}:{});l(o,()=>{u()}),l(()=>t.scrollTarget,()=>{w(),C()});function v(){let e=a.value;return e===null?null:e.$el||e}function S(){return i}function C(){let e=v();e!==null&&(i=ce(e,t.scrollTarget),i.addEventListener(`scroll`,m,E.passive))}function w(){i!==void 0&&(i.removeEventListener(`scroll`,m,E.passive),i=void 0)}function T(){let e=f(t.type===`list`?`div`:`tbody`,h.value.map(n.default));return n.before!==void 0&&(e=n.before().concat(e)),D(n.after,e)}return u(),p(()=>{C()}),b(()=>{C()}),d(()=>{w()}),y(()=>{w()}),()=>{if(n.default===void 0){console.error(`QVirtualScroll: default scoped slot is required for rendering`);return}return t.type===`__qtable`?Te({ref:a,class:`q-table__middle `+g.value,tabindex:0},T()):s(Ee[t.type],{...r,ref:a,class:[r.class,g.value],..._.value},T)}}}),K=new Set([`setup`,`cleanup`,`archive`]);function Oe(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:K.has(e.sender)||ae(e.sender)?`script`:`user`;case`session`:return`session`;default:return`agent`}}function ke(e){let t=[],n=null,r=null;for(let i of e){let e=Oe(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 Ae(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 je(e){let t=e.items[0];return`${e.speaker}:${t?Ae(t):`empty`}`}function Me(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 Ne(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))};if(o=l(e,e=>{t(e)&&s(!0)},{immediate:!0}),i){o();return}a=setTimeout(()=>s(!1),n)})}var Pe={class:`agent-error-item`},Fe={class:`agent-error-item__body`},Ie={class:`agent-error-item__title`},Le={class:`agent-error-item__message`},Re=B(T({__name:`AgentErrorItem`,props:{item:{}},setup(e){let t=e,{te:n}=N(),r=x(()=>{let e=`agent.error.${t.item.category}`;return n(e)?e:`agent.error.other`});return(t,n)=>(u(),S(`div`,Pe,[h(O,{name:`error_outline`,size:`16px`,class:`agent-error-item__icon`}),w(`div`,Fe,[w(`div`,Ie,a(t.$t(r.value)),1),w(`div`,Le,a(e.item.message),1)])]))}}),[[`__scopeId`,`data-v-e98c3c02`]]),q={class:`text-caption text-kobo-3`},ze=T({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,n=x(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return W(t.item.detail);case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(u(),S(`span`,q,a(e.$t(n.value)),1))}});function J(e,t){if(t.length===0||e.length===0)return e;let n=[...t].sort((e,t)=>t.length-e.length),r=new DOMParser().parseFromString(`<div>${e}</div>`,`text/html`),i=r.body.firstChild;if(!i)return e;function a(e){if(e.nodeType===Node.TEXT_NODE){Be(e,n,r);return}if(e.nodeName===`A`)return;let t=Array.from(e.childNodes);for(let e of t)a(e)}return a(i),i.innerHTML}function Be(e,t,n){let r=e.textContent??``;if(!t.some(e=>r.includes(e)))return;let i=n.createDocumentFragment(),a=0;for(;a<r.length;){let e=Y(r,a,t);if(!e){i.appendChild(n.createTextNode(r.slice(a)));break}e.index>a&&i.appendChild(n.createTextNode(r.slice(a,e.index)));let o=n.createElement(`a`);o.className=`document-link`,o.setAttribute(`data-document-path`,e.path),o.setAttribute(`href`,`#`),o.textContent=e.path,i.appendChild(o),a=e.index+e.path.length}e.parentNode?.replaceChild(i,e)}function Y(e,t,n){let r=null;for(let i of n){let n=e.indexOf(i,t);n<0||(!r||n<r.index||n===r.index&&i.length>r.path.length)&&(r={index:n,path:i})}return r}var X=[`innerHTML`],Ve=B(T({__name:`TextMessageItem`,props:{item:{}},setup(e){let t=e,n=z(),r=fe(),i=ie(),{t:a}=N(),o=x(()=>{let e=r.selectedWorkspaceId;return e?n.documentsFor(e).map(e=>e.path):[]}),s=x(()=>{let e=t.item;if(e.streaming)return U(e.text);let n=o.value;return ye(`${e.messageId}|${n.join(`,`)}`,e.text,e=>{let t=J(H.parse(e,{async:!1,breaks:!0,gfm:!0}),n);return _e(t,{addAttr:[`data-document-path`]})})});function c(e){let t=e.target?.closest(`.document-link`);if(!t)return;e.preventDefault();let o=t.getAttribute(`data-document-path`),s=r.selectedWorkspaceId;o&&s&&n.openDocumentByPath(s,o).catch(e=>{console.error(`[TextMessageItem] openDocumentByPath failed:`,e),i.notify({type:`negative`,message:a(`documents.loadFailed`),position:`top`})})}return(t,n)=>(u(),S(`div`,{class:`markdown-message`,onClick:c},[w(`div`,{innerHTML:s.value},null,8,X),e.item.streaming?(u(),g(re,{key:0,size:`xs`,class:`q-ml-xs`})):_(``,!0)]))}}),[[`__scopeId`,`data-v-99371035`]]);function He(e,t){let n=e.split(`
|
|
2
2
|
`),r=t.split(`
|
|
3
3
|
`),i=0;for(;i<n.length&&i<r.length&&n[i]===r[i];)i++;let a=n.length,o=r.length;for(;a>i&&o>i&&n[a-1]===r[o-1];)a--,o--;let s=n.slice(0,i).map(e=>({type:`context`,content:e})),c=n.slice(a).map(e=>({type:`context`,content:e})),l=n.slice(i,a),u=r.slice(i,o);return l.length>800||u.length>800?[...s,...l.map(e=>({type:`del`,content:e})),...u.map(e=>({type:`add`,content:e})),...c]:[...s,...Z(l,u),...c]}function Z(e,t){let n=e.length,r=t.length;if(n===0&&r===0)return[];if(n===0)return t.map(e=>({type:`add`,content:e}));if(r===0)return e.map(e=>({type:`del`,content:e}));let i=r+1,a=new Int32Array((n+1)*i);for(let o=n-1;o>=0;o--)for(let n=r-1;n>=0;n--)a[o*i+n]=e[o]===t[n]?a[(o+1)*i+(n+1)]+1:Math.max(a[(o+1)*i+n],a[o*i+(n+1)]);let o=[],s=0,c=0;for(;s<n&&c<r;)e[s]===t[c]?(o.push({type:`context`,content:e[s]}),s++,c++):a[(s+1)*i+c]>=a[s*i+(c+1)]?(o.push({type:`del`,content:e[s]}),s++):(o.push({type:`add`,content:t[c]}),c++);for(;s<n;)o.push({type:`del`,content:e[s++]});for(;c<r;)o.push({type:`add`,content:t[c++]});return o}function Q(e){let t=e.split(`
|
|
4
4
|
`),n=[],r=!1;for(let e of t){if(e.startsWith(`@@`)){r=!0;continue}(r||!e.startsWith(`+++`)&&!e.startsWith(`---`))&&(e.startsWith(`+`)?n.push({type:`add`,content:e.slice(1)}):e.startsWith(`-`)?n.push({type:`del`,content:e.slice(1)}):e.startsWith(` `)?n.push({type:`context`,content:e.slice(1)}):e.length>0&&n.push({type:`context`,content:e}))}return n}function Ue(e,t){if(!t||typeof t!=`object`)return null;let n=t;if(e===`Edit`){let e=n.file_path;if(!e)return null;let t=n.old_string??``,r=n.new_string??``,i=typeof n.diff==`string`?n.diff:``;if(!t&&!r&&i.length>0){let t=Q(i);return{toolName:`Edit`,filePath:e,additions:t.filter(e=>e.type===`add`).length,deletions:t.filter(e=>e.type===`del`).length,diffLines:t}}return{toolName:`Edit`,filePath:e,oldString:t,newString:r,replaceAll:n.replace_all??!1,additions:r?r.split(`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as e,ut as t,v as n}from"./reactivity.esm-bundler-DEJj9cFv.js";import{L as r,P as i,Y as a,_ as o,d as s,f as c,p as l,r as u,u as d,y as f,z as p}from"./runtime-core.esm-bundler-CDnVUfeZ.js";import{t as m}from"./QBtn-CZaBclNw.js";import{Ut as h,Vt as g,n as _}from"./index-
|
|
1
|
+
import{j as e,ut as t,v as n}from"./reactivity.esm-bundler-DEJj9cFv.js";import{L as r,P as i,Y as a,_ as o,d as s,f as c,p as l,r as u,u as d,y as f,z as p}from"./runtime-core.esm-bundler-CDnVUfeZ.js";import{t as m}from"./QBtn-CZaBclNw.js";import{Ut as h,Vt as g,n as _}from"./index-DtsrXdzx.js";import{a as v,i as y,n as b}from"./use-tours-CS5oBFk6.js";import{n as x}from"./render-chat-markdown-K3tUW_1p.js";import{t as S}from"./QChip-owciRuZu.js";import{t as C}from"./QPage-DaX9sMQC.js";import{t as w}from"./DrawerToggleButton-CBZpPa30.js";import{t as T}from"./TourReplayButton-U3mwT0fV.js";var E={class:`row items-center q-mb-md`},D={class:`text-h6 q-ml-sm`},O={key:0,class:`text-kobo-3 text-center q-pa-lg`},k={key:1,class:`text-negative text-center q-pa-lg`},A={key:2,class:`text-kobo-3 text-center q-pa-lg`},j={key:3,class:`column q-gutter-md`,"data-tour":`changelog-list`},M={key:0,class:`text-caption text-kobo-3`},N={class:`row items-center q-mb-sm`},P={class:`text-subtitle1 text-kobo-2`,style:{"font-family":`var(--kobo-font-mono, monospace)`}},F=[`innerHTML`],I=v(f({__name:`ChangelogPage`,setup(f){let v=_(),{scheduleAutoRun:I}=b(),L=n([]),R=n(``),z=n(!1),B=n(null);function V(e){return x(e)}async function H(){z.value=!0,B.value=null;try{let e=await fetch(`/api/changelog`);if(!e.ok)throw Error(`HTTP ${e.status}`);let t=await e.json();R.value=t.currentVersion??``,L.value=t.versions??[]}catch(e){B.value=e instanceof Error?e.message:String(e)}finally{z.value=!1}}return i(()=>{H(),I(`changelog`)}),(n,i)=>(r(),s(C,{class:`q-pa-md`,style:{"max-width":`900px`,margin:`0 auto`}},{default:a(()=>[d(`div`,E,[o(w,{class:`q-mr-xs`}),o(m,{flat:``,dense:``,round:``,icon:`arrow_back`,onClick:i[0]||=t=>e(v).back()}),d(`div`,D,t(n.$t(`changelog.title`)),1),o(y),o(m,{flat:``,dense:``,icon:`refresh`,loading:z.value,label:n.$t(`common.refresh`),onClick:H},null,8,[`loading`,`label`]),o(T,{"tour-id":`changelog`,class:`q-ml-xs`})]),z.value&&L.value.length===0?(r(),l(`div`,O,t(n.$t(`common.loading`)),1)):B.value?(r(),l(`div`,k,t(B.value),1)):L.value.length===0?(r(),l(`div`,A,t(n.$t(`changelog.empty`)),1)):(r(),l(`div`,j,[R.value?(r(),l(`div`,M,t(n.$t(`changelog.currentVersion`,{version:R.value})),1)):c(``,!0),(r(!0),l(u,null,p(L.value,e=>(r(),s(g,{key:e.version,dark:``,flat:``,bordered:``},{default:a(()=>[o(h,null,{default:a(()=>[d(`div`,N,[d(`div`,P,` v`+t(e.version),1),e.version===R.value?(r(),s(S,{key:0,dense:``,size:`sm`,color:`primary`,"text-color":`kobo-1`,label:n.$t(`changelog.current`),class:`q-ml-sm`},null,8,[`label`])):c(``,!0)]),d(`div`,{class:`changelog-notes`,innerHTML:V(e.notes)},null,8,F)]),_:2},1024)]),_:2},1024))),128))]))]),_:1}))}}),[[`__scopeId`,`data-v-6137cb7c`]]);export{I as default};
|
package/src/client/dist/pwa/assets/{ComparisonPanel-DjiMDAUi.js → ComparisonPanel-BmRzCu1c.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ot as e,ut as t,v as n}from"./reactivity.esm-bundler-DEJj9cFv.js";import{J as r,L as i,P as a,Y as ee,_ as o,d as te,f as s,g as ne,l as c,p as l,r as u,u as d,y as f,z as re}from"./runtime-core.esm-bundler-CDnVUfeZ.js";import{t as p}from"./QBtn-CZaBclNw.js";import{Tt as ie,n as ae,p as oe}from"./index-
|
|
1
|
+
import{ot as e,ut as t,v as n}from"./reactivity.esm-bundler-DEJj9cFv.js";import{J as r,L as i,P as a,Y as ee,_ as o,d as te,f as s,g as ne,l as c,p as l,r as u,u as d,y as f,z as re}from"./runtime-core.esm-bundler-CDnVUfeZ.js";import{t as p}from"./QBtn-CZaBclNw.js";import{Tt as ie,n as ae,p as oe}from"./index-DtsrXdzx.js";import{t as se}from"./QMarkupTable-B9QuKtK_.js";import{a as m,i as h}from"./use-tours-CS5oBFk6.js";var g=[`workspace`,`engine`,`model`,`status`,`branch`,`tasks_done`,`tasks_total`,`commits`,`files_changed`,`insertions`,`deletions`,`agent_messages`,`user_messages`,`injected_prompts`,`questions`,`tool_calls`,`errors`,`input_tokens`,`output_tokens`,`sessions`,`duration_seconds`];function _(e){if(e==null)return``;let t=String(e);return/[",\r\n]/.test(t)?`"${t.replace(/"/g,`""`)}"`:t}function v(e){let t=e.map(e=>{let t=e.gitStats,n=e.activity;return[e.workspace.name,e.workspace.engine,e.workspace.model,e.workspace.status,e.workspace.workingBranch,e.tasks.done,e.tasks.total,t?.commitCount??null,t?.filesChanged??null,t?.insertions??null,t?.deletions??null,n.agentMessages,n.userMessages,n.injectedPrompts,n.questions,n.toolCalls,n.errors,n.inputTokens,n.outputTokens,n.sessions,Math.round(n.durationMs/1e3)].map(_).join(`,`)});return`${[g.join(`,`),...t].join(`\r
|
|
2
2
|
`)}`}var y={key:0,class:`comparison-panel q-pa-md`},b={class:`row items-center q-mb-sm`},x={class:`text-subtitle2`},S={class:`text-caption text-kobo-3 q-mb-sm`},C={class:`text-left`},w={class:`text-left`},T={class:`text-right`},E={class:`text-right`},D={class:`text-right`},O={class:`text-right`},k={class:`text-right`},A={class:`text-right`},j={class:`text-right`},M={class:`text-right`},N={class:`text-right`},P={class:`text-right`},F={class:`text-right`},I={class:`text-caption text-kobo-3 comparison-mono`},L={key:0,class:`text-caption text-kobo-3`},R={class:`text-right comparison-mono`},z={class:`text-right`},B={class:`text-right`},V={class:`text-right`},ce={key:0,class:`comparison-mono`},H={class:`text-kobo-success`},U={class:`text-kobo-danger q-ml-xs`},W={key:1,class:`text-kobo-3`},G={class:`text-right comparison-mono`},le=[`title`],ue={class:`text-right`},de={class:`text-right`},fe={class:`text-right comparison-mono`},pe=[`title`],me={class:`text-right`},he={class:`text-right comparison-mono`},ge={class:`text-right`},_e={key:1,class:`text-caption text-kobo-3`},ve={key:0,class:`text-caption text-kobo-warning q-mt-sm`},K=m(f({__name:`ComparisonPanel`,props:{workspaceId:{}},setup(f){let m=f;function g(e){return e<1e3?String(e):e<1e6?`${(e/1e3).toFixed(+(e<1e4))}k`:`${(e/1e6).toFixed(1)}M`}function _(e){if(e<=0)return`-`;let t=Math.round(e/1e3);if(t<60)return q(`reliability.durationSeconds`,{s:t});let n=Math.floor(t/60);return n<60?q(`reliability.durationMinutes`,{m:n,s:t%60}):q(`reliability.durationHours`,{h:Math.floor(n/60),m:n%60})}let K=ae(),ye=oe(),{t:q}=ie(),J=n([]),Y=n(!1),be={created:`workspaceStatus.created`,extracting:`workspaceStatus.extracting`,brainstorming:`workspaceStatus.brainstorming`,executing:`workspaceStatus.executing`,compacting:`workspaceStatus.compacting`,"awaiting-user":`workspaceStatus.awaitingUser`,completed:`workspaceStatus.completed`,idle:`workspaceStatus.idle`,error:`workspaceStatus.error`,quota:`workspaceStatus.quota`,retrying:`workspaceStatus.retrying`},X=n({});function xe(e){return X.value[e]??e}async function Se(){try{let e=await fetch(`/api/engines`);if(!e.ok)return;let t=await e.json();X.value=Object.fromEntries(t.map(e=>[e.id,e.displayName]))}catch{}}function Z(e){let t=be[e];return t?q(t):e}let Ce=c(()=>m.workspaceId);function Q(e){return e.workspace.id===Ce.value}function we(e){ye.selectWorkspace(e),K.push({name:`workspace`,params:{id:e}})}function Te(){let e=v(J.value),t=new Blob([e],{type:`text/csv;charset=utf-8`}),n=URL.createObjectURL(t),r=document.createElement(`a`);r.href=n,r.download=`kobo-comparison-${J.value[0]?.workspace.comparisonId??`comparison`}.csv`,r.click(),URL.revokeObjectURL(n)}async function $(){Y.value=!0;try{let e=await fetch(`/api/workspaces/${m.workspaceId}/comparison`);if(!e.ok){J.value=[];return}let t=await e.json();J.value=t.members??[]}catch{J.value=[]}finally{Y.value=!1}}return a(()=>{Se(),$()}),r(()=>m.workspaceId,$),(n,r)=>J.value.length>0?(i(),l(`div`,y,[d(`div`,b,[d(`div`,x,t(n.$t(`comparison.title`)),1),o(h),o(p,{flat:``,dense:``,round:``,size:`sm`,icon:`download`,title:n.$t(`comparison.exportCsv`),onClick:Te},null,8,[`title`]),o(p,{flat:``,dense:``,round:``,size:`sm`,icon:`refresh`,loading:Y.value,onClick:$},null,8,[`loading`])]),d(`div`,S,t(n.$t(`comparison.hint`)),1),o(se,{flat:``,dense:``,dark:``,class:`comparison-table`},{default:ee(()=>[d(`thead`,null,[d(`tr`,null,[d(`th`,C,t(n.$t(`comparison.engine`)),1),d(`th`,w,t(n.$t(`comparison.status`)),1),d(`th`,T,t(n.$t(`comparison.tasks`)),1),d(`th`,E,t(n.$t(`comparison.commits`)),1),d(`th`,D,t(n.$t(`comparison.files`)),1),d(`th`,O,t(n.$t(`comparison.diff`)),1),d(`th`,k,t(n.$t(`comparison.messages`)),1),d(`th`,A,t(n.$t(`comparison.questions`)),1),d(`th`,j,t(n.$t(`comparison.tools`)),1),d(`th`,M,t(n.$t(`comparison.errors`)),1),d(`th`,N,t(n.$t(`comparison.tokens`)),1),d(`th`,P,t(n.$t(`comparison.sessions`)),1),d(`th`,F,t(n.$t(`comparison.duration`)),1),r[0]||=d(`th`,null,null,-1)])]),d(`tbody`,null,[(i(!0),l(u,null,re(J.value,r=>(i(),l(`tr`,{key:r.workspace.id,class:e({"comparison-current":Q(r)})},[d(`td`,null,[d(`div`,null,t(xe(r.workspace.engine)),1),d(`div`,I,t(r.workspace.model),1)]),d(`td`,null,[ne(t(Z(r.workspace.status))+` `,1),r.workspace.archivedAt?(i(),l(`span`,L,` · `+t(n.$t(`comparison.archived`)),1)):s(``,!0)]),d(`td`,R,t(r.tasks.done)+`/`+t(r.tasks.total),1),d(`td`,z,t(r.gitStats?.commitCount??`-`),1),d(`td`,B,t(r.gitStats?.filesChanged??`-`),1),d(`td`,V,[r.gitStats?(i(),l(`span`,ce,[d(`span`,H,`+`+t(r.gitStats.insertions),1),d(`span`,U,`-`+t(r.gitStats.deletions),1)])):(i(),l(`span`,W,`-`))]),d(`td`,G,[d(`span`,{title:n.$t(`comparison.messagesTitle`)},t(r.activity.agentMessages)+` / `+t(r.activity.userMessages),9,le)]),d(`td`,ue,t(r.activity.questions),1),d(`td`,de,t(r.activity.toolCalls),1),d(`td`,{class:e([`text-right`,r.activity.errors>0?`text-kobo-danger`:``])},t(r.activity.errors),3),d(`td`,fe,[d(`span`,{title:n.$t(`comparison.tokensTitle`)},t(g(r.activity.inputTokens))+` / `+t(g(r.activity.outputTokens)),9,pe)]),d(`td`,me,t(r.activity.sessions),1),d(`td`,he,t(_(r.activity.durationMs)),1),d(`td`,ge,[Q(r)?(i(),l(`span`,_e,t(n.$t(`comparison.current`)),1)):(i(),te(p,{key:0,flat:``,dense:``,"no-caps":``,size:`sm`,label:n.$t(`comparison.open`),onClick:e=>we(r.workspace.id)},null,8,[`label`,`onClick`]))])],2))),128))])]),_:1}),J.value.length<2?(i(),l(`div`,ve,t(n.$t(`comparison.incomplete`)),1)):s(``,!0)])):s(``,!0)}}),[[`__scopeId`,`data-v-e1a1dbcc`]]);export{K as default};
|