@xcanwin/manyoyo 5.8.0 → 5.8.2

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;
@@ -165,12 +165,7 @@
165
165
  </div>
166
166
  </label>
167
167
  <label>containerName<input id="createContainerName" placeholder="my-dev 或 my-{now}" /></label>
168
- <label>containerPath
169
- <div class="field-inline">
170
- <input id="createContainerPath" placeholder="/workspace" />
171
- <button type="button" id="pickContainerPathBtn" class="secondary field-picker-btn">选择</button>
172
- </div>
173
- </label>
168
+ <label>containerPath<input id="createContainerPath" placeholder="/workspace" /></label>
174
169
  <label>imageName<input id="createImageName" placeholder="localhost/xcanwin/manyoyo" /></label>
175
170
  <label>imageVersion<input id="createImageVersion" placeholder="1.7.4-common" /></label>
176
171
  <label>containerMode
@@ -184,7 +179,15 @@
184
179
  <label>shellPrefix<input id="createShellPrefix" placeholder="例如 IS_SANDBOX=1" /></label>
185
180
  <label>shell<input id="createShell" placeholder="例如 claude / codex" /></label>
186
181
  <label>shellSuffix<input id="createShellSuffix" placeholder="例如 --dangerously-skip-permissions" /></label>
187
- <label>yolo<input id="createYolo" placeholder="例如 c / cx / gm / oc" /></label>
182
+ <label>CLI
183
+ <select id="createYolo">
184
+ <option value="">(不使用)</option>
185
+ <option value="claude">claude</option>
186
+ <option value="codex">codex</option>
187
+ <option value="gemini">gemini</option>
188
+ <option value="opencode">opencode</option>
189
+ </select>
190
+ </label>
188
191
  <label>agentPromptCommand<input id="createAgentPromptCommand" placeholder="例如 codex exec --plain-text {prompt}" /></label>
189
192
  </div>
190
193
  <label class="text-block">env (KEY=VALUE,每行一项)
@@ -231,15 +234,38 @@
231
234
  <button type="button" id="agentTemplateCancelBtn" class="secondary">关闭</button>
232
235
  </header>
233
236
  <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>
237
+ <div id="agentTemplatePrimary" class="agent-template-primary">
238
+ <label class="text-block">CLI
239
+ <select id="containerCliSelect">
240
+ <option value="custom">自定义</option>
241
+ <option value="claude">claude</option>
242
+ <option value="codex">codex</option>
243
+ <option value="gemini">gemini</option>
244
+ <option value="opencode">opencode</option>
245
+ </select>
246
+ </label>
247
+ <label class="text-block">高级编辑 agentPromptCommand
248
+ <textarea id="containerAgentPromptEditor" placeholder="例如 codex exec --skip-git-repo-check {prompt}"></textarea>
241
249
  </label>
242
250
  </div>
251
+ <div id="agentTemplateOverrideGroup">
252
+ <section class="agent-template-section">
253
+ <div class="agent-template-section-title">当前 AGENT 覆盖</div>
254
+ <label class="text-block">CLI
255
+ <select id="agentCliSelect">
256
+ <option value="">继承容器默认</option>
257
+ <option value="custom">自定义</option>
258
+ <option value="claude">claude</option>
259
+ <option value="codex">codex</option>
260
+ <option value="gemini">gemini</option>
261
+ <option value="opencode">opencode</option>
262
+ </select>
263
+ </label>
264
+ <label class="text-block">高级编辑 agentPromptCommand
265
+ <textarea id="agentPromptOverrideEditor" placeholder="留空则继承容器默认模板"></textarea>
266
+ </label>
267
+ </section>
268
+ </div>
243
269
  <div id="agentTemplateError" class="modal-error" hidden></div>
244
270
  </div>
245
271
  <footer class="modal-footer">
@@ -254,7 +280,6 @@
254
280
  <script src="/app/vendor/xterm-addon-fit.js"></script>
255
281
  <script src="/app/vendor/marked.min.js"></script>
256
282
  <script src="/app/frontend/markdown-renderer.js"></script>
257
- <script src="/app/frontend/path-picker-utils.js"></script>
258
283
  <script src="/app/frontend/app.js"></script>
259
284
  </body>
260
285
  </html>
@@ -62,7 +62,6 @@
62
62
  sessionDetailRequestId: 0,
63
63
  agentTemplateError: '',
64
64
  createAgentPromptAuto: false,
65
- createContainerPathBase: '',
66
65
  createDefaults: null,
67
66
  createRuns: {},
68
67
  sessionNodeMap: new Map(),
@@ -80,8 +79,6 @@
80
79
  title: '',
81
80
  tip: '',
82
81
  currentPath: '',
83
- basePath: '',
84
- baseContainerPath: '',
85
82
  entries: [],
86
83
  error: ''
87
84
  },
@@ -145,7 +142,6 @@
145
142
  const createHostPath = document.getElementById('createHostPath');
146
143
  const createContainerPath = document.getElementById('createContainerPath');
147
144
  const pickHostPathBtn = document.getElementById('pickHostPathBtn');
148
- const pickContainerPathBtn = document.getElementById('pickContainerPathBtn');
149
145
  const createImageName = document.getElementById('createImageName');
150
146
  const createImageVersion = document.getElementById('createImageVersion');
151
147
  const createContainerMode = document.getElementById('createContainerMode');
@@ -188,9 +184,11 @@
188
184
  const sendBtn = document.getElementById('sendBtn');
189
185
  const stopBtn = document.getElementById('stopBtn');
190
186
  const agentTemplateModal = document.getElementById('agentTemplateModal');
191
- const agentTemplateTip = document.getElementById('agentTemplateTip');
187
+ const agentTemplatePrimary = document.getElementById('agentTemplatePrimary');
188
+ const containerCliSelect = document.getElementById('containerCliSelect');
192
189
  const containerAgentPromptEditor = document.getElementById('containerAgentPromptEditor');
193
190
  const agentTemplateOverrideGroup = document.getElementById('agentTemplateOverrideGroup');
191
+ const agentCliSelect = document.getElementById('agentCliSelect');
194
192
  const agentPromptOverrideEditor = document.getElementById('agentPromptOverrideEditor');
195
193
  const agentTemplateError = document.getElementById('agentTemplateError');
196
194
  const agentTemplateCancelBtn = document.getElementById('agentTemplateCancelBtn');
@@ -230,16 +228,18 @@
230
228
  const GEMINI_YOLO_FLAG = '--yolo';
231
229
  const CODEX_DANGEROUS_FLAG = '--dangerously-bypass-approvals-and-sandbox';
232
230
  const OPENCODE_PERMISSION_KEY = 'OPENCODE_PERMISSION=';
231
+ const AGENT_TEMPLATE_CLI_COMMAND_MAP = {
232
+ claude: YOLO_COMMAND_MAP.claude,
233
+ codex: YOLO_COMMAND_MAP.codex,
234
+ gemini: YOLO_COMMAND_MAP.gemini,
235
+ opencode: YOLO_COMMAND_MAP.opencode
236
+ };
233
237
  const SIDEBAR_TREE_STORAGE_KEY = 'manyoyo.web.sidebarTree.v1';
234
238
  const markdownRenderer = window.ManyoyoMarkdown
235
239
  && typeof window.ManyoyoMarkdown.shouldRenderMessage === 'function'
236
240
  && typeof window.ManyoyoMarkdown.render === 'function'
237
241
  ? window.ManyoyoMarkdown
238
242
  : null;
239
- const pathPickerUtils = window.ManyoyoPathPickerUtils && typeof window.ManyoyoPathPickerUtils === 'object'
240
- ? window.ManyoyoPathPickerUtils
241
- : {};
242
-
243
243
  function normalizeBooleanMap(source) {
244
244
  const result = {};
245
245
  if (!source || typeof source !== 'object' || Array.isArray(source)) {
@@ -696,41 +696,6 @@
696
696
  return false;
697
697
  }
698
698
 
699
- function normalizeSlashPath(value) {
700
- if (typeof pathPickerUtils.normalizeSlashPath === 'function') {
701
- return pathPickerUtils.normalizeSlashPath(value);
702
- }
703
- return String(value || '').replace(/\\/g, '/');
704
- }
705
-
706
- function isChildPath(basePath, targetPath) {
707
- if (typeof pathPickerUtils.isChildPath === 'function') {
708
- return pathPickerUtils.isChildPath(basePath, targetPath);
709
- }
710
- const normalizedBase = normalizeSlashPath(basePath).replace(/\/+$/, '');
711
- const normalizedTarget = normalizeSlashPath(targetPath).replace(/\/+$/, '');
712
- if (!normalizedBase) {
713
- return false;
714
- }
715
- return normalizedTarget === normalizedBase || normalizedTarget.startsWith(normalizedBase + '/');
716
- }
717
-
718
- function buildContainerPathFromHostSelection(baseHostPath, baseContainerPath, selectedHostPath) {
719
- if (typeof pathPickerUtils.buildContainerPathFromHostSelection === 'function') {
720
- return pathPickerUtils.buildContainerPathFromHostSelection(baseHostPath, baseContainerPath, selectedHostPath);
721
- }
722
- const normalizedBaseHost = normalizeSlashPath(baseHostPath).replace(/\/+$/, '');
723
- const normalizedContainer = normalizeSlashPath(baseContainerPath).replace(/\/+$/, '') || '/workspace';
724
- const normalizedSelected = normalizeSlashPath(selectedHostPath).replace(/\/+$/, '');
725
- if (!normalizedBaseHost || !isChildPath(normalizedBaseHost, normalizedSelected)) {
726
- return normalizedContainer;
727
- }
728
- const relative = normalizedSelected === normalizedBaseHost
729
- ? ''
730
- : normalizedSelected.slice(normalizedBaseHost.length + 1);
731
- return relative ? `${normalizedContainer}/${relative}`.replace(/\/+/g, '/') : normalizedContainer;
732
- }
733
-
734
699
  function setModalVisible(modalNode, visible) {
735
700
  if (!modalNode) return;
736
701
  modalNode.hidden = !visible;
@@ -829,6 +794,26 @@
829
794
  return YOLO_COMMAND_MAP[key] || '';
830
795
  }
831
796
 
797
+ function normalizeCreateYoloValue(yolo) {
798
+ const key = String(yolo || '').trim().toLowerCase();
799
+ if (!key) {
800
+ return '';
801
+ }
802
+ if (key === 'claude' || key === 'cc' || key === 'c') {
803
+ return 'claude';
804
+ }
805
+ if (key === 'codex' || key === 'cx') {
806
+ return 'codex';
807
+ }
808
+ if (key === 'gemini' || key === 'gm' || key === 'g') {
809
+ return 'gemini';
810
+ }
811
+ if (key === 'opencode' || key === 'oc') {
812
+ return 'opencode';
813
+ }
814
+ return '';
815
+ }
816
+
832
817
  function stripLeadingAssignments(commandText) {
833
818
  let rest = String(commandText || '').trim();
834
819
  const assignmentPattern = /^(?:[A-Za-z_][A-Za-z0-9_]*=)(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^\s]+)(?:\s+|$)/;
@@ -919,7 +904,6 @@
919
904
  createContainerName.value = value.containerName || '';
920
905
  createHostPath.value = value.hostPath || '';
921
906
  createContainerPath.value = value.containerPath || '';
922
- state.createContainerPathBase = String(value.containerPath || '').trim();
923
907
  createImageName.value = value.imageName || '';
924
908
  createImageVersion.value = value.imageVersion || '';
925
909
  createContainerMode.value = value.containerMode || '';
@@ -928,7 +912,7 @@
928
912
  createShellSuffix.value = value.shellSuffix || '';
929
913
  createAgentPromptCommand.value = value.agentPromptCommand || '';
930
914
  state.createAgentPromptAuto = false;
931
- createYolo.value = value.yolo || '';
915
+ createYolo.value = normalizeCreateYoloValue(value.yolo);
932
916
  // 敏感 env 与继承数组由服务端在创建时合并,前端表单默认不回显,避免泄露或重复提交。
933
917
  createEnv.value = '';
934
918
  createEnvFile.value = '';
@@ -1106,28 +1090,101 @@
1106
1090
  agentTemplateError.textContent = state.agentTemplateError;
1107
1091
  }
1108
1092
 
1093
+ function inferTemplateCliValue(templateText, options) {
1094
+ const text = String(templateText || '').trim();
1095
+ const opts = options && typeof options === 'object' ? options : {};
1096
+ if (!text) {
1097
+ return opts.allowEmpty ? '' : 'custom';
1098
+ }
1099
+ const program = resolveAgentProgram(text);
1100
+ return AGENT_PROMPT_TEMPLATE_MAP[program] ? program : 'custom';
1101
+ }
1102
+
1103
+ function buildTemplateFromCliValue(cliValue) {
1104
+ const cli = String(cliValue || '').trim().toLowerCase();
1105
+ if (!cli || cli === 'custom') {
1106
+ return '';
1107
+ }
1108
+ const baseCommand = AGENT_TEMPLATE_CLI_COMMAND_MAP[cli] || '';
1109
+ if (!baseCommand) {
1110
+ return '';
1111
+ }
1112
+ return resolveAgentPromptTemplate(baseCommand);
1113
+ }
1114
+
1115
+ function syncAgentTemplateSelectFromEditor(selectNode, editorNode, options) {
1116
+ if (!selectNode || !editorNode) {
1117
+ return;
1118
+ }
1119
+ const value = inferTemplateCliValue(editorNode.value, options);
1120
+ selectNode.value = value;
1121
+ }
1122
+
1123
+ function getInheritedContainerTemplateText() {
1124
+ if (containerAgentPromptEditor) {
1125
+ const editorText = String(containerAgentPromptEditor.value || '').trim();
1126
+ if (editorText) {
1127
+ return editorText;
1128
+ }
1129
+ }
1130
+ if (state.sessionDetail && typeof state.sessionDetail.containerAgentPromptCommand === 'string') {
1131
+ const containerText = String(state.sessionDetail.containerAgentPromptCommand || '').trim();
1132
+ if (containerText) {
1133
+ return containerText;
1134
+ }
1135
+ }
1136
+ if (state.sessionDetail && typeof state.sessionDetail.agentPromptCommand === 'string') {
1137
+ const effectiveText = String(state.sessionDetail.agentPromptCommand || '').trim();
1138
+ if (effectiveText) {
1139
+ return effectiveText;
1140
+ }
1141
+ }
1142
+ return '';
1143
+ }
1144
+
1145
+ function applyAgentTemplateCliSelection(selectNode, editorNode, options) {
1146
+ if (!selectNode || !editorNode) {
1147
+ return;
1148
+ }
1149
+ const opts = options && typeof options === 'object' ? options : {};
1150
+ const cliValue = String(selectNode.value || '').trim();
1151
+ if (!cliValue && opts.allowEmpty === true) {
1152
+ editorNode.value = getInheritedContainerTemplateText();
1153
+ showAgentTemplateError('');
1154
+ return;
1155
+ }
1156
+ if (cliValue === 'custom') {
1157
+ showAgentTemplateError('');
1158
+ return;
1159
+ }
1160
+ const nextTemplate = buildTemplateFromCliValue(cliValue);
1161
+ if (nextTemplate) {
1162
+ editorNode.value = nextTemplate;
1163
+ }
1164
+ showAgentTemplateError('');
1165
+ }
1166
+
1109
1167
  function fillAgentTemplateForm(detail) {
1110
1168
  const currentDetail = detail && typeof detail === 'object' ? detail : {};
1169
+ const overrideEditable = isActiveAgentOverrideEditable();
1170
+ const containerTemplateText = String(
1171
+ currentDetail.containerAgentPromptCommand || currentDetail.agentPromptCommand || ''
1172
+ ).trim();
1111
1173
  if (containerAgentPromptEditor) {
1112
- containerAgentPromptEditor.value = currentDetail.containerAgentPromptCommand || '';
1174
+ containerAgentPromptEditor.value = containerTemplateText;
1113
1175
  }
1114
1176
  if (agentPromptOverrideEditor) {
1115
- agentPromptOverrideEditor.value = currentDetail.agentPromptCommandOverride || '';
1177
+ agentPromptOverrideEditor.value = currentDetail.agentPromptCommandOverride || containerTemplateText;
1178
+ }
1179
+ syncAgentTemplateSelectFromEditor(containerCliSelect, containerAgentPromptEditor);
1180
+ if (agentCliSelect) {
1181
+ agentCliSelect.value = currentDetail.agentPromptCommandOverride ? inferTemplateCliValue(currentDetail.agentPromptCommandOverride, { allowEmpty: true }) : '';
1182
+ }
1183
+ if (agentTemplatePrimary) {
1184
+ agentTemplatePrimary.hidden = overrideEditable;
1116
1185
  }
1117
1186
  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}`;
1187
+ agentTemplateOverrideGroup.hidden = !overrideEditable;
1131
1188
  }
1132
1189
  showAgentTemplateError('');
1133
1190
  }
@@ -1155,7 +1212,11 @@
1155
1212
  state.agentTemplateModalOpen = true;
1156
1213
  fillAgentTemplateForm(detail);
1157
1214
  syncUi();
1158
- if (containerAgentPromptEditor) {
1215
+ if (isActiveAgentOverrideEditable() && agentCliSelect) {
1216
+ agentCliSelect.focus();
1217
+ } else if (containerCliSelect) {
1218
+ containerCliSelect.focus();
1219
+ } else if (containerAgentPromptEditor) {
1159
1220
  containerAgentPromptEditor.focus();
1160
1221
  }
1161
1222
  }
@@ -1177,11 +1238,15 @@
1177
1238
  showAgentTemplateError('');
1178
1239
  syncUi();
1179
1240
  try {
1180
- const payload = {
1181
- containerAgentPromptCommand: containerAgentPromptEditor ? String(containerAgentPromptEditor.value || '').trim() : ''
1182
- };
1183
- if (isActiveAgentOverrideEditable() && agentPromptOverrideEditor) {
1184
- payload.agentPromptCommandOverride = String(agentPromptOverrideEditor.value || '').trim();
1241
+ const payload = {};
1242
+ if (isActiveAgentOverrideEditable()) {
1243
+ if (agentCliSelect && String(agentCliSelect.value || '').trim() === '') {
1244
+ payload.agentPromptCommandOverride = '';
1245
+ } else if (agentPromptOverrideEditor) {
1246
+ payload.agentPromptCommandOverride = String(agentPromptOverrideEditor.value || '').trim();
1247
+ }
1248
+ } else if (containerAgentPromptEditor) {
1249
+ payload.containerAgentPromptCommand = String(containerAgentPromptEditor.value || '').trim();
1185
1250
  }
1186
1251
  const data = await api('/api/sessions/' + encodeURIComponent(state.active) + '/agent-template', {
1187
1252
  method: 'PUT',
@@ -2020,6 +2085,18 @@
2020
2085
  if (agentTemplateCancelBtn) {
2021
2086
  agentTemplateCancelBtn.disabled = state.agentTemplateSaving;
2022
2087
  }
2088
+ if (containerCliSelect) {
2089
+ containerCliSelect.disabled = state.agentTemplateSaving || isActiveAgentOverrideEditable();
2090
+ }
2091
+ if (containerAgentPromptEditor) {
2092
+ containerAgentPromptEditor.disabled = state.agentTemplateSaving || isActiveAgentOverrideEditable();
2093
+ }
2094
+ if (agentCliSelect) {
2095
+ agentCliSelect.disabled = state.agentTemplateSaving || !isActiveAgentOverrideEditable();
2096
+ }
2097
+ if (agentPromptOverrideEditor) {
2098
+ agentPromptOverrideEditor.disabled = state.agentTemplateSaving || !isActiveAgentOverrideEditable();
2099
+ }
2023
2100
  document.body.classList.toggle(
2024
2101
  'modal-open',
2025
2102
  state.configModalOpen || state.createModalOpen || state.directoryPicker.open || state.agentTemplateModalOpen
@@ -2290,12 +2367,8 @@
2290
2367
  try {
2291
2368
  const params = new URLSearchParams();
2292
2369
  params.set('path', picker.currentPath || '/');
2293
- if (picker.basePath) {
2294
- params.set('basePath', picker.basePath);
2295
- }
2296
2370
  const data = await api('/api/fs/directories?' + params.toString());
2297
2371
  picker.currentPath = data.currentPath || picker.currentPath;
2298
- picker.basePath = data.basePath || picker.basePath || '';
2299
2372
  picker.parentPath = data.parentPath || '';
2300
2373
  picker.entries = Array.isArray(data.entries) ? data.entries : [];
2301
2374
  } catch (e) {
@@ -2314,8 +2387,6 @@
2314
2387
  state.directoryPicker.title = '';
2315
2388
  state.directoryPicker.tip = '';
2316
2389
  state.directoryPicker.currentPath = '';
2317
- state.directoryPicker.basePath = '';
2318
- state.directoryPicker.baseContainerPath = '';
2319
2390
  state.directoryPicker.parentPath = '';
2320
2391
  state.directoryPicker.entries = [];
2321
2392
  state.directoryPicker.error = '';
@@ -2327,54 +2398,22 @@
2327
2398
  if (!picker.currentPath) {
2328
2399
  return;
2329
2400
  }
2330
- if (picker.mode === 'host') {
2331
- createHostPath.value = picker.currentPath;
2332
- if (!(createContainerPath.value || '').trim()) {
2333
- createContainerPath.value = '/workspace';
2334
- state.createContainerPathBase = '/workspace';
2335
- }
2336
- } else if (picker.mode === 'container') {
2337
- const mapped = buildContainerPathFromHostSelection(
2338
- picker.basePath,
2339
- picker.baseContainerPath || '/workspace',
2340
- picker.currentPath
2341
- );
2342
- createContainerPath.value = mapped;
2343
- }
2401
+ createHostPath.value = picker.currentPath;
2402
+ createContainerPath.value = picker.currentPath;
2344
2403
  closeDirectoryPicker();
2345
2404
  }
2346
2405
 
2347
- function openDirectoryPicker(mode) {
2406
+ function openDirectoryPicker() {
2348
2407
  const picker = state.directoryPicker;
2349
2408
  picker.open = true;
2350
2409
  picker.loading = false;
2351
2410
  picker.error = '';
2352
2411
  picker.entries = [];
2353
2412
  picker.parentPath = '';
2354
- if (mode === 'container') {
2355
- const baseHostPath = (createHostPath.value || '').trim();
2356
- if (!baseHostPath) {
2357
- showCreateError('请先选择 hostPath,再选择 containerPath。');
2358
- picker.open = false;
2359
- renderDirectoryPicker();
2360
- return;
2361
- }
2362
- picker.mode = 'container';
2363
- picker.title = '选择 containerPath 对应目录';
2364
- picker.tip = '从 hostPath 下选择子目录,结果会映射到容器路径。';
2365
- picker.basePath = baseHostPath;
2366
- picker.baseContainerPath = typeof pathPickerUtils.resolveContainerPickerBase === 'function'
2367
- ? pathPickerUtils.resolveContainerPickerBase(state.createContainerPathBase, createContainerPath.value)
2368
- : (String(state.createContainerPathBase || '').trim() || String(createContainerPath.value || '').trim() || '/workspace');
2369
- picker.currentPath = baseHostPath;
2370
- } else {
2371
- picker.mode = 'host';
2372
- picker.title = '选择 hostPath';
2373
- picker.tip = '浏览宿主机目录,选中后会回填 create 表单。';
2374
- picker.basePath = '';
2375
- picker.baseContainerPath = '';
2376
- picker.currentPath = (createHostPath.value || '').trim() || '/';
2377
- }
2413
+ picker.mode = 'host';
2414
+ picker.title = '选择 hostPath';
2415
+ picker.tip = '浏览宿主机目录,选中后会回填 create 表单。';
2416
+ picker.currentPath = (createHostPath.value || '').trim() || '/';
2378
2417
  renderDirectoryPicker();
2379
2418
  loadDirectoryPicker(picker.currentPath);
2380
2419
  }
@@ -3538,13 +3577,7 @@
3538
3577
 
3539
3578
  if (pickHostPathBtn) {
3540
3579
  pickHostPathBtn.addEventListener('click', function () {
3541
- openDirectoryPicker('host');
3542
- });
3543
- }
3544
-
3545
- if (pickContainerPathBtn) {
3546
- pickContainerPathBtn.addEventListener('click', function () {
3547
- openDirectoryPicker('container');
3580
+ openDirectoryPicker();
3548
3581
  });
3549
3582
  }
3550
3583
 
@@ -3568,6 +3601,30 @@
3568
3601
  });
3569
3602
  }
3570
3603
 
3604
+ if (containerCliSelect) {
3605
+ containerCliSelect.addEventListener('change', function () {
3606
+ applyAgentTemplateCliSelection(containerCliSelect, containerAgentPromptEditor);
3607
+ });
3608
+ }
3609
+
3610
+ if (agentCliSelect) {
3611
+ agentCliSelect.addEventListener('change', function () {
3612
+ applyAgentTemplateCliSelection(agentCliSelect, agentPromptOverrideEditor, { allowEmpty: true });
3613
+ });
3614
+ }
3615
+
3616
+ if (containerAgentPromptEditor) {
3617
+ containerAgentPromptEditor.addEventListener('input', function () {
3618
+ syncAgentTemplateSelectFromEditor(containerCliSelect, containerAgentPromptEditor);
3619
+ });
3620
+ }
3621
+
3622
+ if (agentPromptOverrideEditor) {
3623
+ agentPromptOverrideEditor.addEventListener('input', function () {
3624
+ syncAgentTemplateSelectFromEditor(agentCliSelect, agentPromptOverrideEditor, { allowEmpty: true });
3625
+ });
3626
+ }
3627
+
3571
3628
  if (agentTemplateCancelBtn) {
3572
3629
  agentTemplateCancelBtn.addEventListener('click', function () {
3573
3630
  closeAgentTemplateModal();
@@ -3599,6 +3656,9 @@
3599
3656
  inputNode.addEventListener('input', function () {
3600
3657
  updateCreateAgentPromptCommandFromCommand();
3601
3658
  });
3659
+ inputNode.addEventListener('change', function () {
3660
+ updateCreateAgentPromptCommandFromCommand();
3661
+ });
3602
3662
  });
3603
3663
 
3604
3664
  if (createAgentPromptCommand) {
@@ -3614,12 +3674,6 @@
3614
3674
  });
3615
3675
  }
3616
3676
 
3617
- if (createContainerPath) {
3618
- createContainerPath.addEventListener('input', function () {
3619
- state.createContainerPathBase = String(createContainerPath.value || '').trim();
3620
- });
3621
- }
3622
-
3623
3677
  if (createForm) {
3624
3678
  createForm.addEventListener('submit', async function (event) {
3625
3679
  event.preventDefault();
package/lib/web/server.js CHANGED
@@ -3767,7 +3767,7 @@ async function startWebServer(options) {
3767
3767
  const appFrontendMatch = pathname.match(/^\/app\/frontend\/([A-Za-z0-9._-]+)$/);
3768
3768
  if (req.method === 'GET' && appFrontendMatch) {
3769
3769
  const assetName = appFrontendMatch[1];
3770
- if (!(assetName === 'app.css' || assetName === 'app.js' || assetName === 'markdown.css' || assetName === 'markdown-renderer.js' || assetName === 'path-picker-utils.js')) {
3770
+ if (!(assetName === 'app.css' || assetName === 'app.js' || assetName === 'markdown.css' || assetName === 'markdown-renderer.js')) {
3771
3771
  sendHtml(res, 404, '<h1>404 Not Found</h1>');
3772
3772
  return;
3773
3773
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcanwin/manyoyo",
3
- "version": "5.8.0",
3
+ "version": "5.8.2",
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",
@@ -1,57 +0,0 @@
1
- (function (root, factory) {
2
- const api = factory();
3
- if (typeof module === 'object' && module.exports) {
4
- module.exports = api;
5
- }
6
- if (root && typeof root === 'object') {
7
- root.ManyoyoPathPickerUtils = api;
8
- }
9
- }(typeof globalThis !== 'undefined' ? globalThis : this, function () {
10
- function normalizeSlashPath(value) {
11
- return String(value || '').replace(/\\/g, '/');
12
- }
13
-
14
- function isChildPath(basePath, targetPath) {
15
- const normalizedBase = normalizeSlashPath(basePath).replace(/\/+$/, '');
16
- const normalizedTarget = normalizeSlashPath(targetPath).replace(/\/+$/, '');
17
- if (!normalizedBase) {
18
- return false;
19
- }
20
- return normalizedTarget === normalizedBase || normalizedTarget.startsWith(normalizedBase + '/');
21
- }
22
-
23
- function resolveContainerPickerBase(baseContainerPath, currentContainerPath) {
24
- return String(baseContainerPath || '').trim()
25
- || String(currentContainerPath || '').trim()
26
- || '/workspace';
27
- }
28
-
29
- function buildContainerPathFromHostSelection(baseHostPath, baseContainerPath, selectedHostPath) {
30
- const normalizedBaseHost = normalizeSlashPath(baseHostPath).replace(/\/+$/, '');
31
- const normalizedContainer = normalizeSlashPath(baseContainerPath).replace(/\/+$/, '') || '/workspace';
32
- const normalizedSelected = normalizeSlashPath(selectedHostPath).replace(/\/+$/, '');
33
- if (!normalizedBaseHost || !isChildPath(normalizedBaseHost, normalizedSelected)) {
34
- return normalizedContainer;
35
- }
36
- const relative = normalizedSelected === normalizedBaseHost
37
- ? ''
38
- : normalizedSelected.slice(normalizedBaseHost.length + 1);
39
- return relative ? `${normalizedContainer}/${relative}`.replace(/\/+/g, '/') : normalizedContainer;
40
- }
41
-
42
- function applyContainerPathSelection(baseHostPath, baseContainerPath, currentContainerPath, selectedHostPath) {
43
- return buildContainerPathFromHostSelection(
44
- baseHostPath,
45
- resolveContainerPickerBase(baseContainerPath, currentContainerPath),
46
- selectedHostPath
47
- );
48
- }
49
-
50
- return {
51
- normalizeSlashPath,
52
- isChildPath,
53
- resolveContainerPickerBase,
54
- buildContainerPathFromHostSelection,
55
- applyContainerPathSelection
56
- };
57
- }));