@loicngr/kobo 1.7.4 → 1.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -8
- package/dist/mcp-server/kobo-tasks-handlers.js +26 -1
- package/dist/mcp-server/kobo-tasks-server.js +41 -1
- package/dist/server/db/migrations.js +49 -0
- package/dist/server/db/schema.js +11 -0
- package/dist/server/index.js +7 -1
- package/dist/server/routes/workspaces.js +82 -4
- package/dist/server/services/agent/engines/claude-code/engine.js +4 -1
- package/dist/server/services/agent/engines/claude-code/event-mapper.js +6 -2
- package/dist/server/services/agent/orchestrator.js +72 -71
- package/dist/server/services/auto-loop-service.js +8 -0
- package/dist/server/services/quota-backoff-service.js +127 -0
- package/dist/server/services/workspace-service.js +80 -0
- package/package.json +1 -1
- package/src/client/dist/spa/assets/{ActivityFeed-ClJLeAXJ.js → ActivityFeed-oW9PgZ8E.js} +1 -1
- package/src/client/dist/spa/assets/AutoLoopChip-Y53cnGfZ.js +1 -0
- package/src/client/dist/spa/assets/{CreatePage-BOkt0Psl.js → CreatePage-CuD7sMR7.js} +1 -1
- package/src/client/dist/spa/assets/{DiffViewer-Dls1jFCN.js → DiffViewer-rc3tE9fq.js} +3 -3
- package/src/client/dist/spa/assets/{HealthPage-CMxH3SBS.js → HealthPage-Dz0yGGMB.js} +1 -1
- package/src/client/dist/spa/assets/{MainLayout-DHNIerYJ.js → MainLayout-B9i06p7n.js} +17 -17
- package/src/client/dist/spa/assets/{MainLayout-DKurmqtk.css → MainLayout-DDa3rGKA.css} +1 -1
- package/src/client/dist/spa/assets/{SearchPage-BEnZ-CLq.js → SearchPage-DdX7JZCD.js} +1 -1
- package/src/client/dist/spa/assets/{SettingsPage-DeCbWvPb.js → SettingsPage-Dnj1CWc3.js} +1 -1
- package/src/client/dist/spa/assets/{WorkspacePage-eymEd4kx.css → WorkspacePage-CCtIrBiR.css} +1 -1
- package/src/client/dist/spa/assets/WorkspacePage-DHp20nl-.js +4 -0
- package/src/client/dist/spa/assets/{cssMode-AlflsawW.js → cssMode-DSB5jkRt.js} +1 -1
- package/src/client/dist/spa/assets/{editor.api-DtvjQlUm.js → editor.api-Bcw50eFD.js} +1 -1
- package/src/client/dist/spa/assets/{editor.main-Ccy_gjVD.js → editor.main-D9piVGaH.js} +3 -3
- package/src/client/dist/spa/assets/{expand-template-AQsvbQ8_.js → expand-template-BIPuNAYV.js} +1 -1
- package/src/client/dist/spa/assets/{freemarker2-DdQktlXK.js → freemarker2-CVh_Zh8H.js} +1 -1
- package/src/client/dist/spa/assets/{handlebars-CE3ee2NH.js → handlebars-CpCgELpu.js} +1 -1
- package/src/client/dist/spa/assets/{html-CCKX8Xv9.js → html-ikWDpvWk.js} +1 -1
- package/src/client/dist/spa/assets/{htmlMode-Dh8jDJum.js → htmlMode-C9TTCKih.js} +1 -1
- package/src/client/dist/spa/assets/i18n-DZCb8dnb.js +1 -0
- package/src/client/dist/spa/assets/index-DuK38XN5.js +2 -0
- package/src/client/dist/spa/assets/{javascript-DhmZNdUp.js → javascript-C4OlkNeA.js} +1 -1
- package/src/client/dist/spa/assets/{jsonMode-B0xAtnNK.js → jsonMode-BiD34_86.js} +1 -1
- package/src/client/dist/spa/assets/{liquid-ByL0HpZ0.js → liquid-Dty0Ui2c.js} +1 -1
- package/src/client/dist/spa/assets/{mdx-DX4pehAZ.js → mdx-yiUjOVv6.js} +1 -1
- package/src/client/dist/spa/assets/{models-ClWoqWeC.js → models-BDkLiht9.js} +1 -1
- package/src/client/dist/spa/assets/{monaco.contribution-Fegh8Y1Y.js → monaco.contribution-Bz9yFPWR.js} +2 -2
- package/src/client/dist/spa/assets/{purify.es-BWZjBa9F.js → purify.es-BIY760fF.js} +1 -1
- package/src/client/dist/spa/assets/{python-COS2MM8n.js → python-7SPSWQoD.js} +1 -1
- package/src/client/dist/spa/assets/{razor-Cc3xCJU7.js → razor-eagZawXK.js} +1 -1
- package/src/client/dist/spa/assets/{render-chat-markdown-DcGIpMoe.js → render-chat-markdown-TvAqpDih.js} +1 -1
- package/src/client/dist/spa/assets/{tsMode-eQIJjERk.js → tsMode-CLYG2xeJ.js} +1 -1
- package/src/client/dist/spa/assets/{typescript-DwIlacVU.js → typescript-CzOXM8yS.js} +1 -1
- package/src/client/dist/spa/assets/{xml-DP-09Aih.js → xml-2_0_6RAX.js} +1 -1
- package/src/client/dist/spa/assets/{yaml-BhrtimeA.js → yaml-CtpgNyXs.js} +1 -1
- package/src/client/dist/spa/index.html +1 -1
- package/src/mcp-server/kobo-tasks-handlers.ts +35 -1
- package/src/mcp-server/kobo-tasks-server.ts +42 -0
- package/src/client/dist/spa/assets/WorkspacePage-DFAFT5OW.js +0 -4
- package/src/client/dist/spa/assets/i18n-BOsrrRj4.js +0 -1
- package/src/client/dist/spa/assets/index-_ZaIBxd6.js +0 -2
- /package/src/client/dist/spa/assets/{QPage-ChUKoaKe.js → QPage-DFi3K093.js} +0 -0
- /package/src/client/dist/spa/assets/{formatters-BD0_hovB.js → formatters-DCAQ6ANJ.js} +0 -0
|
@@ -4,7 +4,9 @@ import { getDb } from '../../db/index.js';
|
|
|
4
4
|
import { ensureKoboHome, getCompiledMcpServerPath, getDbPath, getKoboHome, getMcpServerSourcePath, getSettingsPath, getSkillsPath, } from '../../utils/paths.js';
|
|
5
5
|
import { unregisterProcess } from '../../utils/process-tracker.js';
|
|
6
6
|
import * as autoLoopService from '../auto-loop-service.js';
|
|
7
|
+
import * as quotaBackoffService from '../quota-backoff-service.js';
|
|
7
8
|
import { getEffectiveSettings } from '../settings-service.js';
|
|
9
|
+
import { refreshNow } from '../usage/poller.js';
|
|
8
10
|
import * as wakeupService from '../wakeup-service.js';
|
|
9
11
|
import { emit, emitEphemeral } from '../websocket-service.js';
|
|
10
12
|
import { getWorkspace as getWs, markWorkspaceUnread, updateWorkspaceStatus, } from '../workspace-service.js';
|
|
@@ -137,8 +139,6 @@ let availableSkills = (() => {
|
|
|
137
139
|
const retryCounts = new Map();
|
|
138
140
|
/** Tracks workspaces where the current session failed due to a stale --resume session ID. */
|
|
139
141
|
const resumeFailedSet = new Set();
|
|
140
|
-
/** workspaceId -> backoff timer */
|
|
141
|
-
const backoffTimers = new Map();
|
|
142
142
|
// ── Watchdog ──────────────────────────────────────────────────────────────────
|
|
143
143
|
const WATCHDOG_INTERVAL_MS = 30_000;
|
|
144
144
|
let watchdogTimer = null;
|
|
@@ -469,7 +469,7 @@ function handleEvent(workspaceId, agentSessionId, ev) {
|
|
|
469
469
|
}
|
|
470
470
|
}
|
|
471
471
|
if (ev.kind === 'error' && ev.category === 'quota') {
|
|
472
|
-
handleQuota(workspaceId, agentSessionId);
|
|
472
|
+
void handleQuota(workspaceId, agentSessionId);
|
|
473
473
|
}
|
|
474
474
|
if (ev.kind === 'error' && ev.category === 'resume_failed') {
|
|
475
475
|
resumeFailedSet.add(workspaceId);
|
|
@@ -569,14 +569,11 @@ function onSessionEnded(workspaceId, agentSessionId, exitCode, reason, resumeFai
|
|
|
569
569
|
if (wasStopping)
|
|
570
570
|
return;
|
|
571
571
|
// When the session hit quota, handleQuota() already transitioned the
|
|
572
|
-
// workspace to `quota` and armed the retry
|
|
573
|
-
//
|
|
572
|
+
// workspace to `quota` and armed the retry via quotaBackoffService.
|
|
573
|
+
// Preserve that pending backoff in the quota path; otherwise clear any
|
|
574
|
+
// stale entry (defensive — shouldn't normally exist on a non-quota end).
|
|
574
575
|
if (!preserveQuotaBackoff) {
|
|
575
|
-
|
|
576
|
-
if (pendingBackoff) {
|
|
577
|
-
clearTimeout(pendingBackoff);
|
|
578
|
-
backoffTimers.delete(workspaceId);
|
|
579
|
-
}
|
|
576
|
+
quotaBackoffService.cancel(workspaceId, 'completed');
|
|
580
577
|
}
|
|
581
578
|
if (preserveQuotaBackoff) {
|
|
582
579
|
try {
|
|
@@ -774,11 +771,8 @@ export function stopAgent(workspaceId) {
|
|
|
774
771
|
// session:ended handler checks identity before removing, so a new controller
|
|
775
772
|
// started in the meantime is preserved.
|
|
776
773
|
controllers.delete(workspaceId);
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
clearTimeout(timer);
|
|
780
|
-
backoffTimers.delete(workspaceId);
|
|
781
|
-
}
|
|
774
|
+
// Manual stop should also drop any pending quota auto-resume.
|
|
775
|
+
quotaBackoffService.cancel(workspaceId, 'user');
|
|
782
776
|
// Fire-and-forget: controller.stop is async but we don't block callers.
|
|
783
777
|
void ctrl.stop().catch((err) => {
|
|
784
778
|
console.error('[orchestrator] controller.stop failed:', err);
|
|
@@ -1098,12 +1092,14 @@ const QUOTA_FALLBACK_LADDER_MINUTES = [15, 30, 60, 180, 300];
|
|
|
1098
1092
|
/**
|
|
1099
1093
|
* Compute the delay before retrying a workspace hit by quota.
|
|
1100
1094
|
*
|
|
1101
|
-
* Prefers the `resetsAt` of the saturated bucket with the furthest-future
|
|
1102
|
-
* reset
|
|
1103
|
-
*
|
|
1104
|
-
*
|
|
1095
|
+
* Prefers (1) the `resetsAt` of the saturated bucket with the furthest-future
|
|
1096
|
+
* reset reported by the agent's `rate_limit` event, then (1.5) the official
|
|
1097
|
+
* Anthropic usage API (`five_hour` bucket) when it reports saturation, and
|
|
1098
|
+
* finally (2) a fixed ladder (15 → 30 → 60 → 180 → 300 min) whenever neither
|
|
1099
|
+
* source is usable.
|
|
1105
1100
|
*/
|
|
1106
|
-
export function computeQuotaBackoffMs(workspaceId, retryCount) {
|
|
1101
|
+
export async function computeQuotaBackoffMs(workspaceId, retryCount) {
|
|
1102
|
+
// 1. Prefer the rate_limit event from the agent stream — most recent + most precise.
|
|
1107
1103
|
const info = latestRateLimitInfo.get(workspaceId);
|
|
1108
1104
|
if (info?.buckets?.length) {
|
|
1109
1105
|
const candidates = info.buckets
|
|
@@ -1119,15 +1115,38 @@ export function computeQuotaBackoffMs(workspaceId, retryCount) {
|
|
|
1119
1115
|
}
|
|
1120
1116
|
}
|
|
1121
1117
|
}
|
|
1118
|
+
// 1.5. Try the official usage API (Claude subscription). Best-effort; never throws.
|
|
1119
|
+
try {
|
|
1120
|
+
const snap = await refreshNow('claude-code');
|
|
1121
|
+
if (snap) {
|
|
1122
|
+
const fiveHour = snap.buckets.find((b) => b.id === 'five_hour');
|
|
1123
|
+
if (fiveHour &&
|
|
1124
|
+
typeof fiveHour.usedPct === 'number' &&
|
|
1125
|
+
fiveHour.usedPct >= QUOTA_SATURATION_THRESHOLD_PCT &&
|
|
1126
|
+
typeof fiveHour.resetsAt === 'string') {
|
|
1127
|
+
const resetTs = Date.parse(fiveHour.resetsAt);
|
|
1128
|
+
const delta = resetTs - Date.now() + QUOTA_SAFETY_MARGIN_MS;
|
|
1129
|
+
if (delta > 0 && delta <= QUOTA_MAX_BACKOFF_MS) {
|
|
1130
|
+
return { delayMs: delta, resetsAt: fiveHour.resetsAt, source: 'usage_api' };
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
catch (err) {
|
|
1136
|
+
console.warn('[orchestrator] computeQuotaBackoffMs — usage API call failed:', err);
|
|
1137
|
+
}
|
|
1138
|
+
// 2. Hard-coded ladder.
|
|
1122
1139
|
const idx = Math.min(Math.max(0, retryCount), QUOTA_FALLBACK_LADDER_MINUTES.length - 1);
|
|
1123
1140
|
const backoffMinutes = QUOTA_FALLBACK_LADDER_MINUTES[idx];
|
|
1124
|
-
return { delayMs: backoffMinutes * 60 * 1000, source: '
|
|
1141
|
+
return { delayMs: backoffMinutes * 60 * 1000, source: 'fallback_ladder' };
|
|
1125
1142
|
}
|
|
1143
|
+
/** @internal test-only — re-export of `computeQuotaBackoffMs` to anchor a stable seam. */
|
|
1144
|
+
export const _computeQuotaBackoffMs = computeQuotaBackoffMs;
|
|
1126
1145
|
/** @internal Test-only. */
|
|
1127
1146
|
export function _test_setRateLimitInfo(workspaceId, info) {
|
|
1128
1147
|
latestRateLimitInfo.set(workspaceId, info);
|
|
1129
1148
|
}
|
|
1130
|
-
function handleQuota(workspaceId, _agentSessionId) {
|
|
1149
|
+
async function handleQuota(workspaceId, _agentSessionId) {
|
|
1131
1150
|
try {
|
|
1132
1151
|
updateWorkspaceStatus(workspaceId, 'quota');
|
|
1133
1152
|
}
|
|
@@ -1135,53 +1154,39 @@ function handleQuota(workspaceId, _agentSessionId) {
|
|
|
1135
1154
|
// May fail if transition is not valid
|
|
1136
1155
|
}
|
|
1137
1156
|
const retryCount = retryCounts.get(workspaceId) ?? 0;
|
|
1138
|
-
const { delayMs, resetsAt, source } = computeQuotaBackoffMs(workspaceId, retryCount);
|
|
1139
|
-
const backoffMs = delayMs;
|
|
1157
|
+
const { delayMs, resetsAt, source } = await computeQuotaBackoffMs(workspaceId, retryCount);
|
|
1140
1158
|
retryCounts.set(workspaceId, retryCount + 1);
|
|
1141
|
-
//
|
|
1142
|
-
//
|
|
1143
|
-
|
|
1144
|
-
retryCount: retryCount + 1,
|
|
1145
|
-
backoffMinutes: Math.round(delayMs / 60_000),
|
|
1146
|
-
resetsAt,
|
|
1147
|
-
source,
|
|
1148
|
-
});
|
|
1149
|
-
const timer = setTimeout(() => {
|
|
1150
|
-
backoffTimers.delete(workspaceId);
|
|
1151
|
-
if (!controllers.has(workspaceId)) {
|
|
1152
|
-
const freshWs = getWs(workspaceId);
|
|
1153
|
-
if (!freshWs || freshWs.archivedAt !== null || freshWs.status !== 'quota') {
|
|
1154
|
-
return;
|
|
1155
|
-
}
|
|
1156
|
-
try {
|
|
1157
|
-
if (freshWs.autoLoop) {
|
|
1158
|
-
autoLoopService.onQuotaBackoffExpired(workspaceId);
|
|
1159
|
-
}
|
|
1160
|
-
else {
|
|
1161
|
-
const freshWorkingDir = freshWs.worktreePath;
|
|
1162
|
-
startAgent(workspaceId, freshWorkingDir, 'Continue the previous task where you left off.', undefined, true);
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
catch (err) {
|
|
1166
|
-
console.error(`[orchestrator] Quota retry for workspace '${workspaceId}' failed:`, err);
|
|
1167
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1168
|
-
try {
|
|
1169
|
-
updateWorkspaceStatus(workspaceId, 'error');
|
|
1170
|
-
}
|
|
1171
|
-
catch {
|
|
1172
|
-
// transition may not be valid
|
|
1173
|
-
}
|
|
1174
|
-
routeEvent(workspaceId, '', {
|
|
1175
|
-
kind: 'error',
|
|
1176
|
-
category: 'other',
|
|
1177
|
-
message: `Quota retry failed: ${msg}`,
|
|
1178
|
-
});
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
}, backoffMs);
|
|
1182
|
-
timer.unref?.();
|
|
1183
|
-
backoffTimers.set(workspaceId, timer);
|
|
1159
|
+
// The quotaBackoffService owns the timer + the persistent row + the
|
|
1160
|
+
// 'agent:quota-backoff' WS emit. Hand off everything to it.
|
|
1161
|
+
quotaBackoffService.arm(workspaceId, delayMs, { resetsAt: resetsAt ?? null, source });
|
|
1184
1162
|
}
|
|
1163
|
+
/** @internal test-only — re-export of `handleQuota` for direct testing. */
|
|
1164
|
+
export const _handleQuota = handleQuota;
|
|
1165
|
+
/**
|
|
1166
|
+
* Rebuild the in-memory `retryCounts` map from the persisted `pending_quota_backoffs`
|
|
1167
|
+
* rows. Called from `index.ts` at boot, before `quotaBackoffService.restoreOnBoot`.
|
|
1168
|
+
* Without this, an arm() after restart would compute the next backoff from
|
|
1169
|
+
* `retryCount=0`, undoing the ladder progression.
|
|
1170
|
+
*/
|
|
1171
|
+
export function restoreRetryCountsFromDb() {
|
|
1172
|
+
for (const pending of quotaBackoffService.listPending()) {
|
|
1173
|
+
retryCounts.set(pending.workspaceId, pending.retryCount);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
// One-time wire: when the persisted backoff timer fires (or a row is
|
|
1177
|
+
// restored at boot), hand the workspace off to auto-loop. The auto-loop
|
|
1178
|
+
// service decides whether to spawn the next iteration or fall back to a
|
|
1179
|
+
// manual resume.
|
|
1180
|
+
//
|
|
1181
|
+
// IMPORTANT — behavioural contract: only auto-loop workspaces auto-resume
|
|
1182
|
+
// after a quota backoff. `onQuotaBackoffExpired` no-ops if `auto_loop !== 1`
|
|
1183
|
+
// (see auto-loop-service). Workspaces hit by quota WITHOUT auto-loop stay
|
|
1184
|
+
// in `quota` status and require manual user action (resume / new message)
|
|
1185
|
+
// to leave that state. This is intentional: without an auto-loop intent,
|
|
1186
|
+
// firing a fresh agent run in the user's absence would surprise them.
|
|
1187
|
+
quotaBackoffService.setOnFireCallback((workspaceId) => {
|
|
1188
|
+
autoLoopService.onQuotaBackoffExpired(workspaceId);
|
|
1189
|
+
});
|
|
1185
1190
|
// ── Testing utilities ─────────────────────────────────────────────────────────
|
|
1186
1191
|
/** @internal test-only */
|
|
1187
1192
|
export function _getControllers() {
|
|
@@ -1192,10 +1197,6 @@ export function _getRetryCounts() {
|
|
|
1192
1197
|
return retryCounts;
|
|
1193
1198
|
}
|
|
1194
1199
|
/** @internal test-only */
|
|
1195
|
-
export function _getBackoffTimers() {
|
|
1196
|
-
return backoffTimers;
|
|
1197
|
-
}
|
|
1198
|
-
/** @internal test-only */
|
|
1199
1200
|
export function _getSessionIds() {
|
|
1200
1201
|
return sessionIds;
|
|
1201
1202
|
}
|
|
@@ -309,6 +309,14 @@ function spawnNextIteration(workspaceId, opts = {}) {
|
|
|
309
309
|
* Called by orchestrator.handleQuota's backoff timer when auto-loop is enabled.
|
|
310
310
|
* Spawns the next auto-loop iteration if the workspace is still in quota status
|
|
311
311
|
* with auto_loop active; no-ops otherwise (race-safe).
|
|
312
|
+
*
|
|
313
|
+
* No-op cases — these all leave the workspace in `quota` status awaiting
|
|
314
|
+
* manual user action:
|
|
315
|
+
* - workspace was deleted between arm and fire
|
|
316
|
+
* - `auto_loop !== 1` (workspace was never an auto-loop target, OR the user
|
|
317
|
+
* toggled the loop off during the backoff window)
|
|
318
|
+
* - `status !== 'quota'` (user already manually resumed, or another path
|
|
319
|
+
* transitioned the workspace)
|
|
312
320
|
*/
|
|
313
321
|
export function onQuotaBackoffExpired(workspaceId) {
|
|
314
322
|
const row = getRow(workspaceId);
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { getDb } from '../db/index.js';
|
|
2
|
+
import { emitEphemeral } from './websocket-service.js';
|
|
3
|
+
import { getWorkspace } from './workspace-service.js';
|
|
4
|
+
const timers = new Map();
|
|
5
|
+
let onFireCallback = null;
|
|
6
|
+
function rowToPending(row) {
|
|
7
|
+
return {
|
|
8
|
+
workspaceId: row.workspace_id,
|
|
9
|
+
targetAt: row.target_at,
|
|
10
|
+
resetsAt: row.resets_at,
|
|
11
|
+
source: row.source,
|
|
12
|
+
retryCount: row.retry_count,
|
|
13
|
+
createdAt: row.created_at,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Schedule (or reschedule) the auto-resume timer for a workspace that just
|
|
18
|
+
* hit a Claude quota. Persists the target time so it survives restarts and
|
|
19
|
+
* keeps the in-RAM `setTimeout` alive for the current process.
|
|
20
|
+
*
|
|
21
|
+
* `delayMs` is the "fire-now-plus-delta" offset; it MUST already include
|
|
22
|
+
* any safety margin the caller wants. orchestrator.handleQuota owns that math.
|
|
23
|
+
*/
|
|
24
|
+
export function arm(workspaceId, delayMs, meta) {
|
|
25
|
+
const db = getDb();
|
|
26
|
+
const now = new Date();
|
|
27
|
+
const targetAt = new Date(now.getTime() + delayMs).toISOString();
|
|
28
|
+
const existing = db
|
|
29
|
+
.prepare('SELECT retry_count FROM pending_quota_backoffs WHERE workspace_id = ?')
|
|
30
|
+
.get(workspaceId);
|
|
31
|
+
const retryCount = (existing?.retry_count ?? 0) + 1;
|
|
32
|
+
db.prepare(`INSERT INTO pending_quota_backoffs (workspace_id, target_at, resets_at, source, retry_count, created_at)
|
|
33
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
34
|
+
ON CONFLICT(workspace_id) DO UPDATE SET
|
|
35
|
+
target_at = excluded.target_at,
|
|
36
|
+
resets_at = excluded.resets_at,
|
|
37
|
+
source = excluded.source,
|
|
38
|
+
retry_count = excluded.retry_count,
|
|
39
|
+
created_at = excluded.created_at`).run(workspaceId, targetAt, meta.resetsAt, meta.source, retryCount, now.toISOString());
|
|
40
|
+
const previous = timers.get(workspaceId);
|
|
41
|
+
if (previous)
|
|
42
|
+
clearTimeout(previous);
|
|
43
|
+
const timer = setTimeout(() => fireOrSkip(workspaceId), Math.max(0, delayMs));
|
|
44
|
+
timer.unref?.();
|
|
45
|
+
timers.set(workspaceId, timer);
|
|
46
|
+
emitEphemeral(workspaceId, 'agent:quota-backoff', {
|
|
47
|
+
targetAt,
|
|
48
|
+
resetsAt: meta.resetsAt,
|
|
49
|
+
source: meta.source,
|
|
50
|
+
retryCount,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Cancel the pending backoff for a workspace. Returns true if a row existed
|
|
55
|
+
* (and was deleted), false if there was nothing to cancel. Idempotent.
|
|
56
|
+
*/
|
|
57
|
+
export function cancel(workspaceId, reason) {
|
|
58
|
+
const db = getDb();
|
|
59
|
+
const result = db.prepare('DELETE FROM pending_quota_backoffs WHERE workspace_id = ?').run(workspaceId);
|
|
60
|
+
const existed = result.changes > 0;
|
|
61
|
+
const previous = timers.get(workspaceId);
|
|
62
|
+
if (previous) {
|
|
63
|
+
clearTimeout(previous);
|
|
64
|
+
timers.delete(workspaceId);
|
|
65
|
+
}
|
|
66
|
+
if (existed) {
|
|
67
|
+
emitEphemeral(workspaceId, 'agent:quota-backoff-cancelled', { reason });
|
|
68
|
+
}
|
|
69
|
+
return existed;
|
|
70
|
+
}
|
|
71
|
+
export function getPending(workspaceId) {
|
|
72
|
+
const db = getDb();
|
|
73
|
+
const row = db.prepare('SELECT * FROM pending_quota_backoffs WHERE workspace_id = ?').get(workspaceId);
|
|
74
|
+
return row ? rowToPending(row) : null;
|
|
75
|
+
}
|
|
76
|
+
export function listPending() {
|
|
77
|
+
const db = getDb();
|
|
78
|
+
const rows = db.prepare('SELECT * FROM pending_quota_backoffs').all();
|
|
79
|
+
return rows.map(rowToPending);
|
|
80
|
+
}
|
|
81
|
+
export function setOnFireCallback(fn) {
|
|
82
|
+
onFireCallback = fn;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Re-arm timers for rows persisted across restart. Future rows get a fresh
|
|
86
|
+
* `setTimeout`; past rows fire immediately (delay = 0). Rows pointing at
|
|
87
|
+
* archived or missing workspaces are deleted without firing.
|
|
88
|
+
*/
|
|
89
|
+
export function restoreOnBoot(onFire) {
|
|
90
|
+
setOnFireCallback(onFire);
|
|
91
|
+
const db = getDb();
|
|
92
|
+
const rows = db.prepare('SELECT * FROM pending_quota_backoffs').all();
|
|
93
|
+
for (const row of rows) {
|
|
94
|
+
const ws = getWorkspace(row.workspace_id);
|
|
95
|
+
if (!ws || ws.archivedAt !== null) {
|
|
96
|
+
db.prepare('DELETE FROM pending_quota_backoffs WHERE workspace_id = ?').run(row.workspace_id);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const delta = new Date(row.target_at).getTime() - Date.now();
|
|
100
|
+
const timer = setTimeout(() => fireOrSkip(row.workspace_id), Math.max(0, delta));
|
|
101
|
+
timer.unref?.();
|
|
102
|
+
timers.set(row.workspace_id, timer);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Internal — invoked when a timer fires. */
|
|
106
|
+
function fireOrSkip(workspaceId) {
|
|
107
|
+
timers.delete(workspaceId);
|
|
108
|
+
// Final archive check before firing — workspace might have been archived
|
|
109
|
+
// between the timer being armed and now.
|
|
110
|
+
const ws = getWorkspace(workspaceId);
|
|
111
|
+
if (!ws || ws.archivedAt !== null) {
|
|
112
|
+
cancel(workspaceId, 'archive');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Consume the persisted row BEFORE invoking the callback. If the server
|
|
116
|
+
// crashes during the spawn the callback triggers, restoreOnBoot won't see
|
|
117
|
+
// a stale row with target_at in the past and re-fire on the next start
|
|
118
|
+
// (which would cause a double spawn). The cb's downstream effects (next
|
|
119
|
+
// iteration, status transitions) are tracked by their own state.
|
|
120
|
+
getDb().prepare('DELETE FROM pending_quota_backoffs WHERE workspace_id = ?').run(workspaceId);
|
|
121
|
+
const cb = onFireCallback;
|
|
122
|
+
if (!cb)
|
|
123
|
+
return;
|
|
124
|
+
cb(workspaceId);
|
|
125
|
+
}
|
|
126
|
+
/** @internal test-only */
|
|
127
|
+
export const _timers = timers;
|
|
@@ -3,7 +3,9 @@ import { getDb } from '../db/index.js';
|
|
|
3
3
|
import { resolveWorkspaceWorktreePath } from '../utils/worktree-paths.js';
|
|
4
4
|
import * as orchestrator from './agent/orchestrator.js';
|
|
5
5
|
import * as autoLoopService from './auto-loop-service.js';
|
|
6
|
+
import * as quotaBackoffService from './quota-backoff-service.js';
|
|
6
7
|
import * as wakeupService from './wakeup-service.js';
|
|
8
|
+
import { emitEphemeral } from './websocket-service.js';
|
|
7
9
|
/** Allowed status transitions per current status. Enforced by updateWorkspaceStatus. */
|
|
8
10
|
const VALID_TRANSITIONS = {
|
|
9
11
|
created: ['extracting', 'brainstorming', 'idle', 'error'],
|
|
@@ -51,6 +53,8 @@ function mapWorkspace(row) {
|
|
|
51
53
|
archivedAt: row.archived_at,
|
|
52
54
|
favoritedAt: row.favorited_at,
|
|
53
55
|
tags: parseTags(row.tags),
|
|
56
|
+
description: row.description,
|
|
57
|
+
agentDescription: row.agent_description,
|
|
54
58
|
engine: row.engine ?? 'claude-code',
|
|
55
59
|
autoLoop: row.auto_loop === 1,
|
|
56
60
|
autoLoopReady: row.auto_loop_ready === 1,
|
|
@@ -270,6 +274,66 @@ export function updateAgentPermissionMode(id, mode) {
|
|
|
270
274
|
}
|
|
271
275
|
return getWorkspace(id);
|
|
272
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Update a workspace's short description (≤ 200 chars after trim).
|
|
279
|
+
* Empty string (after trim) or `null` clears the column.
|
|
280
|
+
*
|
|
281
|
+
* Emits an ephemeral `workspace:description-updated` WebSocket event so every
|
|
282
|
+
* subscribed client (sidebar + the workspace header) refreshes in real-time
|
|
283
|
+
* without a manual reload. The truth lives in the DB; sync replay on
|
|
284
|
+
* reconnect re-fetches via GET /api/workspaces.
|
|
285
|
+
*
|
|
286
|
+
* @throws when the description exceeds 200 chars after trim or the workspace
|
|
287
|
+
* does not exist.
|
|
288
|
+
*/
|
|
289
|
+
export function updateWorkspaceDescription(id, description) {
|
|
290
|
+
const trimmed = description == null ? null : description.trim();
|
|
291
|
+
if (trimmed !== null && trimmed.length > 200) {
|
|
292
|
+
throw new Error(`Description must be 200 characters or fewer (got ${trimmed.length})`);
|
|
293
|
+
}
|
|
294
|
+
const stored = trimmed && trimmed.length > 0 ? trimmed : null;
|
|
295
|
+
const db = getDb();
|
|
296
|
+
const result = db
|
|
297
|
+
.prepare('UPDATE workspaces SET description = ?, updated_at = ? WHERE id = ?')
|
|
298
|
+
.run(stored, new Date().toISOString(), id);
|
|
299
|
+
if (result.changes === 0) {
|
|
300
|
+
throw new Error(`Workspace '${id}' not found`);
|
|
301
|
+
}
|
|
302
|
+
emitEphemeral(id, 'workspace:description-updated', { description: stored });
|
|
303
|
+
return getWorkspace(id);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Update a workspace's agent-side description (≤ 200 chars after trim).
|
|
307
|
+
* Empty string (after trim) or `null` clears the column.
|
|
308
|
+
*
|
|
309
|
+
* Mirror of `updateWorkspaceDescription` but writes the `agent_description`
|
|
310
|
+
* column, which is exclusively the agent's to set via the
|
|
311
|
+
* `set_workspace_agent_description` MCP tool. The user's `description`
|
|
312
|
+
* column is untouched.
|
|
313
|
+
*
|
|
314
|
+
* Emits an ephemeral `workspace:agent-description-updated` event so every
|
|
315
|
+
* subscribed client (sidebar fallback display + workspace header read-only
|
|
316
|
+
* line) refreshes in real-time.
|
|
317
|
+
*
|
|
318
|
+
* @throws when the description exceeds 200 chars after trim or the workspace
|
|
319
|
+
* does not exist.
|
|
320
|
+
*/
|
|
321
|
+
export function updateWorkspaceAgentDescription(id, description) {
|
|
322
|
+
const trimmed = description == null ? null : description.trim();
|
|
323
|
+
if (trimmed !== null && trimmed.length > 200) {
|
|
324
|
+
throw new Error(`Description must be 200 characters or fewer (got ${trimmed.length})`);
|
|
325
|
+
}
|
|
326
|
+
const stored = trimmed && trimmed.length > 0 ? trimmed : null;
|
|
327
|
+
const db = getDb();
|
|
328
|
+
const result = db
|
|
329
|
+
.prepare('UPDATE workspaces SET agent_description = ?, updated_at = ? WHERE id = ?')
|
|
330
|
+
.run(stored, new Date().toISOString(), id);
|
|
331
|
+
if (result.changes === 0) {
|
|
332
|
+
throw new Error(`Workspace '${id}' not found`);
|
|
333
|
+
}
|
|
334
|
+
emitEphemeral(id, 'workspace:agent-description-updated', { agentDescription: stored });
|
|
335
|
+
return getWorkspace(id);
|
|
336
|
+
}
|
|
273
337
|
/** Update the dev-server status column for a workspace. */
|
|
274
338
|
export function updateDevServerStatus(id, status) {
|
|
275
339
|
const db = getDb();
|
|
@@ -291,6 +355,14 @@ export function deleteWorkspace(id) {
|
|
|
291
355
|
// The DB row is removed via ON DELETE CASCADE, but the timer would
|
|
292
356
|
// otherwise fire and hit an empty workspace.
|
|
293
357
|
wakeupService.cancel(id, 'deleted');
|
|
358
|
+
// Same for any pending quota backoff. Best-effort: failure must not
|
|
359
|
+
// block delete. The DB row is also removed via ON DELETE CASCADE.
|
|
360
|
+
try {
|
|
361
|
+
quotaBackoffService.cancel(id, 'deleted');
|
|
362
|
+
}
|
|
363
|
+
catch (err) {
|
|
364
|
+
console.error('[workspace-service] cancel quota backoff on delete failed:', err);
|
|
365
|
+
}
|
|
294
366
|
// Drop the cached rate_limit.info so memory doesn't leak on workspace
|
|
295
367
|
// churn. The Map has no FK to clean up for it automatically.
|
|
296
368
|
orchestrator.forgetRateLimitInfo(id);
|
|
@@ -383,6 +455,14 @@ export function archiveWorkspace(id) {
|
|
|
383
455
|
}
|
|
384
456
|
// Cancel any pending wakeup — archived workspaces should not wake up.
|
|
385
457
|
wakeupService.cancel(id, 'archived');
|
|
458
|
+
// Cancel any pending quota backoff — archived workspaces should not auto-resume.
|
|
459
|
+
// Best-effort: failure here must not block archive.
|
|
460
|
+
try {
|
|
461
|
+
quotaBackoffService.cancel(id, 'archive');
|
|
462
|
+
}
|
|
463
|
+
catch (err) {
|
|
464
|
+
console.error('[workspace-service] cancel quota backoff on archive failed:', err);
|
|
465
|
+
}
|
|
386
466
|
// Disable auto-loop — archived workspaces should not keep looping.
|
|
387
467
|
// Idempotent: no-op if auto_loop was already 0.
|
|
388
468
|
autoLoopService.disable(id, 'user-action');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loicngr/kobo",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.5",
|
|
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{E as e,F as t,H as n,L as r,M as i,Q as a,U as o,_t as s,bt as c,d as l,f as u,g as d,h as f,l as p,p as m,r as h,rt as g,u as _,v,yt as y}from"./runtime-core.esm-bundler-C3IgBgY5.js";import{U as b,l as x,t as S}from"./QIcon-BJuyqdsT.js";import{c as C,s as w}from"./notifications-OnPq4FrH.js";import{t as T}from"./QBtn-a6jxWjmW.js";import{n as E}from"./vue-i18n-BcfTCFFS.js";import{d as D,g as O,l as k,p as A}from"./index-
|
|
1
|
+
import{E as e,F as t,H as n,L as r,M as i,Q as a,U as o,_t as s,bt as c,d as l,f as u,g as d,h as f,l as p,p as m,r as h,rt as g,u as _,v,yt as y}from"./runtime-core.esm-bundler-C3IgBgY5.js";import{U as b,l as x,t as S}from"./QIcon-BJuyqdsT.js";import{c as C,s as w}from"./notifications-OnPq4FrH.js";import{t as T}from"./QBtn-a6jxWjmW.js";import{n as E}from"./vue-i18n-BcfTCFFS.js";import{d as D,g as O,l as k,p as A}from"./index-DuK38XN5.js";import{t as j}from"./QSpinnerDots-CszPQQ9J.js";import{t as M}from"./QTooltip-fDNzBEfN.js";import{t as N}from"./QExpansionItem-CH1ipL9n.js";import{n as ee,r as te,t as P}from"./render-chat-markdown-TvAqpDih.js";import{n as F}from"./purify.es-BIY760fF.js";import{t as I}from"./documents-kx0vLfSG.js";import{t as L}from"./_plugin-vue_export-helper-Cj6tcsj6.js";function ne(e,t,n=!0){let r=[],i=new Map,a=new Map;for(let n=0;n<e.length;n++){let o=e[n],s=t?.[n];switch(o.kind){case`message:text`:{let e=i.get(o.messageId);if(e)e.text+=o.text,e.streaming=o.streaming;else{let e={type:`text`,messageId:o.messageId,text:o.text,streaming:o.streaming,ts:s};i.set(o.messageId,e),r.push(e)}break}case`message:end`:{let e=i.get(o.messageId);e&&(e.streaming=!1);break}case`message:thinking`:r.push({type:`thinking`,messageId:o.messageId,text:o.text,ts:s});break;case`tool:call`:{let e={type:`tool`,toolCallId:o.toolCallId,name:o.name,input:o.input,ts:s};a.set(o.toolCallId,e),r.push(e);break}case`tool:result`:{let e=a.get(o.toolCallId);e&&(e.result={output:o.output,isError:o.isError});break}case`session:started`:r.push({type:`session`,kind:`started`,detail:{engineSessionId:o.engineSessionId,model:o.model},ts:s});break;case`session:ended`:r.push({type:`session`,kind:`ended`,detail:{reason:o.reason,exitCode:o.exitCode},ts:s});break;case`session:compacted`:r.push({type:`session`,kind:`compacted`,ts:s});break;case`session:brainstorm-complete`:case`session:user-input-requested`:case`message:raw`:case`skills:discovered`:case`usage`:case`rate_limit`:case`subagent:progress`:case`error`:break;default:}}let o=null;for(let e of r)e.type===`text`&&e.streaming&&(o&&(o.streaming=!1),o=e);return o&&!n&&(o.streaming=!1),r}function re(e,t){if(t.length===0)return e;let n=t.map(e=>({type:`user`,content:e.content,sender:e.sender,ts:e.ts})),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}function R(e){switch(e.type){case`user`:return e.sender===`system-prompt`?`system-prompt`:`user`;case`session`:return`session`;default:return`agent`}}function ie(e){let t=[],n=null;for(let r of e){let e=R(r),i=e===`session`||e===`system-prompt`;!n||n.speaker!==e||i?(n={speaker:e,ts:r.ts,items:[r]},t.push(n),i&&(n=null)):n.items.push(r)}return t}var z={class:`text-caption text-grey-6`},B=v({__name:`SessionEventItem`,props:{item:{}},setup(e){let n=e,r=p(()=>{switch(n.item.kind){case`started`:return`session.started`;case`ended`:return`session.ended`;case`compacted`:return`session.compacted`;default:return`session.started`}});return(e,n)=>(t(),m(`span`,z,c(e.$t(r.value)),1))}});function V(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){ae(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 ae(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=H(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 H(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`],W=L(v({__name:`TextMessageItem`,props:{item:{}},setup(e){let n=e,r=I(),i=k(),a=p(()=>{let e=i.selectedWorkspaceId;return e?r.documentsFor(e).map(e=>e.path):[]}),o=p(()=>ee(V(F.parse(n.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 n=t.getAttribute(`data-document-path`),a=i.selectedWorkspaceId;!n||!a||r.openDocumentByPath(a,n)}return(n,r)=>(t(),m(`div`,{class:`markdown-message`,onClick:s},[_(`div`,{innerHTML:o.value},null,8,U),e.item.streaming?(t(),l(x,{key:0,size:`xs`,class:`q-ml-xs`})):u(``,!0)]))}}),[[`__scopeId`,`data-v-1b7bd8ca`]]),oe={key:0,class:`text-caption text-grey-5`,style:{"font-style":`italic`}},G=[`innerHTML`],K={key:1,style:{"white-space":`pre-wrap`}},se=L(v({__name:`ThinkingItem`,props:{item:{}},setup(e){let n=e,r=p(()=>n.item.text.trim().slice(0,100)),i=p(()=>n.item.text.trim().length>0),a=p(()=>n.item.text.trim().length>100),s=p(()=>P(n.item.text));return(n,d)=>i.value?(t(),m(`div`,oe,[a.value?(t(),l(N,{key:0,dense:``,"dense-toggle":``,label:r.value,"header-class":`text-grey-5 text-caption`,style:{"font-style":`italic`}},{default:o(()=>[_(`div`,{class:`q-py-xs markdown-thinking`,innerHTML:s.value},null,8,G)]),_:1},8,[`label`])):(t(),m(`span`,K,c(e.item.text),1))])):u(``,!0)}}),[[`__scopeId`,`data-v-7f45ed94`]]);function ce(e,t){let n=e.split(`
|
|
2
2
|
`),r=t.split(`
|
|
3
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 q(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??``;return{toolName:`Edit`,filePath:e,oldString:t,newString:r,replaceAll:n.replace_all??!1,additions:r?r.split(`
|
|
4
4
|
`).length:0,deletions:t?t.split(`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{F as e,U as t,bt as n,d as r,f as i,g as a,h as o,l as s,rt as c,v as l}from"./runtime-core.esm-bundler-C3IgBgY5.js";import{n as u}from"./vue-i18n-BcfTCFFS.js";import{l as d}from"./index-DuK38XN5.js";import{t as f}from"./QTooltip-fDNzBEfN.js";import{t as p}from"./QChip-ByxK0Tuf.js";var m=l({__name:`AutoLoopChip`,props:{workspace:{default:null}},setup(l){let m=l,{t:h}=u(),g=d(),_=s(()=>m.workspace??g.selectedWorkspace),v=s(()=>_.value?.id??null),y=s(()=>v.value?g.autoLoopStates[v.value]??null:null),b=s(()=>!!y.value?.auto_loop),x=s(()=>!!y.value?.auto_loop_ready),S=s(()=>v.value!==null&&g.selectedWorkspaceId===v.value),C=s(()=>S.value?g.tasks.filter(e=>e.status===`done`).length:y.value?.tasks_done??0),w=s(()=>S.value?g.tasks.length:y.value?.tasks_total??0);return(s,l)=>b.value&&!x.value?(e(),r(p,{key:0,dense:``,square:``,size:`sm`,color:`indigo-4`,"text-color":`white`,icon:`hourglass_top`,class:`q-ml-sm`},{default:t(()=>[o(n(c(h)(`autoLoop.preparing`))+` `,1),a(f,null,{default:t(()=>[o(n(c(h)(`autoLoop.preparingTooltip`)),1)]),_:1})]),_:1})):b.value?(e(),r(p,{key:1,dense:``,square:``,size:`sm`,color:`amber-9`,"text-color":`white`,icon:`autorenew`,class:`q-ml-sm`},{default:t(()=>[o(n(c(h)(`autoLoop.progress`,{done:C.value,total:w.value})),1)]),_:1})):i(``,!0)}});export{m as t};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{E as e,F as t,H as n,L as r,M as ee,N as te,Q as i,T as ne,U as a,_t as re,bt as o,d as s,f as c,g as l,h as u,l as d,p as f,r as ie,rt as p,u as m,v as ae,vt as oe,x as se}from"./runtime-core.esm-bundler-C3IgBgY5.js";import{H as ce,R as le,U as ue,t as h}from"./QIcon-BJuyqdsT.js";import{r as g}from"./use-id-CuaR1RiE.js";import{s as de}from"./notifications-OnPq4FrH.js";import{t as _}from"./QBtn-a6jxWjmW.js";import{n as fe}from"./vue-i18n-BcfTCFFS.js";import{t as v}from"./QInput-Cm5-AGQ4.js";import{a as pe,h as me,l as he,s as ge}from"./index-
|
|
1
|
+
import{E as e,F as t,H as n,L as r,M as ee,N as te,Q as i,T as ne,U as a,_t as re,bt as o,d as s,f as c,g as l,h as u,l as d,p as f,r as ie,rt as p,u as m,v as ae,vt as oe,x as se}from"./runtime-core.esm-bundler-C3IgBgY5.js";import{H as ce,R as le,U as ue,t as h}from"./QIcon-BJuyqdsT.js";import{r as g}from"./use-id-CuaR1RiE.js";import{s as de}from"./notifications-OnPq4FrH.js";import{t as _}from"./QBtn-a6jxWjmW.js";import{n as fe}from"./vue-i18n-BcfTCFFS.js";import{t as v}from"./QInput-Cm5-AGQ4.js";import{a as pe,h as me,l as he,s as ge}from"./index-DuK38XN5.js";import{n as y,t as b}from"./QItemSection-5YpFpPDm.js";import{t as x}from"./QItemLabel-DrTxqTqV.js";import{t as S}from"./QTooltip-fDNzBEfN.js";import{t as _e}from"./QExpansionItem-CH1ipL9n.js";import{t as ve}from"./_plugin-vue_export-helper-Cj6tcsj6.js";import{t as ye}from"./QSpace-CLtL3aPy.js";import{t as be}from"./use-quasar-Sdcq6zzV.js";import{n as C,t as xe}from"./models-BDkLiht9.js";import{t as Se}from"./QPage-DFi3K093.js";import{i as Ce,n as we,r as Te,t as Ee}from"./expand-template-BIPuNAYV.js";var w=`kobo:create-page-prefs`;function De(){try{let e=localStorage.getItem(w);return e===null?void 0:JSON.parse(e)}catch{return}}function T(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Oe(){let e=De();if(!T(e))return{};let t={};return typeof e.projectPath==`string`&&e.projectPath.length>0&&(t.projectPath=e.projectPath),typeof e.autoLoop==`boolean`&&(t.autoLoop=e.autoLoop),t}function ke(e){try{localStorage.setItem(w,JSON.stringify(e))}catch{}}var Ae={class:`create-inner`},je={class:`create-title text-center text-weight-bold q-mb-lg text-grey-3`},Me={class:`create-card rounded-borders`},Ne={class:`card-top-bar row items-center q-px-md q-py-xs`},Pe={class:`model-badge cursor-default row items-center q-gutter-xs`},Fe={class:`text-indigo-3 text-weight-medium text-caption`},Ie={key:0,class:`notion-url-wrap`},Le={key:0,class:`notion-error text-caption q-px-md q-pb-xs text-red-5`},Re={key:1,class:`notion-valid text-caption q-px-md q-pb-xs text-green-4`},ze={key:2,class:`notion-peek-choice q-px-md q-pb-sm`},Be={class:`text-caption text-grey-4 q-mb-sm`},Ve={class:`row q-gutter-sm`},He={class:`peek-card-text`},Ue={class:`peek-card-title`},We={class:`peek-card-desc`},Ge={class:`peek-card-text`},Ke={class:`peek-card-title`},qe={class:`peek-card-desc`},Je={key:0,class:`sentry-url-wrap`},Ye={key:0,class:`sentry-error text-caption q-px-md q-pb-xs text-red-5`},Xe={key:1,class:`sentry-valid text-caption q-px-md q-pb-xs text-red-4`},Ze={class:`card-name-wrap`},Qe={class:`card-textarea-wrap`},$e={class:`manual-hint q-px-md q-py-sm text-caption text-grey-6`},et={class:`q-pa-sm manual-section-body`},tt={class:`row items-center q-gutter-sm q-mb-sm`},nt={class:`col text-caption text-grey-4`},rt={class:`q-pa-sm manual-section-body`},it={class:`row items-center q-gutter-sm q-mb-sm`},at={class:`col text-caption text-grey-4`},ot={class:`card-bottom-bar`},st={class:`row q-col-gutter-xs q-px-xs`},ct={key:0,class:`col-12 col-sm-6 col-md-3`},lt={class:`bottom-select-label row items-center no-wrap`},ut={class:`col-12 col-sm-6 col-md-3`},dt={class:`bottom-select-label row items-center no-wrap`},ft={class:`col-12 col-sm-6 col-md-3`},pt={class:`bottom-select-label row items-center no-wrap`},mt={class:`col-12 col-md-3`},ht={class:`bottom-select-label row items-center no-wrap`},gt={class:`row q-col-gutter-xs q-pa-xs col-12 items-center justify-center`},_t={class:`col-12 col-md-auto`},vt={class:`col-12 col-md-auto`},yt={class:`col-12 col-md-auto`},bt={class:`row q-col-gutter-xs q-px-xs bottom-row-git`},xt={class:`col-12 col-md-4`},St={key:0,class:`col-12 col-md-4`},Ct={class:`bottom-select-label row items-center no-wrap`},wt={key:1,class:`col-12 col-md-4`},Tt={class:`bottom-select-label row items-center no-wrap`},Et={class:`col-12 col-md-4`},Dt={class:`bottom-select-label row items-center no-wrap`},Ot={class:`row justify-center q-px-sm q-py-sm`},kt={class:`create-hint text-center text-body2 q-mt-md text-grey-8`},E=ve(ae({__name:`CreatePage`,setup(ae){let ve=pe(),w=be(),De=he(),T=de(),{t:E}=fe(),At=i([]),jt=i(``),D=i(``),Mt=i(null),O=i(``),k=i(!1),A=i(`claude-opus-4-7`),j=i(`auto`),M=i(``),N=i(null),Nt=i(`feature`),P=i(!1),Pt=i([]),F=i(`claude-code`),Ft=d(()=>Pt.value.find(e=>e.id===F.value)),It=d(()=>Pt.value.map(e=>({value:e.id,label:e.displayName}))),Lt=[{label:`feature/`,value:`feature`},{label:`fix/`,value:`fix`},{label:`hotfix/`,value:`hotfix`},{label:`chore/`,value:`chore`},{label:`refactor/`,value:`refactor`},{label:`docs/`,value:`docs`},{label:`test/`,value:`test`}],Rt=[`plan`,`bypass`,`strict`,`interactive`];function zt(e){let t=e?T.getProjectByPath(e):void 0,n=t?.agentPermissionMode;if(typeof n==`string`&&Rt.includes(n))return n;let r=T.global.defaultPermissionMode;return Rt.includes(r)?r:r===`plan`?`plan`:t?.dangerouslySkipPermissions??T.global.dangerouslySkipPermissions??!0?`bypass`:`interactive`}let I=i(zt(``)),Bt=d(()=>{switch(I.value){case`plan`:return`visibility`;case`bypass`:return`flash_on`;case`strict`:return`lock`;case`interactive`:return`security`}}),Vt=d(()=>(Ft.value?.capabilities.permissionModes??Rt).map(e=>({value:e,label:E(`agentPermissionMode.${e}`),disabled:e===`plan`&&J.value}))),L=i([]),Ht=i([]),Ut=i(!1),Wt=i(!1);function Gt(e,t){t(()=>{Ht.value=e?L.value.filter(t=>t.toLowerCase().includes(e.toLowerCase())):L.value})}let Kt=me();function qt(){return Mt.value?.nativeEl??null}let{showSkills:Jt,selectedSkillIndex:R,groupedDropdown:Yt,flatDropdown:z,fetchSkills:Xt,detectSlashFragment:Zt,replaceFragmentWith:Qt,closeDropdown:$t}=Te(D,qt,{excludeKoboCommands:!0});Xt(),n(D,async()=>{await e(),await Zt()});function en(e){if(e.type===`template`){let t=Kt.templates.find(t=>t.slug===e.name);if(!t)return;Qt(we(t.content,Ee({workspace:null,gitStats:null,sessionName:null}))),$t();return}Qt(`/${e.name} `),$t()}function tn(e){!Jt.value||z.value.length===0||(e.key===`ArrowDown`?(e.preventDefault(),R.value=(R.value+1)%z.value.length):e.key===`ArrowUp`?(e.preventDefault(),R.value=(R.value-1+z.value.length)%z.value.length):e.key===`Enter`&&!e.shiftKey&&!e.ctrlKey&&!e.metaKey?(e.preventDefault(),en(z.value[R.value])):e.key===`Escape`&&(e.preventDefault(),$t()))}let nn=d(()=>xe.map(e=>({label:E(e.i18nLabelKey),value:e.value,description:E(e.i18nDescriptionKey)})));function B(e){let t=e.indexOf(`:`);return t>=0?e.slice(t+1).trim():e}let rn=d(()=>[{label:B(E(`reasoning.auto`)),value:`auto`,description:E(`reasoning.autoDescription`)},{label:B(E(`reasoning.low`)),value:`low`,description:E(`reasoning.lowDescription`)},{label:B(E(`reasoning.medium`)),value:`medium`,description:E(`reasoning.mediumDescription`)},{label:B(E(`reasoning.high`)),value:`high`,description:E(`reasoning.highDescription`)},{label:B(E(`reasoning.xhigh`)),value:`xhigh`,description:E(`reasoning.xhighDescription`)},{label:B(E(`reasoning.max`)),value:`max`,description:E(`reasoning.maxDescription`)}]),V=d(()=>O.value.trim().startsWith(`https://www.notion.so/`)),an=d(()=>/[?&]p=[0-9a-f]{32}(?:[&#]|$)/i.test(O.value)),H=i(`panel`);function on(){let e=O.value.trim();return H.value===`parent`&&an.value?e.replace(/([?&])p=[0-9a-f]{32}(?=[&#]|$)/i,`$1`).replace(/([?&])pm=[a-z]+(?=[&#]|$)/i,`$1`).replace(/[?&]+$/,``).replace(/\?&/,`?`):e}let U=i([]),W=i([]),G=i(``),K=i(``),sn=d(()=>!k.value||!V.value);function cn(){let e=G.value.trim();e&&(U.value.push(e),G.value=``)}function ln(e){U.value.splice(e,1)}function un(){let e=K.value.trim();e&&(W.value.push(e),K.value=``)}function dn(e){W.value.splice(e,1)}function fn(){k.value=!k.value,k.value||(O.value=``)}let q=i(!1),J=i(!1);n(()=>J.value,e=>{e&&I.value!==`bypass`&&(I.value=`bypass`,w.notify({type:`info`,message:E(`agentPermissionMode.autoLoopLocked`),timeout:4e3}))});let Y=i(``),X=d(()=>/\/issues\/\d+/.test(Y.value.trim()));function pn(){q.value=!q.value,q.value||(Y.value=``)}let Z=i(!1),Q=i(null),$=i([]),mn=i(!1);async function hn(){if(!M.value.trim()){$.value=[];return}mn.value=!0;try{$.value=await De.fetchOrphanWorktrees(M.value.trim())}catch{$.value=[]}finally{mn.value=!1}}function gn(){Z.value=!Z.value,Z.value?(k.value=!1,O.value=``,q.value=!1,Y.value=``,P.value=!0,hn()):Q.value=null}n(M,()=>{Q.value=null,Z.value&&hn()}),n(Q,e=>{if(!e)return;let t=$.value.find(t=>t.path===e);t&&(N.value=t.suggestedSourceBranch),P.value=!0});async function _n(e){if(!e.trim()){L.value=[],N.value=null;return}Ut.value=!0;try{let t=await fetch(`/api/git/branches?path=${encodeURIComponent(e.trim())}`);if(!t.ok)throw Error(`HTTP ${t.status}`);let n=await t.json();L.value=n.local??n.branches??[],L.value.length>0&&!N.value&&(N.value=L.value[0]??null)}catch{L.value=[],N.value=null}finally{Ut.value=!1}}function vn(e){let t=T.getProjectByPath(e);t&&(t.defaultSourceBranch&&(N.value=t.defaultSourceBranch),t.defaultModel?A.value=t.defaultModel:T.global.defaultModel&&(A.value=T.global.defaultModel)),I.value=zt(e)}let yn=null;n(M,e=>{yn&&clearTimeout(yn),yn=setTimeout(()=>{N.value=null,_n(e),vn(e)},500)});function bn(e,t){t(()=>{At.value=T.projectPaths.filter(t=>t.toLowerCase().includes(e.toLowerCase()))})}ee(async()=>{await T.fetchSettings();let e=Oe();e.autoLoop===!0&&(J.value=!0),e.projectPath&&T.projectPaths.includes(e.projectPath)&&(M.value=e.projectPath),I.value=zt(M.value);try{let e=await fetch(`/api/engines`);e.ok&&(Pt.value=await e.json())}catch{}}),te(()=>{yn&&clearTimeout(yn)});function xn(e){return e.normalize(`NFD`).replace(/[\u0300-\u036f]/g,``).toLowerCase().replace(/[^a-z0-9\s-]/g,``).trim().replace(/\s+/g,`-`).replace(/-+/g,`-`).substring(0,50)}function Sn(){return jt.value.trim()?jt.value.trim().substring(0,80):!k.value&&!q.value&&D.value.trim()&&(D.value.trim().split(`
|
|
2
2
|
`)[0]??``).substring(0,80)||`workspace`}function Cn(e){let t=(e.split(`/`).pop()??``).split(`-`);t.length>1&&/^[0-9a-f]{12,}$/i.test(t[t.length-1])&&t.pop();let n=t.join(`-`).toLowerCase(),r=n.match(/tk-(\d+)/);if(r){let e=`TK-${r[1]}`,t=n.replace(/tk-\d+/i,``).replace(/-+/g,`-`).replace(/^-|-$/g,``).substring(0,40);return t?`${e}--${t}`:e}return n.substring(0,50)||`task-${Date.now()}`}function wn(){return k.value&&!V.value?E(`createPage.validationNotionUrl`):q.value&&!X.value?E(`createPage.sentryValidation`):!k.value&&!q.value&&!D.value.trim()?E(`createPage.validationDescription`):!k.value&&!q.value&&(!Sn()||Sn()===`workspace`)&&!jt.value.trim()&&!D.value.trim()?E(`createPage.validationName`):M.value.trim()?N.value?null:E(`createPage.validationBranch`):E(`createPage.validationPath`)}async function Tn(){let e=wn();if(e){w.notify({type:`negative`,message:e,position:`top`});return}if(Z.value&&!Q.value){w.notify({type:`negative`,message:E(`createPage.pickWorktreeRequired`),position:`top`});return}Wt.value=!0;try{let e=Sn(),t;t=k.value&&V.value?Cn(on()):e===`workspace`?`task-${Date.now()}`:xn(e);let n=`${Nt.value}/${t}`,r={name:e,projectPath:M.value.trim(),sourceBranch:N.value,...Z.value&&Q.value?{worktreePath:Q.value,skipSetupScript:!0}:{workingBranch:n},engine:F.value,model:A.value,reasoningEffort:j.value,...k.value&&V.value?{notionUrl:on()}:{},...q.value&&X.value?{sentryUrl:Y.value.trim()}:{},...sn.value&&U.value.length>0?{tasks:U.value}:{},...sn.value&&W.value.length>0?{acceptanceCriteria:W.value}:{},...P.value&&!Z.value?{skipSetupScript:!0}:{},...D.value.trim()?{description:D.value.trim()}:{},...J.value?{autoLoop:!0}:{},agentPermissionMode:J.value&&I.value===`plan`?`bypass`:I.value},ee=await De.createWorkspace(r);ke({projectPath:M.value.trim(),autoLoop:J.value}),ge().subscribe(ee.id),De.selectWorkspace(ee.id),ve.push({name:`workspace`,params:{id:ee.id}})}catch{w.notify({type:`negative`,message:E(`createPage.errorCreating`),position:`top`})}finally{Wt.value=!1}}return(e,n)=>(t(),s(Se,{class:`create-page flex flex-center column`},{default:a(()=>[m(`div`,Ae,[m(`div`,je,o(e.$t(`createPage.title`)),1),m(`div`,Me,[m(`div`,Ne,[m(`span`,Pe,[l(h,{name:`auto_awesome`,size:`14px`,color:`indigo-4`}),m(`span`,Fe,o(Ft.value?.displayName??e.$t(`createPage.claudeCode`)),1)]),l(ye),l(_,{flat:``,dense:``,"no-caps":``,size:`sm`,color:k.value?`green-4`:`grey-5`,class:`notion-toggle-btn text-caption rounded-borders`,disable:Z.value,onClick:fn},{default:a(()=>[l(h,{name:`description`,size:`14px`,class:`q-mr-xs`}),u(` `+o(k.value?e.$t(`createPage.notionEnabled`):e.$t(`createPage.importNotion`)),1)]),_:1},8,[`color`,`disable`]),l(_,{flat:``,dense:``,"no-caps":``,size:`sm`,color:q.value?`red-4`:`grey-5`,class:`sentry-toggle-btn text-caption rounded-borders q-ml-sm`,disable:Z.value,onClick:pn},{default:a(()=>[l(h,{name:`bug_report`,size:`14px`,class:`q-mr-xs`}),u(` `+o(q.value?e.$t(`createPage.sentryEnabled`):e.$t(`createPage.importSentry`)),1)]),_:1},8,[`color`,`disable`])]),l(g,{color:`grey-9`}),l(le,{name:`slide`},{default:a(()=>[k.value?(t(),f(`div`,Ie,[l(v,{modelValue:O.value,"onUpdate:modelValue":n[0]||=e=>O.value=e,borderless:``,dense:``,placeholder:e.$t(`createPage.notionPlaceholder`),class:`notion-url-input`,"input-class":`notion-url-input-inner`},{prepend:a(()=>[l(h,{name:`link`,size:`16px`,color:V.value?`green-4`:`grey-6`},null,8,[`color`])]),_:1},8,[`modelValue`,`placeholder`]),O.value.trim()&&!V.value?(t(),f(`div`,Le,o(e.$t(`createPage.notionValidation`)),1)):c(``,!0),V.value?(t(),f(`div`,Re,o(e.$t(`createPage.notionAutoExtract`)),1)):c(``,!0),V.value&&an.value?(t(),f(`div`,ze,[m(`div`,Be,[l(h,{name:`info`,size:`14px`,color:`indigo-4`,class:`q-mr-xs`}),u(` `+o(e.$t(`createPage.notionPanelChoiceLabel`)),1)]),m(`div`,Ve,[m(`button`,{type:`button`,class:re([`peek-card col`,{"peek-card--active":H.value===`panel`}]),onClick:n[1]||=e=>H.value=`panel`},[l(h,{name:`article`,size:`22px`,class:`peek-card-icon`}),m(`div`,He,[m(`div`,Ue,o(e.$t(`createPage.notionPanelOption`)),1),m(`div`,We,o(e.$t(`createPage.notionPanelOptionDesc`)),1)]),H.value===`panel`?(t(),s(h,{key:0,name:`check_circle`,size:`18px`,color:`indigo-4`,class:`peek-card-check`})):c(``,!0)],2),m(`button`,{type:`button`,class:re([`peek-card col`,{"peek-card--active":H.value===`parent`}]),onClick:n[2]||=e=>H.value=`parent`},[l(h,{name:`folder_open`,size:`22px`,class:`peek-card-icon`}),m(`div`,Ge,[m(`div`,Ke,o(e.$t(`createPage.notionParentOption`)),1),m(`div`,qe,o(e.$t(`createPage.notionParentOptionDesc`)),1)]),H.value===`parent`?(t(),s(h,{key:0,name:`check_circle`,size:`18px`,color:`indigo-4`,class:`peek-card-check`})):c(``,!0)],2)])])):c(``,!0)])):c(``,!0)]),_:1}),k.value?(t(),s(g,{key:0,color:`grey-9`})):c(``,!0),l(le,{name:`slide`},{default:a(()=>[q.value?(t(),f(`div`,Je,[l(v,{modelValue:Y.value,"onUpdate:modelValue":n[3]||=e=>Y.value=e,borderless:``,dense:``,placeholder:e.$t(`createPage.sentryPlaceholder`),class:`sentry-url-input`,"input-class":`sentry-url-input-inner`},{prepend:a(()=>[l(h,{name:`link`,size:`16px`,color:X.value?`red-4`:`grey-6`},null,8,[`color`])]),_:1},8,[`modelValue`,`placeholder`]),Y.value.trim()&&!X.value?(t(),f(`div`,Ye,o(e.$t(`createPage.sentryValidation`)),1)):c(``,!0),X.value?(t(),f(`div`,Xe,o(e.$t(`createPage.sentryAutoExtract`)),1)):c(``,!0)])):c(``,!0)]),_:1}),q.value?(t(),s(g,{key:1,color:`grey-9`})):c(``,!0),m(`div`,Ze,[l(v,{modelValue:jt.value,"onUpdate:modelValue":n[4]||=e=>jt.value=e,borderless:``,dense:``,placeholder:k.value&&V.value?e.$t(`createPage.workspaceName`):e.$t(`createPage.workspaceNamePlaceholder`),class:`name-input`,"input-class":`name-input-inner`},null,8,[`modelValue`,`placeholder`])]),l(g,{color:`grey-9`}),m(`div`,Qe,[l(v,{ref_key:`descriptionRef`,ref:Mt,modelValue:D.value,"onUpdate:modelValue":n[5]||=e=>D.value=e,type:`textarea`,borderless:``,autogrow:``,rows:3,placeholder:k.value?e.$t(`createPage.instructions`):e.$t(`createPage.instructionsPlaceholder`),class:`create-textarea`,"input-class":`create-textarea-input`,onKeydown:[tn,ce(ue(Tn,[`ctrl`]),[`enter`]),ce(ue(Tn,[`meta`]),[`enter`])]},null,8,[`modelValue`,`placeholder`,`onKeydown`]),p(Jt)&&p(z).length>0?(t(),s(Ce,{key:0,class:`create-slash-popup`,"grouped-dropdown":p(Yt),"flat-dropdown":p(z),"selected-index":p(R),onSelect:en},null,8,[`grouped-dropdown`,`flat-dropdown`,`selected-index`])):c(``,!0)]),l(g,{color:`grey-9`}),sn.value?(t(),f(ie,{key:2},[m(`div`,$e,o(e.$t(`createPage.manualHint`)),1),l(_e,{dark:``,dense:``,label:e.$t(`createPage.tasks`,{count:U.value.length}),"header-class":`text-grey-4 manual-expansion-header`,class:`manual-expansion q-mx-sm`},{default:a(()=>[m(`div`,et,[m(`div`,tt,[l(v,{modelValue:G.value,"onUpdate:modelValue":n[6]||=e=>G.value=e,dark:``,dense:``,borderless:``,placeholder:e.$t(`createPage.addTask`),class:`col manual-input`,"input-class":`manual-input-inner`,onKeydown:ce(ue(cn,[`prevent`]),[`enter`])},null,8,[`modelValue`,`placeholder`,`onKeydown`]),l(_,{flat:``,dense:``,round:``,icon:`add`,color:`indigo-4`,disable:!G.value.trim(),onClick:cn},{default:a(()=>[l(S,null,{default:a(()=>[u(o(e.$t(`tooltip.addTask`)),1)]),_:1})]),_:1},8,[`disable`])]),(t(!0),f(ie,null,r(U.value,(n,r)=>(t(),f(`div`,{key:`task-${r}`,class:`row items-center q-py-xs manual-item`},[m(`span`,nt,o(n),1),l(_,{flat:``,dense:``,round:``,icon:`close`,size:`xs`,color:`grey-6`,onClick:e=>ln(r)},{default:a(()=>[l(S,null,{default:a(()=>[u(o(e.$t(`tooltip.removeTask`)),1)]),_:1})]),_:1},8,[`onClick`])]))),128))])]),_:1},8,[`label`]),l(_e,{dark:``,dense:``,label:e.$t(`createPage.acceptanceCriteria`,{count:W.value.length}),"header-class":`text-grey-4 manual-expansion-header`,class:`manual-expansion q-mx-sm q-mb-sm`},{default:a(()=>[m(`div`,rt,[m(`div`,it,[l(v,{modelValue:K.value,"onUpdate:modelValue":n[7]||=e=>K.value=e,dark:``,dense:``,borderless:``,placeholder:e.$t(`createPage.addCriterion`),class:`col manual-input`,"input-class":`manual-input-inner`,onKeydown:ce(ue(un,[`prevent`]),[`enter`])},null,8,[`modelValue`,`placeholder`,`onKeydown`]),l(_,{flat:``,dense:``,round:``,icon:`add`,color:`indigo-4`,disable:!K.value.trim(),onClick:un},{default:a(()=>[l(S,null,{default:a(()=>[u(o(e.$t(`tooltip.addCriterion`)),1)]),_:1})]),_:1},8,[`disable`])]),(t(!0),f(ie,null,r(W.value,(n,r)=>(t(),f(`div`,{key:`crit-${r}`,class:`row items-center q-py-xs manual-item`},[m(`span`,at,o(n),1),l(_,{flat:``,dense:``,round:``,icon:`close`,size:`xs`,color:`grey-6`,onClick:e=>dn(r)},{default:a(()=>[l(S,null,{default:a(()=>[u(o(e.$t(`tooltip.removeCriterion`)),1)]),_:1})]),_:1},8,[`onClick`])]))),128))])]),_:1},8,[`label`]),l(g,{color:`grey-9`})],64)):c(``,!0),m(`div`,ot,[m(`div`,st,[It.value.length>0?(t(),f(`div`,ct,[l(C,{modelValue:F.value,"onUpdate:modelValue":n[8]||=e=>F.value=e,options:It.value,dense:``,borderless:``,class:`bottom-select rounded-borders`,"hide-dropdown-icon":``,"emit-value":``,"map-options":``,"option-value":`value`,"option-label":`label`},{selected:a(()=>[m(`span`,lt,[l(h,{name:`hub`,size:`12px`,color:`grey-5`,class:`q-mr-xs`}),u(` `+o(It.value.find(e=>e.value===F.value)?.label??F.value)+` `,1),l(h,{name:`expand_more`,size:`12px`,color:`grey-5`})])]),default:a(()=>[l(S,null,{default:a(()=>[u(o(e.$t(`engine.select`)),1)]),_:1})]),_:1},8,[`modelValue`,`options`])])):c(``,!0),m(`div`,ut,[l(C,{modelValue:A.value,"onUpdate:modelValue":n[9]||=e=>A.value=e,options:nn.value,dense:``,borderless:``,class:`bottom-select rounded-borders model-select`,"hide-dropdown-icon":``,"emit-value":``,"map-options":``,"option-value":`value`,"option-label":`label`},{selected:a(()=>[m(`span`,dt,[u(o(nn.value.find(e=>e.value===A.value)?.label??A.value)+` `,1),l(h,{name:`expand_more`,size:`12px`,color:`grey-5`})])]),option:a(({opt:e,itemProps:t})=>[l(y,ne(t,{class:`model-option`}),{default:a(()=>[l(b,null,{default:a(()=>[l(x,{class:`text-white`},{default:a(()=>[u(o(e.label),1)]),_:2},1024),l(x,{caption:``,class:`text-grey-5`},{default:a(()=>[u(o(e.description),1)]),_:2},1024)]),_:2},1024)]),_:2},1040)]),_:1},8,[`modelValue`,`options`])]),m(`div`,ft,[l(C,{modelValue:j.value,"onUpdate:modelValue":n[10]||=e=>j.value=e,options:rn.value,dense:``,borderless:``,class:`bottom-select rounded-borders`,"hide-dropdown-icon":``,"emit-value":``,"map-options":``,"option-value":`value`,"option-label":`label`},{selected:a(()=>[m(`span`,pt,[l(h,{name:`psychology`,size:`12px`,color:`grey-5`,class:`q-mr-xs`}),u(` `+o(rn.value.find(e=>e.value===j.value)?.label??j.value)+` `,1),l(h,{name:`expand_more`,size:`12px`,color:`grey-5`})])]),option:a(({opt:e,itemProps:t})=>[l(y,oe(se(t)),{default:a(()=>[l(b,null,{default:a(()=>[l(x,{class:`text-white`},{default:a(()=>[u(o(e.label),1)]),_:2},1024),l(x,{caption:``,class:`text-grey-5`},{default:a(()=>[u(o(e.description),1)]),_:2},1024)]),_:2},1024)]),_:2},1040)]),_:1},8,[`modelValue`,`options`])]),m(`div`,mt,[l(C,{modelValue:I.value,"onUpdate:modelValue":n[11]||=e=>I.value=e,options:Vt.value,disable:J.value,dense:``,borderless:``,class:`bottom-select rounded-borders`,"hide-dropdown-icon":``,"emit-value":``,"map-options":``,"option-value":`value`,"option-label":`label`,"option-disable":e=>e.disabled===!0},{selected:a(()=>[m(`span`,ht,[l(h,{name:Bt.value,size:`12px`,color:`amber-6`,class:`q-mr-xs`},null,8,[`name`]),u(` `+o(Vt.value.find(e=>e.value===I.value)?.label??I.value)+` `,1),J.value?c(``,!0):(t(),s(h,{key:0,name:`expand_more`,size:`12px`,color:`grey-5`}))])]),default:a(()=>[l(S,null,{default:a(()=>[u(o(J.value?e.$t(`agentPermissionMode.autoLoopLocked`):e.$t(`agentPermissionMode.tooltip`)),1)]),_:1})]),_:1},8,[`modelValue`,`options`,`disable`,`option-disable`])])]),m(`div`,gt,[m(`div`,_t,[l(_,{flat:``,dense:``,size:`sm`,"no-caps":``,icon:J.value?`autorenew`:`sync_disabled`,color:J.value?`amber-4`:`grey-5`,label:e.$t(`autoLoop.startInMode`),class:`skip-setup-btn`,onClick:n[12]||=e=>J.value=!J.value},{default:a(()=>[l(S,null,{default:a(()=>[u(o(e.$t(`autoLoop.startInMode`)),1)]),_:1})]),_:1},8,[`icon`,`color`,`label`])]),m(`div`,vt,[l(_,{flat:``,dense:``,size:`sm`,"no-caps":``,icon:P.value?`play_disabled`:`play_circle`,color:P.value?`orange-4`:`grey-5`,label:e.$t(`createPage.skipSetupScript`),class:`skip-setup-btn`,onClick:n[13]||=e=>P.value=!P.value},{default:a(()=>[l(S,null,{default:a(()=>[u(o(e.$t(`createPage.skipSetupScript`)),1)]),_:1})]),_:1},8,[`icon`,`color`,`label`])]),m(`div`,yt,[l(_,{flat:``,dense:``,size:`sm`,"no-caps":``,icon:`folder_open`,color:Z.value?`cyan-4`:`grey-5`,label:Z.value?e.$t(`createPage.attachWorktreeEnabled`):e.$t(`createPage.attachWorktreeToggle`),class:`skip-setup-btn`,onClick:gn},null,8,[`color`,`label`])])]),m(`div`,bt,[m(`div`,xt,[l(C,{modelValue:M.value,"onUpdate:modelValue":n[14]||=e=>M.value=e,options:At.value,dense:``,borderless:``,"use-input":``,"fill-input":``,"hide-selected":``,"input-debounce":`0`,"new-value-mode":`add`,class:`bottom-select rounded-borders repo-select`,"hide-dropdown-icon":``,"input-class":M.value?``:`repo-input-empty`,placeholder:e.$t(`createPage.projectPath`),behavior:p(T).projectPaths.length>0?`menu`:`dialog`,onFilter:bn,onInputValue:n[15]||=e=>{M.value=e}},{prepend:a(()=>[l(h,{name:`folder`,size:`14px`,color:`grey-5`})]),"no-option":a(()=>[l(y,null,{default:a(()=>[l(b,{class:`text-grey-6 text-caption`},{default:a(()=>[u(o(e.$t(`createPage.enterPath`)),1)]),_:1})]),_:1})]),_:1},8,[`modelValue`,`options`,`input-class`,`placeholder`,`behavior`])]),Z.value?c(``,!0):(t(),f(`div`,St,[l(C,{modelValue:Nt.value,"onUpdate:modelValue":n[16]||=e=>Nt.value=e,options:Lt,"emit-value":``,"map-options":``,dense:``,borderless:``,class:`bottom-select rounded-borders branch-type-select`,"hide-dropdown-icon":``},{selected:a(()=>[m(`span`,Ct,[l(h,{name:`account_tree`,size:`12px`,color:`grey-5`,class:`q-mr-xs`}),u(` `+o(Nt.value)+`/ `,1),l(h,{name:`expand_more`,size:`12px`,color:`grey-5`})])]),default:a(()=>[l(S,null,{default:a(()=>[u(o(e.$t(`createPage.branchType`)),1)]),_:1})]),_:1},8,[`modelValue`])])),Z.value?(t(),f(`div`,wt,[l(C,{modelValue:Q.value,"onUpdate:modelValue":n[17]||=e=>Q.value=e,options:$.value,"option-label":`branch`,"option-value":`path`,"emit-value":``,"map-options":``,"use-input":``,dense:``,borderless:``,class:`bottom-select rounded-borders worktree-select`,"hide-dropdown-icon":``,loading:mn.value,disable:!M.value.trim()||mn.value},{selected:a(()=>[m(`span`,Tt,[l(h,{name:`folder_open`,size:`12px`,color:`cyan-5`,class:`q-mr-xs`}),u(` `+o(Q.value?$.value.find(e=>e.path===Q.value)?.branch??Q.value:e.$t(`createPage.worktreePickerLabel`))+` `,1),l(h,{name:`expand_more`,size:`12px`,color:`grey-5`})])]),option:a(e=>[l(y,oe(se(e.itemProps)),{default:a(()=>[l(b,null,{default:a(()=>[l(x,null,{default:a(()=>[u(o(e.opt.branch),1)]),_:2},1024),l(x,{caption:``},{default:a(()=>[u(o(e.opt.path),1)]),_:2},1024)]),_:2},1024)]),_:2},1040)]),"no-option":a(()=>[l(y,null,{default:a(()=>[l(b,{class:`text-grey-6 text-caption`},{default:a(()=>[u(o(M.value.trim()?e.$t(`createPage.noOrphanWorktrees`):e.$t(`createPage.enterPath`)),1)]),_:1})]),_:1})]),_:1},8,[`modelValue`,`options`,`loading`,`disable`])])):c(``,!0),m(`div`,Et,[l(C,{modelValue:N.value,"onUpdate:modelValue":n[18]||=e=>N.value=e,options:Ht.value,dense:``,borderless:``,class:`bottom-select rounded-borders branch-select`,"hide-dropdown-icon":``,"use-input":``,"input-debounce":`0`,loading:Ut.value,disable:!M.value.trim()||Ut.value,onFilter:Gt},{selected:a(()=>[m(`span`,Dt,[l(h,{name:`call_split`,size:`12px`,color:`grey-5`,class:`q-mr-xs`}),u(` `+o(N.value??e.$t(`createPage.branch`))+` `,1),l(h,{name:`expand_more`,size:`12px`,color:`grey-5`})])]),"no-option":a(()=>[l(y,null,{default:a(()=>[l(b,{class:`text-grey-6 text-caption`},{default:a(()=>[u(o(M.value.trim()?e.$t(`createPage.noBranches`):e.$t(`createPage.enterPath`)),1)]),_:1})]),_:1})]),_:1},8,[`modelValue`,`options`,`loading`,`disable`])])])]),m(`div`,Ot,[l(_,{label:e.$t(`createPage.create`),"no-caps":``,unelevated:``,class:`create-btn text-weight-bold rounded-borders`,loading:Wt.value,disable:Wt.value||Z.value&&!Q.value,onClick:Tn},null,8,[`label`,`loading`,`disable`])])]),m(`div`,kt,o(k.value?e.$t(`createPage.notionExtractHint`):e.$t(`createPage.notionImportHint`)),1)])]),_:1}))}}),[[`__scopeId`,`data-v-79b6a61a`]]);export{E as default};
|