@xcanwin/manyoyo 5.8.0 → 5.8.1

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.
@@ -407,6 +407,75 @@ textarea:focus-visible {
407
407
  width: min(760px, calc(100vw - 24px));
408
408
  }
409
409
 
410
+ .agent-template-primary .text-block {
411
+ margin-top: 0;
412
+ }
413
+
414
+ .agent-template-primary .text-block + .text-block,
415
+ #agentTemplateOverrideGroup {
416
+ margin-top: 12px;
417
+ }
418
+
419
+ .agent-template-primary select,
420
+ .agent-template-primary textarea {
421
+ border: 1px solid var(--line);
422
+ border-radius: 10px;
423
+ padding: 9px 11px;
424
+ background: var(--panel-strong);
425
+ color: var(--text);
426
+ font-size: 13px;
427
+ font-family: var(--font-ui);
428
+ }
429
+
430
+ .agent-template-primary textarea {
431
+ min-height: 92px;
432
+ font-family: var(--font-mono);
433
+ resize: vertical;
434
+ }
435
+
436
+ .agent-template-section {
437
+ padding: 12px;
438
+ border: 1px solid var(--line);
439
+ border-radius: 12px;
440
+ background: rgba(255, 253, 250, 0.92);
441
+ }
442
+
443
+ .agent-template-section + .agent-template-section {
444
+ margin-top: 12px;
445
+ }
446
+
447
+ .agent-template-section-title {
448
+ margin-bottom: 8px;
449
+ font-size: 12px;
450
+ font-weight: 700;
451
+ color: var(--text);
452
+ }
453
+
454
+ .agent-template-section .text-block {
455
+ margin-top: 0;
456
+ }
457
+
458
+ .agent-template-section .text-block + .text-block {
459
+ margin-top: 10px;
460
+ }
461
+
462
+ .agent-template-section select,
463
+ .agent-template-section textarea {
464
+ border: 1px solid var(--line);
465
+ border-radius: 10px;
466
+ padding: 9px 11px;
467
+ background: var(--panel-strong);
468
+ color: var(--text);
469
+ font-size: 13px;
470
+ font-family: var(--font-ui);
471
+ }
472
+
473
+ .agent-template-section textarea {
474
+ min-height: 92px;
475
+ font-family: var(--font-mono);
476
+ resize: vertical;
477
+ }
478
+
410
479
  .session-head {
411
480
  display: flex;
412
481
  justify-content: space-between;
@@ -184,7 +184,15 @@
184
184
  <label>shellPrefix<input id="createShellPrefix" placeholder="例如 IS_SANDBOX=1" /></label>
185
185
  <label>shell<input id="createShell" placeholder="例如 claude / codex" /></label>
186
186
  <label>shellSuffix<input id="createShellSuffix" placeholder="例如 --dangerously-skip-permissions" /></label>
187
- <label>yolo<input id="createYolo" placeholder="例如 c / cx / gm / oc" /></label>
187
+ <label>CLI
188
+ <select id="createYolo">
189
+ <option value="">(不使用)</option>
190
+ <option value="claude">claude</option>
191
+ <option value="codex">codex</option>
192
+ <option value="gemini">gemini</option>
193
+ <option value="opencode">opencode</option>
194
+ </select>
195
+ </label>
188
196
  <label>agentPromptCommand<input id="createAgentPromptCommand" placeholder="例如 codex exec --plain-text {prompt}" /></label>
189
197
  </div>
190
198
  <label class="text-block">env (KEY=VALUE,每行一项)
@@ -231,15 +239,38 @@
231
239
  <button type="button" id="agentTemplateCancelBtn" class="secondary">关闭</button>
232
240
  </header>
233
241
  <div class="modal-body">
234
- <div id="agentTemplateTip" class="modal-tip"></div>
235
- <label class="text-block">容器默认 agentPromptCommand
236
- <textarea id="containerAgentPromptEditor" placeholder="例如 codex exec --skip-git-repo-check {prompt}"></textarea>
237
- </label>
238
- <div id="agentTemplateOverrideGroup">
239
- <label class="text-block">当前 AGENT 覆盖模板
240
- <textarea id="agentPromptOverrideEditor" placeholder="留空则继承容器默认模板"></textarea>
242
+ <div id="agentTemplatePrimary" class="agent-template-primary">
243
+ <label class="text-block">CLI
244
+ <select id="containerCliSelect">
245
+ <option value="custom">自定义</option>
246
+ <option value="claude">claude</option>
247
+ <option value="codex">codex</option>
248
+ <option value="gemini">gemini</option>
249
+ <option value="opencode">opencode</option>
250
+ </select>
251
+ </label>
252
+ <label class="text-block">高级编辑 agentPromptCommand
253
+ <textarea id="containerAgentPromptEditor" placeholder="例如 codex exec --skip-git-repo-check {prompt}"></textarea>
241
254
  </label>
242
255
  </div>
256
+ <div id="agentTemplateOverrideGroup">
257
+ <section class="agent-template-section">
258
+ <div class="agent-template-section-title">当前 AGENT 覆盖</div>
259
+ <label class="text-block">CLI
260
+ <select id="agentCliSelect">
261
+ <option value="">继承容器默认</option>
262
+ <option value="custom">自定义</option>
263
+ <option value="claude">claude</option>
264
+ <option value="codex">codex</option>
265
+ <option value="gemini">gemini</option>
266
+ <option value="opencode">opencode</option>
267
+ </select>
268
+ </label>
269
+ <label class="text-block">高级编辑 agentPromptCommand
270
+ <textarea id="agentPromptOverrideEditor" placeholder="留空则继承容器默认模板"></textarea>
271
+ </label>
272
+ </section>
273
+ </div>
243
274
  <div id="agentTemplateError" class="modal-error" hidden></div>
244
275
  </div>
245
276
  <footer class="modal-footer">
@@ -188,9 +188,11 @@
188
188
  const sendBtn = document.getElementById('sendBtn');
189
189
  const stopBtn = document.getElementById('stopBtn');
190
190
  const agentTemplateModal = document.getElementById('agentTemplateModal');
191
- const agentTemplateTip = document.getElementById('agentTemplateTip');
191
+ const agentTemplatePrimary = document.getElementById('agentTemplatePrimary');
192
+ const containerCliSelect = document.getElementById('containerCliSelect');
192
193
  const containerAgentPromptEditor = document.getElementById('containerAgentPromptEditor');
193
194
  const agentTemplateOverrideGroup = document.getElementById('agentTemplateOverrideGroup');
195
+ const agentCliSelect = document.getElementById('agentCliSelect');
194
196
  const agentPromptOverrideEditor = document.getElementById('agentPromptOverrideEditor');
195
197
  const agentTemplateError = document.getElementById('agentTemplateError');
196
198
  const agentTemplateCancelBtn = document.getElementById('agentTemplateCancelBtn');
@@ -230,6 +232,12 @@
230
232
  const GEMINI_YOLO_FLAG = '--yolo';
231
233
  const CODEX_DANGEROUS_FLAG = '--dangerously-bypass-approvals-and-sandbox';
232
234
  const OPENCODE_PERMISSION_KEY = 'OPENCODE_PERMISSION=';
235
+ const AGENT_TEMPLATE_CLI_COMMAND_MAP = {
236
+ claude: YOLO_COMMAND_MAP.claude,
237
+ codex: YOLO_COMMAND_MAP.codex,
238
+ gemini: YOLO_COMMAND_MAP.gemini,
239
+ opencode: YOLO_COMMAND_MAP.opencode
240
+ };
233
241
  const SIDEBAR_TREE_STORAGE_KEY = 'manyoyo.web.sidebarTree.v1';
234
242
  const markdownRenderer = window.ManyoyoMarkdown
235
243
  && typeof window.ManyoyoMarkdown.shouldRenderMessage === 'function'
@@ -829,6 +837,26 @@
829
837
  return YOLO_COMMAND_MAP[key] || '';
830
838
  }
831
839
 
840
+ function normalizeCreateYoloValue(yolo) {
841
+ const key = String(yolo || '').trim().toLowerCase();
842
+ if (!key) {
843
+ return '';
844
+ }
845
+ if (key === 'claude' || key === 'cc' || key === 'c') {
846
+ return 'claude';
847
+ }
848
+ if (key === 'codex' || key === 'cx') {
849
+ return 'codex';
850
+ }
851
+ if (key === 'gemini' || key === 'gm' || key === 'g') {
852
+ return 'gemini';
853
+ }
854
+ if (key === 'opencode' || key === 'oc') {
855
+ return 'opencode';
856
+ }
857
+ return '';
858
+ }
859
+
832
860
  function stripLeadingAssignments(commandText) {
833
861
  let rest = String(commandText || '').trim();
834
862
  const assignmentPattern = /^(?:[A-Za-z_][A-Za-z0-9_]*=)(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^\s]+)(?:\s+|$)/;
@@ -928,7 +956,7 @@
928
956
  createShellSuffix.value = value.shellSuffix || '';
929
957
  createAgentPromptCommand.value = value.agentPromptCommand || '';
930
958
  state.createAgentPromptAuto = false;
931
- createYolo.value = value.yolo || '';
959
+ createYolo.value = normalizeCreateYoloValue(value.yolo);
932
960
  // 敏感 env 与继承数组由服务端在创建时合并,前端表单默认不回显,避免泄露或重复提交。
933
961
  createEnv.value = '';
934
962
  createEnvFile.value = '';
@@ -1106,28 +1134,101 @@
1106
1134
  agentTemplateError.textContent = state.agentTemplateError;
1107
1135
  }
1108
1136
 
1137
+ function inferTemplateCliValue(templateText, options) {
1138
+ const text = String(templateText || '').trim();
1139
+ const opts = options && typeof options === 'object' ? options : {};
1140
+ if (!text) {
1141
+ return opts.allowEmpty ? '' : 'custom';
1142
+ }
1143
+ const program = resolveAgentProgram(text);
1144
+ return AGENT_PROMPT_TEMPLATE_MAP[program] ? program : 'custom';
1145
+ }
1146
+
1147
+ function buildTemplateFromCliValue(cliValue) {
1148
+ const cli = String(cliValue || '').trim().toLowerCase();
1149
+ if (!cli || cli === 'custom') {
1150
+ return '';
1151
+ }
1152
+ const baseCommand = AGENT_TEMPLATE_CLI_COMMAND_MAP[cli] || '';
1153
+ if (!baseCommand) {
1154
+ return '';
1155
+ }
1156
+ return resolveAgentPromptTemplate(baseCommand);
1157
+ }
1158
+
1159
+ function syncAgentTemplateSelectFromEditor(selectNode, editorNode, options) {
1160
+ if (!selectNode || !editorNode) {
1161
+ return;
1162
+ }
1163
+ const value = inferTemplateCliValue(editorNode.value, options);
1164
+ selectNode.value = value;
1165
+ }
1166
+
1167
+ function getInheritedContainerTemplateText() {
1168
+ if (containerAgentPromptEditor) {
1169
+ const editorText = String(containerAgentPromptEditor.value || '').trim();
1170
+ if (editorText) {
1171
+ return editorText;
1172
+ }
1173
+ }
1174
+ if (state.sessionDetail && typeof state.sessionDetail.containerAgentPromptCommand === 'string') {
1175
+ const containerText = String(state.sessionDetail.containerAgentPromptCommand || '').trim();
1176
+ if (containerText) {
1177
+ return containerText;
1178
+ }
1179
+ }
1180
+ if (state.sessionDetail && typeof state.sessionDetail.agentPromptCommand === 'string') {
1181
+ const effectiveText = String(state.sessionDetail.agentPromptCommand || '').trim();
1182
+ if (effectiveText) {
1183
+ return effectiveText;
1184
+ }
1185
+ }
1186
+ return '';
1187
+ }
1188
+
1189
+ function applyAgentTemplateCliSelection(selectNode, editorNode, options) {
1190
+ if (!selectNode || !editorNode) {
1191
+ return;
1192
+ }
1193
+ const opts = options && typeof options === 'object' ? options : {};
1194
+ const cliValue = String(selectNode.value || '').trim();
1195
+ if (!cliValue && opts.allowEmpty === true) {
1196
+ editorNode.value = getInheritedContainerTemplateText();
1197
+ showAgentTemplateError('');
1198
+ return;
1199
+ }
1200
+ if (cliValue === 'custom') {
1201
+ showAgentTemplateError('');
1202
+ return;
1203
+ }
1204
+ const nextTemplate = buildTemplateFromCliValue(cliValue);
1205
+ if (nextTemplate) {
1206
+ editorNode.value = nextTemplate;
1207
+ }
1208
+ showAgentTemplateError('');
1209
+ }
1210
+
1109
1211
  function fillAgentTemplateForm(detail) {
1110
1212
  const currentDetail = detail && typeof detail === 'object' ? detail : {};
1213
+ const overrideEditable = isActiveAgentOverrideEditable();
1214
+ const containerTemplateText = String(
1215
+ currentDetail.containerAgentPromptCommand || currentDetail.agentPromptCommand || ''
1216
+ ).trim();
1111
1217
  if (containerAgentPromptEditor) {
1112
- containerAgentPromptEditor.value = currentDetail.containerAgentPromptCommand || '';
1218
+ containerAgentPromptEditor.value = containerTemplateText;
1113
1219
  }
1114
1220
  if (agentPromptOverrideEditor) {
1115
- agentPromptOverrideEditor.value = currentDetail.agentPromptCommandOverride || '';
1221
+ agentPromptOverrideEditor.value = currentDetail.agentPromptCommandOverride || containerTemplateText;
1222
+ }
1223
+ syncAgentTemplateSelectFromEditor(containerCliSelect, containerAgentPromptEditor);
1224
+ if (agentCliSelect) {
1225
+ agentCliSelect.value = currentDetail.agentPromptCommandOverride ? inferTemplateCliValue(currentDetail.agentPromptCommandOverride, { allowEmpty: true }) : '';
1226
+ }
1227
+ if (agentTemplatePrimary) {
1228
+ agentTemplatePrimary.hidden = overrideEditable;
1116
1229
  }
1117
1230
  if (agentTemplateOverrideGroup) {
1118
- agentTemplateOverrideGroup.hidden = !isActiveAgentOverrideEditable();
1119
- }
1120
- if (agentTemplateTip) {
1121
- const sessionName = currentDetail.agentName || state.active || '当前会话';
1122
- const sourceMap = {
1123
- agent: '当前 AGENT 覆盖',
1124
- container: '容器默认模板',
1125
- inferred: '从容器启动命令推导',
1126
- none: '未配置'
1127
- };
1128
- const sourceLabel = sourceMap[currentDetail.agentPromptSource] || '未配置';
1129
- const effectiveTemplate = currentDetail.agentPromptCommand || '—';
1130
- agentTemplateTip.textContent = `${sessionName} · 当前生效来源:${sourceLabel} · 生效模板:${effectiveTemplate}`;
1231
+ agentTemplateOverrideGroup.hidden = !overrideEditable;
1131
1232
  }
1132
1233
  showAgentTemplateError('');
1133
1234
  }
@@ -1155,7 +1256,11 @@
1155
1256
  state.agentTemplateModalOpen = true;
1156
1257
  fillAgentTemplateForm(detail);
1157
1258
  syncUi();
1158
- if (containerAgentPromptEditor) {
1259
+ if (isActiveAgentOverrideEditable() && agentCliSelect) {
1260
+ agentCliSelect.focus();
1261
+ } else if (containerCliSelect) {
1262
+ containerCliSelect.focus();
1263
+ } else if (containerAgentPromptEditor) {
1159
1264
  containerAgentPromptEditor.focus();
1160
1265
  }
1161
1266
  }
@@ -1177,11 +1282,15 @@
1177
1282
  showAgentTemplateError('');
1178
1283
  syncUi();
1179
1284
  try {
1180
- const payload = {
1181
- containerAgentPromptCommand: containerAgentPromptEditor ? String(containerAgentPromptEditor.value || '').trim() : ''
1182
- };
1183
- if (isActiveAgentOverrideEditable() && agentPromptOverrideEditor) {
1184
- payload.agentPromptCommandOverride = String(agentPromptOverrideEditor.value || '').trim();
1285
+ const payload = {};
1286
+ if (isActiveAgentOverrideEditable()) {
1287
+ if (agentCliSelect && String(agentCliSelect.value || '').trim() === '') {
1288
+ payload.agentPromptCommandOverride = '';
1289
+ } else if (agentPromptOverrideEditor) {
1290
+ payload.agentPromptCommandOverride = String(agentPromptOverrideEditor.value || '').trim();
1291
+ }
1292
+ } else if (containerAgentPromptEditor) {
1293
+ payload.containerAgentPromptCommand = String(containerAgentPromptEditor.value || '').trim();
1185
1294
  }
1186
1295
  const data = await api('/api/sessions/' + encodeURIComponent(state.active) + '/agent-template', {
1187
1296
  method: 'PUT',
@@ -2020,6 +2129,18 @@
2020
2129
  if (agentTemplateCancelBtn) {
2021
2130
  agentTemplateCancelBtn.disabled = state.agentTemplateSaving;
2022
2131
  }
2132
+ if (containerCliSelect) {
2133
+ containerCliSelect.disabled = state.agentTemplateSaving || isActiveAgentOverrideEditable();
2134
+ }
2135
+ if (containerAgentPromptEditor) {
2136
+ containerAgentPromptEditor.disabled = state.agentTemplateSaving || isActiveAgentOverrideEditable();
2137
+ }
2138
+ if (agentCliSelect) {
2139
+ agentCliSelect.disabled = state.agentTemplateSaving || !isActiveAgentOverrideEditable();
2140
+ }
2141
+ if (agentPromptOverrideEditor) {
2142
+ agentPromptOverrideEditor.disabled = state.agentTemplateSaving || !isActiveAgentOverrideEditable();
2143
+ }
2023
2144
  document.body.classList.toggle(
2024
2145
  'modal-open',
2025
2146
  state.configModalOpen || state.createModalOpen || state.directoryPicker.open || state.agentTemplateModalOpen
@@ -3568,6 +3689,30 @@
3568
3689
  });
3569
3690
  }
3570
3691
 
3692
+ if (containerCliSelect) {
3693
+ containerCliSelect.addEventListener('change', function () {
3694
+ applyAgentTemplateCliSelection(containerCliSelect, containerAgentPromptEditor);
3695
+ });
3696
+ }
3697
+
3698
+ if (agentCliSelect) {
3699
+ agentCliSelect.addEventListener('change', function () {
3700
+ applyAgentTemplateCliSelection(agentCliSelect, agentPromptOverrideEditor, { allowEmpty: true });
3701
+ });
3702
+ }
3703
+
3704
+ if (containerAgentPromptEditor) {
3705
+ containerAgentPromptEditor.addEventListener('input', function () {
3706
+ syncAgentTemplateSelectFromEditor(containerCliSelect, containerAgentPromptEditor);
3707
+ });
3708
+ }
3709
+
3710
+ if (agentPromptOverrideEditor) {
3711
+ agentPromptOverrideEditor.addEventListener('input', function () {
3712
+ syncAgentTemplateSelectFromEditor(agentCliSelect, agentPromptOverrideEditor, { allowEmpty: true });
3713
+ });
3714
+ }
3715
+
3571
3716
  if (agentTemplateCancelBtn) {
3572
3717
  agentTemplateCancelBtn.addEventListener('click', function () {
3573
3718
  closeAgentTemplateModal();
@@ -3599,6 +3744,9 @@
3599
3744
  inputNode.addEventListener('input', function () {
3600
3745
  updateCreateAgentPromptCommandFromCommand();
3601
3746
  });
3747
+ inputNode.addEventListener('change', function () {
3748
+ updateCreateAgentPromptCommandFromCommand();
3749
+ });
3602
3750
  });
3603
3751
 
3604
3752
  if (createAgentPromptCommand) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcanwin/manyoyo",
3
- "version": "5.8.0",
3
+ "version": "5.8.1",
4
4
  "imageVersion": "1.9.0-common",
5
5
  "playwrightCliVersion": "0.1.1",
6
6
  "description": "AI Agent CLI Security Sandbox for Docker and Podman",