@profoundlogic/coderflow-server 0.2.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.
Files changed (202) hide show
  1. package/LICENSE.txt +322 -0
  2. package/README.md +158 -0
  3. package/dist/LICENSE.txt +322 -0
  4. package/dist/README.md +158 -0
  5. package/dist/base-image/Dockerfile +184 -0
  6. package/dist/base-image/agent-wrapper.sh +143 -0
  7. package/dist/base-image/apply-local-state.sh +357 -0
  8. package/dist/base-image/coder-git-credential-helper +307 -0
  9. package/dist/base-image/entrypoint.sh +942 -0
  10. package/dist/base-image/ssh_config_template +41 -0
  11. package/dist/base-image/start-code-server.sh +76 -0
  12. package/dist/base-image/sync-repos.sh +170 -0
  13. package/dist/base-image/vscode-extensions.txt +10 -0
  14. package/dist/base-image/vscode-settings.json +41 -0
  15. package/dist/coder-server.js +2 -0
  16. package/dist/config/cli-models.json +45 -0
  17. package/dist/config/imported-skills.schema.json +83 -0
  18. package/dist/config/skill-catalog.json +18 -0
  19. package/dist/config/skill-catalog.schema.json +140 -0
  20. package/dist/config.js +1 -0
  21. package/dist/examples/oidc.json.example +11 -0
  22. package/dist/lib/agent-keepalive.js +1 -0
  23. package/dist/lib/api-keys.js +1 -0
  24. package/dist/lib/apiKeys.js +1 -0
  25. package/dist/lib/auto-judge.js +1 -0
  26. package/dist/lib/basic-auth.js +1 -0
  27. package/dist/lib/build-history.js +1 -0
  28. package/dist/lib/build-output-service.js +1 -0
  29. package/dist/lib/build-scheduler.js +1 -0
  30. package/dist/lib/build-service.js +1 -0
  31. package/dist/lib/claude-oauth-refresh.js +1 -0
  32. package/dist/lib/cli/build.js +1 -0
  33. package/dist/lib/cli/config-command.js +1 -0
  34. package/dist/lib/cli/config.js +1 -0
  35. package/dist/lib/cli/create-user.js +1 -0
  36. package/dist/lib/cli/init.js +1 -0
  37. package/dist/lib/cli/jira.js +1 -0
  38. package/dist/lib/cli/license.js +1 -0
  39. package/dist/lib/cli/server-manager.js +1 -0
  40. package/dist/lib/container-tokens.js +1 -0
  41. package/dist/lib/data-dir.js +1 -0
  42. package/dist/lib/deployment-history.js +1 -0
  43. package/dist/lib/deployment-service.js +1 -0
  44. package/dist/lib/docker-utils.js +1 -0
  45. package/dist/lib/email.js +1 -0
  46. package/dist/lib/emailTemplates.js +1 -0
  47. package/dist/lib/entitlement.js +1 -0
  48. package/dist/lib/fetch-utils.js +1 -0
  49. package/dist/lib/git-provider-service.js +1 -0
  50. package/dist/lib/git-provider-setup/assets/coderflow_github_app.png +0 -0
  51. package/dist/lib/git-provider-setup/github-setup-handler.js +1 -0
  52. package/dist/lib/git-provider-setup/index.js +1 -0
  53. package/dist/lib/git-provider-setup/setup-factory.js +1 -0
  54. package/dist/lib/git-provider-setup/setup-interface.js +1 -0
  55. package/dist/lib/git-providers/azure-devops-provider.js +1 -0
  56. package/dist/lib/git-providers/github-app-provider.js +1 -0
  57. package/dist/lib/git-providers/index.js +1 -0
  58. package/dist/lib/git-providers/provider-factory.js +1 -0
  59. package/dist/lib/git-providers/provider-interface.js +1 -0
  60. package/dist/lib/jira-client.js +1 -0
  61. package/dist/lib/logger.js +1 -0
  62. package/dist/lib/model-fetcher.js +1 -0
  63. package/dist/lib/notifications.js +1 -0
  64. package/dist/lib/oidc-auth.js +1 -0
  65. package/dist/lib/oidc-device-flow.js +1 -0
  66. package/dist/lib/passwordTokens.js +1 -0
  67. package/dist/lib/pin-cascade.js +1 -0
  68. package/dist/lib/provider-accounts.js +1 -0
  69. package/dist/lib/provider-oauth.js +1 -0
  70. package/dist/lib/provider-profile.js +1 -0
  71. package/dist/lib/provider-token-refresh.js +1 -0
  72. package/dist/lib/roles.js +1 -0
  73. package/dist/lib/secrets.js +1 -0
  74. package/dist/lib/state-capture.js +1 -0
  75. package/dist/lib/static-files.js +1 -0
  76. package/dist/lib/task-name-generator.js +1 -0
  77. package/dist/lib/users.js +1 -0
  78. package/dist/middleware/requireAuth.js +1 -0
  79. package/dist/middleware/requireInit.js +1 -0
  80. package/dist/middleware/requirePermission.js +1 -0
  81. package/dist/package-lock.json +4151 -0
  82. package/dist/package.json +50 -0
  83. package/dist/routes/apiKeys.js +1 -0
  84. package/dist/routes/auth-oidc.js +1 -0
  85. package/dist/routes/auth.js +1 -0
  86. package/dist/routes/build.js +1 -0
  87. package/dist/routes/containers.js +1 -0
  88. package/dist/routes/deploy-task.js +1 -0
  89. package/dist/routes/environment-management.js +1 -0
  90. package/dist/routes/environments.js +1 -0
  91. package/dist/routes/external-skills.js +1 -0
  92. package/dist/routes/git-credentials.js +1 -0
  93. package/dist/routes/git-provider-setup.js +1 -0
  94. package/dist/routes/health.js +1 -0
  95. package/dist/routes/jira.js +1 -0
  96. package/dist/routes/objective-management.js +1 -0
  97. package/dist/routes/password.js +1 -0
  98. package/dist/routes/prompt.js +1 -0
  99. package/dist/routes/provider-auth.js +1 -0
  100. package/dist/routes/qa.js +1 -0
  101. package/dist/routes/settings.js +1 -0
  102. package/dist/routes/skill-management.js +1 -0
  103. package/dist/routes/skills.js +1 -0
  104. package/dist/routes/tasks.js +2 -0
  105. package/dist/routes/templates.js +1 -0
  106. package/dist/routes/test-task.js +1 -0
  107. package/dist/routes/test.js +1 -0
  108. package/dist/routes/users.js +1 -0
  109. package/dist/routes/visualizations.js +1 -0
  110. package/dist/schemas/template-metadata.schema.json +178 -0
  111. package/dist/scripts/create-user.js +2 -0
  112. package/dist/shipped-skills/environment-instructions/SKILL.md +154 -0
  113. package/dist/shipped-skills/environment-templates/SKILL.md +282 -0
  114. package/dist/shipped-skills/objective-management/SKILL.md +238 -0
  115. package/dist/shipped-skills/skill-editor/SKILL.md +326 -0
  116. package/dist/start.js +2 -0
  117. package/dist/web-ui/public/activity-detail-modal.js +1 -0
  118. package/dist/web-ui/public/activity-feed.js +1 -0
  119. package/dist/web-ui/public/activity-formatters.js +1 -0
  120. package/dist/web-ui/public/agent-event-parser.js +1 -0
  121. package/dist/web-ui/public/app.js +1 -0
  122. package/dist/web-ui/public/approve-dialog.js +1 -0
  123. package/dist/web-ui/public/coderflow-logo-reversed.svg +46 -0
  124. package/dist/web-ui/public/coderflow-logo.svg +46 -0
  125. package/dist/web-ui/public/comments-widget.js +1 -0
  126. package/dist/web-ui/public/docs/.nojekyll +0 -0
  127. package/dist/web-ui/public/docs/README.md +26 -0
  128. package/dist/web-ui/public/docs/_sidebar.md +47 -0
  129. package/dist/web-ui/public/docs/admin/ai-providers.md +132 -0
  130. package/dist/web-ui/public/docs/admin/email-notifications.md +69 -0
  131. package/dist/web-ui/public/docs/admin/environments.md +215 -0
  132. package/dist/web-ui/public/docs/admin/git-providers.md +147 -0
  133. package/dist/web-ui/public/docs/admin/installation.md +313 -0
  134. package/dist/web-ui/public/docs/admin/skills.md +35 -0
  135. package/dist/web-ui/public/docs/admin/sso.md +241 -0
  136. package/dist/web-ui/public/docs/admin/users-and-roles.md +57 -0
  137. package/dist/web-ui/public/docs/code/cli.md +102 -0
  138. package/dist/web-ui/public/docs/code/files-and-editing.md +86 -0
  139. package/dist/web-ui/public/docs/code/terminal-access.md +110 -0
  140. package/dist/web-ui/public/docs/code/vscode-extension.md +58 -0
  141. package/dist/web-ui/public/docs/getting-started/core-concepts.md +129 -0
  142. package/dist/web-ui/public/docs/getting-started/overview.md +46 -0
  143. package/dist/web-ui/public/docs/index.html +151 -0
  144. package/dist/web-ui/public/docs/integrations/custom.md +58 -0
  145. package/dist/web-ui/public/docs/integrations/ibmi/overview.md +58 -0
  146. package/dist/web-ui/public/docs/integrations/overview.md +48 -0
  147. package/dist/web-ui/public/docs/objectives/qa-mode.md +90 -0
  148. package/dist/web-ui/public/docs/objectives/staged-tasks.md +60 -0
  149. package/dist/web-ui/public/docs/objectives/working-with-objectives.md +102 -0
  150. package/dist/web-ui/public/docs/tasks/approval-and-deployment.md +83 -0
  151. package/dist/web-ui/public/docs/tasks/creating-tasks.md +111 -0
  152. package/dist/web-ui/public/docs/tasks/judging.md +114 -0
  153. package/dist/web-ui/public/docs/tasks/providing-feedback.md +41 -0
  154. package/dist/web-ui/public/docs/tasks/task-groups.md +73 -0
  155. package/dist/web-ui/public/docs/tasks/winner-selection.md +75 -0
  156. package/dist/web-ui/public/docs/templates/batch-processing.md +152 -0
  157. package/dist/web-ui/public/docs/templates/task-templates.md +44 -0
  158. package/dist/web-ui/public/docs/templates/template-examples.md +93 -0
  159. package/dist/web-ui/public/docs/testing/profound-automated-testing.md +77 -0
  160. package/dist/web-ui/public/docs/testing/task-visualizations.md +42 -0
  161. package/dist/web-ui/public/docs/testing/testing-menu.md +118 -0
  162. package/dist/web-ui/public/environments.css +3942 -0
  163. package/dist/web-ui/public/environments.html +1791 -0
  164. package/dist/web-ui/public/environments.js +1 -0
  165. package/dist/web-ui/public/favicon-16.png +0 -0
  166. package/dist/web-ui/public/favicon-32.png +0 -0
  167. package/dist/web-ui/public/favicon.ico +0 -0
  168. package/dist/web-ui/public/feedback-widget.css +3133 -0
  169. package/dist/web-ui/public/feedback-widget.js +1 -0
  170. package/dist/web-ui/public/git-history.css +2663 -0
  171. package/dist/web-ui/public/git-history.html +272 -0
  172. package/dist/web-ui/public/git-history.js +1 -0
  173. package/dist/web-ui/public/git-status.js +1 -0
  174. package/dist/web-ui/public/index.html +1459 -0
  175. package/dist/web-ui/public/index.js +1 -0
  176. package/dist/web-ui/public/login.html +346 -0
  177. package/dist/web-ui/public/login.js +1 -0
  178. package/dist/web-ui/public/markdown-editor.js +1 -0
  179. package/dist/web-ui/public/markdown-file-editor.js +1 -0
  180. package/dist/web-ui/public/modal-maximize.js +1 -0
  181. package/dist/web-ui/public/notifications.js +1 -0
  182. package/dist/web-ui/public/server-health.js +1 -0
  183. package/dist/web-ui/public/settings.css +761 -0
  184. package/dist/web-ui/public/settings.html +1044 -0
  185. package/dist/web-ui/public/settings.js +1 -0
  186. package/dist/web-ui/public/setup-password.html +355 -0
  187. package/dist/web-ui/public/setup-password.js +1 -0
  188. package/dist/web-ui/public/skills.css +1949 -0
  189. package/dist/web-ui/public/skills.html +820 -0
  190. package/dist/web-ui/public/skills.js +1 -0
  191. package/dist/web-ui/public/sse-client.js +1 -0
  192. package/dist/web-ui/public/sse-shared-worker.js +1 -0
  193. package/dist/web-ui/public/styles.css +18614 -0
  194. package/dist/web-ui/public/task.html +1779 -0
  195. package/dist/web-ui/public/task.js +1 -0
  196. package/dist/web-ui/public/terminal.html +45 -0
  197. package/dist/web-ui/public/terminal.js +1 -0
  198. package/dist/web-ui/public/theme.js +1 -0
  199. package/dist/web-ui/public/users.html +298 -0
  200. package/dist/web-ui/public/users.js +1 -0
  201. package/dist/web-ui/public/variant-grouping.js +1 -0
  202. package/package.json +63 -0
@@ -0,0 +1,1791 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Environment Management - CoderFlow</title>
7
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml">
8
+ <link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32">
9
+ <link rel="icon" href="/favicon-16.png" type="image/png" sizes="16x16">
10
+ <link rel="icon" href="/favicon.ico">
11
+ <script>
12
+ (() => {
13
+ const key = 'profound-coder-theme';
14
+ try {
15
+ const stored = localStorage.getItem(key);
16
+ const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
17
+ const theme = stored === 'dark' || stored === 'light' ? stored : (prefersDark ? 'dark' : 'light');
18
+ document.documentElement.dataset.theme = theme;
19
+ document.documentElement.style.colorScheme = theme === 'dark' ? 'dark' : 'light';
20
+ } catch (error) {
21
+ document.documentElement.dataset.theme = 'light';
22
+ }
23
+ })();
24
+ </script>
25
+ <link rel="stylesheet" href="styles.css?v=84">
26
+ <link rel="stylesheet" href="environments.css?v=15">
27
+ <!-- Markdown editor dependencies -->
28
+ <script src="https://cdn.jsdelivr.net/npm/marked@11.1.1/marked.min.js"></script>
29
+ <script src="https://cdn.jsdelivr.net/npm/turndown@7.1.2/dist/turndown.js"></script>
30
+ <script type="module" src="markdown-editor.js?v=1"></script>
31
+ <script src="markdown-file-editor.js?v=1"></script>
32
+ <script type="module" src="app.js?v=66"></script>
33
+ <script type="module" src="environments.js?v=43"></script>
34
+ <!-- Dev QA shortcut: Ctrl+Shift+Q to launch current page in QA mode -->
35
+ </head>
36
+ <body>
37
+ <main class="task-detail">
38
+ <div class="task-hero">
39
+ <div class="hero-top">
40
+ <nav class="breadcrumb" aria-label="Breadcrumb">
41
+ <a class="breadcrumb-link" href="index.html">
42
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
43
+ <path d="M3 11.5L12 4l9 7.5"></path>
44
+ <path d="M5 10.5V20h5v-5h4v5h5v-9.5"></path>
45
+ </svg>
46
+ <span>Home</span>
47
+ </a>
48
+ <span class="breadcrumb-separator" aria-hidden="true">/</span>
49
+ <span class="breadcrumb-current">Environments</span>
50
+ </nav>
51
+ <span class="task-context">Administration</span>
52
+ <!-- Admin Menu (gear icon) -->
53
+ <div class="admin-menu-container" id="admin-menu-container" hidden>
54
+ <button class="btn-icon-admin" id="admin-menu-btn" title="Admin">
55
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
56
+ <circle cx="12" cy="12" r="3"></circle>
57
+ <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
58
+ </svg>
59
+ </button>
60
+ <div class="dropdown-menu admin-dropdown" id="admin-menu" hidden>
61
+ <button type="button" class="dropdown-item admin-menu-item" onclick="window.location.href='users.html'">
62
+ <svg class="option-icon icon-users" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
63
+ <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
64
+ <circle cx="9" cy="7" r="4"></circle>
65
+ <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
66
+ <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
67
+ </svg>
68
+ <span class="option-text">Users</span>
69
+ </button>
70
+ <button type="button" class="dropdown-item admin-menu-item" onclick="window.location.href='skills.html'">
71
+ <svg class="option-icon icon-skills" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
72
+ <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path>
73
+ </svg>
74
+ <span class="option-text">Skills</span>
75
+ </button>
76
+ <button type="button" class="dropdown-item admin-menu-item" onclick="window.location.href='settings.html'">
77
+ <svg class="option-icon icon-server-settings" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
78
+ <line x1="4" y1="21" x2="4" y2="14"></line>
79
+ <line x1="4" y1="10" x2="4" y2="3"></line>
80
+ <line x1="12" y1="21" x2="12" y2="12"></line>
81
+ <line x1="12" y1="8" x2="12" y2="3"></line>
82
+ <line x1="20" y1="21" x2="20" y2="16"></line>
83
+ <line x1="20" y1="12" x2="20" y2="3"></line>
84
+ <line x1="1" y1="14" x2="7" y2="14"></line>
85
+ <line x1="9" y1="8" x2="15" y2="8"></line>
86
+ <line x1="17" y1="16" x2="23" y2="16"></line>
87
+ </svg>
88
+ <span class="option-text">Server Settings</span>
89
+ </button>
90
+ <button type="button" class="dropdown-item admin-menu-item" id="server-health-btn">
91
+ <svg class="option-icon icon-health" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
92
+ <path d="M22 12h-4l-3 9L9 3l-3 9H2"></path>
93
+ </svg>
94
+ <span class="option-text">Server Health</span>
95
+ </button>
96
+ <button type="button" class="dropdown-item admin-menu-item" onclick="window.location.href='git-history.html'">
97
+ <svg class="option-icon icon-git-history" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
98
+ <circle cx="6" cy="6" r="2"></circle>
99
+ <circle cx="6" cy="18" r="2"></circle>
100
+ <circle cx="18" cy="12" r="2"></circle>
101
+ <path d="M6 8v8"></path>
102
+ <path d="M6 12h10"></path>
103
+ </svg>
104
+ <span class="option-text">Git History</span>
105
+ </button>
106
+ </div>
107
+ </div>
108
+ <div id="theme-toggle-container"></div>
109
+ </div>
110
+ <div class="status-hero">
111
+ <div class="status-copy">
112
+ <h1>Environment Management</h1>
113
+ <p class="task-subtitle">Manage development environments and configurations</p>
114
+ </div>
115
+ <div class="status-actions">
116
+ <div id="repo-status" class="repo-status-indicator" title="Repository git status">
117
+ <span id="repo-status-icon">⏳</span>
118
+ <span id="repo-status-text">Loading...</span>
119
+ <span id="repo-branch-text" class="repo-branch"></span>
120
+ </div>
121
+ <button class="btn-secondary btn-small" id="save-btn" title="Save changes to files" disabled>
122
+ <span>💾</span> Save
123
+ </button>
124
+ <div class="dropdown-container">
125
+ <button class="btn-ghost btn-small" id="actions-menu-btn" title="Additional actions">
126
+ Actions ▼
127
+ </button>
128
+ <div class="dropdown-menu" id="actions-menu" hidden>
129
+ <button class="dropdown-item" id="repo-pull-btn">
130
+ <span>🔄</span> Pull from Git
131
+ </button>
132
+ <button class="dropdown-item" id="repo-commit-btn" disabled>
133
+ <span>📤</span> Commit & Push...
134
+ </button>
135
+ <button class="dropdown-item" id="repo-discard-btn" disabled>
136
+ <span>⚠️</span> Discard Changes...
137
+ </button>
138
+ <div class="dropdown-divider"></div>
139
+ <button class="dropdown-item" id="new-env-btn">
140
+ <span>+</span> New Environment...
141
+ </button>
142
+ <button class="dropdown-item" id="delete-env-btn">
143
+ <span>🗑</span> Delete Environment
144
+ </button>
145
+ <div class="dropdown-divider"></div>
146
+ <button class="dropdown-item" id="build-base-image-btn">
147
+ <span>📦</span> Build Base Image...
148
+ </button>
149
+ </div>
150
+ </div>
151
+ </div>
152
+ </div>
153
+ </div>
154
+
155
+ <!-- Environment Tabs -->
156
+ <section class="env-tabs-container">
157
+ <button class="env-tabs-scroll-btn env-tabs-scroll-left" id="env-tabs-scroll-left" aria-label="Scroll tabs left">
158
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
159
+ <polyline points="15 18 9 12 15 6"></polyline>
160
+ </svg>
161
+ </button>
162
+ <div class="env-tabs-wrapper" id="env-tabs-wrapper">
163
+ <div id="env-tabs" class="env-tabs">
164
+ <!-- Environment tabs will be populated here -->
165
+ </div>
166
+ </div>
167
+ <button class="env-tabs-scroll-btn env-tabs-scroll-right" id="env-tabs-scroll-right" aria-label="Scroll tabs right">
168
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
169
+ <polyline points="9 18 15 12 9 6"></polyline>
170
+ </svg>
171
+ </button>
172
+ </section>
173
+
174
+ <!-- Environment Content -->
175
+ <div id="env-empty-state" class="empty-state-card">
176
+ <div class="empty-state-icon">🌍</div>
177
+ <h2>Select an Environment</h2>
178
+ <p>Choose an environment from the tabs above to view and manage its configuration</p>
179
+ </div>
180
+
181
+ <section id="env-details" class="env-details-section" hidden>
182
+ <!-- Environment Header -->
183
+ <div class="env-header-card">
184
+ <div class="env-header-content">
185
+ <div class="env-title-row">
186
+ <h2 class="env-title" id="env-name-display">-</h2>
187
+ <button class="env-rename-btn" id="env-rename-btn" title="Rename environment">
188
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
189
+ <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
190
+ <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
191
+ </svg>
192
+ </button>
193
+ </div>
194
+ <p class="env-description-display" id="env-description-display"></p>
195
+ </div>
196
+ </div>
197
+
198
+ <!-- Navigation Tabs -->
199
+ <div class="content-nav-container">
200
+ <button class="content-nav-scroll-btn content-nav-scroll-left" id="content-nav-scroll-left" aria-label="Scroll tabs left">
201
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
202
+ <polyline points="15 18 9 12 15 6"></polyline>
203
+ </svg>
204
+ </button>
205
+ <nav class="content-nav" id="content-nav" role="tablist">
206
+ <button class="content-tab active" data-tab="overview" role="tab">
207
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px; vertical-align: text-bottom;">
208
+ <rect x="3" y="3" width="7" height="7"></rect>
209
+ <rect x="14" y="3" width="7" height="7"></rect>
210
+ <rect x="14" y="14" width="7" height="7"></rect>
211
+ <rect x="3" y="14" width="7" height="7"></rect>
212
+ </svg>
213
+ Overview
214
+ </button>
215
+ <button class="content-tab" data-tab="repos" role="tab">
216
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px; vertical-align: text-bottom;">
217
+ <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
218
+ <polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline>
219
+ <line x1="12" y1="22.08" x2="12" y2="12"></line>
220
+ </svg>
221
+ Repos
222
+ </button>
223
+ <button class="content-tab" data-tab="server" role="tab">
224
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px; vertical-align: text-bottom;">
225
+ <rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect>
226
+ <rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect>
227
+ <line x1="6" y1="6" x2="6.01" y2="6"></line>
228
+ <line x1="6" y1="18" x2="6.01" y2="18"></line>
229
+ </svg>
230
+ Server
231
+ </button>
232
+ <button class="content-tab" data-tab="agents" role="tab">
233
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px; vertical-align: text-bottom;">
234
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
235
+ <polyline points="14 2 14 8 20 8"></polyline>
236
+ <line x1="16" y1="13" x2="8" y2="13"></line>
237
+ <line x1="16" y1="17" x2="8" y2="17"></line>
238
+ <polyline points="10 9 9 9 8 9"></polyline>
239
+ </svg>
240
+ Instructions
241
+ </button>
242
+ <!-- Files tab removed - content moved to Build tab -->
243
+ <button class="content-tab" data-tab="env-vars" role="tab">
244
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px; vertical-align: text-bottom;">
245
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
246
+ <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
247
+ </svg>
248
+ Secrets
249
+ </button>
250
+ <button class="content-tab" data-tab="build" role="tab">
251
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px; vertical-align: text-bottom;">
252
+ <polyline points="21 8 21 21 3 21 3 8"></polyline>
253
+ <rect x="1" y="3" width="22" height="5"></rect>
254
+ <line x1="10" y1="12" x2="14" y2="12"></line>
255
+ </svg>
256
+ Build
257
+ </button>
258
+ <button class="content-tab" data-tab="deploy" role="tab">
259
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px; vertical-align: text-bottom;">
260
+ <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon>
261
+ <polyline points="2 17 12 22 22 17"></polyline>
262
+ <polyline points="2 12 12 17 22 12"></polyline>
263
+ </svg>
264
+ Deploy
265
+ </button>
266
+ <button class="content-tab" data-tab="tests" role="tab">
267
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px; vertical-align: text-bottom;">
268
+ <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
269
+ <polyline points="22 4 12 14.01 9 11.01"></polyline>
270
+ </svg>
271
+ Tests
272
+ </button>
273
+ <button class="content-tab" data-tab="templates" role="tab">
274
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px; vertical-align: text-bottom;">
275
+ <path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path>
276
+ <rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect>
277
+ </svg>
278
+ Templates
279
+ </button>
280
+ </nav>
281
+ <button class="content-nav-scroll-btn content-nav-scroll-right" id="content-nav-scroll-right" aria-label="Scroll tabs right">
282
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
283
+ <polyline points="9 18 15 12 9 6"></polyline>
284
+ </svg>
285
+ </button>
286
+ </div>
287
+
288
+ <!-- Tab Content -->
289
+ <div class="tab-content-wrapper">
290
+ <!-- Overview Tab -->
291
+ <div id="tab-overview" class="tab-panel active" role="tabpanel">
292
+ <div class="content-card">
293
+ <div class="card-header">
294
+ <h3>Environment Information</h3>
295
+ </div>
296
+ <div class="card-body">
297
+ <div class="form-single-column">
298
+ <div class="form-row">
299
+ <label for="env-description">Description</label>
300
+ <input type="text" id="env-description" class="form-input" placeholder="Environment description">
301
+ </div>
302
+ <div class="form-row">
303
+ <label for="env-default-agent">Default Agent</label>
304
+ <div id="env-default-agent-wrapper"></div>
305
+ </div>
306
+ <div class="form-row">
307
+ <label>Skills</label>
308
+ <div class="skills-form-controls">
309
+ <div class="skills-form-row">
310
+ <div id="env-skills-chips" class="skills-chip-grid skills-chip-grid-inline"></div>
311
+ <span id="env-skills-empty" class="skills-empty" hidden>No skills assigned.</span>
312
+ <button class="skills-add-btn" id="env-skills-manage-btn" aria-label="Add skills" title="Add skills">
313
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
314
+ <line x1="12" y1="5" x2="12" y2="19"></line>
315
+ <line x1="5" y1="12" x2="19" y2="12"></line>
316
+ </svg>
317
+ </button>
318
+ </div>
319
+ </div>
320
+ </div>
321
+ <div class="form-row">
322
+ <label for="env-image-name">Docker Image</label>
323
+ <input type="text" id="env-image-name" class="form-input" placeholder="Image name">
324
+ </div>
325
+ <div class="form-row">
326
+ <label for="env-timezone">Timezone</label>
327
+ <input type="text" id="env-timezone" class="form-input" placeholder="e.g., America/New_York">
328
+ </div>
329
+ <div class="form-row">
330
+ <label for="env-screen-render-url">Screen Render URL</label>
331
+ <input type="text" id="env-screen-render-url" class="form-input code-font" placeholder="e.g., http://host:port/profoundui/genie">
332
+ </div>
333
+ </div>
334
+ </div>
335
+ </div>
336
+
337
+ <div class="content-card">
338
+ <div class="card-header">
339
+ <h3>README</h3>
340
+ <button class="btn-secondary btn-small" id="preview-readme-btn" onclick="toggleReadmePreview()">
341
+ <span>✏️</span> Edit
342
+ </button>
343
+ </div>
344
+ <div class="card-body">
345
+ <div id="readme-preview" class="markdown-preview-container">
346
+ <p class="text-muted">No README content yet.</p>
347
+ </div>
348
+ <div id="readme-editor" style="height: 400px; border: 1px solid var(--color-border);" hidden></div>
349
+ </div>
350
+ </div>
351
+
352
+ <div class="stats-grid">
353
+ <div class="stat-card clickable" onclick="switchTab('repos')" title="View repositories">
354
+ <div class="stat-icon">📦</div>
355
+ <div class="stat-content">
356
+ <div class="stat-value" id="stat-repos">-</div>
357
+ <div class="stat-label" id="stat-repos-label">Repos</div>
358
+ </div>
359
+ </div>
360
+ <div class="stat-card clickable" onclick="switchTab('tests')" title="View test commands">
361
+ <div class="stat-icon">✅</div>
362
+ <div class="stat-content">
363
+ <div class="stat-value" id="stat-tests">-</div>
364
+ <div class="stat-label" id="stat-tests-label">Test Commands</div>
365
+ </div>
366
+ </div>
367
+ <div class="stat-card clickable" onclick="switchTab('templates')" title="View templates">
368
+ <div class="stat-icon">📝</div>
369
+ <div class="stat-content">
370
+ <div class="stat-value" id="stat-templates">-</div>
371
+ <div class="stat-label" id="stat-templates-label">Templates</div>
372
+ </div>
373
+ </div>
374
+ <div class="stat-card clickable" onclick="switchTab('deploy')" title="View deployment profiles">
375
+ <div class="stat-icon stat-icon-svg">
376
+ <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
377
+ <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon>
378
+ <polyline points="2 17 12 22 22 17"></polyline>
379
+ <polyline points="2 12 12 17 22 12"></polyline>
380
+ </svg>
381
+ </div>
382
+ <div class="stat-content">
383
+ <div class="stat-value" id="stat-deploy-profiles">-</div>
384
+ <div class="stat-label" id="stat-deploy-profiles-label">Deploy Profiles</div>
385
+ </div>
386
+ </div>
387
+ </div>
388
+ </div>
389
+
390
+ <!-- Repos Tab -->
391
+ <div id="tab-repos" class="tab-panel" role="tabpanel" hidden>
392
+ <div class="content-card repos-card">
393
+ <div class="repos-layout">
394
+ <div class="repos-sidebar">
395
+ <!-- Repository tabs will be populated here -->
396
+ <button class="repo-tab" data-repo="new" id="new-repo-tab">
397
+ <span class="repo-icon">➕</span>
398
+ <span>Add Repository</span>
399
+ </button>
400
+ </div>
401
+ <div class="repos-content">
402
+ <div class="repo-header">
403
+ <h4 id="current-repo-name">Select a Repository</h4>
404
+ <div class="repo-actions">
405
+ <button class="btn-ghost btn-small" id="delete-repo-btn" hidden>
406
+ <span>🗑</span> Remove
407
+ </button>
408
+ </div>
409
+ </div>
410
+ <div id="repo-details-container">
411
+ <p class="text-muted">Select a repository from the sidebar to view details, or add a new one.</p>
412
+ </div>
413
+ </div>
414
+ </div>
415
+ </div>
416
+ </div>
417
+
418
+ <!-- Server Tab -->
419
+ <div id="tab-server" class="tab-panel" role="tabpanel" hidden>
420
+ <div class="content-card">
421
+ <div class="card-header">
422
+ <h3>App Server</h3>
423
+ <label class="toggle-switch">
424
+ <input type="checkbox" id="server-enabled">
425
+ <span class="toggle-slider"></span>
426
+ </label>
427
+ </div>
428
+ <div class="card-body" id="server-config" hidden>
429
+ <div class="form-single-column">
430
+ <div class="form-row">
431
+ <label for="server-name">Server Name</label>
432
+ <input type="text" id="server-name" class="form-input">
433
+ </div>
434
+ <div class="form-row">
435
+ <label for="server-command">Start Command</label>
436
+ <input type="text" id="server-command" class="form-input code-font" placeholder="Optional - e.g., npm start">
437
+ </div>
438
+ <div class="form-row">
439
+ <label for="server-proxy">Proxy URL</label>
440
+ <input type="text" id="server-proxy" class="form-input code-font" placeholder="Optional - e.g., http://host:port">
441
+ </div>
442
+ <div class="form-row">
443
+ <label for="server-qa-url">QA URL</label>
444
+ <input type="text" id="server-qa-url" class="form-input code-font" placeholder="e.g., http://host:port or {{origin}}">
445
+ <small class="form-hint">Use <code>{{origin}}</code>, <code>{{host}}</code>, or <code>{{hostname}}</code> for dynamic resolution</small>
446
+ </div>
447
+ </div>
448
+
449
+ <div class="subsection">
450
+ <div class="subsection-header">
451
+ <h4>Port-Specific QA URLs</h4>
452
+ <button class="btn-secondary btn-small" id="add-port-qa-url-btn">+ Add Override</button>
453
+ </div>
454
+ <p class="text-muted" style="margin-top: 4px; margin-bottom: 12px;">Override the default QA URL for specific ports (e.g., port 3001 → https://production.example.com)</p>
455
+ <div id="port-qa-urls-list" class="config-list"></div>
456
+ </div>
457
+
458
+ <div class="subsection">
459
+ <h4>Basic Authentication</h4>
460
+ <p class="text-muted" style="margin-top: 4px; margin-bottom: 12px;">Load HTTP Basic credentials from secrets (must have <code>available_for: ['tasks']</code>)</p>
461
+ <div class="form-single-column">
462
+ <div class="form-row">
463
+ <label for="server-auth-user-property">User Secret Name</label>
464
+ <input type="text" id="server-auth-user-property" class="form-input code-font" placeholder="Secret name for username - e.g., IBMI_USER">
465
+ </div>
466
+ <div class="form-row">
467
+ <label for="server-auth-password-property">Password Secret Name</label>
468
+ <input type="text" id="server-auth-password-property" class="form-input code-font" placeholder="Secret name for password - e.g., IBMI_PASSWORD">
469
+ </div>
470
+ </div>
471
+ </div>
472
+
473
+ <div class="subsection">
474
+ <div class="subsection-header">
475
+ <h4>Proxy Headers</h4>
476
+ <button class="btn-secondary btn-small" id="add-proxy-header-btn">+ Add Header</button>
477
+ </div>
478
+ <div id="proxy-headers-list" class="config-list"></div>
479
+ </div>
480
+
481
+ <div class="subsection">
482
+ <div class="subsection-header">
483
+ <h4>Ports</h4>
484
+ <button class="btn-secondary btn-small" id="add-port-btn">+ Add Port</button>
485
+ </div>
486
+ <div id="ports-list" class="config-list"></div>
487
+ </div>
488
+
489
+ <div class="subsection">
490
+ <div class="subsection-header">
491
+ <h4>Launch URLs</h4>
492
+ <div class="subsection-actions">
493
+ <button class="btn-ghost btn-small" id="clear-primary-url-btn" title="Remove the primary launch URL">Clear Primary</button>
494
+ <button class="btn-secondary btn-small" id="add-url-btn">+ Add URL</button>
495
+ </div>
496
+ </div>
497
+ <p class="text-muted" style="margin-bottom: 12px;">Mark one URL as Primary to open it when users click the Testing button in tasks.</p>
498
+ <div id="urls-list" class="config-list"></div>
499
+ </div>
500
+
501
+ <div class="subsection">
502
+ <div class="subsection-header">
503
+ <h4>Volume Mounts</h4>
504
+ <button class="btn-secondary btn-small" id="add-volume-mount-btn">+ Add Mount</button>
505
+ </div>
506
+ <p class="text-muted" style="margin-bottom: 12px;">Mount host directories into containers for testing with host data</p>
507
+ <div id="volume-mounts-list" class="config-list"></div>
508
+ </div>
509
+ </div>
510
+ </div>
511
+ </div>
512
+
513
+ <!-- Instructions Tab -->
514
+ <div id="tab-agents" class="tab-panel" role="tabpanel" hidden>
515
+ <div class="content-card">
516
+ <div class="card-header">
517
+ <h3>Instructions (AGENTS.md)</h3>
518
+ <button class="btn-secondary btn-small" id="preview-agents-btn">
519
+ <span>👁</span> Preview
520
+ </button>
521
+ </div>
522
+ <div class="card-body">
523
+ <div id="agents-editor" style="height: 500px; border: 1px solid var(--color-border);"></div>
524
+ <div id="agents-preview" class="markdown-preview-container" hidden></div>
525
+ <div class="editor-footer">
526
+ <span id="agents-stats" class="text-muted"></span>
527
+ </div>
528
+ </div>
529
+ </div>
530
+ </div>
531
+
532
+ <!-- Files Tab removed - content moved to Build tab -->
533
+
534
+ <!-- Build Tab -->
535
+ <div id="tab-build" class="tab-panel" role="tabpanel" hidden>
536
+ <!-- Schedule -->
537
+ <div class="content-card">
538
+ <div class="card-header">
539
+ <h3>Schedule</h3>
540
+ </div>
541
+ <div class="card-body">
542
+ <div class="form-single-column">
543
+ <div class="form-row">
544
+ <label class="toggle-switch">
545
+ <input type="checkbox" id="rebuild-schedule-enabled-build-tab">
546
+ <span class="toggle-slider"></span>
547
+ <span class="toggle-label">Enable Scheduled Rebuilds</span>
548
+ </label>
549
+ <p class="text-muted" style="margin-top: 4px;">Automatically rebuild this environment's Docker image on a schedule</p>
550
+ </div>
551
+ <div id="rebuild-schedule-config-build-tab" hidden>
552
+ <div class="form-row">
553
+ <label for="rebuild-interval-build-tab">Rebuild Interval (hours)</label>
554
+ <input type="number" id="rebuild-interval-build-tab" class="form-input" min="1" max="168" value="24" placeholder="24" style="max-width: 120px;">
555
+ </div>
556
+ </div>
557
+ </div>
558
+ </div>
559
+ </div>
560
+
561
+ <!-- Scripts Section (Collapsible) -->
562
+ <div class="content-card collapsible-card">
563
+ <div class="card-header collapsible-header" id="scripts-section-header">
564
+ <div class="collapsible-title">
565
+ <svg class="collapse-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
566
+ <polyline points="6 9 12 15 18 9"></polyline>
567
+ </svg>
568
+ <h3>Scripts</h3>
569
+ <span class="badge badge-muted">Advanced</span>
570
+ </div>
571
+ </div>
572
+ <div class="card-body collapsible-body" id="scripts-section-body" hidden>
573
+ <p class="text-muted" style="margin-bottom: 16px;">
574
+ Optional scripts for customizing the build and task lifecycle. Leave blank to use defaults.
575
+ </p>
576
+
577
+ <div class="scripts-tabs-container">
578
+ <div class="scripts-tabs">
579
+ <button class="script-tab active" data-script="pre-clone">
580
+ pre-clone
581
+ </button>
582
+ <button class="script-tab" data-script="post-clone">
583
+ post-clone
584
+ </button>
585
+ <button class="script-tab" data-script="setup.sh">
586
+ setup.sh
587
+ </button>
588
+ <button class="script-tab" data-script="cleanup.sh">
589
+ cleanup.sh
590
+ </button>
591
+ </div>
592
+ <div class="script-description" id="script-description">
593
+ <span class="script-desc-pre-clone">Docker build instructions to run BEFORE repositories are cloned.</span>
594
+ <span class="script-desc-post-clone" hidden>Docker build instructions to run AFTER repositories are cloned.</span>
595
+ <span class="script-desc-setup" hidden>Shell script that runs when a task container starts.</span>
596
+ <span class="script-desc-cleanup" hidden>Shell script that runs when a task completes.</span>
597
+ </div>
598
+ </div>
599
+ <div id="script-editor" style="height: 300px; border: 1px solid var(--color-border); border-radius: var(--radius-medium);"></div>
600
+ </div>
601
+ </div>
602
+
603
+ <!-- Build History -->
604
+ <div class="content-card">
605
+ <div class="card-header">
606
+ <h3>Build History</h3>
607
+ <div style="display: flex; gap: 8px;">
608
+ <button class="btn-secondary btn-small" id="preview-dockerfile-btn" title="View the auto-generated Dockerfile">
609
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px;">
610
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
611
+ <circle cx="12" cy="12" r="3"></circle>
612
+ </svg>
613
+ Preview Dockerfile
614
+ </button>
615
+ <button class="btn-primary btn-small" id="build-now-btn" onclick="showBuildConfirmation()">
616
+ Build Now
617
+ </button>
618
+ <button class="btn-ghost btn-small" id="refresh-build-history-btn" onclick="loadBuildHistory()" title="Refresh">
619
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
620
+ <polyline points="23 4 23 10 17 10"></polyline>
621
+ <path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path>
622
+ </svg>
623
+ </button>
624
+ </div>
625
+ </div>
626
+ <div class="card-body">
627
+ <div id="build-status-message" style="margin-bottom: 12px;" hidden>
628
+ <!-- Build status message will appear here -->
629
+ </div>
630
+ <div id="build-output-container" style="margin-bottom: 16px;" hidden>
631
+ <div class="build-output-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
632
+ <span style="font-weight: 500;">Build Output</span>
633
+ <button class="btn-ghost btn-small" onclick="toggleBuildOutput()" id="build-output-toggle">
634
+ <span id="build-output-toggle-icon">&#9660;</span>
635
+ </button>
636
+ </div>
637
+ <div id="build-output-content" style="background: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: 6px; max-height: 400px; overflow-y: auto;">
638
+ <pre id="build-output-text" style="margin: 0; padding: 12px; font-family: var(--font-mono); font-size: 12px; white-space: pre-wrap; word-break: break-all; color: var(--color-text);"></pre>
639
+ </div>
640
+ </div>
641
+ <div id="build-history-container">
642
+ <p class="text-muted">Loading build history...</p>
643
+ </div>
644
+ </div>
645
+ </div>
646
+ </div>
647
+
648
+ <!-- Secrets Tab -->
649
+ <div id="tab-env-vars" class="tab-panel" role="tabpanel" hidden>
650
+ <div class="content-card">
651
+ <div class="card-header">
652
+ <h3>Secrets</h3>
653
+ <div class="card-header-actions">
654
+ <label for="secrets-filter" class="filter-label">Show:</label>
655
+ <select id="secrets-filter" class="secrets-filter-dropdown" title="Filter by availability">
656
+ <option value="all">All</option>
657
+ <option value="build">Build</option>
658
+ <option value="tasks">Tasks</option>
659
+ <option value="deploy">Deploy</option>
660
+ </select>
661
+ <button class="btn-secondary btn-small" id="add-secret-btn">+ Add Secret</button>
662
+ </div>
663
+ </div>
664
+ <div class="card-body">
665
+ <div id="secrets-list" class="secrets-list">
666
+ <!-- Secrets will be rendered here -->
667
+ </div>
668
+ <div id="secrets-empty" class="secrets-empty">
669
+ <p>No secrets configured.</p>
670
+ <p class="text-muted">Secrets can be referenced in builds, tasks, and deployment profiles and exposed as environment variables or mounted files.</p>
671
+ </div>
672
+ </div>
673
+ </div>
674
+
675
+ </div>
676
+
677
+ <!-- Tests Tab -->
678
+ <div id="tab-tests" class="tab-panel" role="tabpanel" hidden>
679
+ <div class="content-card tests-card">
680
+ <div class="tests-layout">
681
+ <div class="tests-sidebar">
682
+ <!-- Test tabs will be populated here -->
683
+ <button class="test-tab" data-test="new" id="new-test-tab">
684
+ <span class="test-icon">➕</span>
685
+ <span>Add Test</span>
686
+ </button>
687
+ </div>
688
+ <div class="tests-content">
689
+ <div class="test-header">
690
+ <h4 id="current-test-name">Select a Test</h4>
691
+ <div class="test-actions">
692
+ <button class="btn-ghost btn-small" id="delete-test-btn" hidden>
693
+ <span>🗑</span> Remove
694
+ </button>
695
+ </div>
696
+ </div>
697
+ <div id="test-details-container">
698
+ <p class="text-muted">Select a test from the sidebar to view details, or add a new one.</p>
699
+ </div>
700
+ </div>
701
+ </div>
702
+ </div>
703
+ </div>
704
+
705
+ <!-- Templates Tab -->
706
+ <div id="tab-templates" class="tab-panel" role="tabpanel" hidden>
707
+ <div class="content-card templates-card">
708
+ <div class="templates-layout">
709
+ <div class="templates-sidebar">
710
+ <!-- Template tabs will be populated here -->
711
+ <button class="template-tab" data-template="new" id="new-template-tab">
712
+ <span class="template-icon">➕</span>
713
+ <span>New Template</span>
714
+ </button>
715
+ </div>
716
+ <div class="templates-content">
717
+ <div class="template-header">
718
+ <h4 id="current-template-name">Select a Template</h4>
719
+ <div class="template-actions">
720
+ <button class="btn-secondary btn-small" id="preview-template-btn" hidden>
721
+ <span>✏️</span> Edit
722
+ </button>
723
+ <button class="btn-ghost btn-small" id="delete-template-btn" hidden>
724
+ <span>🗑</span> Delete
725
+ </button>
726
+ </div>
727
+ </div>
728
+ <div id="template-editor-container">
729
+ <p class="text-muted">Select a template from the sidebar to edit, or create a new one.</p>
730
+ </div>
731
+ </div>
732
+ </div>
733
+ </div>
734
+ </div>
735
+
736
+ <!-- Deploy Tab -->
737
+ <div id="tab-deploy" class="tab-panel" role="tabpanel" hidden>
738
+ <div class="content-card deploy-card">
739
+ <div class="deploy-layout">
740
+ <div class="deploy-sidebar">
741
+ <!-- Deploy profile tabs will be populated here -->
742
+ <button class="deploy-profile-tab" data-profile="new" id="new-deploy-profile-tab">
743
+ <span class="deploy-add-icon">➕</span>
744
+ <span>Add Profile</span>
745
+ </button>
746
+ </div>
747
+ <div class="deploy-content">
748
+ <div class="deploy-header">
749
+ <h4 id="current-deploy-profile-name">Select a Profile</h4>
750
+ <div class="deploy-actions">
751
+ <button class="btn-primary btn-small" id="run-deploy-profile-btn" hidden>
752
+ <span>▶</span> Run
753
+ </button>
754
+ <button class="btn-ghost btn-small" id="delete-deploy-profile-btn" hidden>
755
+ <span>🗑</span> Delete
756
+ </button>
757
+ </div>
758
+ </div>
759
+ <div id="deployment-progress-panel" class="deployment-progress" hidden>
760
+ <div class="deployment-progress-header">
761
+ <h4>Deploying: <span id="deployment-progress-profile"></span></h4>
762
+ <button class="btn-ghost btn-small" id="cancel-deployment-btn">Cancel</button>
763
+ </div>
764
+ <div class="deployment-progress-status">
765
+ <span class="deployment-status-badge running" id="deployment-progress-badge">Running</span>
766
+ <span id="deployment-progress-duration">0:00</span>
767
+ </div>
768
+ <div class="deployment-output-container">
769
+ <pre id="deployment-output"></pre>
770
+ </div>
771
+ </div>
772
+ <div id="deploy-profile-details-container">
773
+ <p class="text-muted">Select a deployment profile to view details, or add a new one.</p>
774
+ <p class="text-muted" style="margin-top: 8px; font-size: 12px;">
775
+ Deployment profiles define scripts that can be run to deploy your application to different environments (e.g., QA, Production).
776
+ </p>
777
+ </div>
778
+
779
+ <!-- Profile Editor (hidden until a profile is selected) -->
780
+ <div id="deploy-profile-editor" class="profile-editor" hidden>
781
+ <div class="deploy-section">
782
+ <div class="deploy-section-header">
783
+ <button class="deploy-section-toggle" type="button" aria-expanded="false" aria-controls="deploy-profile-details-section">
784
+ <span class="deploy-section-title">Profile Details</span>
785
+ </button>
786
+ </div>
787
+ <div class="deploy-section-body" id="deploy-profile-details-section" hidden>
788
+ <div class="profile-section">
789
+ <div class="form-row">
790
+ <label for="profile-description">Description</label>
791
+ <input type="text" id="profile-description" class="form-input" placeholder="Describe what this profile does">
792
+ </div>
793
+ </div>
794
+
795
+ <div class="profile-section">
796
+ <div class="section-header">
797
+ <h5>Deployment Script</h5>
798
+ <span class="script-filename" id="profile-script-filename"></span>
799
+ </div>
800
+ <div id="profile-script-editor" class="monaco-editor-container"></div>
801
+ </div>
802
+ </div>
803
+ </div>
804
+
805
+ <div class="deploy-section">
806
+ <div class="deploy-section-header">
807
+ <button class="deploy-section-toggle" type="button" aria-expanded="false" aria-controls="deploy-profile-secrets-section">
808
+ <span class="deploy-section-title">Secrets</span>
809
+ <span class="deploy-section-count" id="deploy-secrets-count">0</span>
810
+ </button>
811
+ <button class="btn-secondary btn-small" id="add-profile-secret-btn">+ Add</button>
812
+ </div>
813
+ <div class="deploy-section-body" id="deploy-profile-secrets-section" hidden>
814
+ <div id="profile-secrets-list" class="profile-list"></div>
815
+ <p class="text-muted small">Reference secrets from the Secrets tab to expose as environment variables or mounted files.</p>
816
+ </div>
817
+ </div>
818
+
819
+ <div class="deploy-section">
820
+ <div class="deploy-section-header">
821
+ <button class="deploy-section-toggle" type="button" aria-expanded="false" aria-controls="deploy-profile-parameters-section">
822
+ <span class="deploy-section-title">Parameters</span>
823
+ <span class="deploy-section-count" id="deploy-parameters-count">0</span>
824
+ </button>
825
+ <button class="btn-secondary btn-small" id="add-profile-parameter-btn">+ Add</button>
826
+ </div>
827
+ <div class="deploy-section-body" id="deploy-profile-parameters-section" hidden>
828
+ <div id="profile-parameters-list" class="profile-list"></div>
829
+ <p class="text-muted small">User-provided values prompted when running this deployment.</p>
830
+ </div>
831
+ </div>
832
+
833
+ <div class="deploy-section deploy-history-section">
834
+ <div class="deploy-section-header">
835
+ <button class="deploy-section-toggle" type="button" aria-expanded="false" aria-controls="deploy-history-section-body">
836
+ <span class="deploy-section-title">Recent Deployments</span>
837
+ <span class="deploy-section-count" id="deploy-history-count">0</span>
838
+ </button>
839
+ <button class="btn-secondary btn-small" id="refresh-deploy-history-btn">Refresh</button>
840
+ </div>
841
+ <div class="deploy-section-body" id="deploy-history-section-body" hidden>
842
+ <div id="deployment-history-list"></div>
843
+ </div>
844
+ </div>
845
+ </div>
846
+ </div>
847
+ </div>
848
+ </div>
849
+ </div>
850
+
851
+ </div>
852
+ </section>
853
+
854
+ <!-- Commit Modal -->
855
+ <div id="commit-modal" class="modal" hidden>
856
+ <div class="modal-overlay"></div>
857
+ <div class="modal-content">
858
+ <div class="modal-header">
859
+ <h2>Commit Changes</h2>
860
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
861
+ </div>
862
+ <div class="modal-body">
863
+ <div id="commit-error" class="modal-error" hidden></div>
864
+
865
+ <div class="commit-author">
866
+ Committing as: <strong id="commit-user-name"></strong> &lt;<span id="commit-user-email"></span>&gt;
867
+ </div>
868
+
869
+ <div class="form-group">
870
+ <label>Modified files:</label>
871
+ <ul id="commit-files-list" class="modified-files-list"></ul>
872
+ </div>
873
+
874
+ <div class="form-group">
875
+ <label for="commit-message">Commit message</label>
876
+ <textarea id="commit-message" class="form-textarea" rows="3" placeholder="Describe your changes..." required></textarea>
877
+ </div>
878
+
879
+ <div class="checkbox-group">
880
+ <label class="checkbox-label">
881
+ <input type="checkbox" id="commit-pull-first" checked>
882
+ <span>Pull latest changes before committing (recommended)</span>
883
+ </label>
884
+ <label class="checkbox-label">
885
+ <input type="checkbox" id="commit-push" checked>
886
+ <span>Push to remote immediately</span>
887
+ </label>
888
+ </div>
889
+ </div>
890
+ <div class="modal-actions">
891
+ <button type="button" class="btn-secondary" id="commit-cancel-btn">Cancel</button>
892
+ <button type="button" class="btn-primary" id="commit-confirm-btn">Commit & Push</button>
893
+ </div>
894
+ </div>
895
+ </div>
896
+
897
+ <!-- Uncommitted Changes Modal -->
898
+ <div id="uncommitted-changes-modal" class="modal" hidden>
899
+ <div class="modal-overlay"></div>
900
+ <div class="modal-content">
901
+ <div class="modal-header">
902
+ <h2>Uncommitted Changes</h2>
903
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
904
+ </div>
905
+ <div class="modal-body">
906
+ <p class="text-muted" style="margin-bottom: 12px;">The following files have uncommitted changes:</p>
907
+ <ul id="uncommitted-files-list" class="modified-files-list"></ul>
908
+ </div>
909
+ <div class="modal-actions">
910
+ <button type="button" class="btn-secondary" id="uncommitted-changes-close-btn">Close</button>
911
+ </div>
912
+ </div>
913
+ </div>
914
+
915
+ <!-- Discard Changes Modal -->
916
+ <div id="discard-modal" class="modal" hidden>
917
+ <div class="modal-overlay"></div>
918
+ <div class="modal-content">
919
+ <div class="modal-header">
920
+ <h2>⚠️ Discard Changes</h2>
921
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
922
+ </div>
923
+ <div class="modal-body">
924
+ <div class="warning-message">
925
+ <p><strong>This will permanently discard all uncommitted changes.</strong></p>
926
+ <p>This action cannot be undone.</p>
927
+ </div>
928
+
929
+ <div class="form-group">
930
+ <label>Files that will be affected:</label>
931
+ <ul id="discard-files-list" class="modified-files-list"></ul>
932
+ </div>
933
+ </div>
934
+ <div class="modal-actions">
935
+ <button type="button" class="btn-secondary" id="discard-cancel-btn">Cancel</button>
936
+ <button type="button" class="btn-danger" id="discard-confirm-btn">Discard Changes</button>
937
+ </div>
938
+ </div>
939
+ </div>
940
+
941
+ <!-- Build Confirmation Modal -->
942
+ <div id="build-confirm-modal" class="modal" hidden>
943
+ <div class="modal-overlay"></div>
944
+ <div class="modal-content">
945
+ <div class="modal-header">
946
+ <h2>📦 Build Environment</h2>
947
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
948
+ </div>
949
+ <div class="modal-body">
950
+ <p>Build environment <strong id="build-confirm-env-name"></strong>?</p>
951
+ <p class="text-muted" style="margin-top: 8px;">This will rebuild the Docker image with <code>--no-cache</code>. The build process typically takes a few minutes.</p>
952
+ </div>
953
+ <div class="modal-actions">
954
+ <button type="button" class="btn-secondary" id="build-confirm-cancel-btn">Cancel</button>
955
+ <button type="button" class="btn-primary" id="build-confirm-start-btn">
956
+ <span>📦</span> Start Build
957
+ </button>
958
+ </div>
959
+ </div>
960
+ </div>
961
+
962
+ <!-- Dockerfile Preview Modal -->
963
+ <div id="dockerfile-preview-modal" class="modal" hidden>
964
+ <div class="modal-overlay"></div>
965
+ <div class="modal-content modal-large">
966
+ <div class="modal-header">
967
+ <h2>Generated Dockerfile</h2>
968
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
969
+ </div>
970
+ <div class="modal-body">
971
+ <p class="text-muted" style="margin-bottom: 16px;">
972
+ This Dockerfile is automatically generated based on your environment configuration.
973
+ It includes repository cloning, authentication handling, and your custom build commands.
974
+ </p>
975
+ <div id="dockerfile-preview-content" style="background: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: 6px; max-height: 500px; overflow-y: auto;">
976
+ <pre id="dockerfile-preview-text" style="margin: 0; padding: 16px; font-family: var(--font-mono); font-size: 13px; white-space: pre-wrap; word-break: break-word; color: var(--color-text); line-height: 1.5;"></pre>
977
+ </div>
978
+ </div>
979
+ <div class="modal-actions">
980
+ <button type="button" class="btn-secondary" id="dockerfile-preview-close-btn">Close</button>
981
+ <button type="button" class="btn-primary" id="dockerfile-copy-btn">
982
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 4px;">
983
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
984
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
985
+ </svg>
986
+ Copy to Clipboard
987
+ </button>
988
+ </div>
989
+ </div>
990
+ </div>
991
+
992
+ <!-- Base Image Build Confirmation Modal -->
993
+ <div id="base-image-build-modal" class="modal" hidden>
994
+ <div class="modal-overlay"></div>
995
+ <div class="modal-content modal-medium">
996
+ <div class="modal-header">
997
+ <h2>Build Base Image</h2>
998
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
999
+ </div>
1000
+ <div class="modal-body">
1001
+ <p class="text-muted">This will rebuild the foundational image that all environment images are based on. The build typically takes several minutes.</p>
1002
+ <div class="checkbox-group" style="margin-top: 16px;">
1003
+ <label class="checkbox-label">
1004
+ <input type="checkbox" id="base-image-no-cache">
1005
+ <span>Build without cache (--no-cache)</span>
1006
+ </label>
1007
+ </div>
1008
+ <div id="base-image-build-status" style="margin-top: 16px;" hidden>
1009
+ <div class="build-status-message"></div>
1010
+ </div>
1011
+
1012
+ <!-- Build Output Section -->
1013
+ <div id="base-image-build-output" style="margin-top: 16px; background: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: 6px; max-height: 300px; overflow-y: auto;" hidden>
1014
+ <pre style="margin: 0; padding: 12px; font-family: var(--font-mono); font-size: 11px; white-space: pre-wrap; word-break: break-all; color: var(--color-text);"></pre>
1015
+ </div>
1016
+
1017
+ <!-- Build History Section -->
1018
+ <div class="subsection" style="margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--color-border);">
1019
+ <div class="subsection-header" style="margin-bottom: 12px;">
1020
+ <h4 style="margin: 0;">Build History</h4>
1021
+ <button type="button" class="btn-ghost btn-small" id="base-image-refresh-history-btn" title="Refresh history">
1022
+ <span>🔄</span>
1023
+ </button>
1024
+ </div>
1025
+ <div id="base-image-build-history" class="build-history-compact">
1026
+ <p class="text-muted">Loading...</p>
1027
+ </div>
1028
+ </div>
1029
+ </div>
1030
+ <div class="modal-actions">
1031
+ <button type="button" class="btn-secondary" id="base-image-build-cancel-btn">Cancel</button>
1032
+ <button type="button" class="btn-primary" id="base-image-build-start-btn">
1033
+ <span>Build</span>
1034
+ </button>
1035
+ </div>
1036
+ </div>
1037
+ </div>
1038
+
1039
+ <!-- New Environment Modal -->
1040
+ <div id="new-env-modal" class="modal" hidden>
1041
+ <div class="modal-overlay"></div>
1042
+ <div class="modal-content modal-large" style="overflow: visible;">
1043
+ <div class="modal-header">
1044
+ <h2>Create New Environment</h2>
1045
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1046
+ </div>
1047
+ <div class="modal-body" style="overflow: visible;">
1048
+ <div id="new-env-error" class="modal-error" hidden></div>
1049
+
1050
+ <div class="form-single-column">
1051
+ <div class="form-row">
1052
+ <label for="new-env-name">Environment Name <span class="required">*</span></label>
1053
+ <input type="text" id="new-env-name" class="form-input" placeholder="e.g., my-project (lowercase letters, numbers, hyphens)" required>
1054
+ </div>
1055
+
1056
+ <div class="form-row">
1057
+ <label for="new-env-description">Description</label>
1058
+ <input type="text" id="new-env-description" class="form-input">
1059
+ </div>
1060
+
1061
+ <div class="form-row">
1062
+ <label>Repository</label>
1063
+ <div class="radio-group">
1064
+ <label class="radio-label">
1065
+ <input type="radio" name="new-env-repo-mode" value="none" checked onchange="onNewEnvRepoModeChange(this)">
1066
+ <span>None</span>
1067
+ </label>
1068
+ <label class="radio-label">
1069
+ <input type="radio" name="new-env-repo-mode" value="manual" onchange="onNewEnvRepoModeChange(this)">
1070
+ <span>Enter URL manually</span>
1071
+ </label>
1072
+ <label class="radio-label">
1073
+ <input type="radio" name="new-env-repo-mode" value="provider" onchange="onNewEnvRepoModeChange(this)">
1074
+ <span>Select from provider</span>
1075
+ </label>
1076
+ </div>
1077
+ </div>
1078
+
1079
+ <!-- Manual URL entry (shown when "Enter URL manually" selected) -->
1080
+ <div id="new-env-repo-url-row" class="form-row" hidden>
1081
+ <label for="new-env-repo-url">Repository URL</label>
1082
+ <input type="text" id="new-env-repo-url" class="form-input code-font" placeholder="https://github.com/owner/repo.git">
1083
+ <span class="form-hint">Will auto-configure Dockerfile, setup.sh, and agent instructions</span>
1084
+ </div>
1085
+
1086
+ <!-- Provider selection (shown when "Select from provider" selected) -->
1087
+ <div id="new-env-repo-provider-selection" hidden>
1088
+ <div class="form-row">
1089
+ <label for="new-env-repo-source-provider">Git Provider</label>
1090
+ <select id="new-env-repo-source-provider" class="form-input" onchange="onNewEnvRepoProviderChange()">
1091
+ <option value="">-- Select a provider --</option>
1092
+ </select>
1093
+ </div>
1094
+ <div class="form-row" style="margin-top: 16px;">
1095
+ <label>Repository <span class="required">*</span></label>
1096
+ <div id="new-env-repo-autocomplete-container"></div>
1097
+ </div>
1098
+ </div>
1099
+
1100
+ <div class="form-row">
1101
+ <label for="new-env-image">Docker Image <span class="required">*</span></label>
1102
+ <input type="text" id="new-env-image" class="form-input" required>
1103
+ </div>
1104
+
1105
+ <div class="form-row">
1106
+ <label for="new-env-agent">Default Agent <span class="required">*</span></label>
1107
+ <div id="new-env-agent-wrapper"></div>
1108
+ </div>
1109
+ </div>
1110
+ </div>
1111
+ <div class="modal-actions">
1112
+ <button type="button" class="btn-secondary" id="new-env-cancel-btn">Cancel</button>
1113
+ <button type="button" class="btn-primary" id="new-env-create-btn">Create Environment</button>
1114
+ </div>
1115
+ </div>
1116
+ </div>
1117
+
1118
+ <!-- Delete Environment Modal -->
1119
+ <div id="delete-env-modal" class="modal" hidden>
1120
+ <div class="modal-overlay"></div>
1121
+ <div class="modal-content">
1122
+ <div class="modal-header">
1123
+ <h2>⚠️ Delete Environment</h2>
1124
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1125
+ </div>
1126
+ <div class="modal-body">
1127
+ <div class="warning-message">
1128
+ <p><strong>This will delete the entire environment directory.</strong></p>
1129
+ <p>All configuration files and uncommitted changes will be removed.</p>
1130
+ </div>
1131
+
1132
+ <p style="margin-top: 16px;">Environment to delete: <strong id="delete-env-name-display"></strong></p>
1133
+ </div>
1134
+ <div class="modal-actions">
1135
+ <button type="button" class="btn-secondary" id="delete-env-cancel-btn">Cancel</button>
1136
+ <button type="button" class="btn-danger" id="delete-env-confirm-btn">Delete Environment</button>
1137
+ </div>
1138
+ </div>
1139
+ </div>
1140
+
1141
+ <div id="skills-modal" class="modal" hidden>
1142
+ <div class="modal-overlay"></div>
1143
+ <div class="modal-content modal-large">
1144
+ <div class="modal-header">
1145
+ <h2>Assign Skills</h2>
1146
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1147
+ </div>
1148
+ <div class="modal-body">
1149
+ <p class="modal-description">Search and select skills to make them available to agents in this environment.</p>
1150
+ <div id="skills-modal-error" class="modal-error" hidden></div>
1151
+ <div class="skills-modal-layout">
1152
+ <div class="skills-modal-panel">
1153
+ <div class="skills-modal-search">
1154
+ <label class="sr-only" for="skills-modal-search-input">Search skills</label>
1155
+ <div class="skills-modal-search-input">
1156
+ <svg class="skills-modal-search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1157
+ <circle cx="11" cy="11" r="8"></circle>
1158
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
1159
+ </svg>
1160
+ <input type="search" id="skills-modal-search-input" placeholder="Search skills..." class="form-input" autocomplete="off">
1161
+ <button class="skills-modal-search-clear" id="skills-modal-search-clear" type="button" aria-label="Clear search" hidden>&times;</button>
1162
+ </div>
1163
+ <div class="skills-modal-search-meta" id="skills-modal-search-meta" aria-live="polite"></div>
1164
+ </div>
1165
+ <div id="skills-modal-loading" class="skills-modal-loading" hidden>
1166
+ <span class="loading-spinner"></span>
1167
+ <span>Loading skills...</span>
1168
+ </div>
1169
+ <div id="skills-modal-list" class="skills-modal-list" role="listbox" aria-label="Available skills"></div>
1170
+ </div>
1171
+ <div class="skills-modal-panel skills-modal-selected">
1172
+ <div class="skills-modal-selected-header">
1173
+ <h4>Selected Skills</h4>
1174
+ <span class="skills-modal-selected-count" id="skills-modal-selected-count">0</span>
1175
+ </div>
1176
+ <div id="skills-modal-selected-list" class="skills-modal-selected-list"></div>
1177
+ <p id="skills-modal-selected-empty" class="text-muted" hidden>No skills selected yet.</p>
1178
+ </div>
1179
+ </div>
1180
+ </div>
1181
+ <div class="modal-actions">
1182
+ <a href="skills.html" class="skills-modal-manage-link">
1183
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1184
+ <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
1185
+ <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
1186
+ </svg>
1187
+ Manage Skills
1188
+ </a>
1189
+ <button type="button" class="btn-secondary" id="skills-modal-cancel-btn">Cancel</button>
1190
+ <button type="button" class="btn-primary" id="skills-modal-apply-btn">Apply Skills</button>
1191
+ </div>
1192
+ </div>
1193
+ </div>
1194
+
1195
+ <!-- Rename Environment Modal -->
1196
+ <div id="rename-env-modal" class="modal" hidden>
1197
+ <div class="modal-overlay"></div>
1198
+ <div class="modal-content">
1199
+ <div class="modal-header">
1200
+ <h2>Rename Environment</h2>
1201
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1202
+ </div>
1203
+ <div class="modal-body">
1204
+ <div id="rename-env-error" class="modal-error" hidden></div>
1205
+
1206
+ <div class="form-single-column">
1207
+ <div class="form-row">
1208
+ <label for="rename-env-current">Current Name</label>
1209
+ <input type="text" id="rename-env-current" class="form-input" disabled>
1210
+ </div>
1211
+ <div class="form-row">
1212
+ <label for="rename-env-new">New Name <span class="required">*</span></label>
1213
+ <input type="text" id="rename-env-new" class="form-input" placeholder="lowercase letters, numbers, hyphens" required>
1214
+ </div>
1215
+ </div>
1216
+ </div>
1217
+ <div class="modal-actions">
1218
+ <button type="button" class="btn-secondary" id="rename-env-cancel-btn">Cancel</button>
1219
+ <button type="button" class="btn-primary" id="rename-env-confirm-btn">Rename</button>
1220
+ </div>
1221
+ </div>
1222
+ </div>
1223
+
1224
+ <!-- Add Repository Modal -->
1225
+ <div id="add-repo-modal" class="modal" hidden>
1226
+ <div class="modal-content modal-content-wide">
1227
+ <div class="modal-header">
1228
+ <h2 id="repo-modal-title">Add Repository</h2>
1229
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1230
+ </div>
1231
+ <div class="modal-body">
1232
+ <div id="add-repo-error" class="modal-error" hidden></div>
1233
+ <div class="form-single-column">
1234
+ <!-- Entry Mode Selection -->
1235
+ <div class="form-row">
1236
+ <label>Repository</label>
1237
+ <div class="radio-group">
1238
+ <label class="radio-label">
1239
+ <input type="radio" name="repo-entry-mode" value="manual" checked>
1240
+ <span>Enter URL manually</span>
1241
+ </label>
1242
+ <label class="radio-label">
1243
+ <input type="radio" name="repo-entry-mode" value="provider">
1244
+ <span>Select from provider</span>
1245
+ </label>
1246
+ </div>
1247
+ </div>
1248
+
1249
+ <!-- Provider Selection (shown when "Select from provider" is selected) -->
1250
+ <div id="repo-provider-selection" hidden>
1251
+ <div class="form-row">
1252
+ <label for="new-repo-source-provider">Git Provider</label>
1253
+ <select id="new-repo-source-provider" class="form-input">
1254
+ <option value="">-- Select a provider --</option>
1255
+ </select>
1256
+ </div>
1257
+ <div class="form-row" style="margin-top: 16px;">
1258
+ <label for="new-repo-from-provider">Repository</label>
1259
+ <div id="repo-autocomplete-container"></div>
1260
+ </div>
1261
+ </div>
1262
+
1263
+ <!-- Repository URL (shown when "Enter URL manually" is selected) -->
1264
+ <div id="repo-url-row" class="form-row">
1265
+ <label for="new-repo-url">Repository URL</label>
1266
+ <input type="text" id="new-repo-url" class="form-input code-font" placeholder="https://github.com/user/repo.git" required>
1267
+ </div>
1268
+ <div class="form-row">
1269
+ <label for="new-repo-name">Name</label>
1270
+ <input type="text" id="new-repo-name" class="form-input" placeholder="Auto-extracted from URL">
1271
+ </div>
1272
+ <div class="form-row">
1273
+ <label for="new-repo-branch">Branch</label>
1274
+ <input type="text" id="new-repo-branch" class="form-input" value="main" required>
1275
+ </div>
1276
+ <div class="form-row">
1277
+ <label for="new-repo-allow-branch">Allow Branch Selection</label>
1278
+ <label class="toggle-switch">
1279
+ <input type="checkbox" id="new-repo-allow-branch">
1280
+ <span class="toggle-slider"></span>
1281
+ </label>
1282
+ </div>
1283
+ <div class="form-row">
1284
+ <label for="new-repo-path">Path</label>
1285
+ <input type="text" id="new-repo-path" class="form-input code-font" placeholder="Optional path in environment">
1286
+ </div>
1287
+ <div class="form-row">
1288
+ <label for="new-repo-description">Description</label>
1289
+ <input type="text" id="new-repo-description" class="form-input" placeholder="Optional description">
1290
+ </div>
1291
+ </div>
1292
+ </div>
1293
+ <div class="modal-actions">
1294
+ <button type="button" class="btn-secondary" id="add-repo-cancel-btn">Cancel</button>
1295
+ <button type="button" class="btn-primary" id="add-repo-confirm-btn">Add Repository</button>
1296
+ </div>
1297
+ </div>
1298
+ </div>
1299
+
1300
+ <!-- Delete Repository Modal -->
1301
+ <div id="delete-repo-modal" class="modal" hidden>
1302
+ <div class="modal-content">
1303
+ <div class="modal-header">
1304
+ <h2>Delete Repository</h2>
1305
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1306
+ </div>
1307
+ <div class="modal-body">
1308
+ <p>Are you sure you want to remove this repository from the environment?</p>
1309
+ <p><strong id="delete-repo-name"></strong></p>
1310
+ <p class="text-muted" style="margin-top: 16px;">This will only remove the repository configuration, not delete any files.</p>
1311
+ </div>
1312
+ <div class="modal-actions">
1313
+ <button type="button" class="btn-secondary" id="delete-repo-cancel-btn">Cancel</button>
1314
+ <button type="button" class="btn-danger" id="delete-repo-confirm-btn">Delete</button>
1315
+ </div>
1316
+ </div>
1317
+ </div>
1318
+
1319
+ <!-- Add Test Modal -->
1320
+ <div id="add-test-modal" class="modal" hidden>
1321
+ <div class="modal-content">
1322
+ <div class="modal-header">
1323
+ <h2>Add Test Command</h2>
1324
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1325
+ </div>
1326
+ <div class="modal-body">
1327
+ <div id="add-test-error" class="modal-error" hidden></div>
1328
+ <div class="form-single-column">
1329
+ <div class="form-row">
1330
+ <label for="new-test-name">Test Name</label>
1331
+ <input type="text" id="new-test-name" class="form-input" placeholder="e.g., unit-tests" required>
1332
+ </div>
1333
+ <div class="form-row">
1334
+ <label for="new-test-description">Description</label>
1335
+ <input type="text" id="new-test-description" class="form-input" placeholder="Test description">
1336
+ </div>
1337
+ <div class="form-row" style="align-items: start;">
1338
+ <label for="new-test-command">Command(s)</label>
1339
+ <textarea id="new-test-command" class="form-input code-font" rows="3" placeholder="One command per line" required></textarea>
1340
+ </div>
1341
+ </div>
1342
+ </div>
1343
+ <div class="modal-actions">
1344
+ <button type="button" class="btn-secondary" id="add-test-cancel-btn">Cancel</button>
1345
+ <button type="button" class="btn-primary" id="add-test-confirm-btn">Add Test</button>
1346
+ </div>
1347
+ </div>
1348
+ </div>
1349
+
1350
+ <!-- Delete Test Modal -->
1351
+ <div id="delete-test-modal" class="modal" hidden>
1352
+ <div class="modal-content">
1353
+ <div class="modal-header">
1354
+ <h2>Delete Test Command</h2>
1355
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1356
+ </div>
1357
+ <div class="modal-body">
1358
+ <p>Are you sure you want to remove this test command?</p>
1359
+ <p><strong id="delete-test-name"></strong></p>
1360
+ </div>
1361
+ <div class="modal-actions">
1362
+ <button type="button" class="btn-secondary" id="delete-test-cancel-btn">Cancel</button>
1363
+ <button type="button" class="btn-danger" id="delete-test-confirm-btn">Delete</button>
1364
+ </div>
1365
+ </div>
1366
+ </div>
1367
+
1368
+ <!-- Add Template Modal -->
1369
+ <div id="add-template-modal" class="modal" hidden>
1370
+ <div class="modal-content">
1371
+ <div class="modal-header">
1372
+ <h2>Add Task Template</h2>
1373
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1374
+ </div>
1375
+ <div class="modal-body">
1376
+ <div id="add-template-error" class="modal-error" hidden></div>
1377
+ <div class="form-single-column">
1378
+ <div class="form-row">
1379
+ <label for="new-template-title">Title</label>
1380
+ <input type="text" id="new-template-title" class="form-input" placeholder="e.g., Bug Fix Template" required>
1381
+ </div>
1382
+ <div class="form-row">
1383
+ <label for="new-template-description">Description</label>
1384
+ <input type="text" id="new-template-description" class="form-input" placeholder="What is this template for?">
1385
+ </div>
1386
+ <div class="form-row">
1387
+ <label for="new-template-filename">Filename</label>
1388
+ <input type="text" id="new-template-filename" class="form-input code-font" placeholder="Auto-generated from title">
1389
+ </div>
1390
+ </div>
1391
+ </div>
1392
+ <div class="modal-actions">
1393
+ <button type="button" class="btn-secondary" id="add-template-cancel-btn">Cancel</button>
1394
+ <button type="button" class="btn-primary" id="add-template-confirm-btn">Add Template</button>
1395
+ </div>
1396
+ </div>
1397
+ </div>
1398
+
1399
+ <!-- Delete Template Modal -->
1400
+ <div id="delete-template-modal" class="modal" hidden>
1401
+ <div class="modal-content">
1402
+ <div class="modal-header">
1403
+ <h2>Delete Template</h2>
1404
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1405
+ </div>
1406
+ <div class="modal-body">
1407
+ <p>Are you sure you want to remove this template?</p>
1408
+ <p><strong id="delete-template-name"></strong></p>
1409
+ </div>
1410
+ <div class="modal-actions">
1411
+ <button type="button" class="btn-secondary" id="delete-template-cancel-btn">Cancel</button>
1412
+ <button type="button" class="btn-danger" id="delete-template-confirm-btn">Delete</button>
1413
+ </div>
1414
+ </div>
1415
+ </div>
1416
+
1417
+
1418
+ <!-- Add Deploy Profile Modal -->
1419
+ <div id="add-deploy-profile-modal" class="modal" hidden>
1420
+ <div class="modal-content">
1421
+ <div class="modal-header">
1422
+ <h2>Add Deployment Profile</h2>
1423
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1424
+ </div>
1425
+ <div class="modal-body">
1426
+ <div id="add-deploy-profile-error" class="modal-error" hidden></div>
1427
+ <div class="form-single-column">
1428
+ <div class="form-row">
1429
+ <label for="new-deploy-profile-name">Profile Name</label>
1430
+ <input type="text" id="new-deploy-profile-name" class="form-input code-font" placeholder="e.g., production, staging, qa" required>
1431
+ <p class="text-muted" style="margin-top: 4px; font-size: 13px;">Alphanumeric, hyphens, and underscores only</p>
1432
+ </div>
1433
+ <div class="form-row">
1434
+ <label for="new-deploy-profile-description">Description</label>
1435
+ <input type="text" id="new-deploy-profile-description" class="form-input" placeholder="e.g., Deploy to production server" required>
1436
+ </div>
1437
+ </div>
1438
+ </div>
1439
+ <div class="modal-actions">
1440
+ <button type="button" class="btn-secondary" id="add-deploy-profile-cancel-btn">Cancel</button>
1441
+ <button type="button" class="btn-primary" id="add-deploy-profile-confirm-btn">Add Profile</button>
1442
+ </div>
1443
+ </div>
1444
+ </div>
1445
+
1446
+ <!-- Run Deployment Modal -->
1447
+ <div id="run-deployment-modal" class="modal" hidden>
1448
+ <div class="modal-overlay"></div>
1449
+ <div class="modal-content">
1450
+ <div class="modal-header">
1451
+ <h2>Run Deployment: <span id="run-deployment-profile-name"></span></h2>
1452
+ <button type="button" class="modal-close">&times;</button>
1453
+ </div>
1454
+ <div class="modal-body">
1455
+ <div id="run-deployment-error" class="modal-error" hidden></div>
1456
+ <div id="run-deployment-parameters"></div>
1457
+ <div class="form-row">
1458
+ <label class="checkbox-label">
1459
+ <input type="checkbox" id="run-deployment-dry-run">
1460
+ <span>Dry run (validate only, don't execute)</span>
1461
+ </label>
1462
+ </div>
1463
+ </div>
1464
+ <div class="modal-actions">
1465
+ <button class="btn-secondary" id="run-deployment-cancel-btn">Cancel</button>
1466
+ <button class="btn-primary" id="run-deployment-start-btn">
1467
+ <span>▶</span> Run Deployment
1468
+ </button>
1469
+ </div>
1470
+ </div>
1471
+ </div>
1472
+
1473
+ <!-- Delete Deploy Profile Modal -->
1474
+ <div id="delete-deploy-profile-modal" class="modal" hidden>
1475
+ <div class="modal-content">
1476
+ <div class="modal-header">
1477
+ <h2>Delete Deployment Profile</h2>
1478
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1479
+ </div>
1480
+ <div class="modal-body">
1481
+ <p>Are you sure you want to delete this deployment profile?</p>
1482
+ <p><strong id="delete-deploy-profile-name"></strong></p>
1483
+ <p class="text-muted">This will delete both the profile configuration and its deployment script.</p>
1484
+ </div>
1485
+ <div class="modal-actions">
1486
+ <button type="button" class="btn-secondary" id="delete-deploy-profile-cancel-btn">Cancel</button>
1487
+ <button type="button" class="btn-danger" id="delete-deploy-profile-confirm-btn">Delete</button>
1488
+ </div>
1489
+ </div>
1490
+ </div>
1491
+
1492
+ <!-- Add/Edit Secret Modal -->
1493
+ <div id="secret-modal" class="modal" hidden>
1494
+ <div class="modal-content">
1495
+ <div class="modal-header">
1496
+ <h2 id="secret-modal-title">Add Secret</h2>
1497
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1498
+ </div>
1499
+ <div class="modal-body">
1500
+ <div id="secret-modal-error" class="modal-error" hidden></div>
1501
+ <div class="form-single-column">
1502
+ <div class="form-row">
1503
+ <label for="secret-name">Secret Name <span class="required">*</span></label>
1504
+ <input type="text" id="secret-name" class="form-input code-font" placeholder="e.g., prod_ssh_key" required>
1505
+ </div>
1506
+ <div class="form-row">
1507
+ <label for="secret-description">Description</label>
1508
+ <input type="text" id="secret-description" class="form-input" placeholder="e.g., Production SSH key">
1509
+ </div>
1510
+ <div class="form-row">
1511
+ <label>Source Type <span class="required">*</span></label>
1512
+ <div class="radio-group">
1513
+ <label class="radio-label">
1514
+ <input type="radio" name="secret-type" value="file" id="secret-type-file">
1515
+ <span>File on host</span>
1516
+ </label>
1517
+ <label class="radio-label">
1518
+ <input type="radio" name="secret-type" value="value" id="secret-type-value" checked>
1519
+ <span>Direct value</span>
1520
+ </label>
1521
+ </div>
1522
+ </div>
1523
+ <div class="form-row">
1524
+ <label>Available For <span class="required">*</span></label>
1525
+ <div class="checkbox-group">
1526
+ <label class="checkbox-label">
1527
+ <input type="checkbox" id="secret-available-build">
1528
+ <span>Build</span>
1529
+ </label>
1530
+ <label class="checkbox-label">
1531
+ <input type="checkbox" id="secret-available-tasks">
1532
+ <span>Tasks</span>
1533
+ </label>
1534
+ <label class="checkbox-label">
1535
+ <input type="checkbox" id="secret-available-deploy" checked>
1536
+ <span>Deploy</span>
1537
+ </label>
1538
+ </div>
1539
+ </div>
1540
+ <div class="form-row" id="secret-expose-as-row" hidden>
1541
+ <label>Expose As <span class="required">*</span></label>
1542
+ <div class="radio-group">
1543
+ <label class="radio-label">
1544
+ <input type="radio" name="secret-expose-as" value="env_var" id="secret-expose-env" checked>
1545
+ <span>Environment Variable</span>
1546
+ </label>
1547
+ <label class="radio-label">
1548
+ <input type="radio" name="secret-expose-as" value="file" id="secret-expose-file">
1549
+ <span>File (bind mount)</span>
1550
+ </label>
1551
+ </div>
1552
+ </div>
1553
+ <div class="form-row" id="secret-target-row" hidden>
1554
+ <label for="secret-target" id="secret-target-label">Variable Name <span class="required">*</span></label>
1555
+ <input type="text" id="secret-target" class="form-input code-font" placeholder="e.g., SSH_PRIVATE_KEY">
1556
+ <span class="form-hint" id="secret-target-hint">The environment variable name</span>
1557
+ </div>
1558
+ <div class="form-row" id="secret-source-row" hidden>
1559
+ <label for="secret-source">Host File Path <span class="required">*</span></label>
1560
+ <input type="text" id="secret-source" class="form-input code-font" placeholder="e.g., /home/coder/.ssh/id_ed25519_prod">
1561
+ <span class="form-hint" id="secret-source-hint">Absolute path, or relative to the environment directory</span>
1562
+ <div id="secret-source-validation" class="field-validation" hidden></div>
1563
+ </div>
1564
+ <div class="form-row" id="secret-value-row">
1565
+ <label for="secret-value">Value <span class="required">*</span></label>
1566
+ <div class="secret-value-group">
1567
+ <textarea id="secret-value" class="form-input code-font" rows="6" placeholder="Enter secret value (e.g., SSH private key content)"></textarea>
1568
+ <button type="button" class="btn-icon secret-toggle-visibility" title="Toggle visibility" hidden>
1569
+ <svg class="eye-open" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1570
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
1571
+ <circle cx="12" cy="12" r="3"></circle>
1572
+ </svg>
1573
+ <svg class="eye-closed" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: none;">
1574
+ <path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>
1575
+ <line x1="1" y1="1" x2="23" y2="23"></line>
1576
+ </svg>
1577
+ </button>
1578
+ </div>
1579
+ <div class="form-row-actions" style="margin-top: 8px;">
1580
+ <button type="button" class="btn-secondary btn-small" id="secret-import-file-btn">
1581
+ <span>📁</span> Import from file...
1582
+ </button>
1583
+ <input type="file" id="secret-file-input" hidden>
1584
+ </div>
1585
+ </div>
1586
+ <div class="form-row" id="secret-git-pat-row" hidden>
1587
+ <label class="checkbox-label">
1588
+ <input type="checkbox" id="secret-git-pat-checkbox">
1589
+ <span>Use as Git PAT (Personal Access Token)</span>
1590
+ </label>
1591
+ <span class="form-hint">Use this secret as a PAT for authenticating with a Git host</span>
1592
+ </div>
1593
+ <div class="form-row" id="secret-git-host-row" hidden>
1594
+ <label for="secret-git-host">Git Host <span class="required">*</span></label>
1595
+ <select id="secret-git-host" class="form-input">
1596
+ <option value="">Select a Git host...</option>
1597
+ <option value="github.com">GitHub (github.com)</option>
1598
+ <option value="dev.azure.com">Azure DevOps (dev.azure.com)</option>
1599
+ <option value="bitbucket.org">Bitbucket (bitbucket.org)</option>
1600
+ <option value="gitlab.com">GitLab (gitlab.com)</option>
1601
+ <option value="custom">Other (custom hostname)...</option>
1602
+ </select>
1603
+ </div>
1604
+ <div class="form-row" id="secret-git-custom-host-row" hidden>
1605
+ <label for="secret-git-custom-host">Custom Git Hostname <span class="required">*</span></label>
1606
+ <input type="text" id="secret-git-custom-host" class="form-input code-font" placeholder="e.g., git.mycompany.com">
1607
+ <span class="form-hint">The hostname of your Git server (without https://)</span>
1608
+ </div>
1609
+ </div>
1610
+ </div>
1611
+ <div class="modal-actions">
1612
+ <button type="button" class="btn-secondary" id="secret-modal-cancel-btn">Cancel</button>
1613
+ <button type="button" class="btn-primary" id="secret-modal-save-btn">Save Secret</button>
1614
+ </div>
1615
+ </div>
1616
+ </div>
1617
+
1618
+ <!-- Delete Secret Modal -->
1619
+ <div id="delete-secret-modal" class="modal" hidden>
1620
+ <div class="modal-content">
1621
+ <div class="modal-header">
1622
+ <h2>Delete Secret</h2>
1623
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1624
+ </div>
1625
+ <div class="modal-body">
1626
+ <p>Are you sure you want to delete this secret?</p>
1627
+ <p><strong id="delete-secret-name"></strong></p>
1628
+ <p class="text-muted">Deployment profiles referencing this secret will need to be updated.</p>
1629
+ </div>
1630
+ <div class="modal-actions">
1631
+ <button type="button" class="btn-secondary" id="delete-secret-cancel-btn">Cancel</button>
1632
+ <button type="button" class="btn-danger" id="delete-secret-confirm-btn">Delete</button>
1633
+ </div>
1634
+ </div>
1635
+ </div>
1636
+
1637
+ <!-- Profile Secret Configuration Modal -->
1638
+ <div id="profile-secret-modal" class="modal" hidden>
1639
+ <div class="modal-overlay"></div>
1640
+ <div class="modal-content modal-content-narrow">
1641
+ <div class="modal-header">
1642
+ <h2 id="profile-secret-modal-title">Add Secret to Profile</h2>
1643
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1644
+ </div>
1645
+ <div class="modal-body">
1646
+ <div id="profile-secret-error" class="modal-error" hidden></div>
1647
+ <div class="form-single-column">
1648
+ <div class="form-row">
1649
+ <label for="profile-secret-name">Secret <span class="required">*</span></label>
1650
+ <select id="profile-secret-name" class="form-input">
1651
+ <!-- Populated from environment secrets (filtered to deploy-available) -->
1652
+ </select>
1653
+ <span class="form-hint">Select a secret defined in the Secrets tab (only secrets available for Deploy are shown)</span>
1654
+ </div>
1655
+ <div id="profile-secret-info" class="secret-info-preview" hidden>
1656
+ <!-- Shows expose_as and target from selected secret -->
1657
+ </div>
1658
+ </div>
1659
+ </div>
1660
+ <div class="modal-actions">
1661
+ <button type="button" class="btn-secondary" id="profile-secret-cancel-btn">Cancel</button>
1662
+ <button type="button" class="btn-primary" id="profile-secret-save-btn">Add</button>
1663
+ </div>
1664
+ </div>
1665
+ </div>
1666
+
1667
+ <!-- Shared Parameter Configuration Modal (used by Deploy, Tests, Templates) -->
1668
+ <div id="profile-parameter-modal" class="modal" hidden>
1669
+ <div class="modal-overlay"></div>
1670
+ <div class="modal-content modal-medium">
1671
+ <div class="modal-header">
1672
+ <h2 id="profile-parameter-modal-title">Configure Parameter</h2>
1673
+ <button type="button" class="modal-close" aria-label="Close">&times;</button>
1674
+ </div>
1675
+ <div class="modal-body">
1676
+ <div id="profile-parameter-error" class="modal-error" hidden></div>
1677
+ <div class="form-single-column">
1678
+ <div class="form-row">
1679
+ <label for="profile-param-name">Parameter Name <span class="required">*</span></label>
1680
+ <input type="text" id="profile-param-name" class="form-input code-font" placeholder="e.g., TARGET_LIBRARY">
1681
+ <span class="form-hint">Will be available as environment variable when running</span>
1682
+ </div>
1683
+ <div class="form-row">
1684
+ <label for="profile-param-label">Display Label</label>
1685
+ <input type="text" id="profile-param-label" class="form-input" placeholder="e.g., Target Library">
1686
+ <span class="form-hint">User-friendly label shown in the run dialog</span>
1687
+ </div>
1688
+ <div class="form-row">
1689
+ <label for="profile-param-description">Description</label>
1690
+ <input type="text" id="profile-param-description" class="form-input" placeholder="e.g., The library to deploy to">
1691
+ </div>
1692
+ <div class="form-row">
1693
+ <label for="profile-param-type">Input Type <span class="required">*</span></label>
1694
+ <select id="profile-param-type" class="form-input">
1695
+ <option value="text">Text Input</option>
1696
+ <option value="list">Select List (static options)</option>
1697
+ <option value="boolean">Checkbox</option>
1698
+ <option value="command">Dynamic (from command)</option>
1699
+ <option value="files">File/Directory Browser</option>
1700
+ </select>
1701
+ </div>
1702
+
1703
+ <!-- List/Select Options (for type=list) -->
1704
+ <div class="form-row" id="profile-param-options-row" hidden>
1705
+ <label for="profile-param-options">Options <span class="required">*</span></label>
1706
+ <div class="options-editor" id="profile-param-options-editor">
1707
+ <div class="options-list" id="profile-param-options-list"></div>
1708
+ <button type="button" class="btn-secondary btn-small" id="profile-param-add-option-btn">+ Add Option</button>
1709
+ </div>
1710
+ <span class="form-hint">Define the available choices. Use description for additional context.</span>
1711
+ </div>
1712
+
1713
+ <!-- Command Options (for type=command) -->
1714
+ <div id="profile-param-command-row" hidden>
1715
+ <div class="form-row">
1716
+ <label for="profile-param-command">Command <span class="required">*</span></label>
1717
+ <input type="text" id="profile-param-command" class="form-input code-font" placeholder="e.g., git branch -r | grep origin/">
1718
+ <span class="form-hint">Shell command to run in container to generate choices</span>
1719
+ </div>
1720
+ <div class="form-row">
1721
+ <label for="profile-param-parser">Output Parser</label>
1722
+ <select id="profile-param-parser" class="form-input">
1723
+ <option value="lines">Lines (one option per line)</option>
1724
+ <option value="json">JSON (array of {value, label, description})</option>
1725
+ <option value="git-branches">Git Branches</option>
1726
+ <option value="files">File Listing</option>
1727
+ </select>
1728
+ </div>
1729
+ <div class="form-row">
1730
+ <label for="profile-param-repos">Repositories</label>
1731
+ <input type="text" id="profile-param-repos" class="form-input code-font" placeholder="e.g., converter, main-app (comma-separated)">
1732
+ <span class="form-hint">Optional: specific repos to update before running command</span>
1733
+ </div>
1734
+ </div>
1735
+
1736
+ <!-- Files Options (for type=files) -->
1737
+ <div id="profile-param-files-row" hidden>
1738
+ <div class="form-row">
1739
+ <label for="profile-param-path">Directory Path <span class="required">*</span></label>
1740
+ <input type="text" id="profile-param-path" class="form-input code-font" placeholder="e.g., /workspace/src">
1741
+ <span class="form-hint">Directory path relative to /workspace</span>
1742
+ </div>
1743
+ <div class="form-row">
1744
+ <label for="profile-param-include">Include</label>
1745
+ <select id="profile-param-include" class="form-input">
1746
+ <option value="files">Files only</option>
1747
+ <option value="directories">Directories only</option>
1748
+ <option value="both">Both files and directories</option>
1749
+ </select>
1750
+ </div>
1751
+ <div class="form-row">
1752
+ <label for="profile-param-pattern">Glob Pattern</label>
1753
+ <input type="text" id="profile-param-pattern" class="form-input code-font" placeholder="e.g., *.{rpg,rpgle,sqlrpgle}">
1754
+ <span class="form-hint">Optional file pattern filter</span>
1755
+ </div>
1756
+ <div class="form-row">
1757
+ <label for="profile-param-files-repos">Repositories</label>
1758
+ <input type="text" id="profile-param-files-repos" class="form-input code-font" placeholder="e.g., converter, main-app (comma-separated)">
1759
+ <span class="form-hint">Optional: specific repos to update before listing</span>
1760
+ </div>
1761
+ </div>
1762
+
1763
+ <!-- Multi-select option (for list, command, files) -->
1764
+ <div class="form-row" id="profile-param-multiselect-row" hidden>
1765
+ <label class="checkbox-label">
1766
+ <input type="checkbox" id="profile-param-multiselect">
1767
+ <span>Allow multiple selections</span>
1768
+ </label>
1769
+ </div>
1770
+
1771
+ <div class="form-row">
1772
+ <label for="profile-param-default">Default Value</label>
1773
+ <input type="text" id="profile-param-default" class="form-input" placeholder="Optional default value">
1774
+ </div>
1775
+ <div class="form-row">
1776
+ <label class="checkbox-label">
1777
+ <input type="checkbox" id="profile-param-required">
1778
+ <span>Required field</span>
1779
+ </label>
1780
+ </div>
1781
+ </div>
1782
+ </div>
1783
+ <div class="modal-actions">
1784
+ <button type="button" class="btn-secondary" id="profile-parameter-cancel-btn">Cancel</button>
1785
+ <button type="button" class="btn-primary" id="profile-parameter-save-btn">Save</button>
1786
+ </div>
1787
+ </div>
1788
+ </div>
1789
+ </main>
1790
+ </body>
1791
+ </html>