tide-commander 1.153.0 → 1.155.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/assets/{BossLogsModal-Dgz-N5gw.js → BossLogsModal-OFobifVq.js} +1 -1
  3. package/dist/assets/{BossSpawnModal-BK_rYu8s.js → BossSpawnModal-Dho-x6rm.js} +1 -1
  4. package/dist/assets/{ControlsModal-hC_AiCpk.js → ControlsModal-DJBDhvNO.js} +1 -1
  5. package/dist/assets/{DockerLogsModal-D7ogyaZr.js → DockerLogsModal-C9TRTcsO.js} +1 -1
  6. package/dist/assets/{EmbeddedEditor-BFzUOmwz.js → EmbeddedEditor-GDE_Khwz.js} +1 -1
  7. package/dist/assets/{GmailOAuthSetup-C_Yc5Ypv.js → GmailOAuthSetup-D9m5zMWC.js} +1 -1
  8. package/dist/assets/{GoogleOAuthSetup-CefQSOYm.js → GoogleOAuthSetup-BxkN7LjY.js} +1 -1
  9. package/dist/assets/{IframeModal-BbeuiE04.js → IframeModal-Do_I_wI7.js} +1 -1
  10. package/dist/assets/{IntegrationsPanel-xh3TgJLH.js → IntegrationsPanel-jRrwC2-G.js} +2 -2
  11. package/dist/assets/{LogViewerModal-CE2L8nWJ.js → LogViewerModal-BtcUe5i2.js} +1 -1
  12. package/dist/assets/{MonitoringModal-A2SN3uAJ.js → MonitoringModal-BTmSXrzv.js} +1 -1
  13. package/dist/assets/{PM2LogsModal-DdlhCa8u.js → PM2LogsModal-CUf7NGHz.js} +1 -1
  14. package/dist/assets/{RestoreArchivedAreaModal-BvHo-OxE.js → RestoreArchivedAreaModal-B72UKeRP.js} +1 -1
  15. package/dist/assets/{Scene2DCanvas-t6RuUegr.js → Scene2DCanvas-BCfxX7JU.js} +1 -1
  16. package/dist/assets/{SceneManager-TQ2_AvFn.js → SceneManager-BQubdlgd.js} +4 -4
  17. package/dist/assets/{SkillsPanel-PjzjcaoG.js → SkillsPanel-3qreMiJs.js} +1 -1
  18. package/dist/assets/{SlackMultiInstanceSetup-ms4r32Z8.js → SlackMultiInstanceSetup-BSCT5nAB.js} +1 -1
  19. package/dist/assets/{SpawnModal-B1FS8m9h.js → SpawnModal-DaWjnugM.js} +1 -1
  20. package/dist/assets/{StatisticsModal-mmz8hnD5.js → StatisticsModal-DCjCAU2o.js} +1 -1
  21. package/dist/assets/{SubordinateAssignmentModal-BXoG-Xgd.js → SubordinateAssignmentModal-9iPXraIi.js} +1 -1
  22. package/dist/assets/{TriggerManagerPanel-ODJyjvY-.js → TriggerManagerPanel-B4dCLEi_.js} +1 -1
  23. package/dist/assets/{WorkflowEditorPanel-9wtY0Tn3.js → WorkflowEditorPanel-BaYXKfYH.js} +1 -1
  24. package/dist/assets/{area-logos-BLyzmh47.js → area-logos-CgVT13bp.js} +1 -1
  25. package/dist/assets/{index-CN2p2fxH.js → index-0y0WY2XK.js} +1 -1
  26. package/dist/assets/index-BPjQZacc.css +1 -0
  27. package/dist/assets/{index-CdbLxNJr.js → index-C-tbJxGb.js} +1 -1
  28. package/dist/assets/{index-1hwiSf7o.js → index-C3Yb-3TP.js} +1 -1
  29. package/dist/assets/index-C3jAbuv2.js +1 -0
  30. package/dist/assets/{index-Ci2ZKu0n.js → index-C7t_6aP3.js} +2 -2
  31. package/dist/assets/{index-CBkh8xcE.js → index-CetQ5OX1.js} +1 -1
  32. package/dist/assets/{index-Cf-PINah.js → index-D8dlRudN.js} +3 -3
  33. package/dist/assets/index-DNjdlOwh.js +1 -0
  34. package/dist/assets/{index-D8vFfLs4.js → index-DiZy3nuB.js} +1 -1
  35. package/dist/assets/main-BHBs2E1L.css +1 -0
  36. package/dist/assets/{main-9TcmAtcq.js → main-DDwa0x9e.js} +113 -113
  37. package/dist/assets/{web-BVIPIXA1.js → web-Dj2DYQG1.js} +1 -1
  38. package/dist/assets/{web-Cz3TYK-R.js → web-EBdZk56y.js} +1 -1
  39. package/dist/assets/{web-BAkfcL4w.js → web-PXjd4vZ8.js} +1 -1
  40. package/dist/build-info.json +1 -1
  41. package/dist/index.html +2 -2
  42. package/dist/locales/de/config.json +1 -0
  43. package/dist/locales/en/config.json +6 -1
  44. package/dist/locales/en/terminal.json +7 -1
  45. package/dist/locales/es/config.json +4 -1
  46. package/dist/locales/es/terminal.json +8 -0
  47. package/dist/locales/fr/config.json +1 -0
  48. package/dist/locales/hi/config.json +1 -0
  49. package/dist/locales/it/config.json +1 -0
  50. package/dist/locales/ja/config.json +1 -0
  51. package/dist/locales/pt/config.json +1 -0
  52. package/dist/locales/ru/config.json +1 -0
  53. package/dist/locales/zh-CN/config.json +1 -0
  54. package/dist/src/packages/server/codex/app-server/app-server-event-adapter.js +222 -0
  55. package/dist/src/packages/server/codex/app-server/app-server-process.js +304 -0
  56. package/dist/src/packages/server/codex/app-server/app-server-recovery-store.js +76 -0
  57. package/dist/src/packages/server/codex/app-server/codex-app-server-runner.js +453 -0
  58. package/dist/src/packages/server/opencode/json-event-parser.js +3 -1
  59. package/dist/src/packages/server/opencode/server/opencode-server-event-adapter.js +127 -0
  60. package/dist/src/packages/server/opencode/server/opencode-server-process.js +285 -0
  61. package/dist/src/packages/server/opencode/server/opencode-server-recovery-store.js +73 -0
  62. package/dist/src/packages/server/opencode/server/opencode-server-runner.js +384 -0
  63. package/dist/src/packages/server/routes/agents.js +57 -1
  64. package/dist/src/packages/server/routes/system.js +110 -1
  65. package/dist/src/packages/server/runtime/codex-runner-router.js +99 -0
  66. package/dist/src/packages/server/runtime/codex-runtime-provider.js +5 -3
  67. package/dist/src/packages/server/runtime/opencode-runner-router.js +93 -0
  68. package/dist/src/packages/server/runtime/opencode-runtime-provider.js +5 -3
  69. package/dist/src/packages/server/services/claude-credentials-service.js +208 -1
  70. package/dist/src/packages/server/services/claude-usage-service.js +35 -28
  71. package/dist/src/packages/server/services/provider-credentials-service.js +338 -0
  72. package/dist/src/packages/server/services/system-prompt-service.js +94 -0
  73. package/package.json +1 -1
  74. package/dist/assets/index-CbfoJ_Vc.js +0 -1
  75. package/dist/assets/index-E5IMeM80.js +0 -1
  76. package/dist/assets/index-XI0_ICy6.css +0 -1
  77. package/dist/assets/main-DP0iv8Le.css +0 -1
@@ -1 +1 @@
1
- import{df as a}from"./main-9TcmAtcq.js";import{ImpactStyle as i,NotificationType as r}from"./index-Ci2ZKu0n.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class h extends a{constructor(){super(...arguments),this.selectionStarted=!1}async impact(t){const e=this.patternForImpact(t==null?void 0:t.style);this.vibrateWithPattern(e)}async notification(t){const e=this.patternForNotification(t==null?void 0:t.type);this.vibrateWithPattern(e)}async vibrate(t){const e=(t==null?void 0:t.duration)||300;this.vibrateWithPattern([e])}async selectionStart(){this.selectionStarted=!0}async selectionChanged(){this.selectionStarted&&this.vibrateWithPattern([70])}async selectionEnd(){this.selectionStarted=!1}patternForImpact(t=i.Heavy){return t===i.Medium?[43]:t===i.Light?[20]:[61]}patternForNotification(t=r.Success){return t===r.Warning?[30,40,30,50,60]:t===r.Error?[27,45,50]:[35,65,21]}vibrateWithPattern(t){if(navigator.vibrate)navigator.vibrate(t);else throw this.unavailable("Browser does not support the vibrate API")}}export{h as HapticsWeb};
1
+ import{df as a}from"./main-DDwa0x9e.js";import{ImpactStyle as i,NotificationType as r}from"./index-C7t_6aP3.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class h extends a{constructor(){super(...arguments),this.selectionStarted=!1}async impact(t){const e=this.patternForImpact(t==null?void 0:t.style);this.vibrateWithPattern(e)}async notification(t){const e=this.patternForNotification(t==null?void 0:t.type);this.vibrateWithPattern(e)}async vibrate(t){const e=(t==null?void 0:t.duration)||300;this.vibrateWithPattern([e])}async selectionStart(){this.selectionStarted=!0}async selectionChanged(){this.selectionStarted&&this.vibrateWithPattern([70])}async selectionEnd(){this.selectionStarted=!1}patternForImpact(t=i.Heavy){return t===i.Medium?[43]:t===i.Light?[20]:[61]}patternForNotification(t=r.Success){return t===r.Warning?[30,40,30,50,60]:t===r.Error?[27,45,50]:[35,65,21]}vibrateWithPattern(t){if(navigator.vibrate)navigator.vibrate(t);else throw this.unavailable("Browser does not support the vibrate API")}}export{h as HapticsWeb};
@@ -1 +1 @@
1
- import{df as s}from"./main-9TcmAtcq.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class l extends s{constructor(){super(...arguments),this.pending=[],this.deliveredNotifications=[],this.hasNotificationSupport=()=>{if(!("Notification"in window)||!Notification.requestPermission)return!1;if(Notification.permission!=="granted")try{new Notification("")}catch(i){if(i instanceof Error&&i.name==="TypeError")return!1}return!0}}async getDeliveredNotifications(){const i=[];for(const t of this.deliveredNotifications){const e={title:t.title,id:parseInt(t.tag),body:t.body};i.push(e)}return{notifications:i}}async removeDeliveredNotifications(i){for(const t of i.notifications){const e=this.deliveredNotifications.find(n=>n.tag===String(t.id));e==null||e.close(),this.deliveredNotifications=this.deliveredNotifications.filter(()=>!e)}}async removeAllDeliveredNotifications(){for(const i of this.deliveredNotifications)i.close();this.deliveredNotifications=[]}async createChannel(){throw this.unimplemented("Not implemented on web.")}async deleteChannel(){throw this.unimplemented("Not implemented on web.")}async listChannels(){throw this.unimplemented("Not implemented on web.")}async schedule(i){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");for(const t of i.notifications)this.sendNotification(t);return{notifications:i.notifications.map(t=>({id:t.id}))}}async getPending(){return{notifications:this.pending}}async registerActionTypes(){throw this.unimplemented("Not implemented on web.")}async cancel(i){this.pending=this.pending.filter(t=>!i.notifications.find(e=>e.id===t.id))}async areEnabled(){const{display:i}=await this.checkPermissions();return{value:i==="granted"}}async changeExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async checkExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async requestPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(await Notification.requestPermission())}}async checkPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(Notification.permission)}}transformNotificationPermission(i){switch(i){case"granted":return"granted";case"denied":return"denied";default:return"prompt"}}sendPending(){var i;const t=[],e=new Date().getTime();for(const n of this.pending)!((i=n.schedule)===null||i===void 0)&&i.at&&n.schedule.at.getTime()<=e&&(this.buildNotification(n),t.push(n));this.pending=this.pending.filter(n=>!t.find(o=>o===n))}sendNotification(i){var t;if(!((t=i.schedule)===null||t===void 0)&&t.at){const e=i.schedule.at.getTime()-new Date().getTime();this.pending.push(i),setTimeout(()=>{this.sendPending()},e);return}this.buildNotification(i)}buildNotification(i){const t=new Notification(i.title,{body:i.body,tag:String(i.id)});return t.addEventListener("click",this.onClick.bind(this,i),!1),t.addEventListener("show",this.onShow.bind(this,i),!1),t.addEventListener("close",()=>{this.deliveredNotifications=this.deliveredNotifications.filter(()=>!this)},!1),this.deliveredNotifications.push(t),t}onClick(i){const t={actionId:"tap",notification:i};this.notifyListeners("localNotificationActionPerformed",t)}onShow(i){this.notifyListeners("localNotificationReceived",i)}}export{l as LocalNotificationsWeb};
1
+ import{df as s}from"./main-DDwa0x9e.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class l extends s{constructor(){super(...arguments),this.pending=[],this.deliveredNotifications=[],this.hasNotificationSupport=()=>{if(!("Notification"in window)||!Notification.requestPermission)return!1;if(Notification.permission!=="granted")try{new Notification("")}catch(i){if(i instanceof Error&&i.name==="TypeError")return!1}return!0}}async getDeliveredNotifications(){const i=[];for(const t of this.deliveredNotifications){const e={title:t.title,id:parseInt(t.tag),body:t.body};i.push(e)}return{notifications:i}}async removeDeliveredNotifications(i){for(const t of i.notifications){const e=this.deliveredNotifications.find(n=>n.tag===String(t.id));e==null||e.close(),this.deliveredNotifications=this.deliveredNotifications.filter(()=>!e)}}async removeAllDeliveredNotifications(){for(const i of this.deliveredNotifications)i.close();this.deliveredNotifications=[]}async createChannel(){throw this.unimplemented("Not implemented on web.")}async deleteChannel(){throw this.unimplemented("Not implemented on web.")}async listChannels(){throw this.unimplemented("Not implemented on web.")}async schedule(i){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");for(const t of i.notifications)this.sendNotification(t);return{notifications:i.notifications.map(t=>({id:t.id}))}}async getPending(){return{notifications:this.pending}}async registerActionTypes(){throw this.unimplemented("Not implemented on web.")}async cancel(i){this.pending=this.pending.filter(t=>!i.notifications.find(e=>e.id===t.id))}async areEnabled(){const{display:i}=await this.checkPermissions();return{value:i==="granted"}}async changeExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async checkExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async requestPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(await Notification.requestPermission())}}async checkPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(Notification.permission)}}transformNotificationPermission(i){switch(i){case"granted":return"granted";case"denied":return"denied";default:return"prompt"}}sendPending(){var i;const t=[],e=new Date().getTime();for(const n of this.pending)!((i=n.schedule)===null||i===void 0)&&i.at&&n.schedule.at.getTime()<=e&&(this.buildNotification(n),t.push(n));this.pending=this.pending.filter(n=>!t.find(o=>o===n))}sendNotification(i){var t;if(!((t=i.schedule)===null||t===void 0)&&t.at){const e=i.schedule.at.getTime()-new Date().getTime();this.pending.push(i),setTimeout(()=>{this.sendPending()},e);return}this.buildNotification(i)}buildNotification(i){const t=new Notification(i.title,{body:i.body,tag:String(i.id)});return t.addEventListener("click",this.onClick.bind(this,i),!1),t.addEventListener("show",this.onShow.bind(this,i),!1),t.addEventListener("close",()=>{this.deliveredNotifications=this.deliveredNotifications.filter(()=>!this)},!1),this.deliveredNotifications.push(t),t}onClick(i){const t={actionId:"tap",notification:i};this.notifyListeners("localNotificationActionPerformed",t)}onShow(i){this.notifyListeners("localNotificationReceived",i)}}export{l as LocalNotificationsWeb};
@@ -1 +1 @@
1
- import{df as t}from"./main-9TcmAtcq.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class o extends t{constructor(){super(),this.handleVisibilityChange=()=>{const e={isActive:document.hidden!==!0};this.notifyListeners("appStateChange",e),document.hidden?this.notifyListeners("pause",null):this.notifyListeners("resume",null)},document.addEventListener("visibilitychange",this.handleVisibilityChange,!1)}exitApp(){throw this.unimplemented("Not implemented on web.")}async getInfo(){throw this.unimplemented("Not implemented on web.")}async getLaunchUrl(){return{url:""}}async getState(){return{isActive:document.hidden!==!0}}async minimizeApp(){throw this.unimplemented("Not implemented on web.")}async toggleBackButtonHandler(){throw this.unimplemented("Not implemented on web.")}async getAppLanguage(){return{value:navigator.language.split("-")[0].toLowerCase()}}}export{o as AppWeb};
1
+ import{df as t}from"./main-DDwa0x9e.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class o extends t{constructor(){super(),this.handleVisibilityChange=()=>{const e={isActive:document.hidden!==!0};this.notifyListeners("appStateChange",e),document.hidden?this.notifyListeners("pause",null):this.notifyListeners("resume",null)},document.addEventListener("visibilitychange",this.handleVisibilityChange,!1)}exitApp(){throw this.unimplemented("Not implemented on web.")}async getInfo(){throw this.unimplemented("Not implemented on web.")}async getLaunchUrl(){return{url:""}}async getState(){return{isActive:document.hidden!==!0}}async minimizeApp(){throw this.unimplemented("Not implemented on web.")}async toggleBackButtonHandler(){throw this.unimplemented("Not implemented on web.")}async getAppLanguage(){return{value:navigator.language.split("-")[0].toLowerCase()}}}export{o as AppWeb};
@@ -1 +1 @@
1
- {"version":"1.153.0"}
1
+ {"version":"1.155.0"}
package/dist/index.html CHANGED
@@ -22,10 +22,10 @@
22
22
  <link rel="icon" type="image/png" sizes="16x16" href="/assets/icons/favicon-16x16.png" />
23
23
  <link rel="apple-touch-icon" sizes="180x180" href="/assets/icons/apple-touch-icon.png" />
24
24
  <title>Tide Commander</title>
25
- <script type="module" crossorigin src="/assets/main-9TcmAtcq.js"></script>
25
+ <script type="module" crossorigin src="/assets/main-DDwa0x9e.js"></script>
26
26
  <link rel="modulepreload" crossorigin href="/assets/vendor-react--Eh9ivFN.js">
27
27
  <link rel="modulepreload" crossorigin href="/assets/vendor-three-Chj50gSY.js">
28
- <link rel="stylesheet" crossorigin href="/assets/main-DP0iv8Le.css">
28
+ <link rel="stylesheet" crossorigin href="/assets/main-BHBs2E1L.css">
29
29
  </head>
30
30
  <body>
31
31
  <div id="app"></div>
@@ -25,6 +25,7 @@
25
25
  "showFPS": "FPS anzeigen",
26
26
  "fpsLimit": "FPS-Limit",
27
27
  "powerSaving": "Energiesparmodus",
28
+ "lowPowerMode": "Energiesparmodus (niedrig)",
28
29
  "tts": "Text-zu-Sprache",
29
30
  "stt": "Sprache-zu-Text",
30
31
  "externalEditor": "Externer Editor",
@@ -21,7 +21,9 @@
21
21
  "integrations": "Integrations",
22
22
  "experimental": "Experimental",
23
23
  "about": "About",
24
- "sky": "Sky Color"
24
+ "sky": "Sky Color",
25
+ "grokAccounts": "Grok Accounts",
26
+ "codexAccounts": "Codex Accounts"
25
27
  },
26
28
  "general": {
27
29
  "history": "History",
@@ -32,8 +34,11 @@
32
34
  "showFPS": "Show FPS",
33
35
  "fpsLimit": "FPS Limit",
34
36
  "powerSaving": "Power Saving",
37
+ "lowPowerMode": "Low Power Mode",
35
38
  "tmuxMode": "Tmux Process Persistence",
36
39
  "interactiveMode": "Interactive TUI Mode (Experimental)",
40
+ "codexAppServerMode": "Codex Streaming / app-server (Experimental)",
41
+ "opencodeServerMode": "OpenCode Streaming / serve (Experimental)",
37
42
  "tts": "Text-to-Speech",
38
43
  "stt": "Speech-to-Text",
39
44
  "externalEditor": "External Editor",
@@ -1323,6 +1323,12 @@
1323
1323
  "renamed": "Renamed \"{{from}}\" → \"{{to}}\".",
1324
1324
  "deleted": "Deleted profile \"{{name}}\".",
1325
1325
  "noProfiles": "No named profiles yet. Save the active account with a name (e.g. david, jc) to create one.",
1326
- "tokenExpires": "token expires {{time}}"
1326
+ "tokenExpires": "token expires {{time}}",
1327
+ "gaugeSession": "Session",
1328
+ "gaugeWeek": "Week",
1329
+ "gaugeWeekOpus": "Opus wk",
1330
+ "gaugeNoSession": "no active session",
1331
+ "providerTitle": "{{provider}} Accounts",
1332
+ "providerHelp": "Named copies of {{dir}}/auth.json. Switch the active {{provider}} account when you hit rate limits — same as renaming the file manually."
1327
1333
  }
1328
1334
  }
@@ -16,7 +16,9 @@
16
16
  "experimental": "Experimental",
17
17
  "about": "Acerca de",
18
18
  "sky": "Color del Cielo",
19
- "systemPrompt": "Prompt de sistema"
19
+ "systemPrompt": "Prompt de sistema",
20
+ "grokAccounts": "Cuentas de Grok",
21
+ "codexAccounts": "Cuentas de Codex"
20
22
  },
21
23
  "general": {
22
24
  "history": "Historial",
@@ -27,6 +29,7 @@
27
29
  "showFPS": "Mostrar FPS",
28
30
  "fpsLimit": "Limite de FPS",
29
31
  "powerSaving": "Ahorro de energia",
32
+ "lowPowerMode": "Modo bajo consumo",
30
33
  "tts": "Texto a voz",
31
34
  "stt": "Voz a texto",
32
35
  "externalEditor": "Editor externo",
@@ -1132,5 +1132,13 @@
1132
1132
  "can-clear-context": "Completado — contexto listo para limpiar",
1133
1133
  "waiting-subordinates": "Esperando trabajo delegado",
1134
1134
  "working": "Trabajando"
1135
+ },
1136
+ "credentials": {
1137
+ "gaugeSession": "Sesión",
1138
+ "gaugeWeek": "Semana",
1139
+ "gaugeWeekOpus": "Opus sem",
1140
+ "gaugeNoSession": "sin sesión activa",
1141
+ "providerTitle": "Cuentas de {{provider}}",
1142
+ "providerHelp": "Copias nombradas de {{dir}}/auth.json. Cambia la cuenta activa de {{provider}} cuando llegues a los límites — igual que renombrar el archivo a mano."
1135
1143
  }
1136
1144
  }
@@ -25,6 +25,7 @@
25
25
  "showFPS": "Afficher les FPS",
26
26
  "fpsLimit": "Limite de FPS",
27
27
  "powerSaving": "Économie d'énergie",
28
+ "lowPowerMode": "Mode basse consommation",
28
29
  "tts": "Synthèse vocale",
29
30
  "stt": "Reconnaissance vocale",
30
31
  "externalEditor": "Éditeur externe",
@@ -25,6 +25,7 @@
25
25
  "showFPS": "FPS दिखाएं",
26
26
  "fpsLimit": "FPS सीमा",
27
27
  "powerSaving": "बिजली बचत",
28
+ "lowPowerMode": "लो पावर मोड",
28
29
  "tts": "टेक्स्ट-टू-स्पीच",
29
30
  "stt": "स्पीच-टू-टेक्स्ट",
30
31
  "externalEditor": "बाहरी संपादक",
@@ -25,6 +25,7 @@
25
25
  "showFPS": "Mostra FPS",
26
26
  "fpsLimit": "Limite FPS",
27
27
  "powerSaving": "Risparmio energetico",
28
+ "lowPowerMode": "Modalità basso consumo",
28
29
  "tts": "Sintesi vocale",
29
30
  "stt": "Riconoscimento vocale",
30
31
  "externalEditor": "Editor esterno",
@@ -25,6 +25,7 @@
25
25
  "showFPS": "FPSを表示",
26
26
  "fpsLimit": "FPS制限",
27
27
  "powerSaving": "省電力モード",
28
+ "lowPowerMode": "低電力モード",
28
29
  "tts": "テキスト読み上げ",
29
30
  "stt": "音声認識",
30
31
  "externalEditor": "外部エディタ",
@@ -25,6 +25,7 @@
25
25
  "showFPS": "Mostrar FPS",
26
26
  "fpsLimit": "Limite de FPS",
27
27
  "powerSaving": "Economia de Energia",
28
+ "lowPowerMode": "Modo de baixo consumo",
28
29
  "tts": "Texto para Fala",
29
30
  "stt": "Fala para Texto",
30
31
  "externalEditor": "Editor Externo",
@@ -25,6 +25,7 @@
25
25
  "showFPS": "Показать FPS",
26
26
  "fpsLimit": "Ограничение FPS",
27
27
  "powerSaving": "Энергосбережение",
28
+ "lowPowerMode": "Режим низкого энергопотребления",
28
29
  "tts": "Синтез речи",
29
30
  "stt": "Распознавание речи",
30
31
  "externalEditor": "Внешний редактор",
@@ -25,6 +25,7 @@
25
25
  "showFPS": "显示帧率",
26
26
  "fpsLimit": "帧率限制",
27
27
  "powerSaving": "省电模式",
28
+ "lowPowerMode": "低功耗模式",
28
29
  "tts": "文字转语音",
29
30
  "stt": "语音转文字",
30
31
  "externalEditor": "外部编辑器",
@@ -0,0 +1,222 @@
1
+ /**
2
+ * CodexAppServerEventAdapter
3
+ *
4
+ * Translates `codex app-server` JSON-RPC notifications (v2 protocol, camelCase
5
+ * items) into Tide `StandardEvent`s, one adapter instance per agent (it holds
6
+ * per-turn state: the last agent-message text for boss delegation, and the most
7
+ * recent token-usage snapshot).
8
+ *
9
+ * The uuid conventions match the exec parser
10
+ * (src/packages/server/codex/json-event-parser.ts): `codex-text-<itemId>` for
11
+ * assistant text and `codex-thinking-<itemId>` for reasoning, so streaming
12
+ * deltas merge into one terminal row and the final `isStreaming:false` event
13
+ * finalizes it — identical downstream behaviour to the exec path, just with
14
+ * real token-level streaming.
15
+ */
16
+ import { createLogger } from '../../utils/logger.js';
17
+ const log = createLogger('CodexAppServerAdapter');
18
+ function asString(v) {
19
+ return typeof v === 'string' ? v : undefined;
20
+ }
21
+ function asNumber(v) {
22
+ return typeof v === 'number' ? v : undefined;
23
+ }
24
+ export class CodexAppServerEventAdapter {
25
+ lastAgentMessageText;
26
+ lastErrorText;
27
+ tokenSnapshot;
28
+ /** Map one app-server notification to zero or more Tide events. */
29
+ handle(method, params) {
30
+ switch (method) {
31
+ // ---- Streaming deltas (the whole point) --------------------------------
32
+ case 'item/agentMessage/delta': {
33
+ const delta = asString(params.delta);
34
+ const itemId = asString(params.itemId) || 'anon';
35
+ if (!delta)
36
+ return [];
37
+ return [{ type: 'text', text: delta, isStreaming: true, uuid: `codex-text-${itemId}` }];
38
+ }
39
+ case 'item/reasoning/textDelta':
40
+ case 'item/reasoning/summaryTextDelta': {
41
+ const delta = asString(params.delta);
42
+ const itemId = asString(params.itemId) || 'anon';
43
+ if (!delta)
44
+ return [];
45
+ return [{ type: 'thinking', text: delta, isStreaming: true, uuid: `codex-thinking-${itemId}` }];
46
+ }
47
+ // ---- Item lifecycle ----------------------------------------------------
48
+ case 'item/started':
49
+ return this.handleItemStarted(params.item || {});
50
+ case 'item/completed':
51
+ return this.handleItemCompleted(params.item || {});
52
+ // ---- Turn + usage ------------------------------------------------------
53
+ case 'thread/tokenUsage/updated':
54
+ this.captureTokenUsage(params.tokenUsage);
55
+ return [];
56
+ case 'turn/completed':
57
+ return this.handleTurnCompleted(params.turn);
58
+ // ---- Errors ------------------------------------------------------------
59
+ case 'error': {
60
+ const errorMessage = asString(params.message) || 'Codex app-server error';
61
+ this.lastErrorText = errorMessage;
62
+ return [{ type: 'error', errorMessage }];
63
+ }
64
+ // Informational / handled elsewhere — stay silent (no raw dumps).
65
+ case 'thread/started':
66
+ case 'turn/started':
67
+ case 'thread/status/changed':
68
+ case 'thread/tokenUsage':
69
+ case 'item/plan/delta':
70
+ case 'turn/plan/updated':
71
+ case 'turn/diff/updated':
72
+ case 'item/reasoning/summaryPartAdded':
73
+ case 'item/commandExecution/outputDelta':
74
+ case 'account/rateLimits/updated':
75
+ case 'remoteControl/status/changed':
76
+ return [];
77
+ default:
78
+ // Unknown notification: swallow it (the exec parser dumps unknowns to the
79
+ // terminal, but at the notification layer that would be far too noisy).
80
+ log.debug(`Unhandled app-server notification: ${method}`);
81
+ return [];
82
+ }
83
+ }
84
+ handleItemStarted(item) {
85
+ switch (item.type) {
86
+ case 'commandExecution': {
87
+ const command = item.command || '';
88
+ return [{ type: 'tool_start', toolName: 'Bash', toolInput: { command, status: item.status } }];
89
+ }
90
+ case 'webSearch':
91
+ return [{ type: 'tool_start', toolName: 'web_search', toolInput: this.webSearchInput(item) }];
92
+ // agentMessage / reasoning: deltas carry the content; fileChange diff lands
93
+ // on completion. Everything else stays silent on start.
94
+ default:
95
+ return [];
96
+ }
97
+ }
98
+ handleItemCompleted(item) {
99
+ switch (item.type) {
100
+ case 'agentMessage': {
101
+ const text = (item.text || '').trim();
102
+ if (!text)
103
+ return [];
104
+ this.lastAgentMessageText = text;
105
+ return [{ type: 'text', text, isStreaming: false, uuid: item.id ? `codex-text-${item.id}` : undefined }];
106
+ }
107
+ case 'reasoning': {
108
+ const text = (item.content && item.content.length ? item.content : item.summary || []).join('\n').trim();
109
+ if (!text)
110
+ return [];
111
+ return [{ type: 'thinking', text, isStreaming: false, uuid: item.id ? `codex-thinking-${item.id}` : undefined }];
112
+ }
113
+ case 'commandExecution': {
114
+ const output = item.aggregatedOutput ??
115
+ (item.exitCode !== undefined ? `[exit ${item.exitCode}]` : item.status ? `Command status: ${item.status}` : '');
116
+ return [{ type: 'tool_result', toolName: 'Bash', toolOutput: output }];
117
+ }
118
+ case 'fileChange':
119
+ return this.handleFileChange(item);
120
+ case 'webSearch':
121
+ return [{ type: 'tool_result', toolName: 'web_search', toolOutput: JSON.stringify(this.webSearchInput(item)) }];
122
+ case 'collabAgentToolCall': {
123
+ const toolName = item.tool || 'collab_tool';
124
+ const toolInput = { tool: item.tool, status: item.status };
125
+ if (item.prompt)
126
+ toolInput.prompt = item.prompt;
127
+ if (item.receiverThreadIds?.length)
128
+ toolInput.receiver_thread_ids = item.receiverThreadIds;
129
+ return [
130
+ { type: 'tool_start', toolName, toolInput },
131
+ { type: 'tool_result', toolName, toolOutput: `Status: ${item.status || 'unknown'}` },
132
+ ];
133
+ }
134
+ case 'error': {
135
+ const errorMessage = item.message || item.text || item.error || 'Codex emitted an error';
136
+ this.lastErrorText = errorMessage;
137
+ return [{ type: 'error', errorMessage }];
138
+ }
139
+ default:
140
+ return [];
141
+ }
142
+ }
143
+ handleFileChange(item) {
144
+ if (!item.changes || item.changes.length === 0)
145
+ return [];
146
+ if (item.status && item.status !== 'completed')
147
+ return [];
148
+ const events = [];
149
+ for (const change of item.changes) {
150
+ if (!change.path)
151
+ continue;
152
+ const toolInput = { file_path: change.path };
153
+ // v2 fileChange carries a per-file unified diff inline — no git shell-out
154
+ // needed (the exec path has to reconstruct it via `git diff`).
155
+ if (change.diff)
156
+ toolInput.unified_diff = change.diff;
157
+ const kind = change.kind?.type || 'update';
158
+ events.push({ type: 'tool_start', toolName: 'Edit', toolInput }, { type: 'tool_result', toolName: 'Edit', toolOutput: `File ${kind}: ${change.path}` });
159
+ }
160
+ return events;
161
+ }
162
+ webSearchInput(item) {
163
+ return {
164
+ query: item.query,
165
+ actionType: item.action?.type,
166
+ actionQuery: item.action?.query,
167
+ actionQueries: item.action?.queries,
168
+ actionUrl: item.action?.url,
169
+ };
170
+ }
171
+ captureTokenUsage(tokenUsage) {
172
+ if (typeof tokenUsage !== 'object' || tokenUsage === null)
173
+ return;
174
+ const tu = tokenUsage;
175
+ const last = tu.last || {};
176
+ this.tokenSnapshot = {
177
+ contextWindow: asNumber(tu.modelContextWindow) ?? this.tokenSnapshot?.contextWindow,
178
+ inputTokens: asNumber(last.inputTokens),
179
+ outputTokens: asNumber(last.outputTokens),
180
+ cacheReadInputTokens: asNumber(last.cachedInputTokens),
181
+ };
182
+ }
183
+ handleTurnCompleted(turn) {
184
+ const events = [];
185
+ // A failed turn carries its error inline — surface it before finalizing.
186
+ if (turn && asString(turn.status) === 'failed') {
187
+ const err = turn.error;
188
+ const errorMessage = (err && asString(err.message)) || 'Codex turn failed';
189
+ this.lastErrorText = errorMessage;
190
+ events.push({ type: 'error', errorMessage });
191
+ }
192
+ const snap = this.tokenSnapshot;
193
+ const event = {
194
+ type: 'step_complete',
195
+ tokens: {
196
+ input: snap?.inputTokens ?? 0,
197
+ output: snap?.outputTokens ?? 0,
198
+ cacheRead: snap?.cacheReadInputTokens,
199
+ },
200
+ };
201
+ if (this.lastAgentMessageText || this.lastErrorText) {
202
+ const parts = [];
203
+ if (this.lastAgentMessageText)
204
+ parts.push(this.lastAgentMessageText);
205
+ if (this.lastErrorText)
206
+ parts.push(`[Error] ${this.lastErrorText}`);
207
+ event.resultText = parts.join('\n\n');
208
+ }
209
+ if (snap && (snap.contextWindow || snap.inputTokens !== undefined || snap.outputTokens !== undefined)) {
210
+ event.modelUsage = {
211
+ contextWindow: snap.contextWindow,
212
+ inputTokens: snap.inputTokens ?? 0,
213
+ outputTokens: snap.outputTokens ?? 0,
214
+ cacheReadInputTokens: snap.cacheReadInputTokens ?? 0,
215
+ };
216
+ }
217
+ this.lastAgentMessageText = undefined;
218
+ this.lastErrorText = undefined;
219
+ events.push(event);
220
+ return events;
221
+ }
222
+ }