tagword-codeagent 1.1.6__py3-none-any.whl

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 (173) hide show
  1. codeagent/__init__.py +10 -0
  2. codeagent/__main__.py +13 -0
  3. codeagent/cli/__init__.py +13 -0
  4. codeagent/cli/entrypoint.py +161 -0
  5. codeagent/cli/main.py +979 -0
  6. codeagent/core/__init__.py +2 -0
  7. codeagent/core/attachments.py +342 -0
  8. codeagent/core/audio_models.py +50 -0
  9. codeagent/core/bootstrap.py +178 -0
  10. codeagent/core/default_presets.py +159 -0
  11. codeagent/core/env.py +106 -0
  12. codeagent/core/folder_picker.py +151 -0
  13. codeagent/core/image_understanding.py +90 -0
  14. codeagent/core/models.py +6 -0
  15. codeagent/core/paths.py +238 -0
  16. codeagent/core/pricing.py +107 -0
  17. codeagent/core/process_ports.py +258 -0
  18. codeagent/core/seed_bridge.py +21 -0
  19. codeagent/core/settings.py +98 -0
  20. codeagent/core/speech_synth.py +330 -0
  21. codeagent/core/team_config.py +247 -0
  22. codeagent/core/team_manager.py +245 -0
  23. codeagent/core/token_counter.py +41 -0
  24. codeagent/core/tts_voices.py +103 -0
  25. codeagent/core/usage_billing.py +72 -0
  26. codeagent/core/vision_models.py +93 -0
  27. codeagent/memory/diary.py +144 -0
  28. codeagent/persona_defaults/README.md +12 -0
  29. codeagent/persona_defaults/agent.md +352 -0
  30. codeagent/persona_defaults/identity.md +29 -0
  31. codeagent/persona_defaults/memory.md +4 -0
  32. codeagent/persona_defaults/skills.md +325 -0
  33. codeagent/persona_defaults/soul.md +38 -0
  34. codeagent/persona_defaults/tools.md +41 -0
  35. codeagent/persona_defaults/user.md +18 -0
  36. codeagent/runtime/__init__.py +11 -0
  37. codeagent/runtime/agent_context.py +6 -0
  38. codeagent/runtime/agent_runtime.py +21 -0
  39. codeagent/runtime/compact_prompt.py +9 -0
  40. codeagent/runtime/compact_state.py +40 -0
  41. codeagent/runtime/llm_worker.py +132 -0
  42. codeagent/runtime/orchestrator.py +161 -0
  43. codeagent/runtime/prompt_enrichment.py +324 -0
  44. codeagent/runtime/session_manager.py +30 -0
  45. codeagent/runtime/task_split.py +38 -0
  46. codeagent/runtime/turn_loop.py +13 -0
  47. codeagent/runtime/worker.py +48 -0
  48. codeagent/server/__init__.py +505 -0
  49. codeagent/server/app_factory.py +1292 -0
  50. codeagent/server/attachment_api.py +261 -0
  51. codeagent/server/hub.py +175 -0
  52. codeagent/server/icon.png +0 -0
  53. codeagent/server/self_healing.py +143 -0
  54. codeagent/server/team_engine.py +243 -0
  55. codeagent/server/tts_api.py +51 -0
  56. codeagent/server/webui_api_app.py +3180 -0
  57. codeagent/skills/__init__.py +2 -0
  58. codeagent/skills/select.py +144 -0
  59. codeagent/tools/__init__.py +6 -0
  60. codeagent/tools/agent_tools.py +10 -0
  61. codeagent/web/__init__.py +1 -0
  62. codeagent/web/__pycache__/__init__.cpython-313.pyc +0 -0
  63. codeagent/web/__pycache__/app.cpython-313.pyc +0 -0
  64. codeagent/web/__pycache__/auth_impl.cpython-313.pyc +0 -0
  65. codeagent/web/auth_impl.py +326 -0
  66. codeagent/web/static/00-marked.min.js +6 -0
  67. codeagent/web/static/00-mermaid.min.js +2314 -0
  68. codeagent/web/static/00-purify.min.js +3 -0
  69. codeagent/web/static/00-storage.js +83 -0
  70. codeagent/web/static/00-utils.js +51 -0
  71. codeagent/web/static/01-theme.css +96 -0
  72. codeagent/web/static/01a-scroll.js +86 -0
  73. codeagent/web/static/01b-think.js +62 -0
  74. codeagent/web/static/01c-session-identity.js +81 -0
  75. codeagent/web/static/01c-session-thinking.js +61 -0
  76. codeagent/web/static/01c-session-tree-menus.js +349 -0
  77. codeagent/web/static/01c-session-tree.js +531 -0
  78. codeagent/web/static/01c-session-utilities.js +83 -0
  79. codeagent/web/static/01c-vision-model.js +132 -0
  80. codeagent/web/static/01d-image-gen-model.js +84 -0
  81. codeagent/web/static/01e-audio-model.js +97 -0
  82. codeagent/web/static/01f-compose-ui.js +88 -0
  83. codeagent/web/static/01g-tts-settings.js +128 -0
  84. codeagent/web/static/01h-music-model.js +85 -0
  85. codeagent/web/static/01i-model-stack.js +291 -0
  86. codeagent/web/static/01j-video-gen-model.js +85 -0
  87. codeagent/web/static/01k-model-stack-bootstrap.js +66 -0
  88. codeagent/web/static/01k-model-stack-state.js +209 -0
  89. codeagent/web/static/01q-inflight.js +195 -0
  90. codeagent/web/static/02-reset.css +22 -0
  91. codeagent/web/static/02-session-ui.js +201 -0
  92. codeagent/web/static/03-layout-p1.css +2 -0
  93. codeagent/web/static/03-layout-p2.css +2 -0
  94. codeagent/web/static/03a-links-stream.js +88 -0
  95. codeagent/web/static/03b-stream-text.js +119 -0
  96. codeagent/web/static/03c-render-bubble.js +520 -0
  97. codeagent/web/static/03d-agent-bubble.js +126 -0
  98. codeagent/web/static/03e-tool-trace.js +182 -0
  99. codeagent/web/static/03f-timeline.js +54 -0
  100. codeagent/web/static/03g-bubble-tts.js +292 -0
  101. codeagent/web/static/04-components-p1.css +1 -0
  102. codeagent/web/static/04-components-p2b.css +1 -0
  103. codeagent/web/static/04-components-p3.css +2 -0
  104. codeagent/web/static/04-components-p4.css +1 -0
  105. codeagent/web/static/04-components-p5.css +1 -0
  106. codeagent/web/static/04-components-p6.css +58 -0
  107. codeagent/web/static/04-components-p7.css +4 -0
  108. codeagent/web/static/04-components-p8.css +1 -0
  109. codeagent/web/static/04-components-p9.css +1 -0
  110. codeagent/web/static/04-ws-connect.js +351 -0
  111. codeagent/web/static/04-ws-helpers.js +116 -0
  112. codeagent/web/static/05-chat-p1.css +1058 -0
  113. codeagent/web/static/05-chat-p2.css +1 -0
  114. codeagent/web/static/05-chat-p3.css +295 -0
  115. codeagent/web/static/05a-session-history.js +176 -0
  116. codeagent/web/static/05b-session-list.js +114 -0
  117. codeagent/web/static/05c-dialog.js +123 -0
  118. codeagent/web/static/05d-project-create.js +172 -0
  119. codeagent/web/static/05e-project-remote.js +143 -0
  120. codeagent/web/static/06-chat.js +552 -0
  121. codeagent/web/static/06-utilities.css +181 -0
  122. codeagent/web/static/06b-attachments.js +304 -0
  123. codeagent/web/static/06c-camera.js +488 -0
  124. codeagent/web/static/07-activity-bar.css +127 -0
  125. codeagent/web/static/09-git-p1.css +1 -0
  126. codeagent/web/static/09-git-p2.css +1 -0
  127. codeagent/web/static/09a-init-bind.js +110 -0
  128. codeagent/web/static/09b-panel-toggle.js +80 -0
  129. codeagent/web/static/09c-api-refresh.js +100 -0
  130. codeagent/web/static/09d-status-log.js +80 -0
  131. codeagent/web/static/09e-commit.js +80 -0
  132. codeagent/web/static/09f-remote.js +93 -0
  133. codeagent/web/static/11a-cron-parse.js +117 -0
  134. codeagent/web/static/11b-cron-panel.js +100 -0
  135. codeagent/web/static/11c-cron-card.js +133 -0
  136. codeagent/web/static/11d-cron-form.js +105 -0
  137. codeagent/web/static/11e-env-config.js +168 -0
  138. codeagent/web/static/11f-env-llm-a-state.js +136 -0
  139. codeagent/web/static/11f-env-llm-b-form.js +409 -0
  140. codeagent/web/static/11f-env-llm-c-board.js +322 -0
  141. codeagent/web/static/11g-env-paths-git.js +973 -0
  142. codeagent/web/static/11h-env-mcp-a-state.js +161 -0
  143. codeagent/web/static/11h-env-mcp-b-form.js +229 -0
  144. codeagent/web/static/11h-env-mcp-c-board.js +565 -0
  145. codeagent/web/static/12-plugins.js +127 -0
  146. codeagent/web/static/13a-skills.js +156 -0
  147. codeagent/web/static/13a-todo-utils.js +140 -0
  148. codeagent/web/static/13b-todo-fetch.js +106 -0
  149. codeagent/web/static/13b-tools.js +127 -0
  150. codeagent/web/static/13c-todo-crud.js +92 -0
  151. codeagent/web/static/14-activity-bar.js +177 -0
  152. codeagent/web/static/15-plans.css +194 -0
  153. codeagent/web/static/15-plans.js +219 -0
  154. codeagent/web/static/16-mobile.css +612 -0
  155. codeagent/web/static/16-mobile.js +178 -0
  156. codeagent/web/static/17-preset-ux.css +287 -0
  157. codeagent/web/static/18-agent-mgr.css +616 -0
  158. codeagent/web/static/18-agent-mgr.js +204 -0
  159. codeagent/web/static/24-health-dashboard.js +68 -0
  160. codeagent/web/static/99-brand-polish.css +152 -0
  161. codeagent/web/static/body.html +824 -0
  162. codeagent/web/static/setup/setup_form.html +171 -0
  163. codeagent/web/static/setup/setup_head.html +100 -0
  164. codeagent/web/static/setup/setup_tail.html +264 -0
  165. codeagent/web/static/setup/setup_test.html +38 -0
  166. codeagent/web/web_login.html +265 -0
  167. codeagent/web/webui.html +35 -0
  168. tagword_codeagent-1.1.6.dist-info/METADATA +273 -0
  169. tagword_codeagent-1.1.6.dist-info/RECORD +173 -0
  170. tagword_codeagent-1.1.6.dist-info/WHEEL +5 -0
  171. tagword_codeagent-1.1.6.dist-info/entry_points.txt +3 -0
  172. tagword_codeagent-1.1.6.dist-info/licenses/LICENSE +21 -0
  173. tagword_codeagent-1.1.6.dist-info/top_level.txt +1 -0
@@ -0,0 +1,123 @@
1
+ try {
2
+ const r = await fetch('/api/ui/flags');
3
+ const f = await r.json();
4
+ if (!f.sessions_ui) return;
5
+ webuiSessionsEnabled = true;
6
+ if (fs) fs.style.display = 'block';
7
+ if (sidebar) {
8
+ var actMode = '';
9
+ actMode = tryGetLS(STORAGE_KEYS.SESS_ACTIVITY_MODE, '');
10
+ if (actMode === 'config' || actMode === 'tasks' || actMode === 'agent') {
11
+ sidebar.style.display = 'none';
12
+ } else if (actMode === 'files') {
13
+ sidebar.style.display = 'none';
14
+ } else {
15
+ sidebar.style.display = 'flex';
16
+ }
17
+ }
18
+ bindSessListOnce();
19
+ const btnProj = document.getElementById('btnNewProject');
20
+ if (btnProj && btnProj.dataset.bound !== '1') {
21
+ btnProj.dataset.bound = '1';
22
+ btnProj.addEventListener('click', () => {
23
+ showNewProjectDialog();
24
+ });
25
+ }
26
+
27
+ // ---- 新建项目对话框逻辑(含来源选择 + 远程配置) ---- //
28
+ function showNewProjectDialog() {
29
+ var dialog = document.getElementById('newProjectDialog');
30
+ var inpName = document.getElementById('inpNewProjectName');
31
+ var inpPath = document.getElementById('inpNewProjectPath');
32
+ var btnConfirm = document.getElementById('btnNewProjectConfirm');
33
+ var btnCancel = document.getElementById('btnNewProjectCancel');
34
+ var btnClose = document.getElementById('btnNewProjectDialogClose');
35
+ if (!dialog || !inpName) return;
36
+
37
+ // 重置
38
+ inpName.value = '';
39
+ inpPath.value = '';
40
+ document.querySelector('input[name="projectSource"][value="scratch"]').checked = true;
41
+ document.getElementById('inpCloneUrl').value = '';
42
+ document.getElementById('chkProjectRemoteEnable').checked = false;
43
+ document.getElementById('projectRemoteForm').style.display = 'none';
44
+ document.getElementById('inpProjectRemoteHost').value = '';
45
+ document.getElementById('inpProjectRemoteHost').style.display = 'none';
46
+ _updateProjectSourceFields('scratch');
47
+
48
+ // 加载默认远程配置
49
+ _loadProjectDefaults();
50
+
51
+ dialog.style.display = 'flex';
52
+ setTimeout(function() { inpName.focus(); }, 100);
53
+
54
+ function closeDialog() { dialog.style.display = 'none'; }
55
+
56
+ function onSubmit() {
57
+ var name = inpName.value.trim();
58
+ if (!name) { inpName.focus(); return; }
59
+ var path = inpPath.value.trim();
60
+
61
+ // 收集来源信息
62
+ var source = document.querySelector('input[name="projectSource"]:checked')?.value || 'scratch';
63
+ var cloneUrl = source === 'clone' ? document.getElementById('inpCloneUrl').value.trim() : '';
64
+ var template = source === 'template' ? document.getElementById('selProjectTemplate').value : '';
65
+
66
+ // 收集远程信息
67
+ var remoteInfo = null;
68
+ if (source !== 'clone' && document.getElementById('chkProjectRemoteEnable').checked) {
69
+ var provider = document.getElementById('selProjectRemoteProvider').value;
70
+ remoteInfo = {
71
+ provider: provider,
72
+ host: document.getElementById('inpProjectRemoteHost').value.trim(),
73
+ owner: document.getElementById('inpProjectRemoteOwner').value.trim(),
74
+ repo: document.getElementById('inpProjectRemoteRepo').value.trim(),
75
+ protocol: document.querySelector('input[name="projectRemoteProtocol"]:checked')?.value || 'ssh',
76
+ autoPush: document.getElementById('chkProjectAutoPush').checked,
77
+ };
78
+ if (provider === 'custom') {
79
+ if (!remoteInfo.host || !remoteInfo.owner || !remoteInfo.repo) remoteInfo = null;
80
+ } else {
81
+ if (!remoteInfo.owner || !remoteInfo.repo) remoteInfo = null;
82
+ }
83
+ }
84
+
85
+ // 不关闭对话框,而是显示进度覆盖层
86
+ _showProjectProgress(true, source, cloneUrl);
87
+ doCreateProject(name, path, source, cloneUrl, template, remoteInfo);
88
+ }
89
+
90
+ // 来源切换
91
+ document.querySelectorAll('input[name="projectSource"]').forEach(function(el) {
92
+ el.addEventListener('change', function() {
93
+ _updateProjectSourceFields(this.value);
94
+ });
95
+ });
96
+
97
+ // 远程启用/禁用
98
+ document.getElementById('chkProjectRemoteEnable').addEventListener('change', function() {
99
+ document.getElementById('projectRemoteForm').style.display = this.checked ? 'block' : 'none';
100
+ });
101
+
102
+ // 方案选择器切换
103
+ var selPreset = document.getElementById('selProjectPreset');
104
+ if (selPreset) {
105
+ selPreset.addEventListener('change', function() {
106
+ var name = this.value;
107
+ if (!name) return;
108
+ fetch('/api/ui/git/defaults').then(function(r) { return r.json(); }).then(function(j) {
109
+ var def = j.defaults || {};
110
+ if (!def.presets && def.provider) {
111
+ def.presets = [{ name: '默认', provider: def.provider, owner: def.owner || '', host: def.host || '', protocol: def.protocol || 'ssh', autoPush: !!def.autoPush }];
112
+ }
113
+ _applyPreset(name, def.presets || []);
114
+ }).catch(function() {});
115
+ });
116
+ }
117
+
118
+ // 远程输入实时预览
119
+ ['selProjectRemoteProvider', 'inpProjectRemoteHost', 'inpProjectRemoteOwner', 'inpProjectRemoteRepo'].forEach(function(id) {
120
+ var el = document.getElementById(id);
121
+ if (el) el.addEventListener('change', _updateProjectRemotePreview);
122
+ });
123
+ document.querySelectorAll('input[name="projectRemoteProtocol"]').forEach(function(el) {
@@ -0,0 +1,172 @@
1
+ el.addEventListener('change', _updateProjectRemotePreview);
2
+ });
3
+
4
+ // 绑定按钮事件:用 onclick 直接赋值,自动覆盖旧监听器
5
+ btnConfirm.onclick = onSubmit;
6
+ btnCancel.onclick = closeDialog;
7
+ btnClose.onclick = closeDialog;
8
+ inpName.addEventListener('keydown', function(e) {
9
+ if (e.key === 'Enter') onSubmit();
10
+ if (e.key === 'Escape') closeDialog();
11
+ });
12
+ inpPath.addEventListener('keydown', function(e) {
13
+ if (e.key === 'Enter') onSubmit();
14
+ if (e.key === 'Escape') closeDialog();
15
+ });
16
+ dialog.addEventListener('click', function(e) {
17
+ if (e.target === dialog) closeDialog();
18
+ });
19
+ }
20
+
21
+ function _updateProjectSourceFields(source) {
22
+ document.getElementById('fieldCloneUrl').style.display = source === 'clone' ? 'block' : 'none';
23
+ document.getElementById('fieldTemplate').style.display = source === 'template' ? 'block' : 'none';
24
+ // 克隆模式隐藏远程配置(克隆自带 origin)
25
+ var remoteField = document.getElementById('fieldProjectRemote');
26
+ if (source === 'clone') {
27
+ remoteField.style.display = 'none';
28
+ } else {
29
+ remoteField.style.display = 'block';
30
+ }
31
+ }
32
+
33
+ async function _loadProjectDefaults() {
34
+ try {
35
+ var r = await fetch('/api/ui/git/defaults');
36
+ if (!r.ok) return;
37
+ var j = await r.json();
38
+ var def = j.defaults || {};
39
+ // 兼容旧格式
40
+ if (!def.presets && def.provider) {
41
+ def.presets = [{
42
+ name: '默认',
43
+ provider: def.provider,
44
+ owner: def.owner || '',
45
+ host: def.host || '',
46
+ protocol: def.protocol || 'ssh',
47
+ autoPush: !!def.autoPush,
48
+ }];
49
+ def.defaultPreset = '默认';
50
+ }
51
+ if (def.enabled) {
52
+ document.getElementById('chkProjectRemoteEnable').checked = true;
53
+ document.getElementById('projectRemoteForm').style.display = 'block';
54
+ // 填充方案选择器
55
+ var presets = def.presets || [];
56
+ var defaultName = def.defaultPreset || '';
57
+ var selPreset = document.getElementById('selProjectPreset');
58
+ if (selPreset) {
59
+ if (presets.length > 1) {
60
+ document.getElementById('presetSelectorRow').style.display = '';
61
+ selPreset.innerHTML = '<option value="">— 选择方案 —</option>';
62
+ for (var i = 0; i < presets.length; i++) {
63
+ var opt = document.createElement('option');
64
+ opt.value = presets[i].name;
65
+ opt.textContent = presets[i].name;
66
+ selPreset.appendChild(opt);
67
+ }
68
+ } else {
69
+ document.getElementById('presetSelectorRow').style.display = 'none';
70
+ }
71
+ // 默认选中 defaultPreset
72
+ if (defaultName) {
73
+ selPreset.value = defaultName;
74
+ _applyPreset(defaultName, presets);
75
+ } else if (presets.length === 1) {
76
+ _applyPreset(presets[0].name, presets);
77
+ }
78
+ } else {
79
+ // fallback: 无选择器时直接填第一套
80
+ if (presets.length) _applyPreset(presets[0].name, presets);
81
+ }
82
+ }
83
+ } catch(_) {}
84
+ }
85
+
86
+ function _applyPreset(name, presets) {
87
+ var p = null;
88
+ for (var i = 0; i < (presets || []).length; i++) {
89
+ if (presets[i].name === name) { p = presets[i]; break; }
90
+ }
91
+ if (!p && presets && presets.length) p = presets[0];
92
+ if (!p) return;
93
+ document.getElementById('selProjectRemoteProvider').value = p.provider || 'github';
94
+ if (p.host) document.getElementById('inpProjectRemoteHost').value = p.host;
95
+ if (p.owner) document.getElementById('inpProjectRemoteOwner').value = p.owner;
96
+ if (p.protocol === 'https') {
97
+ var el = document.querySelector('input[name="projectRemoteProtocol"][value="https"]');
98
+ if (el) el.checked = true;
99
+ } else {
100
+ var el2 = document.querySelector('input[name="projectRemoteProtocol"][value="ssh"]');
101
+ if (el2) el2.checked = true;
102
+ }
103
+ document.getElementById('chkProjectAutoPush').checked = !!p.autoPush;
104
+ _updateProjectRemotePreview();
105
+ }
106
+
107
+ function _updateProjectRemotePreview() {
108
+ var preview = document.getElementById('projectRemoteUrlPreview');
109
+ if (!preview) return;
110
+ var provider = document.getElementById('selProjectRemoteProvider').value;
111
+ var hostEl = document.getElementById('inpProjectRemoteHost');
112
+ var ownerEl = document.getElementById('inpProjectRemoteOwner');
113
+ var repoEl = document.getElementById('inpProjectRemoteRepo');
114
+
115
+ // 显示/隐藏 主机地址输入
116
+ hostEl.style.display = provider === 'custom' ? '' : 'none';
117
+
118
+ var owner = ownerEl.value.trim() || '{owner}';
119
+ var repo = repoEl.value.trim() || '{repo}';
120
+ var protocol = document.querySelector('input[name="projectRemoteProtocol"]:checked')?.value || 'ssh';
121
+ var templates = {
122
+ 'github': { ssh: 'git@github.com:{owner}/{repo}.git', https: 'https://github.com/{owner}/{repo}.git' },
123
+ 'gitlab': { ssh: 'git@gitlab.com:{owner}/{repo}.git', https: 'https://gitlab.com/{owner}/{repo}.git' },
124
+ 'gitee': { ssh: 'git@gitee.com:{owner}/{repo}.git', https: 'https://gitee.com/{owner}/{repo}.git' },
125
+ 'bitbucket': { ssh: 'git@bitbucket.org:{owner}/{repo}.git', https: 'https://bitbucket.org/{owner}/{repo}.git' },
126
+ };
127
+ var t = templates[provider];
128
+ if (t) {
129
+ preview.textContent = 'URL: ' + t[protocol].replace('{owner}', owner).replace('{repo}', repo);
130
+ } else if (provider === 'custom') {
131
+ var host = hostEl.value.trim() || '{host}';
132
+ var url;
133
+ if (protocol === 'ssh') {
134
+ url = 'git@' + host + ':' + owner + '/' + repo + '.git';
135
+ } else {
136
+ url = 'https://' + host + '/' + owner + '/' + repo + '.git';
137
+ }
138
+ preview.textContent = 'URL: ' + url;
139
+ }
140
+ }
141
+
142
+ // ---- 目录选择器:服务端调系统对话框(macOS choose folder / Win 文件夹选择器),返回完整路径 ---- //
143
+ function initNativeDirPicker() {
144
+ var btnBrowse = document.getElementById('btnBrowseDir');
145
+ var inpPath = document.getElementById('inpNewProjectPath');
146
+ if (!btnBrowse || btnBrowse.dataset.bound) return;
147
+ btnBrowse.dataset.bound = '1';
148
+
149
+ btnBrowse.addEventListener('click', function() {
150
+ btnBrowse.textContent = '⏳ 选择中…';
151
+ btnBrowse.disabled = true;
152
+
153
+ fetch('/api/ui/pick-directory', { method: 'POST', credentials: 'same-origin' })
154
+ .then(function(r) { return r.json(); })
155
+ .then(function(j) {
156
+ if (j.path) inpPath.value = j.path;
157
+ else if (j.hint) console.warn('pick-directory:', j.hint);
158
+ else if (j.detail) console.warn('pick-directory:', j.detail);
159
+ })
160
+ .catch(function(e) { console.error('pick-directory error:', e); })
161
+ .finally(function() {
162
+ btnBrowse.textContent = '📁 浏览';
163
+ btnBrowse.disabled = false;
164
+ });
165
+ });
166
+ }
167
+ initNativeDirPicker();
168
+
169
+ async function doCreateProject(name, path, source, cloneUrl, template, remoteInfo) {
170
+ // 显示进度提示
171
+ _showProjectProgress(true, source, cloneUrl);
172
+ try {
@@ -0,0 +1,143 @@
1
+ var body = { agent_id: agentId, name: name };
2
+ if (path) body.path = path;
3
+ if (source) body.source = source;
4
+ if (cloneUrl) body.clone_url = cloneUrl;
5
+ if (template) body.template = template;
6
+ if (remoteInfo) body.remote = remoteInfo;
7
+
8
+ // 启动心跳 + 计时器
9
+ var heartbeatMsgs = [
10
+ '正在连接远程仓库...',
11
+ '克隆进行中,请耐心等待...',
12
+ '网络较慢,仍在努力...',
13
+ '对方服务器响应中...',
14
+ ];
15
+ var heartbeatIdx = 0;
16
+ var startTime = Date.now();
17
+ var heartbeatTimer = null;
18
+ if (source === 'clone') {
19
+ heartbeatTimer = setInterval(function() {
20
+ var elapsed = Math.round((Date.now() - startTime) / 1000);
21
+ var timeStr = elapsed < 60 ? elapsed + '秒' : Math.floor(elapsed/60) + '分' + (elapsed%60) + '秒';
22
+ _updateProjectProgress(heartbeatMsgs[heartbeatIdx % heartbeatMsgs.length] + ' (' + timeStr + ')');
23
+ heartbeatIdx++;
24
+ }, 3000);
25
+ }
26
+
27
+ var r = await fetch('/api/ui/projects', {
28
+ method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' },
29
+ body: JSON.stringify(body)
30
+ });
31
+ if (heartbeatTimer) clearInterval(heartbeatTimer);
32
+
33
+ // 更新进度:正在处理
34
+ _updateProjectProgress('📦 正在初始化项目...');
35
+ var j = await r.json().catch(function() { return {}; });
36
+ if (!r.ok) {
37
+ _hideProjectProgress();
38
+ throw new Error(j.detail || r.statusText);
39
+ }
40
+ // 显示后端返回的消息(如推送结果等)
41
+ // Note: j.message already contains emoji (e.g. "✅ 初始提交完成"),
42
+ // so we do NOT add another '✅ ' prefix — that would double it.
43
+ if (j.message) {
44
+ _updateProjectProgress(j.message.split('\n').pop());
45
+ setTimeout(_hideProjectProgress, 2000);
46
+ } else {
47
+ _hideProjectProgress();
48
+ }
49
+ if (j.project && j.project.id) {
50
+ projectId = j.project.id; saveProjectIdForAgent(agentId, projectId);
51
+ if (typeof saveMsgDraft === 'function') saveMsgDraft();
52
+ sessionId = loadSessionIdForAgent(agentId, projectId);
53
+ if (typeof restoreMsgDraft === 'function') restoreMsgDraft(sessionId);
54
+ if (treeProjectsCache && treeProjectsCache.aid === agentId) {
55
+ treeProjectsCache.projects.push(j.project);
56
+ }
57
+ treeExpanded[treePid(j.project.id)] = true;
58
+ appendProjectTreeNode(j.project); resetAgentReplyDedupe(); log.innerHTML = ''; reconnectWsForSession();
59
+ if (webuiSessionsEnabled) {
60
+ await refreshSessionList(); updateMainHeaderForSession(sessionId);
61
+ await loadSessionHistoryIntoLog(true);
62
+ }
63
+ try { document.dispatchEvent(new CustomEvent('project-changed')); } catch (_) {}
64
+ }
65
+ } catch (e) {
66
+ if (heartbeatTimer) clearInterval(heartbeatTimer);
67
+ _hideProjectProgress();
68
+ systemMsg('err', '创建项目失败:' + String(e)); }
69
+ }
70
+
71
+ // ---- 进度提示控制 ---- //
72
+ function _showProjectProgress(show, source, cloneUrl) {
73
+ var overlay = document.getElementById('projectProgressOverlay');
74
+ if (!overlay) return;
75
+ if (show) {
76
+ overlay.style.display = 'flex';
77
+ var text = document.getElementById('projectProgressText');
78
+ var detail = document.getElementById('projectProgressDetail');
79
+ var bar = document.getElementById('projectProgressBar');
80
+ if (text) text.textContent = '正在创建项目...';
81
+ if (detail) {
82
+ if (source === 'clone') {
83
+ detail.textContent = '正在从远程仓库克隆代码,这可能需要一些时间...';
84
+ } else if (source === 'template') {
85
+ detail.textContent = '正在从模板生成项目骨架...';
86
+ } else {
87
+ detail.textContent = '正在初始化项目...';
88
+ }
89
+ }
90
+ if (bar) bar.style.width = '30%';
91
+ } else {
92
+ overlay.style.display = 'none';
93
+ }
94
+ }
95
+
96
+ function _updateProjectProgress(text, detail) {
97
+ var el = document.getElementById('projectProgressText');
98
+ var detailEl = document.getElementById('projectProgressDetail');
99
+ var bar = document.getElementById('projectProgressBar');
100
+ if (el) el.textContent = text || '处理中...';
101
+ if (detailEl && detail) detailEl.textContent = detail;
102
+ if (bar) {
103
+ var w = parseInt(bar.style.width) || 30;
104
+ bar.style.width = Math.min(w + 20, 80) + '%';
105
+ }
106
+ }
107
+
108
+ function _hideProjectProgress() {
109
+ var overlay = document.getElementById('projectProgressOverlay');
110
+ var bar = document.getElementById('projectProgressBar');
111
+ if (bar) bar.style.width = '100%';
112
+ // 延迟关闭对话框,让进度条动画走完
113
+ setTimeout(function() {
114
+ if (overlay) overlay.style.display = 'none';
115
+ var dialog = document.getElementById('newProjectDialog');
116
+ if (dialog) dialog.style.display = 'none';
117
+ }, 600);
118
+ }
119
+ await refreshProjects();
120
+ const btnSessNew = document.getElementById('btnNewSession');
121
+ if (btnSessNew && btnSessNew.dataset.bound !== '1') {
122
+ btnSessNew.dataset.bound = '1';
123
+ btnSessNew.addEventListener('click', async () => {
124
+ if (typeof saveMsgDraft === 'function') saveMsgDraft();
125
+ sessionId = oaRandomUUID();
126
+ localStorage.setItem(_sidStorageKey(agentId, projectId), sessionId);
127
+ if (typeof msg !== 'undefined' && msg) msg.value = '';
128
+ updateComposerButtons(); updateMainHeaderForSession(sessionId);
129
+ await refreshSessionList(); reconnectWsForSession(); resetAgentReplyDedupe(); log.innerHTML = '';
130
+ await loadSessionHistoryIntoLog(true);
131
+ if (typeof refreshSessionsUnderProject === 'function') refreshSessionsUnderProject(projectId);
132
+ if (typeof activatePage === 'function') activatePage('chat');
133
+ });
134
+ }
135
+ await refreshSessionList();
136
+ updateMainHeaderForSession(sessionId);
137
+ await loadSessionHistoryIntoLog(true);
138
+ // 单窗口下不再定时轮询会话子树;新建/删会话、切项目等路径会主动 refreshSessionsUnderProject / refreshSessionList。
139
+ } catch (_) { if (sidebar) sidebar.style.display = 'none'; }
140
+ }
141
+ initWebUiSessions();
142
+ // Load LLM preset list for model selector
143
+ refreshModelSelect().catch(function() {});