@lazyingart/agintiflow 0.1.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/public/app.js ADDED
@@ -0,0 +1,1060 @@
1
+ const supportedLanguages = [
2
+ "en",
3
+ "ar",
4
+ "es",
5
+ "fr",
6
+ "ja",
7
+ "ko",
8
+ "vi",
9
+ "zh-Hans",
10
+ "zh-Hant",
11
+ "de",
12
+ "ru",
13
+ ];
14
+
15
+ const translations = {
16
+ en: {
17
+ documentTitle: "AgInTiFlow",
18
+ brandKicker: "AI automation product by AgInTi Inc.",
19
+ languageLabel: "Language",
20
+ intro:
21
+ "Web-first agent platform with smart model routing, resumable runs, guarded tools, and optional external agent wrappers.",
22
+ routingModeLabel: "Routing policy",
23
+ routingSmartOption: "Smart: fast unless complex",
24
+ routingFastOption: "DeepSeek v4 flash",
25
+ routingComplexOption: "DeepSeek v4 pro",
26
+ routingManualOption: "Manual provider/model",
27
+ routingHintSmart: "Smart routing uses DeepSeek v4 flash by default and escalates to DeepSeek v4 pro for complex work.",
28
+ routingHintFast: "Fast route: DeepSeek v4 flash for normal browser, shell, and short coding tasks.",
29
+ routingHintComplex: "Complex route: DeepSeek v4 pro for deeper implementation, debugging, and design work.",
30
+ routingHintManual: "Manual route uses the provider and model fields exactly as entered.",
31
+ providerLabel: "Provider",
32
+ modelLabel: "Model",
33
+ goalLabel: "Goal",
34
+ goalPlaceholder: "Open a site and summarize it, or use run_command for simple terminal inspection.",
35
+ startUrlLabel: "Start URL",
36
+ startUrlPlaceholder: "https://news.ycombinator.com",
37
+ allowedDomainsLabel: "Allowed domains",
38
+ allowedDomainsPlaceholder: "news.ycombinator.com,github.com",
39
+ commandCwdLabel: "Working directory",
40
+ maxStepsLabel: "Max steps",
41
+ sandboxModeLabel: "Sandbox mode",
42
+ sandboxHostOption: "Host read-only",
43
+ sandboxDockerReadonlyOption: "Docker read-only",
44
+ sandboxDockerWorkspaceOption: "Docker workspace-write",
45
+ packagePolicyLabel: "Package installs",
46
+ packageBlockOption: "Blocked",
47
+ packagePromptOption: "Require approval",
48
+ packageAllowOption: "Approved in Docker",
49
+ headlessLabel: "Headless browser",
50
+ shellToolLabel: "Enable shell tool",
51
+ wrapperToolLabel: "Enable agent wrappers",
52
+ dockerSandboxLabel: "Use Docker sandbox",
53
+ allowPasswordsLabel: "Allow password typing",
54
+ allowDestructiveLabel: "Allow destructive actions",
55
+ dockerImageLabel: "Docker image",
56
+ dockerImagePlaceholder: "agintiflow-sandbox:latest",
57
+ startRunButton: "Start run",
58
+ runOutputTitle: "Run output",
59
+ noRunStarted: "No run started.",
60
+ conversationTitle: "Conversation",
61
+ noSessionSelected: "No session selected",
62
+ selectRecentSession: "Select a recent session",
63
+ noConversationLoaded: "No conversation loaded.",
64
+ chatPlaceholder: "Continue the selected conversation here.",
65
+ sendMessageButton: "Send message",
66
+ assistantLabel: "Assistant",
67
+ youLabel: "You",
68
+ keysLabel: "Keys",
69
+ wrappersLabel: "Wrappers",
70
+ sandboxStatusTitle: "Sandbox status",
71
+ sandboxStatusEmpty: "Not checked yet.",
72
+ sandboxLogsEmpty: "No sandbox logs yet.",
73
+ checkSandboxButton: "Check",
74
+ preflightSandboxButton: "Preflight",
75
+ sandboxReadyLabel: "ready",
76
+ sandboxNotReadyLabel: "not ready",
77
+ availableLabel: "available",
78
+ missingLabel: "missing",
79
+ goalRequired: "Goal is required.",
80
+ startingRun: "Starting run...",
81
+ failedStartRun: "Failed to start run.",
82
+ noRunSelected: "No run selected.",
83
+ selectSessionFirst: "Select or start a session first.",
84
+ messageRequired: "Message is required.",
85
+ sendingStatus: "Sending...",
86
+ runningStatus: "Running...",
87
+ failedContinue: "Failed to continue the conversation.",
88
+ },
89
+ ar: {
90
+ documentTitle: "AgInTiFlow",
91
+ brandKicker: "منتج أتمتة بالذكاء الاصطناعي من AgInTi Inc.",
92
+ languageLabel: "اللغة",
93
+ intro:
94
+ "وكيل متصفح مع اختيار المزوّد، تشغيل قابل للاستئناف، إعدادات محفوظة، وأداة shell محمية اختيارية مع دعم Docker sandbox.",
95
+ providerLabel: "المزوّد",
96
+ modelLabel: "النموذج",
97
+ goalLabel: "الهدف",
98
+ goalPlaceholder: "افتح موقعا ولخصه، أو استخدم run_command لفحص طرفية بسيط.",
99
+ startUrlLabel: "رابط البدء",
100
+ startUrlPlaceholder: "https://news.ycombinator.com",
101
+ allowedDomainsLabel: "النطاقات المسموحة",
102
+ allowedDomainsPlaceholder: "news.ycombinator.com,github.com",
103
+ commandCwdLabel: "مجلد العمل",
104
+ maxStepsLabel: "أقصى خطوات",
105
+ headlessLabel: "متصفح بدون واجهة",
106
+ shellToolLabel: "تفعيل أداة shell",
107
+ dockerSandboxLabel: "استخدم Docker sandbox",
108
+ allowPasswordsLabel: "السماح بكتابة كلمات المرور",
109
+ allowDestructiveLabel: "السماح بإجراءات مدمرة",
110
+ dockerImageLabel: "صورة Docker",
111
+ dockerImagePlaceholder: "agintiflow-sandbox:latest",
112
+ startRunButton: "ابدأ التشغيل",
113
+ runOutputTitle: "مخرجات التشغيل",
114
+ noRunStarted: "لم يبدأ أي تشغيل.",
115
+ conversationTitle: "المحادثة",
116
+ noSessionSelected: "لا توجد جلسة محددة",
117
+ selectRecentSession: "اختر جلسة حديثة",
118
+ noConversationLoaded: "لم يتم تحميل محادثة.",
119
+ chatPlaceholder: "تابع المحادثة المحددة هنا.",
120
+ sendMessageButton: "إرسال الرسالة",
121
+ assistantLabel: "المساعد",
122
+ youLabel: "أنت",
123
+ keysLabel: "المفاتيح",
124
+ availableLabel: "متاح",
125
+ missingLabel: "غير موجود",
126
+ goalRequired: "الهدف مطلوب.",
127
+ startingRun: "جار بدء التشغيل...",
128
+ failedStartRun: "فشل بدء التشغيل.",
129
+ noRunSelected: "لا يوجد تشغيل محدد.",
130
+ selectSessionFirst: "اختر أو ابدأ جلسة أولا.",
131
+ messageRequired: "الرسالة مطلوبة.",
132
+ sendingStatus: "جار الإرسال...",
133
+ runningStatus: "قيد التشغيل...",
134
+ failedContinue: "فشل متابعة المحادثة.",
135
+ },
136
+ es: {
137
+ documentTitle: "AgInTiFlow",
138
+ brandKicker: "Producto de automatización IA de AgInTi Inc.",
139
+ languageLabel: "Idioma",
140
+ intro:
141
+ "Agente de navegador con selección de proveedor, ejecuciones reanudables, ajustes persistentes y shell protegido opcional con Docker sandbox.",
142
+ providerLabel: "Proveedor",
143
+ modelLabel: "Modelo",
144
+ goalLabel: "Objetivo",
145
+ goalPlaceholder: "Abre un sitio y resúmelo, o usa run_command para una inspección simple de terminal.",
146
+ startUrlLabel: "URL inicial",
147
+ startUrlPlaceholder: "https://news.ycombinator.com",
148
+ allowedDomainsLabel: "Dominios permitidos",
149
+ allowedDomainsPlaceholder: "news.ycombinator.com,github.com",
150
+ commandCwdLabel: "Directorio de trabajo",
151
+ maxStepsLabel: "Pasos máximos",
152
+ headlessLabel: "Navegador headless",
153
+ shellToolLabel: "Activar shell",
154
+ dockerSandboxLabel: "Usar Docker sandbox",
155
+ allowPasswordsLabel: "Permitir escribir contraseñas",
156
+ allowDestructiveLabel: "Permitir acciones destructivas",
157
+ dockerImageLabel: "Imagen Docker",
158
+ dockerImagePlaceholder: "agintiflow-sandbox:latest",
159
+ startRunButton: "Iniciar ejecución",
160
+ runOutputTitle: "Salida de ejecución",
161
+ noRunStarted: "No hay ejecución iniciada.",
162
+ conversationTitle: "Conversación",
163
+ noSessionSelected: "Sin sesión seleccionada",
164
+ selectRecentSession: "Selecciona una sesión reciente",
165
+ noConversationLoaded: "No hay conversación cargada.",
166
+ chatPlaceholder: "Continúa aquí la conversación seleccionada.",
167
+ sendMessageButton: "Enviar mensaje",
168
+ assistantLabel: "Asistente",
169
+ youLabel: "Tú",
170
+ keysLabel: "Claves",
171
+ availableLabel: "disponible",
172
+ missingLabel: "faltante",
173
+ goalRequired: "El objetivo es obligatorio.",
174
+ startingRun: "Iniciando ejecución...",
175
+ failedStartRun: "No se pudo iniciar la ejecución.",
176
+ noRunSelected: "No hay ejecución seleccionada.",
177
+ selectSessionFirst: "Selecciona o inicia una sesión primero.",
178
+ messageRequired: "El mensaje es obligatorio.",
179
+ sendingStatus: "Enviando...",
180
+ runningStatus: "Ejecutando...",
181
+ failedContinue: "No se pudo continuar la conversación.",
182
+ },
183
+ fr: {
184
+ documentTitle: "AgInTiFlow",
185
+ brandKicker: "Produit d'automatisation IA par AgInTi Inc.",
186
+ languageLabel: "Langue",
187
+ intro:
188
+ "Agent navigateur avec choix du fournisseur, runs reprenables, réglages persistants et outil shell gardé optionnel avec Docker sandbox.",
189
+ providerLabel: "Fournisseur",
190
+ modelLabel: "Modèle",
191
+ goalLabel: "Objectif",
192
+ goalPlaceholder: "Ouvrez un site et résumez-le, ou utilisez run_command pour une inspection terminal simple.",
193
+ startUrlLabel: "URL de départ",
194
+ startUrlPlaceholder: "https://news.ycombinator.com",
195
+ allowedDomainsLabel: "Domaines autorisés",
196
+ allowedDomainsPlaceholder: "news.ycombinator.com,github.com",
197
+ commandCwdLabel: "Dossier de travail",
198
+ maxStepsLabel: "Étapes max",
199
+ headlessLabel: "Navigateur headless",
200
+ shellToolLabel: "Activer l'outil shell",
201
+ dockerSandboxLabel: "Utiliser Docker sandbox",
202
+ allowPasswordsLabel: "Autoriser la saisie de mots de passe",
203
+ allowDestructiveLabel: "Autoriser les actions destructives",
204
+ dockerImageLabel: "Image Docker",
205
+ dockerImagePlaceholder: "agintiflow-sandbox:latest",
206
+ startRunButton: "Démarrer",
207
+ runOutputTitle: "Sortie du run",
208
+ noRunStarted: "Aucun run démarré.",
209
+ conversationTitle: "Conversation",
210
+ noSessionSelected: "Aucune session sélectionnée",
211
+ selectRecentSession: "Sélectionner une session récente",
212
+ noConversationLoaded: "Aucune conversation chargée.",
213
+ chatPlaceholder: "Continuez ici la conversation sélectionnée.",
214
+ sendMessageButton: "Envoyer",
215
+ assistantLabel: "Assistant",
216
+ youLabel: "Vous",
217
+ keysLabel: "Clés",
218
+ availableLabel: "disponible",
219
+ missingLabel: "manquante",
220
+ goalRequired: "L'objectif est requis.",
221
+ startingRun: "Démarrage du run...",
222
+ failedStartRun: "Échec du démarrage.",
223
+ noRunSelected: "Aucun run sélectionné.",
224
+ selectSessionFirst: "Sélectionnez ou démarrez d'abord une session.",
225
+ messageRequired: "Le message est requis.",
226
+ sendingStatus: "Envoi...",
227
+ runningStatus: "Exécution...",
228
+ failedContinue: "Impossible de continuer la conversation.",
229
+ },
230
+ ja: {
231
+ documentTitle: "AgInTiFlow",
232
+ brandKicker: "AgInTi Inc. のAI自動化プロダクト",
233
+ languageLabel: "言語",
234
+ intro:
235
+ "プロバイダー選択、再開可能な実行、保存される設定、Docker sandbox対応の保護されたshellツールを備えたブラウザエージェントです。",
236
+ providerLabel: "プロバイダー",
237
+ modelLabel: "モデル",
238
+ goalLabel: "目標",
239
+ goalPlaceholder: "サイトを開いて要約する、または run_command で簡単な端末確認を行う。",
240
+ startUrlLabel: "開始URL",
241
+ startUrlPlaceholder: "https://news.ycombinator.com",
242
+ allowedDomainsLabel: "許可ドメイン",
243
+ allowedDomainsPlaceholder: "news.ycombinator.com,github.com",
244
+ commandCwdLabel: "作業ディレクトリ",
245
+ maxStepsLabel: "最大ステップ",
246
+ headlessLabel: "ヘッドレスブラウザ",
247
+ shellToolLabel: "shellツールを有効化",
248
+ dockerSandboxLabel: "Docker sandboxを使う",
249
+ allowPasswordsLabel: "パスワード入力を許可",
250
+ allowDestructiveLabel: "破壊的操作を許可",
251
+ dockerImageLabel: "Dockerイメージ",
252
+ dockerImagePlaceholder: "agintiflow-sandbox:latest",
253
+ startRunButton: "実行開始",
254
+ runOutputTitle: "実行出力",
255
+ noRunStarted: "実行はまだ開始されていません。",
256
+ conversationTitle: "会話",
257
+ noSessionSelected: "セッション未選択",
258
+ selectRecentSession: "最近のセッションを選択",
259
+ noConversationLoaded: "会話は読み込まれていません。",
260
+ chatPlaceholder: "選択した会話をここで続けます。",
261
+ sendMessageButton: "送信",
262
+ assistantLabel: "アシスタント",
263
+ youLabel: "あなた",
264
+ keysLabel: "キー",
265
+ availableLabel: "利用可能",
266
+ missingLabel: "未設定",
267
+ goalRequired: "目標が必要です。",
268
+ startingRun: "実行を開始しています...",
269
+ failedStartRun: "実行開始に失敗しました。",
270
+ noRunSelected: "実行が選択されていません。",
271
+ selectSessionFirst: "先にセッションを選択または開始してください。",
272
+ messageRequired: "メッセージが必要です。",
273
+ sendingStatus: "送信中...",
274
+ runningStatus: "実行中...",
275
+ failedContinue: "会話の継続に失敗しました。",
276
+ },
277
+ ko: {
278
+ documentTitle: "AgInTiFlow",
279
+ brandKicker: "AgInTi Inc.의 AI 자동화 제품",
280
+ languageLabel: "언어",
281
+ intro:
282
+ "제공자 선택, 재개 가능한 실행, 저장되는 설정, Docker sandbox 지원 보호 shell 도구를 갖춘 브라우저 에이전트입니다.",
283
+ providerLabel: "제공자",
284
+ modelLabel: "모델",
285
+ goalLabel: "목표",
286
+ goalPlaceholder: "사이트를 열고 요약하거나 run_command로 간단한 터미널 검사를 실행하세요.",
287
+ startUrlLabel: "시작 URL",
288
+ startUrlPlaceholder: "https://news.ycombinator.com",
289
+ allowedDomainsLabel: "허용 도메인",
290
+ allowedDomainsPlaceholder: "news.ycombinator.com,github.com",
291
+ commandCwdLabel: "작업 디렉터리",
292
+ maxStepsLabel: "최대 단계",
293
+ headlessLabel: "헤드리스 브라우저",
294
+ shellToolLabel: "shell 도구 활성화",
295
+ dockerSandboxLabel: "Docker sandbox 사용",
296
+ allowPasswordsLabel: "비밀번호 입력 허용",
297
+ allowDestructiveLabel: "파괴적 작업 허용",
298
+ dockerImageLabel: "Docker 이미지",
299
+ dockerImagePlaceholder: "agintiflow-sandbox:latest",
300
+ startRunButton: "실행 시작",
301
+ runOutputTitle: "실행 출력",
302
+ noRunStarted: "아직 실행이 시작되지 않았습니다.",
303
+ conversationTitle: "대화",
304
+ noSessionSelected: "선택된 세션 없음",
305
+ selectRecentSession: "최근 세션 선택",
306
+ noConversationLoaded: "불러온 대화가 없습니다.",
307
+ chatPlaceholder: "선택한 대화를 여기서 계속하세요.",
308
+ sendMessageButton: "메시지 보내기",
309
+ assistantLabel: "어시스턴트",
310
+ youLabel: "나",
311
+ keysLabel: "키",
312
+ availableLabel: "사용 가능",
313
+ missingLabel: "없음",
314
+ goalRequired: "목표가 필요합니다.",
315
+ startingRun: "실행을 시작하는 중...",
316
+ failedStartRun: "실행 시작에 실패했습니다.",
317
+ noRunSelected: "선택된 실행이 없습니다.",
318
+ selectSessionFirst: "먼저 세션을 선택하거나 시작하세요.",
319
+ messageRequired: "메시지가 필요합니다.",
320
+ sendingStatus: "보내는 중...",
321
+ runningStatus: "실행 중...",
322
+ failedContinue: "대화를 계속하지 못했습니다.",
323
+ },
324
+ vi: {
325
+ documentTitle: "AgInTiFlow",
326
+ brandKicker: "Sản phẩm tự động hóa AI của AgInTi Inc.",
327
+ languageLabel: "Ngôn ngữ",
328
+ intro:
329
+ "Agent trình duyệt có chọn provider, phiên chạy tiếp tục được, cài đặt được lưu, và shell tool được bảo vệ tùy chọn với Docker sandbox.",
330
+ providerLabel: "Provider",
331
+ modelLabel: "Model",
332
+ goalLabel: "Mục tiêu",
333
+ goalPlaceholder: "Mở một trang và tóm tắt, hoặc dùng run_command để kiểm tra terminal đơn giản.",
334
+ startUrlLabel: "URL bắt đầu",
335
+ startUrlPlaceholder: "https://news.ycombinator.com",
336
+ allowedDomainsLabel: "Domain được phép",
337
+ allowedDomainsPlaceholder: "news.ycombinator.com,github.com",
338
+ commandCwdLabel: "Thư mục làm việc",
339
+ maxStepsLabel: "Số bước tối đa",
340
+ headlessLabel: "Trình duyệt headless",
341
+ shellToolLabel: "Bật shell tool",
342
+ dockerSandboxLabel: "Dùng Docker sandbox",
343
+ allowPasswordsLabel: "Cho phép nhập mật khẩu",
344
+ allowDestructiveLabel: "Cho phép hành động phá hủy",
345
+ dockerImageLabel: "Docker image",
346
+ dockerImagePlaceholder: "agintiflow-sandbox:latest",
347
+ startRunButton: "Bắt đầu chạy",
348
+ runOutputTitle: "Kết quả chạy",
349
+ noRunStarted: "Chưa có phiên chạy.",
350
+ conversationTitle: "Hội thoại",
351
+ noSessionSelected: "Chưa chọn phiên",
352
+ selectRecentSession: "Chọn phiên gần đây",
353
+ noConversationLoaded: "Chưa tải hội thoại.",
354
+ chatPlaceholder: "Tiếp tục hội thoại đã chọn tại đây.",
355
+ sendMessageButton: "Gửi tin nhắn",
356
+ assistantLabel: "Trợ lý",
357
+ youLabel: "Bạn",
358
+ keysLabel: "Khóa",
359
+ availableLabel: "có",
360
+ missingLabel: "thiếu",
361
+ goalRequired: "Cần nhập mục tiêu.",
362
+ startingRun: "Đang bắt đầu chạy...",
363
+ failedStartRun: "Không thể bắt đầu chạy.",
364
+ noRunSelected: "Chưa chọn phiên chạy.",
365
+ selectSessionFirst: "Hãy chọn hoặc bắt đầu một phiên trước.",
366
+ messageRequired: "Cần nhập tin nhắn.",
367
+ sendingStatus: "Đang gửi...",
368
+ runningStatus: "Đang chạy...",
369
+ failedContinue: "Không thể tiếp tục hội thoại.",
370
+ },
371
+ "zh-Hans": {
372
+ documentTitle: "AgInTiFlow",
373
+ brandKicker: "AgInTi Inc. 的 AI 自动化产品",
374
+ languageLabel: "语言",
375
+ intro:
376
+ "浏览器智能体,支持模型供应商选择、可恢复运行、持久设置,以及可选的受保护 shell 工具和 Docker 沙箱。",
377
+ providerLabel: "供应商",
378
+ modelLabel: "模型",
379
+ goalLabel: "目标",
380
+ goalPlaceholder: "打开网站并总结,或使用 run_command 做简单终端检查。",
381
+ startUrlLabel: "起始 URL",
382
+ startUrlPlaceholder: "https://news.ycombinator.com",
383
+ allowedDomainsLabel: "允许域名",
384
+ allowedDomainsPlaceholder: "news.ycombinator.com,github.com",
385
+ commandCwdLabel: "工作目录",
386
+ maxStepsLabel: "最大步数",
387
+ headlessLabel: "无头浏览器",
388
+ shellToolLabel: "启用 shell 工具",
389
+ dockerSandboxLabel: "使用 Docker 沙箱",
390
+ allowPasswordsLabel: "允许输入密码",
391
+ allowDestructiveLabel: "允许破坏性操作",
392
+ dockerImageLabel: "Docker 镜像",
393
+ dockerImagePlaceholder: "agintiflow-sandbox:latest",
394
+ startRunButton: "开始运行",
395
+ runOutputTitle: "运行输出",
396
+ noRunStarted: "还没有开始运行。",
397
+ conversationTitle: "对话",
398
+ noSessionSelected: "未选择会话",
399
+ selectRecentSession: "选择最近会话",
400
+ noConversationLoaded: "未加载对话。",
401
+ chatPlaceholder: "在这里继续当前选中的对话。",
402
+ sendMessageButton: "发送消息",
403
+ assistantLabel: "助手",
404
+ youLabel: "你",
405
+ keysLabel: "密钥",
406
+ availableLabel: "可用",
407
+ missingLabel: "缺失",
408
+ goalRequired: "目标是必填项。",
409
+ startingRun: "正在开始运行...",
410
+ failedStartRun: "启动运行失败。",
411
+ noRunSelected: "未选择运行。",
412
+ selectSessionFirst: "请先选择或开始一个会话。",
413
+ messageRequired: "消息是必填项。",
414
+ sendingStatus: "正在发送...",
415
+ runningStatus: "正在运行...",
416
+ failedContinue: "继续对话失败。",
417
+ },
418
+ "zh-Hant": {
419
+ documentTitle: "AgInTiFlow",
420
+ brandKicker: "AgInTi Inc. 的 AI 自動化產品",
421
+ languageLabel: "語言",
422
+ intro:
423
+ "瀏覽器智能體,支援模型供應商選擇、可恢復執行、持久設定,以及可選的受保護 shell 工具與 Docker 沙箱。",
424
+ providerLabel: "供應商",
425
+ modelLabel: "模型",
426
+ goalLabel: "目標",
427
+ goalPlaceholder: "開啟網站並總結,或使用 run_command 做簡單終端檢查。",
428
+ startUrlLabel: "起始 URL",
429
+ startUrlPlaceholder: "https://news.ycombinator.com",
430
+ allowedDomainsLabel: "允許網域",
431
+ allowedDomainsPlaceholder: "news.ycombinator.com,github.com",
432
+ commandCwdLabel: "工作目錄",
433
+ maxStepsLabel: "最大步數",
434
+ headlessLabel: "無頭瀏覽器",
435
+ shellToolLabel: "啟用 shell 工具",
436
+ dockerSandboxLabel: "使用 Docker 沙箱",
437
+ allowPasswordsLabel: "允許輸入密碼",
438
+ allowDestructiveLabel: "允許破壞性操作",
439
+ dockerImageLabel: "Docker 映像",
440
+ dockerImagePlaceholder: "agintiflow-sandbox:latest",
441
+ startRunButton: "開始執行",
442
+ runOutputTitle: "執行輸出",
443
+ noRunStarted: "尚未開始執行。",
444
+ conversationTitle: "對話",
445
+ noSessionSelected: "未選擇會話",
446
+ selectRecentSession: "選擇最近會話",
447
+ noConversationLoaded: "未載入對話。",
448
+ chatPlaceholder: "在這裡繼續目前選中的對話。",
449
+ sendMessageButton: "傳送訊息",
450
+ assistantLabel: "助手",
451
+ youLabel: "你",
452
+ keysLabel: "金鑰",
453
+ availableLabel: "可用",
454
+ missingLabel: "缺失",
455
+ goalRequired: "目標為必填。",
456
+ startingRun: "正在開始執行...",
457
+ failedStartRun: "啟動執行失敗。",
458
+ noRunSelected: "未選擇執行。",
459
+ selectSessionFirst: "請先選擇或開始一個會話。",
460
+ messageRequired: "訊息為必填。",
461
+ sendingStatus: "正在傳送...",
462
+ runningStatus: "正在執行...",
463
+ failedContinue: "繼續對話失敗。",
464
+ },
465
+ de: {
466
+ documentTitle: "AgInTiFlow",
467
+ brandKicker: "KI-Automationsprodukt von AgInTi Inc.",
468
+ languageLabel: "Sprache",
469
+ intro:
470
+ "Browser-Agent mit Provider-Auswahl, fortsetzbaren Läufen, gespeicherten Einstellungen und optional geschütztem Shell-Tool mit Docker-Sandbox.",
471
+ providerLabel: "Provider",
472
+ modelLabel: "Modell",
473
+ goalLabel: "Ziel",
474
+ goalPlaceholder: "Eine Website öffnen und zusammenfassen oder run_command für einfache Terminalprüfung nutzen.",
475
+ startUrlLabel: "Start-URL",
476
+ startUrlPlaceholder: "https://news.ycombinator.com",
477
+ allowedDomainsLabel: "Erlaubte Domains",
478
+ allowedDomainsPlaceholder: "news.ycombinator.com,github.com",
479
+ commandCwdLabel: "Arbeitsverzeichnis",
480
+ maxStepsLabel: "Max. Schritte",
481
+ headlessLabel: "Headless-Browser",
482
+ shellToolLabel: "Shell-Tool aktivieren",
483
+ dockerSandboxLabel: "Docker-Sandbox nutzen",
484
+ allowPasswordsLabel: "Passworteingabe erlauben",
485
+ allowDestructiveLabel: "Destruktive Aktionen erlauben",
486
+ dockerImageLabel: "Docker-Image",
487
+ dockerImagePlaceholder: "agintiflow-sandbox:latest",
488
+ startRunButton: "Lauf starten",
489
+ runOutputTitle: "Laufausgabe",
490
+ noRunStarted: "Kein Lauf gestartet.",
491
+ conversationTitle: "Konversation",
492
+ noSessionSelected: "Keine Session ausgewählt",
493
+ selectRecentSession: "Aktuelle Session auswählen",
494
+ noConversationLoaded: "Keine Konversation geladen.",
495
+ chatPlaceholder: "Die ausgewählte Konversation hier fortsetzen.",
496
+ sendMessageButton: "Nachricht senden",
497
+ assistantLabel: "Assistent",
498
+ youLabel: "Du",
499
+ keysLabel: "Keys",
500
+ availableLabel: "verfügbar",
501
+ missingLabel: "fehlt",
502
+ goalRequired: "Ziel ist erforderlich.",
503
+ startingRun: "Lauf wird gestartet...",
504
+ failedStartRun: "Lauf konnte nicht gestartet werden.",
505
+ noRunSelected: "Kein Lauf ausgewählt.",
506
+ selectSessionFirst: "Bitte zuerst eine Session auswählen oder starten.",
507
+ messageRequired: "Nachricht ist erforderlich.",
508
+ sendingStatus: "Senden...",
509
+ runningStatus: "Läuft...",
510
+ failedContinue: "Konversation konnte nicht fortgesetzt werden.",
511
+ },
512
+ ru: {
513
+ documentTitle: "AgInTiFlow",
514
+ brandKicker: "AI-автоматизация от AgInTi Inc.",
515
+ languageLabel: "Язык",
516
+ intro:
517
+ "Браузерный агент с выбором провайдера, возобновляемыми запусками, сохраненными настройками и опциональным защищенным shell tool с Docker sandbox.",
518
+ providerLabel: "Провайдер",
519
+ modelLabel: "Модель",
520
+ goalLabel: "Цель",
521
+ goalPlaceholder: "Откройте сайт и сделайте summary или используйте run_command для простой проверки терминала.",
522
+ startUrlLabel: "Начальный URL",
523
+ startUrlPlaceholder: "https://news.ycombinator.com",
524
+ allowedDomainsLabel: "Разрешенные домены",
525
+ allowedDomainsPlaceholder: "news.ycombinator.com,github.com",
526
+ commandCwdLabel: "Рабочая папка",
527
+ maxStepsLabel: "Макс. шагов",
528
+ headlessLabel: "Headless browser",
529
+ shellToolLabel: "Включить shell tool",
530
+ dockerSandboxLabel: "Использовать Docker sandbox",
531
+ allowPasswordsLabel: "Разрешить ввод паролей",
532
+ allowDestructiveLabel: "Разрешить опасные действия",
533
+ dockerImageLabel: "Docker image",
534
+ dockerImagePlaceholder: "agintiflow-sandbox:latest",
535
+ startRunButton: "Запустить",
536
+ runOutputTitle: "Вывод запуска",
537
+ noRunStarted: "Запуск еще не начат.",
538
+ conversationTitle: "Диалог",
539
+ noSessionSelected: "Сессия не выбрана",
540
+ selectRecentSession: "Выберите недавнюю сессию",
541
+ noConversationLoaded: "Диалог не загружен.",
542
+ chatPlaceholder: "Продолжите выбранный диалог здесь.",
543
+ sendMessageButton: "Отправить",
544
+ assistantLabel: "Ассистент",
545
+ youLabel: "Вы",
546
+ keysLabel: "Ключи",
547
+ availableLabel: "доступен",
548
+ missingLabel: "нет",
549
+ goalRequired: "Цель обязательна.",
550
+ startingRun: "Запуск начинается...",
551
+ failedStartRun: "Не удалось начать запуск.",
552
+ noRunSelected: "Запуск не выбран.",
553
+ selectSessionFirst: "Сначала выберите или начните сессию.",
554
+ messageRequired: "Сообщение обязательно.",
555
+ sendingStatus: "Отправка...",
556
+ runningStatus: "Выполняется...",
557
+ failedContinue: "Не удалось продолжить диалог.",
558
+ },
559
+ };
560
+
561
+ const form = document.querySelector("#run-form");
562
+ const languageField = document.querySelector("#language");
563
+ const routingModeField = document.querySelector("#routingMode");
564
+ const providerField = document.querySelector("#provider");
565
+ const modelField = document.querySelector("#model");
566
+ const routingHintEl = document.querySelector("#routing-hint");
567
+ const sandboxModeField = document.querySelector("#sandboxMode");
568
+ const packageInstallPolicyField = document.querySelector("#packageInstallPolicy");
569
+ const logsEl = document.querySelector("#logs");
570
+ const runMetaEl = document.querySelector("#run-meta");
571
+ const keyStatusEl = document.querySelector("#key-status");
572
+ const wrapperStatusEl = document.querySelector("#wrapper-status");
573
+ const sandboxStatusEl = document.querySelector("#sandbox-status");
574
+ const sandboxLogsEl = document.querySelector("#sandbox-logs");
575
+ const checkSandboxButton = document.querySelector("#check-sandbox");
576
+ const preflightSandboxButton = document.querySelector("#preflight-sandbox");
577
+ const sessionSelectEl = document.querySelector("#session-select");
578
+ const chatThreadEl = document.querySelector("#chat-thread");
579
+ const chatFormEl = document.querySelector("#chat-form");
580
+ const chatInputEl = document.querySelector("#chat-input");
581
+ const chatStatusEl = document.querySelector("#chat-status");
582
+ const translatableNodes = [...document.querySelectorAll("[data-i18n]")];
583
+ const placeholderNodes = [...document.querySelectorAll("[data-i18n-placeholder]")];
584
+
585
+ const defaults = {
586
+ openai: "gpt-5.4-mini",
587
+ deepseek: "deepseek-v4-flash",
588
+ };
589
+
590
+ let currentLanguage = "en";
591
+ let routingPresets = {};
592
+ let currentSessionId = "";
593
+ let pollTimer = null;
594
+ let saveTimer = null;
595
+ let lastChatEntries = [];
596
+ let lastSessions = [];
597
+ let lastKeyStatus = null;
598
+ let lastWrappers = [];
599
+ let lastSandbox = null;
600
+
601
+ function normalizeLanguage(language) {
602
+ const lower = String(language || "").toLowerCase();
603
+ if (lower.startsWith("zh-hant") || lower.includes("tw") || lower.includes("hk")) return "zh-Hant";
604
+ if (lower.startsWith("zh")) return "zh-Hans";
605
+ return supportedLanguages.find((supported) => lower.startsWith(supported.toLowerCase())) || "en";
606
+ }
607
+
608
+ function t(key) {
609
+ return translations[currentLanguage]?.[key] || translations.en[key] || key;
610
+ }
611
+
612
+ function setLogs(text, mode = "active") {
613
+ logsEl.dataset.mode = mode;
614
+ logsEl.textContent = text;
615
+ }
616
+
617
+ function renderKeyStatus(status = lastKeyStatus) {
618
+ lastKeyStatus = status;
619
+ if (!status) return;
620
+ keyStatusEl.textContent = `${t("keysLabel")}: OpenAI ${
621
+ status.openai ? t("availableLabel") : t("missingLabel")
622
+ } · DeepSeek ${status.deepseek ? t("availableLabel") : t("missingLabel")}`;
623
+ }
624
+
625
+ function renderWrapperStatus(wrappers = lastWrappers) {
626
+ lastWrappers = wrappers || [];
627
+ if (lastWrappers.length === 0) {
628
+ wrapperStatusEl.textContent = "";
629
+ return;
630
+ }
631
+ wrapperStatusEl.textContent = `${t("wrappersLabel")}: ${lastWrappers
632
+ .map((wrapper) => `${wrapper.label || wrapper.name} ${wrapper.available ? t("availableLabel") : t("missingLabel")}`)
633
+ .join(" · ")}`;
634
+ }
635
+
636
+ function renderSandboxStatus(status = lastSandbox) {
637
+ lastSandbox = status;
638
+ if (!status) {
639
+ sandboxStatusEl.textContent = t("sandboxStatusEmpty");
640
+ sandboxLogsEl.textContent = t("sandboxLogsEmpty");
641
+ return;
642
+ }
643
+
644
+ const readiness =
645
+ status.sandboxMode === "host"
646
+ ? status.workspaceReadable
647
+ : status.dockerAvailable && status.imageReady && status.workspaceReadable;
648
+ const parts = [
649
+ `${status.sandboxMode} · ${readiness ? t("sandboxReadyLabel") : t("sandboxNotReadyLabel")}`,
650
+ `image=${status.image}`,
651
+ `workspace=${status.workspace}`,
652
+ `package=${status.packageInstallPolicy}`,
653
+ `docker=${status.dockerAvailable ? t("availableLabel") : t("missingLabel")}`,
654
+ `imageReady=${status.imageReady}`,
655
+ ];
656
+ sandboxStatusEl.textContent = parts.join(" · ");
657
+ renderSandboxLogs(status.logs || []);
658
+ }
659
+
660
+ function renderSandboxLogs(logs) {
661
+ if (!logs || logs.length === 0) {
662
+ sandboxLogsEl.textContent = t("sandboxLogsEmpty");
663
+ return;
664
+ }
665
+ sandboxLogsEl.textContent = logs
666
+ .slice(-12)
667
+ .map((entry) => `[${entry.at}] ${entry.type}\n${JSON.stringify(entry.data || {}, null, 2)}`)
668
+ .join("\n\n");
669
+ }
670
+
671
+ function updateRoutingHint() {
672
+ const mode = routingModeField.value || "smart";
673
+ const hintKey = {
674
+ smart: "routingHintSmart",
675
+ fast: "routingHintFast",
676
+ complex: "routingHintComplex",
677
+ manual: "routingHintManual",
678
+ }[mode];
679
+ routingHintEl.textContent = t(hintKey);
680
+
681
+ if (mode !== "manual") {
682
+ const preset = mode === "complex" ? routingPresets.complex : routingPresets.fast;
683
+ if (preset) {
684
+ providerField.value = preset.provider === "deepseek" ? "deepseek" : providerField.value;
685
+ modelField.value = preset.model || modelField.value;
686
+ }
687
+ }
688
+ }
689
+
690
+ function applyLanguage(language, { persist = true } = {}) {
691
+ currentLanguage = normalizeLanguage(language);
692
+ document.documentElement.lang = currentLanguage;
693
+ document.documentElement.dir = currentLanguage === "ar" ? "rtl" : "ltr";
694
+ document.title = t("documentTitle");
695
+ languageField.value = currentLanguage;
696
+
697
+ for (const node of translatableNodes) {
698
+ if (node === logsEl && logsEl.dataset.mode !== "empty") continue;
699
+ node.textContent = t(node.dataset.i18n);
700
+ }
701
+
702
+ for (const node of placeholderNodes) {
703
+ node.placeholder = t(node.dataset.i18nPlaceholder);
704
+ }
705
+
706
+ renderKeyStatus();
707
+ renderWrapperStatus();
708
+ renderSandboxStatus();
709
+ updateRoutingHint();
710
+ renderSessions(lastSessions);
711
+ renderChat(lastChatEntries);
712
+ if (persist) schedulePreferenceSave();
713
+ }
714
+
715
+ function formPayload() {
716
+ return {
717
+ language: languageField.value,
718
+ routingMode: routingModeField.value,
719
+ provider: providerField.value,
720
+ model: modelField.value.trim(),
721
+ startUrl: document.querySelector("#startUrl").value.trim(),
722
+ allowedDomains: document.querySelector("#allowedDomains").value.trim(),
723
+ commandCwd: document.querySelector("#commandCwd").value.trim(),
724
+ maxSteps: Number(document.querySelector("#maxSteps").value) || 15,
725
+ sandboxMode: sandboxModeField.value,
726
+ packageInstallPolicy: packageInstallPolicyField.value,
727
+ headless: document.querySelector("#headless").checked,
728
+ allowShellTool: document.querySelector("#allowShellTool").checked,
729
+ allowWrapperTools: document.querySelector("#allowWrapperTools").checked,
730
+ useDockerSandbox: sandboxModeField.value !== "host",
731
+ dockerSandboxImage: document.querySelector("#dockerSandboxImage").value.trim(),
732
+ allowPasswords: document.querySelector("#allowPasswords").checked,
733
+ allowDestructive: document.querySelector("#allowDestructive").checked,
734
+ };
735
+ }
736
+
737
+ function renderLogs(run) {
738
+ const lines = [];
739
+ lines.push(`status=${run.status} session=${run.sessionId} provider=${run.provider} model=${run.model}`);
740
+ if (run.result) lines.push(`result=${run.result}`);
741
+ if (run.error) lines.push(`error=${run.error}`);
742
+ lines.push("");
743
+
744
+ for (const entry of run.logs || []) {
745
+ lines.push(`[${entry.at}] ${entry.kind}: ${entry.message}`);
746
+ if (entry.data && Object.keys(entry.data).length > 0) {
747
+ lines.push(JSON.stringify(entry.data, null, 2));
748
+ }
749
+ lines.push("");
750
+ }
751
+
752
+ setLogs(lines.join("\n"));
753
+ }
754
+
755
+ function escapeHtml(value) {
756
+ return String(value || "").replace(/[&<>]/g, (ch) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" }[ch]));
757
+ }
758
+
759
+ function renderChat(chatEntries) {
760
+ lastChatEntries = chatEntries || [];
761
+
762
+ if (lastChatEntries.length === 0) {
763
+ chatThreadEl.innerHTML = `<div class="subtle">${t("noConversationLoaded")}</div>`;
764
+ return;
765
+ }
766
+
767
+ chatThreadEl.innerHTML = lastChatEntries
768
+ .map((entry) => {
769
+ const role = entry.role === "assistant" ? "assistant" : "user";
770
+ const label = role === "assistant" ? t("assistantLabel") : t("youLabel");
771
+ const content = escapeHtml(entry.content).replace(/\n/g, "<br>");
772
+ return `
773
+ <article class="chat-item ${role}">
774
+ <div class="chat-meta">${label}${entry.at ? ` · ${new Date(entry.at).toLocaleString()}` : ""}</div>
775
+ <div class="chat-content">${content}</div>
776
+ </article>
777
+ `;
778
+ })
779
+ .join("");
780
+
781
+ chatThreadEl.scrollTop = chatThreadEl.scrollHeight;
782
+ }
783
+
784
+ function renderSessions(sessions) {
785
+ lastSessions = sessions || [];
786
+ const current = sessionSelectEl.value;
787
+ const options = [`<option value="">${t("selectRecentSession")}</option>`]
788
+ .concat(
789
+ lastSessions.map((session) => {
790
+ const goal = escapeHtml(`${session.goal.slice(0, 60)}${session.goal.length > 60 ? "..." : ""}`);
791
+ const label = `${escapeHtml(session.provider)} · ${goal}`;
792
+ return `<option value="${escapeHtml(session.sessionId)}">${label}</option>`;
793
+ })
794
+ )
795
+ .join("");
796
+
797
+ sessionSelectEl.innerHTML = options;
798
+ if (current && [...sessionSelectEl.options].some((opt) => opt.value === current)) {
799
+ sessionSelectEl.value = current;
800
+ }
801
+ }
802
+
803
+ async function refreshSessions() {
804
+ const response = await fetch("/api/sessions");
805
+ const data = await response.json();
806
+ renderSessions(data.sessions || []);
807
+ }
808
+
809
+ async function refreshRun() {
810
+ if (!currentSessionId) return;
811
+ const response = await fetch(`/api/runs/${encodeURIComponent(currentSessionId)}`);
812
+ if (!response.ok) return;
813
+ const run = await response.json();
814
+ runMetaEl.textContent = `${run.status} · ${run.sessionId}`;
815
+ renderLogs(run);
816
+
817
+ if (run.status === "finished" || run.status === "failed") {
818
+ clearInterval(pollTimer);
819
+ pollTimer = null;
820
+ await refreshChat();
821
+ await refreshSessions();
822
+ }
823
+ }
824
+
825
+ async function refreshChat() {
826
+ if (!currentSessionId) {
827
+ renderChat([]);
828
+ return;
829
+ }
830
+
831
+ const response = await fetch(`/api/sessions/${encodeURIComponent(currentSessionId)}/chat`);
832
+ if (!response.ok) {
833
+ renderChat([]);
834
+ return;
835
+ }
836
+
837
+ const data = await response.json();
838
+ renderChat(data.chat || []);
839
+ }
840
+
841
+ async function savePreferences() {
842
+ await fetch("/api/preferences", {
843
+ method: "POST",
844
+ headers: { "Content-Type": "application/json" },
845
+ body: JSON.stringify(formPayload()),
846
+ });
847
+ }
848
+
849
+ async function refreshSandboxStatus() {
850
+ await savePreferences();
851
+ sandboxStatusEl.textContent = "Checking sandbox...";
852
+ const response = await fetch("/api/sandbox/status");
853
+ const data = await response.json();
854
+ if (!response.ok) {
855
+ sandboxStatusEl.textContent = data.error || "Sandbox status failed.";
856
+ return;
857
+ }
858
+ renderSandboxStatus(data.status);
859
+ }
860
+
861
+ async function runSandboxPreflight() {
862
+ await savePreferences();
863
+ sandboxStatusEl.textContent = "Running preflight...";
864
+ const response = await fetch("/api/sandbox/preflight", {
865
+ method: "POST",
866
+ headers: { "Content-Type": "application/json" },
867
+ body: JSON.stringify({ ...formPayload(), buildImage: true }),
868
+ });
869
+ const data = await response.json();
870
+ if (!response.ok) {
871
+ sandboxStatusEl.textContent = data.error || "Sandbox preflight failed.";
872
+ return;
873
+ }
874
+ renderSandboxStatus(data.status);
875
+ const checkLines = (data.checks || [])
876
+ .map((check) => `${check.ok ? "OK" : "FAIL"} ${check.command}: ${check.stdout || check.stderr || check.error || ""}`)
877
+ .join("\n");
878
+ sandboxLogsEl.textContent = [checkLines, JSON.stringify({ manifests: data.manifests || [] }, null, 2)]
879
+ .filter(Boolean)
880
+ .join("\n\n");
881
+ }
882
+
883
+ function schedulePreferenceSave() {
884
+ clearTimeout(saveTimer);
885
+ saveTimer = setTimeout(() => {
886
+ savePreferences().catch(() => {});
887
+ }, 250);
888
+ }
889
+
890
+ languageField.addEventListener("change", () => {
891
+ applyLanguage(languageField.value);
892
+ });
893
+
894
+ routingModeField.addEventListener("change", () => {
895
+ updateRoutingHint();
896
+ schedulePreferenceSave();
897
+ });
898
+
899
+ checkSandboxButton.addEventListener("click", () => {
900
+ refreshSandboxStatus().catch((error) => {
901
+ sandboxStatusEl.textContent = String(error);
902
+ });
903
+ });
904
+
905
+ preflightSandboxButton.addEventListener("click", () => {
906
+ runSandboxPreflight().catch((error) => {
907
+ sandboxStatusEl.textContent = String(error);
908
+ });
909
+ });
910
+
911
+ providerField.addEventListener("change", () => {
912
+ if (!modelField.value.trim() || modelField.value === defaults.openai || modelField.value === defaults.deepseek) {
913
+ modelField.value = defaults[providerField.value] || "";
914
+ }
915
+ schedulePreferenceSave();
916
+ });
917
+
918
+ form.addEventListener("input", schedulePreferenceSave);
919
+ form.addEventListener("change", schedulePreferenceSave);
920
+
921
+ form.addEventListener("submit", async (event) => {
922
+ event.preventDefault();
923
+
924
+ const goal = document.querySelector("#goal").value.trim();
925
+ if (!goal) {
926
+ setLogs(t("goalRequired"), "empty");
927
+ return;
928
+ }
929
+
930
+ const payload = {
931
+ ...formPayload(),
932
+ goal,
933
+ };
934
+
935
+ setLogs(t("startingRun"));
936
+ runMetaEl.textContent = "";
937
+ chatStatusEl.textContent = "";
938
+
939
+ const response = await fetch("/api/runs", {
940
+ method: "POST",
941
+ headers: { "Content-Type": "application/json" },
942
+ body: JSON.stringify(payload),
943
+ });
944
+
945
+ const data = await response.json();
946
+ if (!response.ok) {
947
+ setLogs(data.error || t("failedStartRun"));
948
+ return;
949
+ }
950
+
951
+ currentSessionId = data.sessionId;
952
+ sessionSelectEl.value = currentSessionId;
953
+ runMetaEl.textContent = `${t("runningStatus").replace("...", "")} · ${currentSessionId}`;
954
+ await refreshSessions();
955
+ await refreshChat();
956
+
957
+ if (pollTimer) clearInterval(pollTimer);
958
+ pollTimer = setInterval(refreshRun, 1500);
959
+ await refreshRun();
960
+ });
961
+
962
+ sessionSelectEl.addEventListener("change", async () => {
963
+ currentSessionId = sessionSelectEl.value;
964
+ if (!currentSessionId) {
965
+ runMetaEl.textContent = "";
966
+ setLogs(t("noRunSelected"), "empty");
967
+ renderChat([]);
968
+ return;
969
+ }
970
+ await refreshRun();
971
+ await refreshChat();
972
+ });
973
+
974
+ chatFormEl.addEventListener("submit", async (event) => {
975
+ event.preventDefault();
976
+
977
+ if (!currentSessionId) {
978
+ chatStatusEl.textContent = t("selectSessionFirst");
979
+ return;
980
+ }
981
+
982
+ const content = chatInputEl.value.trim();
983
+ if (!content) {
984
+ chatStatusEl.textContent = t("messageRequired");
985
+ return;
986
+ }
987
+
988
+ chatStatusEl.textContent = t("sendingStatus");
989
+
990
+ const response = await fetch(`/api/sessions/${encodeURIComponent(currentSessionId)}/messages`, {
991
+ method: "POST",
992
+ headers: { "Content-Type": "application/json" },
993
+ body: JSON.stringify({
994
+ ...formPayload(),
995
+ content,
996
+ }),
997
+ });
998
+
999
+ const data = await response.json();
1000
+ if (!response.ok) {
1001
+ chatStatusEl.textContent = data.error || t("failedContinue");
1002
+ return;
1003
+ }
1004
+
1005
+ currentSessionId = data.sessionId;
1006
+ chatInputEl.value = "";
1007
+ chatStatusEl.textContent = t("runningStatus");
1008
+ await refreshSessions();
1009
+ await refreshChat();
1010
+
1011
+ if (pollTimer) clearInterval(pollTimer);
1012
+ pollTimer = setInterval(refreshRun, 1500);
1013
+ await refreshRun();
1014
+ });
1015
+
1016
+ async function loadConfig() {
1017
+ const response = await fetch("/api/config");
1018
+ const data = await response.json();
1019
+ const prefs = data.preferences || {};
1020
+ routingPresets = data.routing?.presets || {};
1021
+ defaults.openai = data.defaults?.openai?.model || defaults.openai;
1022
+ defaults.deepseek = routingPresets.fast?.model || data.defaults?.deepseek?.model || defaults.deepseek;
1023
+
1024
+ applyLanguage(prefs.language || normalizeLanguage(navigator.language || "en"), { persist: false });
1025
+
1026
+ routingModeField.value = prefs.routingMode || "smart";
1027
+ providerField.value = prefs.provider || "deepseek";
1028
+ modelField.value = prefs.model || defaults[providerField.value] || "deepseek-v4-flash";
1029
+ document.querySelector("#startUrl").value = prefs.startUrl || "";
1030
+ document.querySelector("#allowedDomains").value = prefs.allowedDomains || "";
1031
+ document.querySelector("#commandCwd").value = prefs.commandCwd || "/home/lachlan/ProjectsLFS/Agent";
1032
+ document.querySelector("#headless").checked = prefs.headless ?? data.defaults.headless;
1033
+ document.querySelector("#maxSteps").value = prefs.maxSteps ?? data.defaults.maxSteps;
1034
+ sandboxModeField.value = prefs.sandboxMode || (prefs.useDockerSandbox ? "docker-readonly" : "host");
1035
+ packageInstallPolicyField.value = prefs.packageInstallPolicy || "prompt";
1036
+ document.querySelector("#allowShellTool").checked = prefs.allowShellTool ?? true;
1037
+ document.querySelector("#allowWrapperTools").checked = prefs.allowWrapperTools ?? false;
1038
+ document.querySelector("#dockerSandboxImage").value = prefs.dockerSandboxImage || "agintiflow-sandbox:latest";
1039
+ document.querySelector("#allowPasswords").checked = prefs.allowPasswords ?? false;
1040
+ document.querySelector("#allowDestructive").checked = prefs.allowDestructive ?? false;
1041
+
1042
+ renderKeyStatus(data.keyStatus);
1043
+ renderWrapperStatus(data.wrappers || []);
1044
+ renderSandboxLogs(data.sandbox?.logs || []);
1045
+ updateRoutingHint();
1046
+
1047
+ renderSessions(data.sessions || []);
1048
+ if (data.sessions && data.sessions.length > 0) {
1049
+ currentSessionId = data.sessions[0].sessionId;
1050
+ sessionSelectEl.value = currentSessionId;
1051
+ await refreshRun();
1052
+ await refreshChat();
1053
+ }
1054
+ }
1055
+
1056
+ applyLanguage(normalizeLanguage(navigator.language || "en"), { persist: false });
1057
+
1058
+ loadConfig().catch((error) => {
1059
+ setLogs(String(error));
1060
+ });