@shawnstack/quickforge 1.6.12 → 1.7.0
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 +1 -1
- package/dist/assets/AgentProfilesPage-DQ8URegq.js +1 -0
- package/dist/assets/ChatPanelHost-vi1tDqlJ.js +291 -0
- package/dist/assets/{PluginsPage-qXTjDrpd.js → PluginsPage-B8bHMwfv.js} +1 -1
- package/dist/assets/ScheduledTasksPage-AaUz5el2.js +2 -0
- package/dist/assets/{SettingsWorkspacePage-CnFDmGvW.js → SettingsWorkspacePage-BOP9CtoI.js} +112 -105
- package/dist/assets/{SharedConversationPage-CkQ-XwYS.js → SharedConversationPage-9A4L9UcQ.js} +1 -1
- package/dist/assets/TerminalDock-DQOXpaDe.js +2 -0
- package/dist/assets/WorkspaceInspector-DaM7TJHb.js +13 -0
- package/dist/assets/icons-ko_i0WpN.js +1 -0
- package/dist/assets/index-B2eauKpo.css +3 -0
- package/dist/assets/index-B8Awq5FV.js +63 -0
- package/dist/assets/{mcp-servers-dialog-CsRQxx7A.js → mcp-servers-dialog-XE4K8PCO.js} +2 -2
- package/dist/assets/{monaco-CdOyGlWD.js → monaco-C13M09wD.js} +1 -1
- package/dist/assets/{react-vendor-VqdnQHHS.js → react-vendor-2RKYr-A4.js} +1 -1
- package/dist/assets/{skills-dialog-CmVLi_h6.js → skills-dialog-MpoxntiV.js} +1 -1
- package/dist/index.html +6 -6
- package/dist/licenses/material-icon-theme.txt +8 -0
- package/package.json +1 -1
- package/server/agent-manager.mjs +182 -39
- package/server/auto-archive.mjs +137 -0
- package/server/auto-compaction.mjs +15 -18
- package/server/conversation-compaction.mjs +69 -3
- package/server/custom-commands.mjs +17 -0
- package/server/image-generation.mjs +130 -0
- package/server/index.mjs +12 -1
- package/server/provider-config.mjs +78 -0
- package/server/routes/agent.mjs +9 -0
- package/server/routes/session-assets.mjs +49 -0
- package/server/routes/shared-conversation.mjs +10 -0
- package/server/routes/storage.mjs +4 -0
- package/server/routes/tools.mjs +1 -1
- package/server/routes/workspace.mjs +5 -2
- package/server/session-assets.mjs +134 -0
- package/server/session-persistence-lock.mjs +9 -0
- package/server/storage.mjs +24 -1
- package/server/tools/definitions.mjs +10 -0
- package/server/tools/index.mjs +2 -0
- package/dist/assets/AgentProfilesPage-CooGLweI.js +0 -1
- package/dist/assets/ChatPanelHost-TEbv0lnL.js +0 -260
- package/dist/assets/ScheduledTasksPage-BBor8Yb2.js +0 -2
- package/dist/assets/TerminalDock-CvqBBMPg.js +0 -2
- package/dist/assets/WorkspaceInspector-6JGc6VGb.js +0 -13
- package/dist/assets/icons-C7j5jdKo.js +0 -1
- package/dist/assets/index-DxNzlPmc.js +0 -63
- package/dist/assets/index-HVxfqXfB.css +0 -3
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { randomUUID } from 'node:crypto'
|
|
4
|
+
import { storageDir } from './storage.mjs'
|
|
5
|
+
|
|
6
|
+
export const MAX_SESSION_IMAGE_BYTES = 25 * 1024 * 1024
|
|
7
|
+
|
|
8
|
+
const MIME_TYPES = new Map([
|
|
9
|
+
['image/png', 'png'],
|
|
10
|
+
['image/jpeg', 'jpg'],
|
|
11
|
+
['image/webp', 'webp'],
|
|
12
|
+
['image/gif', 'gif'],
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
const SAFE_SEGMENT_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/
|
|
16
|
+
const ASSET_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\.(?:png|jpg|webp|gif)$/i
|
|
17
|
+
|
|
18
|
+
function requestError(message, statusCode = 400) {
|
|
19
|
+
const error = new Error(message)
|
|
20
|
+
error.statusCode = statusCode
|
|
21
|
+
return error
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function assertSafeSegment(value, label) {
|
|
25
|
+
if (typeof value !== 'string' || !SAFE_SEGMENT_RE.test(value)) {
|
|
26
|
+
throw requestError(`Invalid ${label}`)
|
|
27
|
+
}
|
|
28
|
+
return value
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function normalizeBucket(bucket) {
|
|
32
|
+
if (!bucket || typeof bucket !== 'object' || Array.isArray(bucket)) {
|
|
33
|
+
throw requestError('Invalid session asset scope')
|
|
34
|
+
}
|
|
35
|
+
if (bucket.scope === 'global') return { scope: 'global' }
|
|
36
|
+
if (bucket.scope === 'project') {
|
|
37
|
+
return {
|
|
38
|
+
scope: 'project',
|
|
39
|
+
projectId: assertSafeSegment(bucket.projectId, 'projectId'),
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
throw requestError('Invalid session asset scope')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function assetsRoot(bucket) {
|
|
46
|
+
if (bucket.scope === 'project') {
|
|
47
|
+
return path.join(storageDir, 'conversations', 'projects', bucket.projectId, 'assets')
|
|
48
|
+
}
|
|
49
|
+
return path.join(storageDir, 'conversations', 'global', 'assets')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function sessionAssetsDir(bucket, sessionId) {
|
|
53
|
+
return path.join(assetsRoot(normalizeBucket(bucket)), assertSafeSegment(sessionId, 'sessionId'))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function normalizeMimeType(mimeType) {
|
|
57
|
+
const value = typeof mimeType === 'string' ? mimeType.trim().toLowerCase() : ''
|
|
58
|
+
const extension = MIME_TYPES.get(value)
|
|
59
|
+
if (!extension) throw requestError(`Unsupported image MIME type: ${mimeType || ''}`)
|
|
60
|
+
return { mimeType: value, extension }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function decodeBase64(value) {
|
|
64
|
+
const normalized = typeof value === 'string' ? value.replace(/\s+/g, '') : ''
|
|
65
|
+
if (!normalized || normalized.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(normalized)) {
|
|
66
|
+
throw requestError('Invalid base64 image data')
|
|
67
|
+
}
|
|
68
|
+
const buffer = Buffer.from(normalized, 'base64')
|
|
69
|
+
if (buffer.toString('base64') !== normalized) throw requestError('Invalid base64 image data')
|
|
70
|
+
return buffer
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function normalizeImageData(data) {
|
|
74
|
+
if (Buffer.isBuffer(data)) return data
|
|
75
|
+
if (data instanceof Uint8Array) return Buffer.from(data)
|
|
76
|
+
return decodeBase64(data)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function mimeTypeFromAssetId(assetId) {
|
|
80
|
+
const extension = path.extname(assetId).slice(1).toLowerCase()
|
|
81
|
+
for (const [mimeType, candidate] of MIME_TYPES) {
|
|
82
|
+
if (candidate === extension) return mimeType
|
|
83
|
+
}
|
|
84
|
+
throw requestError('Invalid assetId')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function assetMetadata(assetId, mimeType, size) {
|
|
88
|
+
return { assetId, mimeType, size }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function validateSessionAssetId(assetId) {
|
|
92
|
+
if (typeof assetId !== 'string' || !ASSET_ID_RE.test(assetId)) {
|
|
93
|
+
throw requestError('Invalid assetId')
|
|
94
|
+
}
|
|
95
|
+
return assetId
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function writeSessionAsset(bucket, sessionId, image) {
|
|
99
|
+
const dir = sessionAssetsDir(bucket, sessionId)
|
|
100
|
+
const { mimeType, extension } = normalizeMimeType(image?.mimeType)
|
|
101
|
+
const data = normalizeImageData(image?.data)
|
|
102
|
+
if (data.byteLength > MAX_SESSION_IMAGE_BYTES) {
|
|
103
|
+
throw requestError(`Image exceeds the ${MAX_SESSION_IMAGE_BYTES} byte limit`, 413)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
await fs.mkdir(dir, { recursive: true })
|
|
107
|
+
const assetId = `${randomUUID()}.${extension}`
|
|
108
|
+
await fs.writeFile(path.join(dir, assetId), data, { flag: 'wx' })
|
|
109
|
+
return assetMetadata(assetId, mimeType, data.byteLength)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function readSessionAsset(bucket, sessionId, assetId) {
|
|
113
|
+
const dir = sessionAssetsDir(bucket, sessionId)
|
|
114
|
+
const safeAssetId = validateSessionAssetId(assetId)
|
|
115
|
+
const data = await fs.readFile(path.join(dir, safeAssetId))
|
|
116
|
+
if (data.byteLength > MAX_SESSION_IMAGE_BYTES) {
|
|
117
|
+
throw requestError('Stored image exceeds the allowed size', 413)
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
...assetMetadata(safeAssetId, mimeTypeFromAssetId(safeAssetId), data.byteLength),
|
|
121
|
+
data,
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function deleteSessionAsset(bucket, sessionId, assetId) {
|
|
126
|
+
const dir = sessionAssetsDir(bucket, sessionId)
|
|
127
|
+
const safeAssetId = validateSessionAssetId(assetId)
|
|
128
|
+
await fs.rm(path.join(dir, safeAssetId), { force: true })
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function deleteSessionAssets(bucket, sessionId) {
|
|
132
|
+
const dir = sessionAssetsDir(bucket, sessionId)
|
|
133
|
+
await fs.rm(dir, { recursive: true, force: true })
|
|
134
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
let persistenceQueue = Promise.resolve()
|
|
2
|
+
|
|
3
|
+
export function withSessionPersistenceLock(operation) {
|
|
4
|
+
const result = persistenceQueue
|
|
5
|
+
.catch(() => undefined)
|
|
6
|
+
.then(operation)
|
|
7
|
+
persistenceQueue = result.then(() => undefined, () => undefined)
|
|
8
|
+
return result
|
|
9
|
+
}
|
package/server/storage.mjs
CHANGED
|
@@ -517,7 +517,12 @@ async function writeSessionValues(data) {
|
|
|
517
517
|
existingFiles.map(async (file) => {
|
|
518
518
|
const sessionId = path.basename(file, '.json')
|
|
519
519
|
if (!nextIds.has(sessionId)) {
|
|
520
|
+
const bucket = sessionBucketIndex.get(sessionId) || await findSessionBucketByDataFile(sessionId)
|
|
520
521
|
await fs.rm(file, { force: true })
|
|
522
|
+
if (bucket) {
|
|
523
|
+
const { deleteSessionAssets } = await import('./session-assets.mjs')
|
|
524
|
+
await deleteSessionAssets(bucket, sessionId)
|
|
525
|
+
}
|
|
521
526
|
sessionBucketIndex.delete(sessionId)
|
|
522
527
|
}
|
|
523
528
|
}),
|
|
@@ -574,7 +579,12 @@ export async function findSessionBucket(sessionId) {
|
|
|
574
579
|
await ensureStorage()
|
|
575
580
|
await rebuildBucketIndex()
|
|
576
581
|
}
|
|
577
|
-
|
|
582
|
+
const indexed = sessionBucketIndex.get(sessionId)
|
|
583
|
+
if (indexed) return indexed
|
|
584
|
+
|
|
585
|
+
const recovered = await findSessionBucketByDataFile(sessionId)
|
|
586
|
+
if (recovered) sessionBucketIndex.set(sessionId, recovered)
|
|
587
|
+
return recovered
|
|
578
588
|
}
|
|
579
589
|
|
|
580
590
|
export async function readSessionValue(sessionId) {
|
|
@@ -594,11 +604,24 @@ export async function writeSessionValue(sessionId, value) {
|
|
|
594
604
|
})
|
|
595
605
|
}
|
|
596
606
|
|
|
607
|
+
export async function atomicSessionValueUpdate(sessionId, updateFn) {
|
|
608
|
+
return enqueueWrite('sessions', async () => {
|
|
609
|
+
await ensureStorage()
|
|
610
|
+
const current = await readSessionValue(sessionId)
|
|
611
|
+
if (!current) return null
|
|
612
|
+
const updated = updateFn(current)
|
|
613
|
+
await writeSessionValueFile(sessionId, updated)
|
|
614
|
+
return updated
|
|
615
|
+
})
|
|
616
|
+
}
|
|
617
|
+
|
|
597
618
|
export async function deleteSessionValue(sessionId) {
|
|
598
619
|
return enqueueWrite('sessions', async () => {
|
|
599
620
|
const bucket = await findSessionBucket(sessionId)
|
|
600
621
|
if (!bucket) return
|
|
601
622
|
await fs.rm(sessionDataFile(sessionId, bucket), { force: true })
|
|
623
|
+
const { deleteSessionAssets } = await import('./session-assets.mjs')
|
|
624
|
+
await deleteSessionAssets(bucket, sessionId)
|
|
602
625
|
sessionBucketIndex.delete(sessionId)
|
|
603
626
|
})
|
|
604
627
|
}
|
|
@@ -116,6 +116,16 @@ export const workspaceTools = [
|
|
|
116
116
|
}),
|
|
117
117
|
executionMode: 'sequential',
|
|
118
118
|
},
|
|
119
|
+
{
|
|
120
|
+
name: 'generate_image',
|
|
121
|
+
label: 'Generate image',
|
|
122
|
+
description: 'Generate images with the configured OpenRouter provider and save them as assets owned by the current conversation. Use this when the user explicitly asks for a generated bitmap image. The operation may incur provider charges.',
|
|
123
|
+
parameters: Type.Object({
|
|
124
|
+
prompt: Type.String({ description: 'Detailed image-generation prompt.' }),
|
|
125
|
+
model: Type.Optional(Type.String({ description: 'Optional OpenRouter image model ID. Defaults to google/gemini-2.5-flash-image.' })),
|
|
126
|
+
}),
|
|
127
|
+
executionMode: 'sequential',
|
|
128
|
+
},
|
|
119
129
|
{
|
|
120
130
|
name: 'present_files',
|
|
121
131
|
label: 'Present files',
|
package/server/tools/index.mjs
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from '../skills.mjs'
|
|
16
16
|
import { getToolWorkspaceRoot } from '../utils/workspace.mjs'
|
|
17
17
|
import { manageGlobalMemory } from '../global-memory.mjs'
|
|
18
|
+
import { generateSessionImages } from '../image-generation.mjs'
|
|
18
19
|
|
|
19
20
|
const require = createRequire(import.meta.url)
|
|
20
21
|
|
|
@@ -1100,6 +1101,7 @@ export const toolHandlers = {
|
|
|
1100
1101
|
write_file: toolWriteFile,
|
|
1101
1102
|
edit_file: toolEditFile,
|
|
1102
1103
|
run_command: toolRunCommand,
|
|
1104
|
+
generate_image: generateSessionImages,
|
|
1103
1105
|
present_files: toolPresentFiles,
|
|
1104
1106
|
activate_skill: toolActivateSkill,
|
|
1105
1107
|
read_skill_resource: toolReadSkillResource,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{i as e}from"./rolldown-runtime-DWdDZTNf.js";import{D as t,Ft as n,Rt as r,Vt as i,a,dt as ee,l as o}from"./icons-C7j5jdKo.js";import{i as s,n as c}from"./react-vendor-VqdnQHHS.js";import{$ as te,H as l,Q as ne,ct as u,et as re,ht as d,nt as ie,tt as ae,ut as f}from"./index-DxNzlPmc.js";var p=e(i(),1),oe=s(),m=c(),h=144,g=82,_=4,v=8;function y(e){return JSON.stringify({provider:e.provider,modelId:e.id,api:e.api,baseUrl:e.baseUrl})}function b(e){if(!e)return{mode:`inherit`};try{let t=JSON.parse(e);return{mode:`fixed`,provider:String(t.provider||``),modelId:String(t.modelId||``),api:t.api?String(t.api):void 0,baseUrl:t.baseUrl?String(t.baseUrl):void 0}}catch{return{mode:`inherit`}}}function x(e){return!e||e.mode!==`fixed`?``:JSON.stringify({provider:e.provider,modelId:e.modelId,api:e.api,baseUrl:e.baseUrl})}function se(e){return e.name||`${e.provider}/${e.id}`}function S(){return{name:``,label:``,description:``,systemPrompt:``,allowedTools:[`read_file`,`grep_files`],maxRuntimeMs:`1800000`,maxToolCalls:`300`,enabledAsSubagent:!0,modelMode:`inherit`,fixedModelValue:``,thinkingLevel:`inherit`}}function ce(e){return{name:e.name,label:e.label,description:e.description??``,systemPrompt:e.systemPrompt??``,allowedTools:e.allowedTools??[],maxRuntimeMs:String(e.maxRuntimeMs??18e5),maxToolCalls:String(e.maxToolCalls??300),enabledAsSubagent:e.enabledAsSubagent,modelMode:e.model?.mode===`fixed`?`fixed`:`inherit`,fixedModelValue:x(e.model),thinkingLevel:e.thinkingLevel??`inherit`}}function le(e){return{name:e.name.trim().toLowerCase(),label:e.label.trim(),description:e.description.trim(),systemPrompt:e.systemPrompt.trim(),allowedTools:e.allowedTools,maxRuntimeMs:Number(e.maxRuntimeMs||18e5),maxToolCalls:Number(e.maxToolCalls||300),enabledAsSubagent:e.enabledAsSubagent,model:e.modelMode===`fixed`?b(e.fixedModelValue):{mode:`inherit`},thinkingLevel:e.thinkingLevel}}function C(e){return!!(e.name.trim()&&e.label.trim()&&e.allowedTools.length>0)}async function w(e,t){let n=await fetch(e,{...t,headers:{"content-type":`application/json`,...t?.headers}}),r=await n.json().catch(()=>null);if(!n.ok)throw Error(r?.error||`请求失败`);return r}function T(){let[e,i]=(0,p.useState)([]),[s,c]=(0,p.useState)([]),[x,T]=(0,p.useState)(!1),[E,D]=(0,p.useState)(null),[O,k]=(0,p.useState)(()=>S()),[A,j]=(0,p.useState)(!1),[M,N]=(0,p.useState)(``),[P,F]=(0,p.useState)(!1),[I,ue]=(0,p.useState)(),[L,de]=(0,p.useState)([]),[fe,pe]=(0,p.useState)(`off`),[R,z]=(0,p.useState)(``),[B,V]=(0,p.useState)(null),[H,U]=(0,p.useState)(null);async function W(){let[e,t]=await Promise.all([w(`/api/agent-profiles`),w(`/api/agent-profiles/available-tools`)]);i(e.agents),c(t.tools)}(0,p.useEffect)(()=>{let e=!1;async function t(){try{let[t,n]=await Promise.all([w(`/api/agent-profiles`),w(`/api/agent-profiles/available-tools`)]);if(e)return;i(t.agents),c(n.tools)}catch(t){e||z(t instanceof Error?t.message:d(`requestFailed`))}}return t(),()=>{e=!0}},[]),(0,p.useEffect)(()=>{let e=!1;async function t(){try{let t=await re(),n=await te(t);de(n);let r=await ae(t),i=r.model??await ie(t)??n[0];if(e)return;ue(i),pe(r.thinkingLevel??ne(i))}catch{}}return t(),()=>{e=!0}},[]),(0,p.useEffect)(()=>{if(!B)return;let e=()=>{V(null),U(null)},t=t=>{t.key===`Escape`&&e()};return window.addEventListener(`click`,e),window.addEventListener(`blur`,e),window.addEventListener(`resize`,e),window.addEventListener(`scroll`,e,!0),document.addEventListener(`keydown`,t),()=>{window.removeEventListener(`click`,e),window.removeEventListener(`blur`,e),window.removeEventListener(`resize`,e),window.removeEventListener(`scroll`,e,!0),document.removeEventListener(`keydown`,t)}},[B]);let G=(0,p.useMemo)(()=>e.find(e=>e.id===E)??null,[e,E]),K=(0,p.useMemo)(()=>e.find(e=>e.id===B)??null,[e,B]),q=!!G?.readonly,J=!!(G?.readonly&&!G?.builtin),Y=(0,p.useMemo)(()=>L.find(e=>y(e)===O.fixedModelValue),[O.fixedModelValue,L]),X=O.modelMode===`fixed`&&!!Y&&Y?.reasoning!==!0;function me(e,t){if(e.stopPropagation(),B===t){V(null),U(null);return}let n=e.currentTarget.getBoundingClientRect(),r=Math.max(v,Math.min(n.right-h,window.innerWidth-h-v)),i=n.bottom+_,a=n.top-_-g;U({left:r,top:i+g<=window.innerHeight-v?i:Math.max(v,a)}),V(t)}function Z(e,t){k(n=>({...n,[e]:t}))}function he(e){k(t=>({...t,allowedTools:t.allowedTools.includes(e)?t.allowedTools.filter(t=>t!==e):[...t.allowedTools,e]}))}function ge(){D(null),k(S()),N(``),z(``),T(!0)}function Q(e){D(e.id),k(ce(e)),N(``),z(``),T(!0)}function $(){A||P||(T(!1),D(null),k(S()),N(``))}async function _e(){let e=M.trim();if(!e){z(d(`aiFillAgentInputRequired`));return}if(!I){z(d(`aiFillAgentNoModel`));return}F(!0),z(``);try{let t=await w(`/api/agent-profiles/ai-fill`,{method:`POST`,body:JSON.stringify({instruction:e,model:I,thinkingLevel:fe})});k(e=>({...e,name:t.agent.name,label:t.agent.label,description:t.agent.description,systemPrompt:t.agent.systemPrompt}))}catch(e){z(e instanceof Error?e.message:d(`aiFillAgentFailed`))}finally{F(!1)}}async function ve(){if(C(O)){j(!0),z(``);try{let e=G?.builtin?{model:O.modelMode===`fixed`?b(O.fixedModelValue):{mode:`inherit`}}:le({...O,thinkingLevel:X?`off`:O.thinkingLevel});E?await w(`/api/agent-profiles/${encodeURIComponent(E)}`,{method:`PATCH`,body:JSON.stringify(e)}):await w(`/api/agent-profiles`,{method:`POST`,body:JSON.stringify(e)}),$(),await W()}catch(e){z(e instanceof Error?e.message:d(`requestFailed`))}finally{j(!1)}}}async function ye(e){if(e.builtin||e.readonly)return;let t=!e.enabledAsSubagent,n=e.enabledAsSubagent;i(n=>n.map(n=>n.id===e.id?{...n,enabledAsSubagent:t}:n)),V(null);try{await w(`/api/agent-profiles/${encodeURIComponent(e.id)}`,{method:`PATCH`,body:JSON.stringify({enabledAsSubagent:t})})}catch(t){i(t=>t.map(t=>t.id===e.id?{...t,enabledAsSubagent:n}:t)),z(t instanceof Error?t.message:d(`requestFailed`))}}async function be(e){if(!(e.builtin||e.readonly)&&await l({description:d(`confirmDeleteAgent`),confirmLabel:d(`confirmDelete`),cancelLabel:d(`cancel`),variant:`destructive`})){z(``);try{await w(`/api/agent-profiles/${encodeURIComponent(e.id)}`,{method:`DELETE`}),await W()}catch(e){z(e instanceof Error?e.message:d(`requestFailed`))}}}return x?(0,m.jsxs)(`div`,{className:`quickforge-settings-stack`,children:[(0,m.jsx)(`div`,{className:`quickforge-settings-heading`,children:(0,m.jsxs)(`h3`,{className:`quickforge-settings-title`,children:[G?.builtin?d(`builtinAgentModelSettings`):d(G?`editAgent`:`createAgent`),(0,m.jsx)(u,{label:G?.builtin?d(`builtinAgentModelOnly`):G?.readonly?d(`readonlyAgentDescription`):d(`agentsDescription`)})]})}),(0,m.jsxs)(`section`,{className:`quickforge-settings-section`,"aria-label":d(G?`editAgent`:`createAgent`),children:[(0,m.jsxs)(`div`,{className:`quickforge-settings-toolbar`,children:[(0,m.jsxs)(`button`,{className:`quickforge-settings-button quickforge-settings-button-secondary`,type:`button`,onClick:$,disabled:A||P,children:[(0,m.jsx)(r,{className:`mr-2 size-4`}),d(`back`)]}),(0,m.jsxs)(`div`,{className:`quickforge-settings-row-main`,children:[(0,m.jsx)(`div`,{className:`quickforge-settings-row-title`,children:G?.builtin?d(`builtinAgentModelSettings`):d(G?`editAgent`:`createAgent`)}),G?.builtin?(0,m.jsx)(`div`,{className:`quickforge-settings-row-description`,children:d(`builtinAgentModelOnly`)}):G?.readonly?(0,m.jsx)(`div`,{className:`quickforge-settings-row-description`,children:d(`readonlyAgentDescription`)}):null]})]}),(0,m.jsx)(`div`,{className:`px-5 py-4`,children:(0,m.jsxs)(`div`,{className:`space-y-4`,children:[(0,m.jsxs)(`div`,{className:`rounded-2xl border border-border bg-muted/20 p-3`,children:[(0,m.jsxs)(`div`,{className:`mb-2 flex items-center gap-2 text-sm font-medium text-foreground`,children:[(0,m.jsx)(o,{className:`size-4 text-primary`}),d(`aiFillAgent`),(0,m.jsx)(u,{label:d(`aiFillAgentDescription`)})]}),(0,m.jsx)(`textarea`,{className:`min-h-20 w-full resize-y rounded-xl border border-input bg-background px-3 py-2 text-sm outline-none transition-colors placeholder:text-muted-foreground/65 focus:border-ring disabled:opacity-60`,value:M,disabled:q||P,onChange:e=>N(e.target.value),placeholder:d(`aiFillAgentPlaceholder`)}),(0,m.jsx)(`div`,{className:`mt-2 flex justify-end`,children:(0,m.jsxs)(f,{variant:`outline`,size:`sm`,onClick:()=>void _e(),disabled:q||P||!M.trim(),children:[(0,m.jsx)(o,{className:`mr-1 size-3.5`}),d(P?`aiFillAgentLoading`:`aiFillAgent`)]})})]}),(0,m.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentName`),(0,m.jsx)(`input`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.name,disabled:q,onChange:e=>Z(`name`,e.target.value),placeholder:`reviewer`})]}),(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentLabel`),(0,m.jsx)(`input`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.label,disabled:q,onChange:e=>Z(`label`,e.target.value),placeholder:d(`agentLabelPlaceholder`)})]})]}),G?(0,m.jsxs)(`div`,{className:`rounded-xl border border-border bg-muted/20 px-3 py-2 text-sm`,children:[(0,m.jsx)(`div`,{className:`text-xs font-medium text-muted-foreground`,children:d(`agentSourcePath`)}),(0,m.jsx)(`div`,{className:`mt-1 truncate font-mono text-xs text-foreground`,title:G.source?`${G.source}${G.relativePath?` · ${G.relativePath}`:``}`:void 0,children:G.source?`${G.source}${G.relativePath?` · ${G.relativePath}`:``}`:G.builtin?d(`builtinAgent`):`-`})]}):null,(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentDescription`),(0,m.jsx)(`input`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.description,disabled:q,onChange:e=>Z(`description`,e.target.value)})]}),(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentSystemPrompt`),(0,m.jsx)(`textarea`,{className:`mt-1 min-h-36 w-full resize-y rounded-xl border border-input bg-background px-3 py-2 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.systemPrompt,disabled:q,onChange:e=>Z(`systemPrompt`,e.target.value)})]}),(0,m.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentModelMode`),(0,m.jsxs)(`select`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.modelMode,disabled:J,onChange:e=>Z(`modelMode`,e.target.value),children:[(0,m.jsx)(`option`,{value:`inherit`,children:d(`agentModelInherit`)}),(0,m.jsx)(`option`,{value:`fixed`,children:d(`agentModelFixed`)})]})]}),(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentFixedModel`),(0,m.jsxs)(`select`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.fixedModelValue,disabled:J||O.modelMode!==`fixed`,onChange:e=>Z(`fixedModelValue`,e.target.value),children:[(0,m.jsx)(`option`,{value:``,children:d(`agentModelInherit`)}),L.map(e=>(0,m.jsx)(`option`,{value:y(e),children:se(e)},y(e)))]})]})]}),(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`agentThinkingLevel`),(0,m.jsxs)(`select`,{className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:X&&O.thinkingLevel!==`inherit`?`off`:O.thinkingLevel,disabled:q||X,onChange:e=>Z(`thinkingLevel`,e.target.value),children:[(0,m.jsx)(`option`,{value:`inherit`,children:d(`agentThinkingInherit`)}),(0,m.jsx)(`option`,{value:`off`,children:d(`thinkingOff`)}),(0,m.jsx)(`option`,{value:`low`,children:d(`thinkingLow`)}),(0,m.jsx)(`option`,{value:`medium`,children:d(`thinkingMedium`)}),(0,m.jsx)(`option`,{value:`high`,children:d(`thinkingHigh`)}),(0,m.jsx)(`option`,{value:`xhigh`,children:d(`thinkingXHigh`)})]}),(0,m.jsx)(`span`,{className:`mt-1 block text-xs text-muted-foreground`,children:d(X?`agentThinkingUnsupported`:`agentThinkingDescription`)})]}),(0,m.jsxs)(`div`,{children:[(0,m.jsx)(`div`,{className:`mb-2 text-sm font-medium text-foreground`,children:d(`allowedTools`)}),(0,m.jsx)(`div`,{className:`grid gap-2 sm:grid-cols-2`,children:s.map(e=>(0,m.jsxs)(`label`,{className:`flex items-start gap-2 rounded-xl border border-border bg-muted/20 p-3 text-sm disabled:opacity-60`,children:[(0,m.jsx)(`input`,{type:`checkbox`,className:`mt-1`,disabled:q,checked:O.allowedTools.includes(e.name),onChange:()=>he(e.name)}),(0,m.jsxs)(`span`,{children:[(0,m.jsx)(`span`,{className:`font-medium text-foreground`,children:e.label}),(0,m.jsx)(`span`,{className:`ml-2 font-mono text-xs text-muted-foreground`,children:e.name}),e.riskLevel===`dangerous`?(0,m.jsx)(`span`,{className:`ml-2 rounded-full bg-amber-500/10 px-2 py-0.5 text-xs text-amber-700`,children:d(`highRiskTool`)}):null,(0,m.jsx)(`span`,{className:`mt-1 block text-xs text-muted-foreground`,children:e.description})]})]},e.name))})]}),(0,m.jsxs)(`div`,{className:`grid gap-3 sm:grid-cols-2`,children:[(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`maxRuntimeMs`),(0,m.jsx)(`input`,{type:`number`,className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.maxRuntimeMs,disabled:q,onChange:e=>Z(`maxRuntimeMs`,e.target.value)})]}),(0,m.jsxs)(`label`,{className:`block text-sm font-medium text-foreground`,children:[d(`maxToolCalls`),(0,m.jsx)(`input`,{type:`number`,className:`mt-1 h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring disabled:opacity-60`,value:O.maxToolCalls,disabled:q,onChange:e=>Z(`maxToolCalls`,e.target.value)})]})]}),(0,m.jsxs)(`label`,{className:`flex items-center gap-2 text-sm text-foreground`,children:[(0,m.jsx)(`input`,{type:`checkbox`,checked:O.enabledAsSubagent,disabled:q,onChange:e=>Z(`enabledAsSubagent`,e.target.checked)}),d(`enabledAsSubagent`)]}),R?(0,m.jsx)(`div`,{className:`rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive`,children:R}):null]})}),(0,m.jsxs)(`div`,{className:`quickforge-settings-divider flex justify-end gap-2 px-5 py-4`,children:[(0,m.jsx)(f,{variant:`outline`,onClick:$,disabled:A||P,children:d(`cancel`)}),(0,m.jsx)(f,{onClick:ve,disabled:A||P||J||!G?.builtin&&!C(O)||O.modelMode===`fixed`&&!O.fixedModelValue,children:d(`save`)})]})]})]}):(0,m.jsxs)(`div`,{className:`quickforge-settings-stack`,children:[(0,m.jsxs)(`section`,{className:`quickforge-settings-section`,"aria-label":d(`agentsTab`),children:[(0,m.jsxs)(`div`,{className:`quickforge-settings-toolbar`,children:[(0,m.jsxs)(`div`,{className:`quickforge-settings-row-main`,children:[(0,m.jsxs)(`div`,{className:`quickforge-settings-row-title`,children:[(0,m.jsx)(n,{className:`size-4 text-primary`}),d(`agentsTab`)]}),(0,m.jsx)(`div`,{className:`quickforge-settings-row-description`,children:d(`agentsDescription`)})]}),(0,m.jsx)(`button`,{className:`quickforge-settings-button quickforge-settings-button-primary`,type:`button`,onClick:ge,children:d(`createAgent`)})]}),R?(0,m.jsx)(`div`,{className:`quickforge-settings-alert quickforge-settings-warning-attached`,children:R}):null,e.length===0?(0,m.jsx)(`div`,{className:`quickforge-settings-empty-row`,children:d(`loading`)}):e.map(e=>(0,m.jsxs)(`div`,{className:`quickforge-settings-list-item quickforge-agent-profile-row`,role:`button`,tabIndex:0,onClick:()=>Q(e),onKeyDown:t=>{t.target===t.currentTarget&&(t.key===`Enter`||t.key===` `)&&(t.preventDefault(),Q(e))},children:[(0,m.jsx)(`div`,{className:`quickforge-settings-list-item-main quickforge-agent-profile-row-main`,children:(0,m.jsxs)(`div`,{className:`quickforge-agent-profile-summary`,children:[(0,m.jsx)(`span`,{className:`quickforge-agent-profile-label`,title:e.label,children:e.label}),e.description?(0,m.jsx)(`span`,{className:`quickforge-agent-profile-description`,title:e.description,children:e.description}):null]})}),(0,m.jsxs)(`div`,{className:`quickforge-settings-list-item-actions`,onClick:e=>e.stopPropagation(),children:[(0,m.jsxs)(`label`,{className:`quickforge-settings-switch`,"aria-disabled":e.builtin||e.readonly?`true`:`false`,title:e.enabledAsSubagent?d(`disableAsSubagent`):d(`enableAsSubagent`),children:[(0,m.jsx)(`input`,{type:`checkbox`,checked:e.enabledAsSubagent,disabled:e.builtin||e.readonly,onChange:()=>void ye(e)}),(0,m.jsx)(`span`,{"aria-hidden":`true`})]}),(0,m.jsx)(`button`,{className:`quickforge-settings-icon-action`,type:`button`,onClick:t=>me(t,e.id),title:d(`moreActions`),"aria-label":d(`moreActions`),"aria-haspopup":`menu`,"aria-expanded":B===e.id,children:(0,m.jsx)(ee,{className:`size-4`})})]})]},e.id))]}),K&&H?(0,oe.createPortal)((0,m.jsxs)(`div`,{className:`fixed z-50 w-36 overflow-hidden rounded-xl border border-border bg-popover py-1 text-sm shadow-quickforge`,style:{left:H.left,top:H.top},role:`menu`,"aria-label":d(`moreActions`),onClick:e=>e.stopPropagation(),children:[(0,m.jsxs)(`button`,{className:`flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50`,type:`button`,role:`menuitem`,disabled:K.readonly&&!K.builtin,onClick:()=>{V(null),U(null),Q(K)},children:[(0,m.jsx)(t,{className:`size-3.5`}),K.builtin?d(`builtinAgentModelSettings`):d(`editTask`)]}),(0,m.jsxs)(`button`,{className:`flex w-full items-center gap-2 px-3 py-2 text-left text-destructive hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50`,type:`button`,role:`menuitem`,disabled:K.builtin||K.readonly,onClick:()=>{V(null),U(null),be(K)},children:[(0,m.jsx)(a,{className:`size-3.5`}),d(`delete`)]})]}),document.body):null]})}export{T as AgentProfilesPage};
|