@loicngr/kobo 1.11.1 → 1.11.3
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/CHANGELOG.md +36 -0
- package/README.md +6 -6
- package/dist/server/routes/workspaces.js +69 -1
- package/dist/server/services/agent/engines/claude-code/engine.js +1 -0
- package/dist/server/services/agent/engines/claude-code/event-mapper.js +1 -1
- package/dist/server/services/agent/engines/claude-code/options-builder.js +2 -0
- package/dist/server/services/agent/engines/codex/engine.js +1 -1
- package/dist/server/services/agent/engines/codex/event-mapper.js +1 -1
- package/dist/server/services/agent/orchestrator.js +91 -3
- package/dist/server/services/forge/bitbucket/provider.js +291 -0
- package/dist/server/services/forge/github/provider.js +10 -1
- package/dist/server/services/forge/gitlab/provider.js +10 -1
- package/dist/server/services/forge/none.js +10 -1
- package/dist/server/services/forge/registry.js +3 -1
- package/dist/server/services/forge/resolve.js +4 -1
- package/dist/server/services/settings-defaults.js +1 -1
- package/dist/server/services/settings-service.js +68 -12
- package/dist/shared/project-colors.js +7 -0
- package/package.json +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-DB_EucGW.js +8 -0
- package/src/client/dist/pwa/assets/{ChangelogPage-CppcIG5K.js → ChangelogPage-CNn9l8Ht.js} +1 -1
- package/src/client/dist/pwa/assets/{ClosePopup-B0-qi7Jo.js → ClosePopup-BycCSMYN.js} +1 -1
- package/src/client/dist/pwa/assets/CreatePage-B_JcmRfU.css +1 -0
- package/src/client/dist/pwa/assets/CreatePage-BnEu_-oB.js +2 -0
- package/src/client/dist/pwa/assets/{DiffViewer-COD5Mdp_.js → DiffViewer-DiV6QEdI.js} +3 -3
- package/src/client/dist/pwa/assets/{HealthPage-Bnad2BVm.js → HealthPage-ByBfOzgZ.js} +1 -1
- package/src/client/dist/pwa/assets/{MainLayout-Bwulksar.css → MainLayout-BY-xOjDK.css} +1 -1
- package/src/client/dist/pwa/assets/{MainLayout-nn4YIcy8.js → MainLayout-DOuqjNgy.js} +17 -17
- package/src/client/dist/pwa/assets/{QBtnToggle-CZXgD3Hf.js → QBtnToggle-BClaEu9N.js} +1 -1
- package/src/client/dist/pwa/assets/{QChip-BJH-iJCV.js → QChip-4nbXNOAl.js} +1 -1
- package/src/client/dist/pwa/assets/QSelect-BCz_9zhf.js +36 -0
- package/src/client/dist/pwa/assets/QTooltip-DSxTMMkV.js +1 -0
- package/src/client/dist/pwa/assets/{SearchPage-BwD6K8qD.js → SearchPage-BRv1M8JZ.js} +1 -1
- package/src/client/dist/pwa/assets/SettingsPage-BqsWfOOK.js +16 -0
- package/src/client/dist/pwa/assets/{SettingsPage-DJEH2ahl.css → SettingsPage-Os-6b-6H.css} +1 -1
- package/src/client/dist/pwa/assets/{ThinkingItem-DBWuhjgW.js → ThinkingItem-Bx_u0wMq.js} +1 -1
- package/src/client/dist/pwa/assets/{TouchPan-txugr_lm.js → TouchPan-a3VbuxQ6.js} +1 -1
- package/src/client/dist/pwa/assets/{WorkspacePage-Cv04uh_0.css → WorkspacePage-D-1HoeQg.css} +1 -1
- package/src/client/dist/pwa/assets/WorkspacePage-Dsj0f4XV.js +4 -0
- package/src/client/dist/pwa/assets/{build-path-tree-BUkEDxlT.js → build-path-tree-BKKu-ryI.js} +1 -1
- package/src/client/dist/pwa/assets/{cssMode-BDLIXvw9.js → cssMode-BKAdI7ff.js} +1 -1
- package/src/client/dist/pwa/assets/documents-Cc-OXiwm.js +1 -0
- package/src/client/dist/pwa/assets/{editor.api2-PhaZVUXg.js → editor.api2-BaCyAVGk.js} +1 -1
- package/src/client/dist/pwa/assets/{editor.main-2nNvSQc1.js → editor.main-BbVQG_bB.js} +2 -2
- package/src/client/dist/pwa/assets/{expand-template-HgUyAUBK.js → expand-template-CZOjiOqB.js} +1 -1
- package/src/client/dist/pwa/assets/{formatters-Chk4XGyr.js → formatters-CCEkVVIc.js} +1 -1
- package/src/client/dist/pwa/assets/{freemarker2-Dy02xxKp.js → freemarker2-Bh-TbpI2.js} +1 -1
- package/src/client/dist/pwa/assets/{handlebars-Do59Qec5.js → handlebars-B8nDlL6P.js} +1 -1
- package/src/client/dist/pwa/assets/{html-MJ9-LLjS.js → html-7xHGi5EV.js} +1 -1
- package/src/client/dist/pwa/assets/{htmlMode-AsZ-SMzZ.js → htmlMode-Bj3pQot4.js} +1 -1
- package/src/client/dist/pwa/assets/i18n-DXWy90AZ.js +1 -0
- package/src/client/dist/pwa/assets/index-CH7ykfX_.js +84 -0
- package/src/client/dist/pwa/assets/{javascript-DoZFpstd.js → javascript-BtBc5IVR.js} +1 -1
- package/src/client/dist/pwa/assets/{jsonMode-CMeFz2Y2.js → jsonMode-0IOmldeM.js} +1 -1
- package/src/client/dist/pwa/assets/{kobo-commands-DmJq1I6k.js → kobo-commands-2L1djzbe.js} +1 -1
- package/src/client/dist/pwa/assets/{layout-BSJkgTMQ.js → layout-DgfOrzwf.js} +1 -1
- package/src/client/dist/pwa/assets/{liquid-Bc70ZdlV.js → liquid-DF94cTT4.js} +1 -1
- package/src/client/dist/pwa/assets/{lspLanguageFeatures-Co4WnPYT.js → lspLanguageFeatures-iHqiZBzm.js} +1 -1
- package/src/client/dist/pwa/assets/{mdx-BsXN2I7z.js → mdx-C-x1eJF5.js} +1 -1
- package/src/client/dist/pwa/assets/{monaco.contribution-BpmQVvpQ.js → monaco.contribution-DSfodOAp.js} +2 -2
- package/src/client/dist/pwa/assets/{network-auth-B0RCBQ5K.js → network-auth-DA6dNTa5.js} +1 -1
- package/src/client/dist/pwa/assets/{pending-diff-open-BiozufWe.js → pending-diff-open-bDBsLjPx.js} +1 -1
- package/src/client/dist/pwa/assets/{permissionModes-kRWgzgvi.js → permissionModes-DQd7fbSy.js} +1 -1
- package/src/client/dist/pwa/assets/project-color-CQtsHSqi.js +1 -0
- package/src/client/dist/pwa/assets/{python-CV5Zt8gJ.js → python-fAiLpu-5.js} +1 -1
- package/src/client/dist/pwa/assets/{razor-4Ev2x3ig.js → razor-Clyexxhi.js} +1 -1
- package/src/client/dist/pwa/assets/{render-chat-markdown-Cjt11WnX.js → render-chat-markdown-MzPqUxrD.js} +1 -1
- package/src/client/dist/pwa/assets/{tsMode-BDysTsn1.js → tsMode-aOX259X0.js} +1 -1
- package/src/client/dist/pwa/assets/{typescript-DM1CefPN.js → typescript-BiPmygzZ.js} +1 -1
- package/src/client/dist/pwa/assets/{use-onboarding-D2BGKiP_.js → use-onboarding-NfnxjTzg.js} +1 -1
- package/src/client/dist/pwa/assets/{use-quasar-Cps11O9Y.js → use-quasar-CyeldpIw.js} +1 -1
- package/src/client/dist/pwa/assets/{workers-BuJZJ6j6.js → workers-BcOflh4r.js} +1 -1
- package/src/client/dist/pwa/assets/{xml-DzARVQ9o.js → xml-6qooibMC.js} +1 -1
- package/src/client/dist/pwa/assets/{yaml-P0ZYSZvj.js → yaml-DzDdyl50.js} +1 -1
- package/src/client/dist/pwa/index.html +1 -1
- package/src/client/dist/pwa/sounds/basic-notification.mp3 +0 -0
- package/src/client/dist/pwa/sw.js +1 -1
- package/src/client/dist/pwa/assets/ActivityFeed-ByJjMo0k.js +0 -8
- package/src/client/dist/pwa/assets/CreatePage-D0xvdJAQ.js +0 -2
- package/src/client/dist/pwa/assets/CreatePage-qMgdzdb-.css +0 -1
- package/src/client/dist/pwa/assets/QSelect-tsFTetLi.js +0 -36
- package/src/client/dist/pwa/assets/QTooltip-CA_Iyybc.js +0 -1
- package/src/client/dist/pwa/assets/SettingsPage-CF-xI5PO.js +0 -16
- package/src/client/dist/pwa/assets/WorkspacePage-DgucXHjd.js +0 -4
- package/src/client/dist/pwa/assets/documents-ClS4k_bT.js +0 -1
- package/src/client/dist/pwa/assets/i18n-Djv0PzK4.js +0 -1
- package/src/client/dist/pwa/assets/index-BpTtR9lB.js +0 -84
- package/src/client/dist/pwa/assets/project-color-D35ZMh9_.js +0 -1
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { getGlobalSettings } from '../../settings-service.js';
|
|
4
|
+
import { deriveReadyToMerge } from '../types.js';
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
6
|
+
function record(value) {
|
|
7
|
+
return value !== null && typeof value === 'object' ? value : {};
|
|
8
|
+
}
|
|
9
|
+
function string(value) {
|
|
10
|
+
return typeof value === 'string' ? value : '';
|
|
11
|
+
}
|
|
12
|
+
function number(value) {
|
|
13
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
14
|
+
}
|
|
15
|
+
function branch(raw, cloudKey, dcKey) {
|
|
16
|
+
const cloud = record(record(raw[cloudKey]).branch);
|
|
17
|
+
return string(cloud.name) || string(record(raw[dcKey]).displayId);
|
|
18
|
+
}
|
|
19
|
+
function login(raw) {
|
|
20
|
+
const user = record(raw);
|
|
21
|
+
return string(user.nickname) || string(user.username) || string(user.name) || string(user.display_name);
|
|
22
|
+
}
|
|
23
|
+
function url(raw) {
|
|
24
|
+
const links = record(raw.links);
|
|
25
|
+
const html = record(links.html);
|
|
26
|
+
if (string(html.href))
|
|
27
|
+
return string(html.href);
|
|
28
|
+
const self = links.self;
|
|
29
|
+
if (Array.isArray(self))
|
|
30
|
+
return string(record(self[0]).href);
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
function mapState(value) {
|
|
34
|
+
const state = value.toUpperCase();
|
|
35
|
+
if (state === 'MERGED')
|
|
36
|
+
return 'MERGED';
|
|
37
|
+
if (state === 'DECLINED' || state === 'CLOSED' || state === 'SUPERSEDED')
|
|
38
|
+
return 'CLOSED';
|
|
39
|
+
return 'OPEN';
|
|
40
|
+
}
|
|
41
|
+
function mapReviewers(raw) {
|
|
42
|
+
const result = new Map();
|
|
43
|
+
const add = (candidate, fallback) => {
|
|
44
|
+
const entry = record(candidate);
|
|
45
|
+
const reviewerLogin = login(entry.user) || login(entry);
|
|
46
|
+
if (!reviewerLogin)
|
|
47
|
+
return;
|
|
48
|
+
const rawState = string(entry.status || entry.state).toUpperCase();
|
|
49
|
+
const state = entry.approved === true || rawState === 'APPROVED'
|
|
50
|
+
? 'APPROVED'
|
|
51
|
+
: rawState === 'NEEDS_WORK' || rawState === 'CHANGES_REQUESTED'
|
|
52
|
+
? 'CHANGES_REQUESTED'
|
|
53
|
+
: fallback;
|
|
54
|
+
result.set(reviewerLogin, state);
|
|
55
|
+
};
|
|
56
|
+
for (const reviewer of Array.isArray(raw.reviewers) ? raw.reviewers : [])
|
|
57
|
+
add(reviewer, 'PENDING');
|
|
58
|
+
for (const participant of Array.isArray(raw.participants) ? raw.participants : [])
|
|
59
|
+
add(participant, 'COMMENTED');
|
|
60
|
+
return [...result].map(([reviewerLogin, state]) => ({ login: reviewerLogin, state }));
|
|
61
|
+
}
|
|
62
|
+
function mapCheck(raw) {
|
|
63
|
+
const check = record(raw);
|
|
64
|
+
const state = string(check.state || check.status).toUpperCase();
|
|
65
|
+
const completed = ['SUCCESSFUL', 'SUCCESS', 'FAILED', 'FAILURE', 'CANCELLED', 'CANCELED'].includes(state);
|
|
66
|
+
const conclusion = ['SUCCESSFUL', 'SUCCESS'].includes(state)
|
|
67
|
+
? 'SUCCESS'
|
|
68
|
+
: ['FAILED', 'FAILURE'].includes(state)
|
|
69
|
+
? 'FAILURE'
|
|
70
|
+
: ['CANCELLED', 'CANCELED'].includes(state)
|
|
71
|
+
? 'CANCELLED'
|
|
72
|
+
: null;
|
|
73
|
+
return {
|
|
74
|
+
name: string(check.name) || string(check.key),
|
|
75
|
+
status: completed ? 'COMPLETED' : 'IN_PROGRESS',
|
|
76
|
+
conclusion,
|
|
77
|
+
detailsUrl: string(check.url) || null,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function mapBktPr(raw, ci = { rollup: null, checks: [] }) {
|
|
81
|
+
const state = mapState(string(raw.state));
|
|
82
|
+
const reviewers = mapReviewers(raw);
|
|
83
|
+
const reviewDecision = reviewers.some((reviewer) => reviewer.state === 'CHANGES_REQUESTED')
|
|
84
|
+
? 'CHANGES_REQUESTED'
|
|
85
|
+
: null;
|
|
86
|
+
const mergeable = raw.open === false ? 'CONFLICTING' : null;
|
|
87
|
+
return {
|
|
88
|
+
number: number(raw.id),
|
|
89
|
+
title: string(raw.title),
|
|
90
|
+
url: url(raw),
|
|
91
|
+
state,
|
|
92
|
+
base: branch(raw, 'destination', 'toRef'),
|
|
93
|
+
reviewDecision,
|
|
94
|
+
author: { login: login(raw.author) },
|
|
95
|
+
assignees: [],
|
|
96
|
+
reviewers,
|
|
97
|
+
labels: [],
|
|
98
|
+
ci,
|
|
99
|
+
updatedAt: string(raw.updated_on) || string(raw.updatedAt) || String(raw.updatedDate ?? ''),
|
|
100
|
+
unresolvedReviewThreadsCount: 0,
|
|
101
|
+
mergeable,
|
|
102
|
+
readyToMerge: deriveReadyToMerge({ state, ci, reviewDecision, reviewers, mergeable }),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function availabilityFromError(err) {
|
|
106
|
+
const code = err.code;
|
|
107
|
+
if (code === 'ENOENT')
|
|
108
|
+
return { available: false, reason: 'cli_missing' };
|
|
109
|
+
const message = err.message?.toLowerCase() ?? '';
|
|
110
|
+
if (message.includes('no active context') ||
|
|
111
|
+
message.includes('not authenticated') ||
|
|
112
|
+
message.includes('auth login')) {
|
|
113
|
+
return { available: false, reason: 'not_authenticated' };
|
|
114
|
+
}
|
|
115
|
+
return { available: false };
|
|
116
|
+
}
|
|
117
|
+
function parseBitbucketRemote(remote) {
|
|
118
|
+
const match = remote.trim().match(/bitbucket(?:\.org)?[/:]([^/\s:]+)\/([^/\s]+?)(?:\.git)?\/?$/i);
|
|
119
|
+
if (!match)
|
|
120
|
+
return null;
|
|
121
|
+
return { workspace: match[1], repo: match[2] };
|
|
122
|
+
}
|
|
123
|
+
function contextName(workspace, repo) {
|
|
124
|
+
return `kobo-${workspace}-${repo}`.toLowerCase().replace(/[^a-z0-9._-]/g, '-');
|
|
125
|
+
}
|
|
126
|
+
function bktArgs(target, args) {
|
|
127
|
+
return target.context ? ['--context', target.context, ...args] : args;
|
|
128
|
+
}
|
|
129
|
+
function bktEnv() {
|
|
130
|
+
try {
|
|
131
|
+
const settings = getGlobalSettings();
|
|
132
|
+
if (settings.bitbucketToken) {
|
|
133
|
+
return {
|
|
134
|
+
...process.env,
|
|
135
|
+
BKT_HOST: 'https://bitbucket.org',
|
|
136
|
+
BKT_TOKEN: settings.bitbucketToken,
|
|
137
|
+
BKT_USERNAME: settings.bitbucketUsername || process.env.BKT_USERNAME,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// Settings are deliberately uninitialized in isolated forge tests.
|
|
143
|
+
}
|
|
144
|
+
return process.env;
|
|
145
|
+
}
|
|
146
|
+
function bktOptions(repoPath) {
|
|
147
|
+
return { cwd: repoPath, encoding: 'utf-8', env: bktEnv() };
|
|
148
|
+
}
|
|
149
|
+
async function resolveTarget(repoPath) {
|
|
150
|
+
const { stdout } = await execFileAsync('git', ['remote', 'get-url', 'origin'], {
|
|
151
|
+
cwd: repoPath,
|
|
152
|
+
encoding: 'utf-8',
|
|
153
|
+
});
|
|
154
|
+
const target = parseBitbucketRemote(stdout);
|
|
155
|
+
if (!target)
|
|
156
|
+
throw new Error('Could not determine Bitbucket workspace and repository from the origin remote');
|
|
157
|
+
// BKT_TOKEN is the CLI's documented headless mode. It deliberately avoids
|
|
158
|
+
// keyring and context access, which cannot prompt from the Kobo server.
|
|
159
|
+
if (bktEnv().BKT_TOKEN)
|
|
160
|
+
return { ...target, context: null };
|
|
161
|
+
const { stdout: contextOutput } = await execFileAsync('bkt', ['context', 'list', '--json'], {
|
|
162
|
+
cwd: repoPath,
|
|
163
|
+
encoding: 'utf-8',
|
|
164
|
+
});
|
|
165
|
+
const contexts = record(JSON.parse(contextOutput)).contexts;
|
|
166
|
+
const existing = (Array.isArray(contexts) ? contexts : [])
|
|
167
|
+
.map(record)
|
|
168
|
+
.find((context) => string(context.workspace) === target.workspace && string(context.default_repo) === target.repo);
|
|
169
|
+
if (existing && string(existing.name))
|
|
170
|
+
return { ...target, context: string(existing.name) };
|
|
171
|
+
const { stdout: authOutput } = await execFileAsync('bkt', ['auth', 'status', '--json'], {
|
|
172
|
+
cwd: repoPath,
|
|
173
|
+
encoding: 'utf-8',
|
|
174
|
+
});
|
|
175
|
+
const hosts = record(JSON.parse(authOutput)).hosts;
|
|
176
|
+
const host = (Array.isArray(hosts) ? hosts : []).map(record).find((item) => string(item.kind) === 'cloud');
|
|
177
|
+
const hostKey = string(host?.key);
|
|
178
|
+
if (!hostKey)
|
|
179
|
+
throw new Error('No authenticated Bitbucket Cloud host found; run bkt auth login first');
|
|
180
|
+
const context = contextName(target.workspace, target.repo);
|
|
181
|
+
await execFileAsync('bkt', ['context', 'create', context, '--host', hostKey, '--workspace', target.workspace, '--repo', target.repo], { cwd: repoPath, encoding: 'utf-8' });
|
|
182
|
+
return { ...target, context };
|
|
183
|
+
}
|
|
184
|
+
function targetArgs(target) {
|
|
185
|
+
return ['--workspace', target.workspace, '--repo', target.repo];
|
|
186
|
+
}
|
|
187
|
+
async function fetchCi(repoPath, number, target) {
|
|
188
|
+
try {
|
|
189
|
+
const { stdout } = await execFileAsync('bkt', bktArgs(target, ['pr', 'checks', String(number), ...targetArgs(target), '--json']), bktOptions(repoPath));
|
|
190
|
+
const statuses = record(JSON.parse(stdout)).statuses;
|
|
191
|
+
const checks = (Array.isArray(statuses) ? statuses : []).map(mapCheck);
|
|
192
|
+
const rollup = checks.length === 0
|
|
193
|
+
? null
|
|
194
|
+
: checks.some((check) => check.conclusion === 'FAILURE')
|
|
195
|
+
? 'FAILURE'
|
|
196
|
+
: checks.some((check) => check.conclusion === 'CANCELLED')
|
|
197
|
+
? 'CANCELLED'
|
|
198
|
+
: checks.some((check) => check.status !== 'COMPLETED')
|
|
199
|
+
? 'PENDING'
|
|
200
|
+
: 'SUCCESS';
|
|
201
|
+
return { rollup, checks };
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return { rollup: null, checks: [] };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export const bitbucketProvider = {
|
|
208
|
+
id: 'bitbucket-community',
|
|
209
|
+
capabilities: {
|
|
210
|
+
canCreatePr: true,
|
|
211
|
+
// bkt 0.30 does not expose a target-branch edit command.
|
|
212
|
+
canChangePrBase: false,
|
|
213
|
+
canMergeRequest: true,
|
|
214
|
+
canDeleteRemoteBranch: true,
|
|
215
|
+
requestTermShort: 'PR',
|
|
216
|
+
},
|
|
217
|
+
async isAvailable(repoPath) {
|
|
218
|
+
try {
|
|
219
|
+
const { stdout } = await execFileAsync('bkt', ['auth', 'status', '--json'], bktOptions(repoPath));
|
|
220
|
+
const status = record(JSON.parse(stdout));
|
|
221
|
+
const hosts = status.hosts;
|
|
222
|
+
if ((Array.isArray(hosts) && hosts.length > 0) || bktEnv().BKT_TOKEN)
|
|
223
|
+
return { available: true };
|
|
224
|
+
return { available: false, reason: 'not_authenticated' };
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
return availabilityFromError(err);
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
async getPrStatus(repoPath, branchName) {
|
|
231
|
+
try {
|
|
232
|
+
const target = await resolveTarget(repoPath);
|
|
233
|
+
let match;
|
|
234
|
+
// The post-merge workflow asks for the final state before offering remote
|
|
235
|
+
// branch deletion, so do not stop at OPEN requests.
|
|
236
|
+
for (const state of ['OPEN', 'MERGED', 'DECLINED']) {
|
|
237
|
+
const { stdout: listOutput } = await execFileAsync('bkt', bktArgs(target, ['pr', 'list', '--state', state, '--limit', '50', ...targetArgs(target), '--json']), bktOptions(repoPath));
|
|
238
|
+
const pullRequests = record(JSON.parse(listOutput)).pull_requests;
|
|
239
|
+
match = (Array.isArray(pullRequests) ? pullRequests : [])
|
|
240
|
+
.map(record)
|
|
241
|
+
.find((pullRequest) => branch(pullRequest, 'source', 'fromRef') === branchName);
|
|
242
|
+
if (match)
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
if (!match)
|
|
246
|
+
return null;
|
|
247
|
+
const { stdout: viewOutput } = await execFileAsync('bkt', bktArgs(target, ['pr', 'view', String(number(match.id)), ...targetArgs(target), '--json']), bktOptions(repoPath));
|
|
248
|
+
const raw = record(record(JSON.parse(viewOutput)).pull_request);
|
|
249
|
+
if (!number(raw.id))
|
|
250
|
+
return null;
|
|
251
|
+
const ci = await fetchCi(repoPath, number(raw.id), target);
|
|
252
|
+
return mapBktPr(raw, ci);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
async createPr(repoPath, opts) {
|
|
259
|
+
const target = await resolveTarget(repoPath);
|
|
260
|
+
const { stdout } = await execFileAsync('bkt', bktArgs(target, [
|
|
261
|
+
'pr',
|
|
262
|
+
'create',
|
|
263
|
+
'--source',
|
|
264
|
+
opts.head,
|
|
265
|
+
'--target',
|
|
266
|
+
opts.base,
|
|
267
|
+
'--title',
|
|
268
|
+
opts.title,
|
|
269
|
+
'--description',
|
|
270
|
+
opts.body,
|
|
271
|
+
...targetArgs(target),
|
|
272
|
+
'--json',
|
|
273
|
+
]), bktOptions(repoPath));
|
|
274
|
+
const raw = record(JSON.parse(stdout));
|
|
275
|
+
const prNumber = number(raw.id);
|
|
276
|
+
const prUrl = string(raw.url);
|
|
277
|
+
if (!prNumber || !prUrl)
|
|
278
|
+
throw new Error('Could not parse PR URL from bkt output');
|
|
279
|
+
return { url: prUrl, number: prNumber };
|
|
280
|
+
},
|
|
281
|
+
async changePrBase() {
|
|
282
|
+
throw new Error('Changing a Bitbucket PR base branch is not supported by the installed bkt CLI');
|
|
283
|
+
},
|
|
284
|
+
async mergeRequest(repoPath, number) {
|
|
285
|
+
const target = await resolveTarget(repoPath);
|
|
286
|
+
await execFileAsync('bkt', bktArgs(target, ['pr', 'merge', String(number), '--close-source=false', ...targetArgs(target)]), bktOptions(repoPath));
|
|
287
|
+
},
|
|
288
|
+
async deleteRemoteBranch(repoPath, branchName) {
|
|
289
|
+
await execFileAsync('git', ['push', 'origin', '--delete', branchName], { cwd: repoPath, encoding: 'utf-8' });
|
|
290
|
+
},
|
|
291
|
+
};
|
|
@@ -92,7 +92,13 @@ function availabilityFromError(err) {
|
|
|
92
92
|
}
|
|
93
93
|
export const githubProvider = {
|
|
94
94
|
id: 'github',
|
|
95
|
-
capabilities: {
|
|
95
|
+
capabilities: {
|
|
96
|
+
canCreatePr: true,
|
|
97
|
+
canChangePrBase: true,
|
|
98
|
+
canMergeRequest: true,
|
|
99
|
+
canDeleteRemoteBranch: true,
|
|
100
|
+
requestTermShort: 'PR',
|
|
101
|
+
},
|
|
96
102
|
async isAvailable(repoPath) {
|
|
97
103
|
try {
|
|
98
104
|
await execFileAsync('gh', ['auth', 'status'], { cwd: repoPath, encoding: 'utf-8' });
|
|
@@ -130,4 +136,7 @@ export const githubProvider = {
|
|
|
130
136
|
async mergeRequest(repoPath, number) {
|
|
131
137
|
await execFileAsync('gh', ['pr', 'merge', String(number), '--merge'], { cwd: repoPath, encoding: 'utf-8' });
|
|
132
138
|
},
|
|
139
|
+
async deleteRemoteBranch(repoPath, branch) {
|
|
140
|
+
await execFileAsync('git', ['push', 'origin', '--delete', branch], { cwd: repoPath, encoding: 'utf-8' });
|
|
141
|
+
},
|
|
133
142
|
};
|
|
@@ -129,7 +129,13 @@ function availabilityFromError(err) {
|
|
|
129
129
|
}
|
|
130
130
|
export const gitlabProvider = {
|
|
131
131
|
id: 'gitlab',
|
|
132
|
-
capabilities: {
|
|
132
|
+
capabilities: {
|
|
133
|
+
canCreatePr: true,
|
|
134
|
+
canChangePrBase: true,
|
|
135
|
+
canMergeRequest: true,
|
|
136
|
+
canDeleteRemoteBranch: true,
|
|
137
|
+
requestTermShort: 'MR',
|
|
138
|
+
},
|
|
133
139
|
async isAvailable(repoPath) {
|
|
134
140
|
try {
|
|
135
141
|
await execFileAsync('glab', ['auth', 'status'], { cwd: repoPath, encoding: 'utf-8' });
|
|
@@ -182,4 +188,7 @@ export const gitlabProvider = {
|
|
|
182
188
|
async mergeRequest(repoPath, number) {
|
|
183
189
|
await execFileAsync('glab', ['mr', 'merge', String(number), '--yes'], { cwd: repoPath, encoding: 'utf-8' });
|
|
184
190
|
},
|
|
191
|
+
async deleteRemoteBranch(repoPath, branch) {
|
|
192
|
+
await execFileAsync('git', ['push', 'origin', '--delete', branch], { cwd: repoPath, encoding: 'utf-8' });
|
|
193
|
+
},
|
|
185
194
|
};
|
|
@@ -7,7 +7,13 @@ import { ForgeUnavailableError } from './types.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export const noneProvider = {
|
|
9
9
|
id: 'none',
|
|
10
|
-
capabilities: {
|
|
10
|
+
capabilities: {
|
|
11
|
+
canCreatePr: false,
|
|
12
|
+
canChangePrBase: false,
|
|
13
|
+
canMergeRequest: false,
|
|
14
|
+
canDeleteRemoteBranch: false,
|
|
15
|
+
requestTermShort: 'PR',
|
|
16
|
+
},
|
|
11
17
|
async isAvailable() {
|
|
12
18
|
return { available: false };
|
|
13
19
|
},
|
|
@@ -23,4 +29,7 @@ export const noneProvider = {
|
|
|
23
29
|
async mergeRequest() {
|
|
24
30
|
throw new ForgeUnavailableError('This project has no supported forge configured');
|
|
25
31
|
},
|
|
32
|
+
async deleteRemoteBranch() {
|
|
33
|
+
throw new ForgeUnavailableError('This project has no supported forge configured');
|
|
34
|
+
},
|
|
26
35
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// src/server/services/forge/registry.ts
|
|
2
|
+
import { bitbucketProvider } from './bitbucket/provider.js';
|
|
2
3
|
import { githubProvider } from './github/provider.js';
|
|
3
4
|
import { gitlabProvider } from './gitlab/provider.js';
|
|
4
5
|
import { noneProvider } from './none.js';
|
|
5
6
|
const PROVIDERS = {
|
|
6
7
|
github: githubProvider,
|
|
7
8
|
gitlab: gitlabProvider,
|
|
9
|
+
'bitbucket-community': bitbucketProvider,
|
|
8
10
|
none: noneProvider,
|
|
9
11
|
};
|
|
10
12
|
/** Resolve a provider by id. Unknown ids fall back to the none provider. */
|
|
@@ -13,5 +15,5 @@ export function getForgeProvider(id) {
|
|
|
13
15
|
}
|
|
14
16
|
/** The selectable forge ids, in display order. */
|
|
15
17
|
export function listForges() {
|
|
16
|
-
return ['github', 'gitlab', 'none'];
|
|
18
|
+
return ['github', 'gitlab', 'bitbucket-community', 'none'];
|
|
17
19
|
}
|
|
@@ -8,6 +8,8 @@ export function forgeFromRemoteUrl(url) {
|
|
|
8
8
|
return 'github';
|
|
9
9
|
if (lower.includes('gitlab'))
|
|
10
10
|
return 'gitlab';
|
|
11
|
+
if (lower.includes('bitbucket'))
|
|
12
|
+
return 'bitbucket-community';
|
|
11
13
|
return 'none';
|
|
12
14
|
}
|
|
13
15
|
/** Read the `origin` remote URL, or '' when there is no remote. */
|
|
@@ -28,7 +30,8 @@ function readRemoteUrl(projectPath) {
|
|
|
28
30
|
*/
|
|
29
31
|
export function resolveForge(projectPath) {
|
|
30
32
|
const setting = getProjectSettings(projectPath)?.forge ?? 'auto';
|
|
31
|
-
if (setting === 'github' || setting === 'gitlab' || setting === 'none')
|
|
33
|
+
if (setting === 'github' || setting === 'gitlab' || setting === 'bitbucket-community' || setting === 'none') {
|
|
32
34
|
return setting;
|
|
35
|
+
}
|
|
33
36
|
return forgeFromRemoteUrl(readRemoteUrl(projectPath));
|
|
34
37
|
}
|
|
@@ -18,7 +18,7 @@ export const DEFAULT_CHANGE_SOURCE_BRANCH_SCRIPT = `#!/usr/bin/env bash
|
|
|
18
18
|
# KOBO_PROJECT_NAME project directory name (basename of KOBO_PROJECT_PATH)
|
|
19
19
|
# KOBO_WORKSPACE_ID Kōbō workspace id (stable across renames)
|
|
20
20
|
# KOBO_WORKSPACE_NAME workspace display name
|
|
21
|
-
# KOBO_FORGE resolved forge: github / gitlab / none
|
|
21
|
+
# KOBO_FORGE resolved forge: github / gitlab / bitbucket-community / none
|
|
22
22
|
# KOBO_PR_NUMBER PR/MR number on the resolved forge (empty if none open)
|
|
23
23
|
set -euo pipefail
|
|
24
24
|
|
|
@@ -768,6 +768,41 @@ const settingsMigrations = [
|
|
|
768
768
|
global.showThinkingBlocks = true;
|
|
769
769
|
},
|
|
770
770
|
},
|
|
771
|
+
{
|
|
772
|
+
version: 48,
|
|
773
|
+
name: 'add-agent-error-notification-sound',
|
|
774
|
+
migrate: ({ global }) => {
|
|
775
|
+
if (typeof global.audioAgentErrorNotifications !== 'boolean')
|
|
776
|
+
global.audioAgentErrorNotifications = false;
|
|
777
|
+
if (typeof global.audioAgentErrorSound !== 'string' || global.audioAgentErrorSound.length === 0) {
|
|
778
|
+
global.audioAgentErrorSound = 'inherit';
|
|
779
|
+
}
|
|
780
|
+
if (typeof global.audioAgentErrorVolume !== 'number' || !Number.isFinite(global.audioAgentErrorVolume)) {
|
|
781
|
+
global.audioAgentErrorVolume = 1;
|
|
782
|
+
}
|
|
783
|
+
},
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
version: 49,
|
|
787
|
+
name: 'add-auto-loop-max-retries',
|
|
788
|
+
migrate: ({ global }) => {
|
|
789
|
+
if (typeof global.autoLoopMaxRetries !== 'number' ||
|
|
790
|
+
!Number.isInteger(global.autoLoopMaxRetries) ||
|
|
791
|
+
global.autoLoopMaxRetries < 1) {
|
|
792
|
+
global.autoLoopMaxRetries = 5;
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
},
|
|
796
|
+
{
|
|
797
|
+
version: 50,
|
|
798
|
+
name: 'add-bitbucket-credentials',
|
|
799
|
+
migrate: ({ global }) => {
|
|
800
|
+
if (typeof global.bitbucketToken !== 'string')
|
|
801
|
+
global.bitbucketToken = '';
|
|
802
|
+
if (typeof global.bitbucketUsername !== 'string')
|
|
803
|
+
global.bitbucketUsername = '';
|
|
804
|
+
},
|
|
805
|
+
},
|
|
771
806
|
];
|
|
772
807
|
/** Current settings schema version — always equals the highest migration version. */
|
|
773
808
|
export const SETTINGS_SCHEMA_VERSION = settingsMigrations.length > 0 ? settingsMigrations[settingsMigrations.length - 1].version : 0;
|
|
@@ -814,40 +849,46 @@ function defaultSettings() {
|
|
|
814
849
|
fileManagerCommand: '',
|
|
815
850
|
terminalCommand: '',
|
|
816
851
|
autoPurgeOnPrMerged: false,
|
|
852
|
+
autoLoopMaxRetries: 5,
|
|
817
853
|
networkAccessEnabled: false,
|
|
818
854
|
networkAccessToken: '',
|
|
819
855
|
networkAccessBehindProxy: false,
|
|
856
|
+
bitbucketToken: '',
|
|
857
|
+
bitbucketUsername: '',
|
|
820
858
|
browserNotifications: true,
|
|
821
859
|
audioNotifications: true,
|
|
822
|
-
audioQuestionNotifications:
|
|
823
|
-
audioWorkspaceCreatedNotifications:
|
|
860
|
+
audioQuestionNotifications: false,
|
|
861
|
+
audioWorkspaceCreatedNotifications: false,
|
|
862
|
+
audioAgentErrorNotifications: false,
|
|
824
863
|
audioNotificationSound: 'hey.mp3',
|
|
825
|
-
audioQuestionSound: '
|
|
826
|
-
audioWorkspaceCreatedSound: '
|
|
864
|
+
audioQuestionSound: 'inherit',
|
|
865
|
+
audioWorkspaceCreatedSound: 'inherit',
|
|
866
|
+
audioAgentErrorSound: 'inherit',
|
|
827
867
|
audioPrCiFailedSound: 'inherit',
|
|
828
|
-
audioPrCiFailedEnabled:
|
|
868
|
+
audioPrCiFailedEnabled: false,
|
|
829
869
|
audioPrCiFailedVolume: 1,
|
|
830
870
|
audioPrCiRecoveredSound: 'inherit',
|
|
831
|
-
audioPrCiRecoveredEnabled:
|
|
871
|
+
audioPrCiRecoveredEnabled: false,
|
|
832
872
|
audioPrCiRecoveredVolume: 1,
|
|
833
873
|
audioPrChangesRequestedSound: 'inherit',
|
|
834
|
-
audioPrChangesRequestedEnabled:
|
|
874
|
+
audioPrChangesRequestedEnabled: false,
|
|
835
875
|
audioPrChangesRequestedVolume: 1,
|
|
836
876
|
audioPrApprovedSound: 'inherit',
|
|
837
|
-
audioPrApprovedEnabled:
|
|
877
|
+
audioPrApprovedEnabled: false,
|
|
838
878
|
audioPrApprovedVolume: 1,
|
|
839
879
|
audioPrMergeConflictSound: 'inherit',
|
|
840
|
-
audioPrMergeConflictEnabled:
|
|
880
|
+
audioPrMergeConflictEnabled: false,
|
|
841
881
|
audioPrMergeConflictVolume: 1,
|
|
842
882
|
audioPrReadyToMergeSound: 'inherit',
|
|
843
|
-
audioPrReadyToMergeEnabled:
|
|
883
|
+
audioPrReadyToMergeEnabled: false,
|
|
844
884
|
audioPrReadyToMergeVolume: 1,
|
|
845
885
|
audioPrMergedSound: 'inherit',
|
|
846
|
-
audioPrMergedEnabled:
|
|
886
|
+
audioPrMergedEnabled: false,
|
|
847
887
|
audioPrMergedVolume: 1,
|
|
848
888
|
audioNotificationVolume: 1,
|
|
849
889
|
audioQuestionVolume: 1,
|
|
850
890
|
audioWorkspaceCreatedVolume: 1,
|
|
891
|
+
audioAgentErrorVolume: 1,
|
|
851
892
|
notionStatusProperty: '',
|
|
852
893
|
notionInProgressStatus: '',
|
|
853
894
|
notionAssigneeProperty: '',
|
|
@@ -1039,7 +1080,7 @@ export function getSettings() {
|
|
|
1039
1080
|
return readSettings();
|
|
1040
1081
|
}
|
|
1041
1082
|
/** Keys stripped from exports — secrets that should stay on the machine. */
|
|
1042
|
-
const SECRET_GLOBAL_KEYS = ['notionMcpKey', 'sentryMcpKey', 'networkAccessToken'];
|
|
1083
|
+
const SECRET_GLOBAL_KEYS = ['notionMcpKey', 'sentryMcpKey', 'networkAccessToken', 'bitbucketToken'];
|
|
1043
1084
|
/** Build an export bundle with settings + templates. MCP keys are stripped. */
|
|
1044
1085
|
export function exportConfigBundle(templates) {
|
|
1045
1086
|
const settings = readSettings();
|
|
@@ -1182,6 +1223,15 @@ export function updateGlobalSettings(data) {
|
|
|
1182
1223
|
delete data.cleanupScriptMode;
|
|
1183
1224
|
}
|
|
1184
1225
|
}
|
|
1226
|
+
if ('autoLoopMaxRetries' in data) {
|
|
1227
|
+
if (typeof data.autoLoopMaxRetries !== 'number' ||
|
|
1228
|
+
!Number.isInteger(data.autoLoopMaxRetries) ||
|
|
1229
|
+
data.autoLoopMaxRetries < 1 ||
|
|
1230
|
+
data.autoLoopMaxRetries > 20) {
|
|
1231
|
+
console.warn(`[settings] Invalid autoLoopMaxRetries value rejected: ${data.autoLoopMaxRetries}`);
|
|
1232
|
+
delete data.autoLoopMaxRetries;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1185
1235
|
const allowedGlobalKeys = [
|
|
1186
1236
|
'defaultModelByEngine',
|
|
1187
1237
|
'dangerouslySkipPermissions',
|
|
@@ -1202,13 +1252,16 @@ export function updateGlobalSettings(data) {
|
|
|
1202
1252
|
'fileManagerCommand',
|
|
1203
1253
|
'terminalCommand',
|
|
1204
1254
|
'autoPurgeOnPrMerged',
|
|
1255
|
+
'autoLoopMaxRetries',
|
|
1205
1256
|
'browserNotifications',
|
|
1206
1257
|
'audioNotifications',
|
|
1207
1258
|
'audioQuestionNotifications',
|
|
1208
1259
|
'audioWorkspaceCreatedNotifications',
|
|
1260
|
+
'audioAgentErrorNotifications',
|
|
1209
1261
|
'audioNotificationSound',
|
|
1210
1262
|
'audioQuestionSound',
|
|
1211
1263
|
'audioWorkspaceCreatedSound',
|
|
1264
|
+
'audioAgentErrorSound',
|
|
1212
1265
|
'audioPrCiFailedSound',
|
|
1213
1266
|
'audioPrCiFailedEnabled',
|
|
1214
1267
|
'audioPrCiFailedVolume',
|
|
@@ -1233,6 +1286,7 @@ export function updateGlobalSettings(data) {
|
|
|
1233
1286
|
'audioNotificationVolume',
|
|
1234
1287
|
'audioQuestionVolume',
|
|
1235
1288
|
'audioWorkspaceCreatedVolume',
|
|
1289
|
+
'audioAgentErrorVolume',
|
|
1236
1290
|
'notionStatusProperty',
|
|
1237
1291
|
'notionInProgressStatus',
|
|
1238
1292
|
'notionAssigneeProperty',
|
|
@@ -1240,6 +1294,8 @@ export function updateGlobalSettings(data) {
|
|
|
1240
1294
|
'defaultPermissionModeByEngine',
|
|
1241
1295
|
'notionMcpKey',
|
|
1242
1296
|
'sentryMcpKey',
|
|
1297
|
+
'bitbucketToken',
|
|
1298
|
+
'bitbucketUsername',
|
|
1243
1299
|
'notionEnabled',
|
|
1244
1300
|
'sentryEnabled',
|
|
1245
1301
|
'showThinkingBlocks',
|
|
@@ -17,6 +17,13 @@ export const PROJECT_COLOR_PALETTE = [
|
|
|
17
17
|
'amber-5',
|
|
18
18
|
'orange-5',
|
|
19
19
|
'brown-5',
|
|
20
|
+
'light-blue-5',
|
|
21
|
+
'light-green-5',
|
|
22
|
+
'lime-5',
|
|
23
|
+
'yellow-6',
|
|
24
|
+
'deep-orange-5',
|
|
25
|
+
'blue-grey-5',
|
|
26
|
+
'grey-6',
|
|
20
27
|
];
|
|
21
28
|
export function isValidProjectColor(value) {
|
|
22
29
|
return typeof value === 'string' && PROJECT_COLOR_PALETTE.includes(value);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loicngr/kobo",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.3",
|
|
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",
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import{$ as e,B as t,F as n,I as r,R as i,S as a,U as o,W as s,_ as c,at as l,b as u,c as d,ct as f,g as p,h as m,it as h,k as g,m as _,n as v,p as y,s as b,st as x,y as S}from"./vue.runtime.esm-bundler-JZnIeD9D.js";import{l as C,t as w}from"./QIcon-C6E-oEjI.js";import{G as T,K as E,d as D,k as O,p as k,v as A,y as j,z as ee}from"./index-CH7ykfX_.js";import{t as M}from"./QSpinnerDots-DIV68-cV.js";import{t as N}from"./QTooltip-DSxTMMkV.js";import{a as te}from"./permissionModes-DQd7fbSy.js";import{r as P,t as ne}from"./pending-diff-open-bDBsLjPx.js";import{t as F}from"./documents-Cc-OXiwm.js";import{i as I,n as re,t as L}from"./render-chat-markdown-MzPqUxrD.js";import{t as R}from"./_plugin-vue_export-helper-BDNMzG2s.js";import{t as z}from"./ThinkingItem-Bx_u0wMq.js";function ie(e,t,n=!0,r){let i=[],a=new Map,o=new Map;for(let n=0;n<e.length;n++){let s=e[n],c=t?.[n],l=r?.[n]??void 0;switch(s.kind){case`message:text`:{let e=a.get(s.messageId);if(e)e.text+=s.text,e.streaming=s.streaming,l&&e.eventIds?.push(l);else{let e={type:`text`,messageId:s.messageId,text:s.text,streaming:s.streaming,ts:c,eventIds:l?[l]:void 0};a.set(s.messageId,e),i.push(e)}break}case`message:end`:{let e=a.get(s.messageId);e&&(e.streaming=!1);break}case`message:thinking`:i.push({type:`thinking`,messageId:s.messageId,text:s.text,ts:c,eventIds:l?[l]:void 0});break;case`tool:call`:{let e={type:`tool`,toolCallId:s.toolCallId,name:s.name,input:s.input,ts:c,eventIds:l?[l]:void 0};o.set(s.toolCallId,e),i.push(e);break}case`tool:result`:{let e=o.get(s.toolCallId);e&&(e.result={output:s.output,isError:s.isError},l&&e.eventIds?.push(l));break}case`session:started`:i.push({type:`session`,kind:`started`,detail:{engineSessionId:s.engineSessionId,model:s.model},ts:c,eventIds:l?[l]:void 0});break;case`session:ended`:i.push({type:`session`,kind:`ended`,detail:{reason:s.reason,exitCode:s.exitCode},ts:c,eventIds:l?[l]:void 0});break;case`session:compacted`:i.push({type:`session`,kind:`compacted`,ts:c,eventIds:l?[l]:void 0});break;case`session:compacting`:case`session:brainstorm-complete`:case`session:user-input-requested`:case`mcp:status`:case`message:raw`:case`skills:discovered`:case`usage`:case`rate_limit`:case`subagent:progress`:case`error`:break;default:}}let s=null;for(let e of i)e.type===`text`&&e.streaming&&(s&&(s.streaming=!1),s=e);return s&&!n&&(s.streaming=!1),i}function ae(e,t){if(t.length===0)return e;let n=t.map(e=>({type:`user`,content:e.content,sender:e.sender,ts:e.ts,eventIds:e.eventIds})),r=[...e,...n];r.sort((e,t)=>{let n=e.ts??``,r=t.ts??``;return n===r?0:n?r?n<r?-1:1:-1:1});let i;for(let e of r)e.type===`user`&&e.sender!==`system-prompt`&&e.ts&&(!i||e.ts>i)&&(i=e.ts);if(i)for(let e of r)e.type===`text`&&e.streaming&&(!e.ts||e.ts<i)&&(e.streaming=!1);return r}var B=new Set([`setup`,`cleanup`,`archive`]);function V(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:B.has(e.sender)?`script`:`user`;case`session`:return`session`;default:return`agent`}}function oe(e){let t=[],n=null,r=null;for(let i of e){let e=V(i),a=e===`session`||e===`system-prompt`,o=e===`script`&&i.type===`user`?`script:${i.sender}`: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}var H={class:`text-caption text-grey-6`},se=a({__name:`SessionEventItem`,props:{item:{}},setup(e){let t=e,n=y(()=>{switch(t.item.kind){case`started`:return`session.started`;case`ended`:return`session.ended`;case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,t)=>(i(),c(`span`,H,f(e.$t(n.value)),1))}});function ce(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){le(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 le(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=ue(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 ue(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 U=[`innerHTML`],de=R(a({__name:`TextMessageItem`,props:{item:{}},setup(e){let t=e,n=F(),r=D(),a=y(()=>{let e=r.selectedWorkspaceId;return e?n.documentsFor(e).map(e=>e.path):[]}),o=y(()=>re(ce(I.parse(t.item.text,{async:!1,breaks:!0,gfm:!0}),a.value),{addAttr:[`data-document-path`]}));function s(e){let t=e.target?.closest(`.document-link`);if(!t)return;e.preventDefault();let i=t.getAttribute(`data-document-path`),a=r.selectedWorkspaceId;!i||!a||n.openDocumentByPath(a,i)}return(t,n)=>(i(),c(`div`,{class:`markdown-message`,onClick:s},[_(`div`,{innerHTML:o.value},null,8,U),e.item.streaming?(i(),m(C,{key:0,size:`xs`,class:`q-ml-xs`})):p(``,!0)]))}}),[[`__scopeId`,`data-v-1b7bd8ca`]]);function fe(e,t){let n=e.split(`
|
|
2
|
+
`),r=t.split(`
|
|
3
|
+
`),i=n.length,a=r.length,o=Array.from({length:i+1},()=>Array(a+1).fill(0));for(let e=i-1;e>=0;e--)for(let t=a-1;t>=0;t--)n[e]===r[t]?o[e][t]=o[e+1][t+1]+1:o[e][t]=Math.max(o[e+1][t],o[e][t+1]);let s=[],c=0,l=0;for(;c<i&&l<a;)n[c]===r[l]?(s.push({type:`context`,content:n[c]}),c++,l++):o[c+1][l]>=o[c][l+1]?(s.push({type:`del`,content:n[c]}),c++):(s.push({type:`add`,content:r[l]}),l++);for(;c<i;)s.push({type:`del`,content:n[c++]});for(;l<a;)s.push({type:`add`,content:r[l++]});return s}function W(e){let t=e.split(`
|
|
4
|
+
`),n=[];for(let e of t)e.startsWith(`@@`)||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 G(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=W(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(`
|
|
5
|
+
`).length:0,deletions:t?t.split(`
|
|
6
|
+
`).length:0}}if(e===`Write`){let e=n.file_path;if(!e)return null;let t=n.content??``;return{toolName:`Write`,filePath:e,content:t,additions:t?t.split(`
|
|
7
|
+
`).length:0,deletions:0}}if(e===`Bash`){let e=(n.command??``).match(/^\s*rm\s+(?:-[a-zA-Z]*\s+)*(.+)/);if(e)return{toolName:`Bash:rm`,filePath:e[1].trim().replace(/["']/g,``),additions:0,deletions:1}}return null}function K(e,t){if(!e||!t?.projectPath)return e;let n=t.worktreePath;if(n){let t=q(e,n);if(t!==e)return t}let r=q(e,`${t.projectPath}/${j}/${t.workingBranch}`);return r===e?q(e,t.projectPath):r}function q(e,t){if(!t)return e;let n=pe(t);return n?e.replace(RegExp(`${n}[\\\\/]+`,`g`),``).replace(RegExp(`${n}(?=\\s|$|["'\`])`,`g`),`.`):e}function pe(e){let t=e.replace(/[\\/]+$/,``);return t?`${/^[\\/]+/.test(t)?`[\\\\/]+`:``}${t.split(/[\\/]+/).filter(Boolean).map(me).join(`[\\\\/]+`)}`:``}function me(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}var J={class:`tool-name`},Y=[`title`],X={key:0,class:`tool-stat-add`},he={key:1,class:`tool-stat-del`},ge={class:`diff-sign`},_e={class:`tool-name`},Z=[`title`],Q=R(a({__name:`ToolCallItem`,props:{item:{}},setup(n){let r=n,a=e(!1),g=D(),{t:v}=T(),x=y(()=>G(r.item.name,r.item.input)),C=y(()=>x.value?K(x.value.filePath,g.selectedWorkspace):``),E={Bash:`terminal`,Read:`description`,Edit:`edit`,Write:`edit_note`,MultiEdit:`edit`,Glob:`folder_open`,Grep:`manage_search`,LS:`list`,Skill:`auto_awesome`,Task:`hub`,Agent:`hub`,TodoWrite:`checklist`,TodoRead:`checklist`,TaskCreate:`add_task`,TaskUpdate:`checklist`,TaskGet:`checklist`,TaskList:`checklist`,ToolSearch:`search`,WebFetch:`public`,WebSearch:`travel_explore`,NotebookRead:`book`,NotebookEdit:`edit_note`,SendMessage:`send`,ExitPlanMode:`check_circle_outline`,KillShell:`stop_circle`,BashOutput:`terminal`},O=y(()=>E[r.item.name]??`build`),k=y(()=>{if(x.value)return``;let e=r.item.input,t=A(e);return t?K(t,g.selectedWorkspace):``});function A(e){if(!e||typeof e!=`object`)return typeof e==`string`?e:``;let t=e;for(let e of[`file_path`,`path`,`command`,`pattern`,`query`,`url`,`skill`,`description`,`subject`,`prompt`]){let n=t[e];if(typeof n==`string`&&n.length>0)return n}for(let e of Object.values(t))if(typeof e==`string`&&e.length>0)return e;return``}let j=y(()=>{let e=x.value;return e?e.diffLines?e.diffLines:e.toolName===`Edit`&&e.oldString!==void 0&&e.newString!==void 0?fe(e.oldString,e.newString):e.toolName===`Write`&&e.content!==void 0?e.content.split(`
|
|
8
|
+
`).map(e=>({type:`add`,content:e})):e.toolName===`Bash:rm`?[{type:`del`,content:`File deleted`}]:null:null}),M=y(()=>{let e=r.item.result;if(!e)return``;if(typeof e.output==`string`)return e.output;try{return JSON.stringify(e.output)}catch{return String(e.output)}}),te=new Set([`Read`]),P=y(()=>!!(r.item.result&&M.value)&&(!te.has(r.item.name)||r.item.result?.isError===!0));function F(){a.value=!a.value}function I(){let e=x.value?.filePath,t=g.selectedWorkspace,n=t?.id,r=e?K(e,t):``;!r||/^(?:[A-Za-z]:)?[\\/]/.test(r)||!n||(ne(n,r),window.dispatchEvent(new CustomEvent(`kobo:open-diff`,{detail:{workspaceId:n,path:r}})))}return o(()=>r.item.result?.isError===!0,e=>{e&&(a.value=!0)},{immediate:!0}),(e,r)=>x.value?(i(),c(`div`,{key:0,class:l([`tool-row`,{"tool-row-expanded":a.value}])},[_(`div`,{class:`tool-header`,onClick:F},[u(w,{name:O.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),_(`span`,J,f(x.value.toolName===`Bash:rm`?`Bash`:x.value.toolName),1),_(`span`,{class:`tool-path`,title:x.value.filePath},f(C.value),9,Y),u(ee,{flat:``,dense:``,round:``,size:`xs`,icon:`open_in_new`,class:`tool-open-diff`,onClick:b(I,[`stop`])},{default:s(()=>[u(N,null,{default:s(()=>[S(f(h(v)(`chat.openInGitDiff`)),1)]),_:1})]),_:1}),x.value.additions>0?(i(),c(`span`,X,`+`+f(x.value.additions),1)):p(``,!0),x.value.deletions>0?(i(),c(`span`,he,`-`+f(x.value.deletions),1)):p(``,!0),n.item.result?.isError?(i(),m(w,{key:2,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-xs`})):n.item.result?(i(),m(w,{key:3,name:`check`,color:`positive`,size:`xs`,class:`q-ml-xs`})):p(``,!0),u(w,{name:a.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-auto text-grey-6`},null,8,[`name`])]),a.value&&j.value?(i(),c(`div`,{key:0,class:`tool-diff`,onClick:r[0]||=b(()=>{},[`stop`])},[(i(!0),c(d,null,t(j.value,(e,t)=>(i(),c(`div`,{key:t,class:l([`diff-line`,{"diff-del":e.type===`del`,"diff-add":e.type===`add`,"diff-context":e.type===`context`}])},[_(`span`,ge,f(e.type===`del`?`-`:e.type===`add`?`+`:` `),1),S(f(e.content),1)],2))),128))])):p(``,!0)],2)):(i(),c(`div`,{key:1,class:l([`tool-row tool-row-generic`,{"tool-row-expanded":a.value,"tool-row--toggleable":P.value}])},[_(`div`,{class:`tool-header`,onClick:r[1]||=e=>P.value&&F()},[u(w,{name:O.value,size:`14px`,class:`tool-icon`},null,8,[`name`]),_(`span`,_e,f(n.item.name),1),k.value?(i(),c(`span`,{key:0,class:`tool-arg`,title:A(n.item.input)||k.value},f(k.value),9,Z)):p(``,!0),n.item.result?.isError?(i(),m(w,{key:1,name:`error_outline`,color:`negative`,size:`xs`,class:`q-ml-auto`})):n.item.result?(i(),m(w,{key:2,name:`check`,color:`positive`,size:`xs`,class:`q-ml-auto`})):p(``,!0),P.value?(i(),m(w,{key:3,name:a.value?`expand_less`:`expand_more`,size:`xs`,class:`q-ml-xs text-grey-6`},null,8,[`name`])):p(``,!0)]),a.value&&P.value?(i(),c(`div`,{key:0,class:`tool-output`,onClick:r[2]||=b(()=>{},[`stop`])},f(M.value),1)):p(``,!0)],2))}}),[[`__scopeId`,`data-v-fc912366`]]);function ve(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 ye=[`innerHTML`],$=[`innerHTML`],be=[`src`],xe=R(a({__name:`UserMessageItem`,props:{item:{}},setup(t){let n=t,r=D(),a=y(()=>n.item.sender===`system-prompt`),o=y(()=>L(ve(n.item.content,r.selectedWorkspaceId??``))),l=e(null),f=e(!1);function h(e){let t=e.target;if(t?.tagName!==`IMG`)return;let n=t;n.src&&(l.value=n.src,f.value=!0)}return(e,t)=>(i(),c(d,null,[a.value?(i(),m(te,{key:0,dense:``,"dense-toggle":``,label:e.$t(`chat.systemPrompt`),"header-class":`text-grey-5 text-caption`},{default:s(()=>[_(`div`,{class:`q-py-xs markdown-user-prompt`,innerHTML:o.value},null,8,ye)]),_:1},8,[`label`])):(i(),c(`div`,{key:1,class:`markdown-message`,onClick:h},[_(`div`,{innerHTML:o.value},null,8,$)])),u(E,{modelValue:f.value,"onUpdate:modelValue":t[1]||=e=>f.value=e},{default:s(()=>[l.value?(i(),c(`img`,{key:0,src:l.value,alt:``,class:`image-lightbox-img`,onClick:t[0]||=e=>f.value=!1},null,8,be)):p(``,!0)]),_:1},8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-f34be4c5`]]),Se=[`data-event-ids`],Ce={class:`turn-header`},we={key:0,class:`turn-time`},Te={class:`turn-time turn-time-updated`},Ee={key:2,class:`turn-actions`},De={class:`turn-body`},Oe={key:0,class:`turn-scroll-top`},ke=R(a({__name:`TurnCard`,props:{turn:{},highlighted:{type:Boolean}},emits:[`scrollTo`],setup(n,{emit:r}){let a=n,o=r,{t:g}=T(),v=e(null);function b(){let e=v.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;o(`scrollTo`,Math.max(0,n-8))}let C=y(()=>{switch(a.turn.speaker){case`user`:return{label:g(`chat.you`),accent:`#ce93d8`,badgeClass:`turn-badge-user`};case`agent`:return{label:g(`chat.agent`),accent:`#7986cb`,badgeClass:`turn-badge-agent`};case`system-prompt`:return{label:g(`chat.systemPrompt`),accent:`#757575`,badgeClass:`turn-badge-system`};case`session`:return{label:g(`chat.session`),accent:`#616161`,badgeClass:`turn-badge-session`};case`script`:{let e=a.turn.items[0],t=e?.type===`user`?e.sender:``;return{label:g(t===`archive`?`chat.archiveScript`:t===`setup`?`chat.setupScript`:`chat.cleanupScript`),accent:`#4db6ac`,badgeClass:`turn-badge-script`}}}});function E(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 D=y(()=>E(a.turn.ts)),O=y(()=>a.turn.items.flatMap(e=>e.eventIds??[]).join(` `)),k=y(()=>{let e=a.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}),A=y(()=>{let e=a.turn.ts,t=k.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?``:E(t,r-n<6e4)}),j=y(()=>A.value!==``),M=y(()=>a.turn.items.filter(e=>e.type===`tool`).length);return(e,r)=>(i(),c(`div`,{ref_key:`cardEl`,ref:v,class:l([`turn-card`,{"turn-card--user":n.turn.speaker===`user`,"turn-card--highlighted":n.highlighted}]),"data-event-ids":O.value,style:x({"--turn-accent":C.value.accent})},[_(`div`,Ce,[_(`span`,{class:l([`turn-badge`,C.value.badgeClass])},f(C.value.label),3),D.value?(i(),c(`span`,we,f(D.value),1)):p(``,!0),j.value?(i(),c(d,{key:1},[u(w,{name:`arrow_forward`,size:`10px`,color:`grey-7`,class:`turn-time-arrow`}),_(`span`,Te,[S(f(A.value)+` `,1),u(N,null,{default:s(()=>[S(f(h(g)(`chat.lastUpdatedAt`,{time:A.value})),1)]),_:1})])],64)):p(``,!0),M.value>0?(i(),c(`span`,Ee,` · `+f(h(g)(`chat.nActions`,{n:M.value})),1)):p(``,!0)]),_(`div`,De,[(i(!0),c(d,null,t(n.turn.items,(e,t)=>(i(),c(d,{key:t},[e.type===`text`?(i(),m(de,{key:0,item:e},null,8,[`item`])):e.type===`thinking`?(i(),m(z,{key:1,item:e},null,8,[`item`])):e.type===`tool`?(i(),m(Q,{key:2,item:e},null,8,[`item`])):e.type===`user`?(i(),m(xe,{key:3,item:e},null,8,[`item`])):e.type===`session`?(i(),m(se,{key:4,item:e},null,8,[`item`])):p(``,!0)],64))),128))]),n.turn.items.length>4?(i(),c(`div`,Oe,[u(ee,{flat:``,round:``,dense:``,size:`xs`,icon:`arrow_upward`,color:`grey-6`,class:`turn-scroll-top-btn`,onClick:b},{default:s(()=>[u(N,null,{default:s(()=>[S(f(h(g)(`chat.scrollToTurnTop`)),1)]),_:1})]),_:1})])):p(``,!0)],14,Se))}}),[[`__scopeId`,`data-v-6b8d8e3e`]]),Ae={key:0,class:`activity-feed-switching`},je={key:1,class:`activity-feed-wrap`},Me={key:0,class:`activity-feed-compacting`},Ne={class:`text-caption text-grey-4`},Pe={key:0,class:`text-center q-py-sm text-caption text-grey-6`},Fe={class:`q-pa-md`},Ie={key:1,class:`q-px-md q-pb-md`},Le={class:`activity-feed-nav-cluster`},Re=60,ze=200,Be=200,Ve=400,He=200,Ue=180,We=R(a({__name:`ActivityFeed`,props:{workspaceId:{}},setup(a){let l=a,h=O(),b=A(),x=D(),w=y(()=>h.isCompacting(l.workspaceId)),T=y(()=>x.selectedSessionId),E=y(()=>x.sessions.find(e=>e.id===T.value)?.engineSessionId??null),j=y(()=>{let e=x.sessions;return e.length===0?!1:T.value===e[e.length-1].id});function N(e){return T.value?e?e===T.value||e===E.value:j.value:!0}let ne=y(()=>(x.activityFeeds[l.workspaceId]??[]).filter(e=>e.type===`text`&&typeof e.content==`string`&&N(e.sessionId)).map(e=>({content:e.content,sender:e.meta?.sender??`user`,ts:e.timestamp,sessionId:e.sessionId,eventIds:[e.id]}))),F=y(()=>k(x.workspaces.find(e=>e.id===l.workspaceId)?.status)),I=y(()=>{let e=h.eventsFor(l.workspaceId),t=h.timestampsFor(l.workspaceId),n=h.sessionIdsFor(l.workspaceId),r=h.eventIdsFor(l.workspaceId),i=[],a=[],o=[];for(let s=0;s<e.length;s++)N(n[s])&&(i.push(e[s]),a.push(t[s]),o.push(r[s]??null));return oe(ae(ie(i,a,F.value,o),ne.value).filter(e=>e.type!==`thinking`&&(b.showVerboseSystemMessages||e.type!==`session`)))}),re=y(()=>b.showVerboseSystemMessages?h.eventsFor(l.workspaceId).filter(e=>e.kind===`message:raw`).map(e=>e.content):[]),L=e(null),R=e(!0),z=e(!1),B=e(null),V=!1,H=e(!0),se=e(new Map);function ce(e){R.value=e.verticalSize-e.verticalPosition-e.verticalContainerSize<=Re,V&&e.verticalPosition<=ze&&!z.value&&ue()&&fe()}function le(e,t){return`${e}:${t}`}function ue(){let e=T.value;return e?se.value.get(le(l.workspaceId,e))??!0:h.hasMoreOlderFor(l.workspaceId)}function U(e,t,n){se.value.set(le(e,t),n)}function de(e){if(!T.value)return h.oldestIdFor(e);let t=h.eventIdsFor(e),n=h.sessionIdsFor(e);for(let e=0;e<t.length;e++){if(!N(n[e]))continue;let r=t[e];if(r)return r}}async function fe(){let e=l.workspaceId,t=T.value,n=de(e);if(!n)return;z.value=!0;let r=Date.now();try{let r=L.value;await g();let i=r?.getScroll().verticalSize??0,a=r?.getScroll().verticalPosition??0,o=new URLSearchParams({before:n,limit:`200`});t&&o.set(`session`,t);let s=fetch(`/api/workspaces/${e}/events?${o.toString()}`),c=new Promise(e=>setTimeout(e,Be)),[l]=await Promise.all([s,c]);if(!l.ok){t?U(e,t,!1):h.prepend(e,[],[],{oldestId:n,hasMoreOlder:!1});return}let u=await l.json(),d=u.events??[],f=d.filter(t=>t.type===`agent:event`&&t.workspaceId===e),p=d.filter(t=>t.type===`user:message`&&t.workspaceId===e),m=f.map(e=>e.payload),_=f.map(e=>e.createdAt),v=f.map(e=>e.sessionId??null),y=f.map(e=>e.id),b=d.length>0?d[0].id:n;t&&U(e,t,u.hasMore),h.prepend(e,m,_,{oldestId:b,hasMoreOlder:t?h.hasMoreOlderFor(e):u.hasMore,sessionIds:v,eventIds:y});for(let t of p){let n=t.payload;typeof n.content==`string`&&x.addActivityItem(e,{id:t.id,type:`text`,content:n.content,timestamp:t.createdAt,sessionId:t.sessionId??void 0,meta:{sender:n.sender??`user`}})}if(await g(),r){let e=r.getScroll().verticalSize-i;if(e>0){let t=Math.max(a+e,250);r.setScrollPosition(`vertical`,t,0)}}}catch(e){console.error(`[ActivityFeed] failed to load older events:`,e)}finally{let e=Date.now()-r,t=Math.max(0,Be-e);await new Promise(e=>setTimeout(e,t+Ve)),z.value=!1}}async function W(e=0){await g();let t=L.value;if(!t)return;let n=t.getScroll();t.setScrollPosition(`vertical`,n.verticalSize,e)}let G=null,K=0;function q(){if(G!=null)return;let e=performance.now()-K<Ue;G=requestAnimationFrame(()=>{G=null,K=performance.now(),W(e?0:180)})}function pe(e){let t=L.value;t&&t.setScrollPosition(`vertical`,Math.max(0,e),250)}function me(){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 J(e){let t=e.detail;if(!(t?.workspaceId!==l.workspaceId||!t.eventId))try{let e=new URLSearchParams({around:t.eventId,limit:`200`});t.sessionId&&e.set(`session`,t.sessionId);let n=await fetch(`/api/workspaces/${l.workspaceId}/events?${e}`);if(!n.ok)return;let r=(await n.json()).events??[],i=r.filter(e=>e.type===`agent:event`&&e.workspaceId===l.workspaceId),a=r.filter(e=>e.type===`user:message`&&e.workspaceId===l.workspaceId);h.reset(l.workspaceId,i.map(e=>e.payload),i.map(e=>e.createdAt),{oldestId:r[0]?.id,hasMoreOlder:!1,sessionIds:i.map(e=>e.sessionId),eventIds:i.map(e=>e.id)});for(let e of a){let t=e.payload;typeof t.content==`string`&&x.addActivityItem(l.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`}})}let o=Date.now()+5e3;for(;H.value&&Date.now()<o;)await new Promise(e=>window.setTimeout(e,20));await g(),await new Promise(e=>requestAnimationFrame(()=>requestAnimationFrame(()=>e())));let s=I.value.findIndex(e=>e.items.some(e=>e.eventIds?.includes(t.eventId)??!1)),c=Y.value[s]?.$el,u=L.value,d=X.value;if(!c||!u||!d)return;let f=c.getBoundingClientRect().top-d.getBoundingClientRect().top;B.value=t.eventId,u.setScrollPosition(`vertical`,Math.max(0,f-16),250),window.setTimeout(()=>{B.value===t.eventId&&(B.value=null)},1800)}catch(e){console.error(`[ActivityFeed] failed to focus history event:`,e)}}let Y=e([]),X=e(null);function he(){let e=I.value,t=Y.value,n=[];if(t.length===e.length){for(let r=0;r<e.length;r++){if(e[r].speaker!==`user`)continue;let i=t[r]?.$el;i&&n.push(i)}if(n.length>0)return n}let r=X.value?.parentElement;if(r){let e=r.querySelectorAll(`.turn-card--user`);for(let t of e)n.push(t)}return n}function ge(){let e=L.value;if(!e)return null;let t=X.value;if(!t)return null;let n=e.getScroll().verticalPosition,r=t.getBoundingClientRect().top,i=null;for(let e of he()){let t=e.getBoundingClientRect().top-r;if(t<n-40)i=t;else break}return i}async function _e(){let e=L.value;if(!e)return;let t=ge();if(t===null)for(let e=0;e<15&&ue();e++){for(;z.value;)await new Promise(e=>setTimeout(e,50));if(await fe(),await g(),t=ge(),t!==null)break}t!==null&&e.setScrollPosition(`vertical`,Math.max(0,t-12),250)}async function Z(){V=!1,await g(),await W(0),requestAnimationFrame(()=>{requestAnimationFrame(()=>{V=!0})})}let Q=y(()=>{let e=h.sessionIdsFor(l.workspaceId);if(!T.value)return e.length;let t=0;for(let n of e)N(n)&&t++;return t}),ve=y(()=>h.eventsFor(l.workspaceId).length);async function ye(){H.value=!0;let e=Date.now();await new Promise(e=>setTimeout(e,He));let t=e+5e3;for(;ve.value===0&&Date.now()<t;)await new Promise(e=>setTimeout(e,50));H.value=!1}o(H,async e=>{!e&&Q.value>0&&await Z(),!e&&Q.value===0&&T.value&&xe()}),n(()=>{window.addEventListener(`kobo:focus-history-event`,J),ye(),Q.value>0&&Z(),T.value&&xe();let e=new URLSearchParams(window.location.hash.split(`?`)[1]??``).get(`eventId`);e&&(J(new CustomEvent(`kobo:focus-history-event`,{detail:{workspaceId:l.workspaceId,sessionId:T.value,eventId:e}})),me())});let $=!1;o(Q,async(e,t)=>{if(!$&&e>0){$=!0,await Z();return}e>t&&R.value&&!z.value&&q()}),r(()=>{window.removeEventListener(`kobo:focus-history-event`,J),G!=null&&(cancelAnimationFrame(G),G=null)}),o(()=>l.workspaceId,()=>{R.value=!0,$=!1,V=!1,ye(),Q.value>0&&Z()}),o(()=>x.selectedSessionId,async()=>{R.value=!0,V=!1,await Z(),xe()});let be=new Set;async function xe(){let e=T.value;if(!e||Q.value>0)return;let t=le(l.workspaceId,e);if(!be.has(t)){be.add(t);try{let t=await fetch(`/api/workspaces/${l.workspaceId}/events?session=${encodeURIComponent(e)}&limit=500`);if(!t.ok)return;let n=await t.json(),r=n.events??[];if(r.length===0)return;let i=r.filter(e=>e.type===`agent:event`&&e.workspaceId===l.workspaceId),a=r.filter(e=>e.type===`user:message`&&e.workspaceId===l.workspaceId),o=i.map(e=>e.payload),s=i.map(e=>e.createdAt),c=i.map(e=>e.sessionId??null),u=i.map(e=>e.id);U(l.workspaceId,e,n.hasMore),o.length>0&&h.prepend(l.workspaceId,o,s,{oldestId:r[0].id,hasMoreOlder:h.hasMoreOlderFor(l.workspaceId),sessionIds:c,eventIds:u});for(let e of a){let t=e.payload;typeof t.content==`string`&&x.addActivityItem(l.workspaceId,{id:e.id,type:`text`,content:t.content,timestamp:e.createdAt,sessionId:e.sessionId??void 0,meta:{sender:t.sender??`user`}})}await g(),await W(0)}catch(e){console.error(`[ActivityFeed] fetchSessionIfMissing failed:`,e),be.delete(t)}}}o(y(()=>ne.value.filter(e=>e.sender!==`system-prompt`).length),async(e,t)=>{e>t&&(R.value=!0,await W(180))});async function Se(){R.value=!0,await W(250)}return(e,n)=>H.value?(i(),c(`div`,Ae,[u(M,{size:`40px`,color:`indigo-4`})])):(i(),c(`div`,je,[u(v,{name:`fade`},{default:s(()=>[w.value?(i(),c(`div`,Me,[u(M,{size:`18px`,color:`indigo-4`}),_(`span`,Ne,f(e.$t(`activity.compacting`)),1)])):p(``,!0)]),_:1}),u(P,{ref_key:`scrollRef`,ref:L,class:`activity-feed-scroll`,onScroll:ce},{default:s(()=>[_(`div`,{ref_key:`contentOriginRef`,ref:X,class:`content-origin-marker`},null,512),z.value?(i(),c(`div`,Pe,[u(C,{size:`sm`}),S(` `+f(e.$t(`activity.loading_older`)),1)])):p(``,!0),_(`div`,Fe,[(i(!0),c(d,null,t(I.value,(e,t)=>(i(),m(ke,{key:t,ref_for:!0,ref_key:`turnRefs`,ref:Y,turn:e,highlighted:e.items.some(e=>e.eventIds?.includes(B.value??``)??!1),onScrollTo:pe},null,8,[`turn`,`highlighted`]))),128))]),re.value.length?(i(),c(`div`,Ie,[u(te,{label:e.$t(`activity.raw_lines`,{n:re.value.length}),dense:``},{default:s(()=>[(i(!0),c(d,null,t(re.value,(e,t)=>(i(),c(`div`,{key:t,class:`text-caption text-grey q-pa-xs`},f(e),1))),128))]),_:1},8,[`label`])])):p(``,!0)]),_:1},512),_(`div`,Le,[R.value?p(``,!0):(i(),m(ee,{key:0,round:``,dense:``,unelevated:``,color:`grey-9`,"text-color":`grey-3`,icon:`arrow_downward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.scroll_to_bottom`),onClick:Se},null,8,[`title`])),u(ee,{round:``,dense:``,unelevated:``,color:`grey-9`,"text-color":`grey-3`,icon:`arrow_upward`,size:`sm`,class:`activity-feed-nav-btn`,title:e.$t(`activity.prev_user_message`),onClick:_e},null,8,[`title`])])]))}}),[[`__scopeId`,`data-v-0cdc51af`]]);export{We as default};
|