acdev 1.0.8 → 1.0.10
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.
- package/.acdev/.env.example +0 -3
- package/README.md +11 -21
- package/bin/acdev.js +3 -13
- package/package.json +1 -2
- package/public/app.js +71 -388
- package/public/index.html +19 -33
- package/public/styles.css +32 -31
- package/src/agent.js +64 -127
- package/src/config.js +45 -151
- package/src/git.js +62 -0
- package/src/models.js +35 -256
- package/src/server.js +72 -52
- package/src/store.js +2 -0
- package/src/openrouter-auth.js +0 -37
package/public/app.js
CHANGED
|
@@ -129,51 +129,18 @@ let settingsSaving = false;
|
|
|
129
129
|
let settingsTab = 'ticket';
|
|
130
130
|
/** Sentinel: no model selected (jobs blocked until user picks one). */
|
|
131
131
|
const NO_MODEL = '-';
|
|
132
|
-
const OPENROUTER_AGENT_MODEL_PREFIX = 'anthropic/';
|
|
133
132
|
/** @type {string} */
|
|
134
133
|
let modelSourceHintBase = '';
|
|
135
|
-
/**
|
|
136
|
-
* @param {string} modelId
|
|
137
|
-
* @returns {boolean}
|
|
138
|
-
*/
|
|
139
|
-
function isOpenRouterAgentCompatibleModel(modelId) {
|
|
140
|
-
if (!modelId || !String(modelId).includes('/')) return false;
|
|
141
|
-
return String(modelId).trim().toLowerCase().startsWith(OPENROUTER_AGENT_MODEL_PREFIX);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* @param {HTMLElement} btn
|
|
146
|
-
* @param {{ id: string, agentCompatible?: boolean }} model
|
|
147
|
-
*/
|
|
148
|
-
function appendModelCompatibilityBadge(btn, model) {
|
|
149
|
-
if (currentLlmProviderSelection() !== 'openrouter') return;
|
|
150
|
-
const compatible =
|
|
151
|
-
model.agentCompatible ?? isOpenRouterAgentCompatibleModel(model.id);
|
|
152
|
-
const badge = document.createElement('span');
|
|
153
|
-
badge.className = compatible
|
|
154
|
-
? 'model-picker-badge model-picker-badge--agent'
|
|
155
|
-
: 'model-picker-badge model-picker-badge--reference';
|
|
156
|
-
badge.textContent = compatible ? 'Agent' : 'May not work';
|
|
157
|
-
badge.title = compatible
|
|
158
|
-
? 'Supported for acdev agent jobs (anthropic/* via Claude Agent SDK)'
|
|
159
|
-
: 'Listed for reference; agent jobs require anthropic/* models';
|
|
160
|
-
btn.appendChild(badge);
|
|
161
|
-
}
|
|
162
134
|
|
|
163
135
|
/**
|
|
164
136
|
* @param {HTMLElement} btn
|
|
165
|
-
* @param {{ id: string, label?: string, name?: string
|
|
137
|
+
* @param {{ id: string, label?: string, name?: string }} model
|
|
166
138
|
*/
|
|
167
139
|
function populateModelPickerOption(btn, model) {
|
|
168
|
-
const titleRow = document.createElement('span');
|
|
169
|
-
titleRow.className = 'model-picker-option-title-row';
|
|
170
|
-
|
|
171
140
|
const title = document.createElement('span');
|
|
172
141
|
title.className = 'model-picker-option-title';
|
|
173
142
|
title.textContent = modelPickerLabel(model);
|
|
174
|
-
|
|
175
|
-
appendModelCompatibilityBadge(titleRow, model);
|
|
176
|
-
btn.appendChild(titleRow);
|
|
143
|
+
btn.appendChild(title);
|
|
177
144
|
|
|
178
145
|
if (model.id !== modelPickerLabel(model)) {
|
|
179
146
|
const sub = document.createElement('span');
|
|
@@ -185,23 +152,12 @@ function populateModelPickerOption(btn, model) {
|
|
|
185
152
|
|
|
186
153
|
function refreshModelSourceHint() {
|
|
187
154
|
if (!els.settingsModelHint) return;
|
|
188
|
-
|
|
189
|
-
let text = modelSourceHintBase;
|
|
190
|
-
if (
|
|
191
|
-
provider === 'openrouter' &&
|
|
192
|
-
!isNoModelSelection(currentModel) &&
|
|
193
|
-
!isOpenRouterAgentCompatibleModel(currentModel)
|
|
194
|
-
) {
|
|
195
|
-
text += ` Selected model "${currentModel}" may not work for agent jobs — choose an anthropic/* model to run jobs.`;
|
|
196
|
-
}
|
|
197
|
-
els.settingsModelHint.textContent = text;
|
|
155
|
+
els.settingsModelHint.textContent = modelSourceHintBase;
|
|
198
156
|
}
|
|
199
157
|
/** @type {string} */
|
|
200
158
|
let currentModel = 'claude-sonnet-5';
|
|
201
159
|
/** @type {Array<{id:string,label?:string,name?:string}>} */
|
|
202
160
|
let availableModels = [];
|
|
203
|
-
/** @type {'claude' | 'openrouter' | null} */
|
|
204
|
-
let availableModelsProvider = null;
|
|
205
161
|
/** @type {'github' | 'jira'} */
|
|
206
162
|
let ticketSource = 'github';
|
|
207
163
|
/** @type {{
|
|
@@ -234,11 +190,6 @@ let ticketSource = 'github';
|
|
|
234
190
|
* anthropicApiKeyMasked?: string | null,
|
|
235
191
|
* claudeOauthTokenSet?: boolean,
|
|
236
192
|
* claudeOauthTokenMasked?: string | null,
|
|
237
|
-
* llmProvider?: 'claude' | 'openrouter',
|
|
238
|
-
* lastModelsByProvider?: Record<string, string>,
|
|
239
|
-
* openrouterAuthOk?: boolean,
|
|
240
|
-
* openrouterApiKeySet?: boolean,
|
|
241
|
-
* openrouterApiKeyMasked?: string | null,
|
|
242
193
|
* llmAuthOk?: boolean,
|
|
243
194
|
* stubAgent?: boolean,
|
|
244
195
|
* }} */
|
|
@@ -254,7 +205,6 @@ const els = {
|
|
|
254
205
|
modelProvider: document.getElementById('model-provider'),
|
|
255
206
|
modelCurrent: document.getElementById('model-current'),
|
|
256
207
|
modelCombined: document.getElementById('model-combined'),
|
|
257
|
-
overviewLlmProvider: document.getElementById('overview-llm-provider'),
|
|
258
208
|
overviewModel: document.getElementById('overview-model'),
|
|
259
209
|
overviewAgentPicker: document.getElementById('overview-agent-picker'),
|
|
260
210
|
overviewAgentTrigger: document.getElementById('overview-agent-trigger'),
|
|
@@ -278,6 +228,7 @@ const els = {
|
|
|
278
228
|
enqueueJiraSettingsLink: document.getElementById('enqueue-jira-settings-link'),
|
|
279
229
|
overviewTicketSource: document.getElementById('overview-ticket-source'),
|
|
280
230
|
enqueueFeedback: document.getElementById('enqueue-feedback'),
|
|
231
|
+
preferredBranch: document.getElementById('preferred-branch'),
|
|
281
232
|
addBtn: document.getElementById('add-btn'),
|
|
282
233
|
statsGrid: document.getElementById('stats-grid'),
|
|
283
234
|
overviewRunning: document.getElementById('overview-running'),
|
|
@@ -340,8 +291,6 @@ const els = {
|
|
|
340
291
|
settingsModelList: document.getElementById('settings-model-list'),
|
|
341
292
|
settingsModelSearch: document.getElementById('settings-model-search'),
|
|
342
293
|
settingsModelHint: document.getElementById('settings-model-hint'),
|
|
343
|
-
settingsLlmProvider: document.getElementById('settings-llm-provider'),
|
|
344
|
-
settingsLlmProviderHint: document.getElementById('settings-llm-provider-hint'),
|
|
345
294
|
settingsMaxTurns: document.getElementById('settings-max-turns'),
|
|
346
295
|
settingsTimeout: document.getElementById('settings-timeout'),
|
|
347
296
|
settingsTimeoutMs: document.getElementById('settings-timeout-ms'),
|
|
@@ -382,10 +331,6 @@ const els = {
|
|
|
382
331
|
settingsClaudeOauth: document.getElementById('settings-claude-oauth'),
|
|
383
332
|
settingsClaudeOauthClear: document.getElementById('settings-claude-oauth-clear'),
|
|
384
333
|
settingsClaudeOauthHint: document.getElementById('settings-claude-oauth-hint'),
|
|
385
|
-
settingsOpenrouterStatus: document.getElementById('settings-openrouter-status'),
|
|
386
|
-
settingsOpenrouterKey: document.getElementById('settings-openrouter-key'),
|
|
387
|
-
settingsOpenrouterKeyClear: document.getElementById('settings-openrouter-key-clear'),
|
|
388
|
-
settingsOpenrouterKeyHint: document.getElementById('settings-openrouter-key-hint'),
|
|
389
334
|
settingsTabs: document.getElementById('settings-tabs'),
|
|
390
335
|
};
|
|
391
336
|
|
|
@@ -956,16 +901,22 @@ function jobTitle(job) {
|
|
|
956
901
|
return job.issueTitle || truncate(job.issueUrl, 48);
|
|
957
902
|
}
|
|
958
903
|
|
|
904
|
+
function jobDisplayBranch(job) {
|
|
905
|
+
return job.branchName || job.preferredBranchName || '';
|
|
906
|
+
}
|
|
907
|
+
|
|
959
908
|
function jobRepoLine(job) {
|
|
960
909
|
if (isJiraJob(job)) {
|
|
961
910
|
const key = parseJiraKeyFromJob(job) || '—';
|
|
962
|
-
const
|
|
911
|
+
const name = jobDisplayBranch(job);
|
|
912
|
+
const branch = name ? ` · ${name}` : '';
|
|
963
913
|
return { repo: 'Jira', number: key, branch, text: `Jira ${key}${branch}`, isJira: true };
|
|
964
914
|
}
|
|
965
915
|
const ref = parseIssueRef(job.issueUrl);
|
|
966
916
|
const repo = ref?.full || '—';
|
|
967
917
|
const num = ref?.number || job.issueNumber || '—';
|
|
968
|
-
const
|
|
918
|
+
const name = jobDisplayBranch(job);
|
|
919
|
+
const branch = name ? ` · ${name}` : '';
|
|
969
920
|
return { repo, number: num, branch, text: `${repo} #${num}${branch}`, isJira: false };
|
|
970
921
|
}
|
|
971
922
|
|
|
@@ -2752,6 +2703,7 @@ function jobMatchesReviewSearch(job, q) {
|
|
|
2752
2703
|
job.issueTitle,
|
|
2753
2704
|
job.issueUrl,
|
|
2754
2705
|
job.branchName,
|
|
2706
|
+
job.preferredBranchName,
|
|
2755
2707
|
job.prTitle,
|
|
2756
2708
|
job.prUrl,
|
|
2757
2709
|
ref?.number,
|
|
@@ -3042,34 +2994,21 @@ async function readJson(res) {
|
|
|
3042
2994
|
}
|
|
3043
2995
|
}
|
|
3044
2996
|
|
|
3045
|
-
function llmProviderLabel(provider) {
|
|
3046
|
-
return provider === 'openrouter' ? 'OpenRouter' : 'Claude';
|
|
3047
|
-
}
|
|
3048
|
-
|
|
3049
2997
|
/**
|
|
3050
|
-
* @param {'claude' | 'openrouter'} [provider]
|
|
3051
2998
|
* @param {string} [modelId]
|
|
3052
2999
|
* @returns {string}
|
|
3053
3000
|
*/
|
|
3054
|
-
function agentDisplayText(
|
|
3055
|
-
|
|
3056
|
-
const modelText = isNoModelSelection(modelId) ? '—' : modelPickerDisplayText(modelId);
|
|
3057
|
-
return `${providerName} • ${modelText}`;
|
|
3001
|
+
function agentDisplayText(modelId = currentModel) {
|
|
3002
|
+
return isNoModelSelection(modelId) ? '—' : modelPickerDisplayText(modelId);
|
|
3058
3003
|
}
|
|
3059
3004
|
|
|
3060
3005
|
function updateModelLabel() {
|
|
3061
|
-
const provider = appConfig.llmProvider === 'openrouter' ? 'openrouter' : 'claude';
|
|
3062
|
-
const providerName = llmProviderLabel(provider);
|
|
3063
3006
|
const modelText = isNoModelSelection(currentModel) ? '—' : currentModel;
|
|
3064
3007
|
const modelTitle = isNoModelSelection(currentModel)
|
|
3065
3008
|
? 'none selected'
|
|
3066
3009
|
: currentModel;
|
|
3067
|
-
const combined = agentDisplayText(
|
|
3010
|
+
const combined = agentDisplayText(currentModel);
|
|
3068
3011
|
|
|
3069
|
-
if (els.modelProvider) {
|
|
3070
|
-
els.modelProvider.textContent = providerName;
|
|
3071
|
-
els.modelProvider.title = `${providerName} — switch in Overview or Settings`;
|
|
3072
|
-
}
|
|
3073
3012
|
if (els.modelCurrent) {
|
|
3074
3013
|
els.modelCurrent.textContent = modelText;
|
|
3075
3014
|
els.modelCurrent.title = `Model: ${modelTitle} — switch in Overview or Settings`;
|
|
@@ -3082,29 +3021,16 @@ function updateModelLabel() {
|
|
|
3082
3021
|
refreshModelSourceHint();
|
|
3083
3022
|
}
|
|
3084
3023
|
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
* @returns {Array<{id:string,label?:string,name?:string}>}
|
|
3088
|
-
*/
|
|
3089
|
-
function getAvailableModels(provider = currentLlmProviderSelection()) {
|
|
3090
|
-
if (availableModelsProvider === provider && availableModels.length) {
|
|
3091
|
-
return availableModels;
|
|
3092
|
-
}
|
|
3093
|
-
return defaultModelsForProvider(provider);
|
|
3024
|
+
function getAvailableModels() {
|
|
3025
|
+
return availableModels.length ? availableModels : defaultModels();
|
|
3094
3026
|
}
|
|
3095
3027
|
|
|
3096
|
-
|
|
3097
|
-
* @param {'claude' | 'openrouter'} provider
|
|
3098
|
-
* @param {Array<{id:string,label?:string,name?:string}>} models
|
|
3099
|
-
*/
|
|
3100
|
-
function setAvailableModels(provider, models) {
|
|
3101
|
-
availableModelsProvider = provider;
|
|
3028
|
+
function setAvailableModels(models) {
|
|
3102
3029
|
availableModels = models;
|
|
3103
3030
|
}
|
|
3104
3031
|
|
|
3105
3032
|
function clearAvailableModels() {
|
|
3106
3033
|
availableModels = [];
|
|
3107
|
-
availableModelsProvider = null;
|
|
3108
3034
|
}
|
|
3109
3035
|
|
|
3110
3036
|
/**
|
|
@@ -3391,76 +3317,8 @@ function renderAllModelComboboxLists(selectedId = currentModel) {
|
|
|
3391
3317
|
/** @type {AgentPickerInstance[]} */
|
|
3392
3318
|
const agentPickers = [];
|
|
3393
3319
|
|
|
3394
|
-
const PROVIDER_UNAVAILABLE_TOOLTIP =
|
|
3395
|
-
'Not configured — add credentials in Settings → Authentication';
|
|
3396
|
-
|
|
3397
|
-
/**
|
|
3398
|
-
* @param {'claude' | 'openrouter'} provider
|
|
3399
|
-
* @param {typeof appConfig} [cfg]
|
|
3400
|
-
*/
|
|
3401
|
-
function isLlmProviderAvailable(provider, cfg = appConfig) {
|
|
3402
|
-
if (cfg.stubAgent) return true;
|
|
3403
|
-
if (provider === 'openrouter') return cfg.openrouterAuthOk === true;
|
|
3404
|
-
return cfg.claudeAuthOk === true;
|
|
3405
|
-
}
|
|
3406
|
-
|
|
3407
|
-
/**
|
|
3408
|
-
* Disable provider tabs and select options when auth is missing.
|
|
3409
|
-
* @param {typeof appConfig} [cfg]
|
|
3410
|
-
*/
|
|
3411
|
-
function syncLlmProviderAvailability(cfg = appConfig) {
|
|
3412
|
-
for (const ap of agentPickers) {
|
|
3413
|
-
for (const tab of ap.tabs) {
|
|
3414
|
-
const provider =
|
|
3415
|
-
tab.dataset.provider === 'openrouter' ? 'openrouter' : 'claude';
|
|
3416
|
-
const available = isLlmProviderAvailable(provider, cfg);
|
|
3417
|
-
tab.disabled = !available;
|
|
3418
|
-
tab.classList.toggle('is-disabled', !available);
|
|
3419
|
-
tab.setAttribute('aria-disabled', available ? 'false' : 'true');
|
|
3420
|
-
if (available) {
|
|
3421
|
-
tab.removeAttribute('title');
|
|
3422
|
-
} else {
|
|
3423
|
-
tab.title = PROVIDER_UNAVAILABLE_TOOLTIP;
|
|
3424
|
-
}
|
|
3425
|
-
}
|
|
3426
|
-
}
|
|
3427
|
-
|
|
3428
|
-
for (const select of [els.settingsLlmProvider, els.overviewLlmProvider]) {
|
|
3429
|
-
if (!select) continue;
|
|
3430
|
-
for (const opt of select.options) {
|
|
3431
|
-
const provider = opt.value === 'openrouter' ? 'openrouter' : 'claude';
|
|
3432
|
-
const available = isLlmProviderAvailable(provider, cfg);
|
|
3433
|
-
opt.disabled = !available;
|
|
3434
|
-
if (available) {
|
|
3435
|
-
opt.removeAttribute('title');
|
|
3436
|
-
} else {
|
|
3437
|
-
opt.title = PROVIDER_UNAVAILABLE_TOOLTIP;
|
|
3438
|
-
}
|
|
3439
|
-
}
|
|
3440
|
-
}
|
|
3441
|
-
}
|
|
3442
|
-
|
|
3443
|
-
/**
|
|
3444
|
-
* @param {'claude' | 'openrouter'} provider
|
|
3445
|
-
*/
|
|
3446
|
-
function syncAgentPickerTabs(provider) {
|
|
3447
|
-
const value = provider === 'openrouter' ? 'openrouter' : 'claude';
|
|
3448
|
-
for (const ap of agentPickers) {
|
|
3449
|
-
for (const tab of ap.tabs) {
|
|
3450
|
-
const active = tab.dataset.provider === value;
|
|
3451
|
-
tab.classList.toggle('is-active', active);
|
|
3452
|
-
tab.setAttribute('aria-selected', active ? 'true' : 'false');
|
|
3453
|
-
}
|
|
3454
|
-
if (ap.iconEl) {
|
|
3455
|
-
ap.iconEl.classList.toggle('agent-pill-icon--claude', value === 'claude');
|
|
3456
|
-
ap.iconEl.classList.toggle('agent-pill-icon--openrouter', value === 'openrouter');
|
|
3457
|
-
}
|
|
3458
|
-
}
|
|
3459
|
-
}
|
|
3460
|
-
|
|
3461
3320
|
function syncAgentPickerDisplay(selectedId = currentModel) {
|
|
3462
|
-
const
|
|
3463
|
-
const text = agentDisplayText(provider, selectedId);
|
|
3321
|
+
const text = agentDisplayText(selectedId);
|
|
3464
3322
|
for (const ap of agentPickers) {
|
|
3465
3323
|
ap.valueEl.textContent = text;
|
|
3466
3324
|
ap.valueEl.classList.toggle('is-empty', isNoModelSelection(selectedId));
|
|
@@ -3468,7 +3326,6 @@ function syncAgentPickerDisplay(selectedId = currentModel) {
|
|
|
3468
3326
|
ap.hiddenInput.value = isNoModelSelection(selectedId) ? NO_MODEL : selectedId;
|
|
3469
3327
|
}
|
|
3470
3328
|
}
|
|
3471
|
-
syncAgentPickerTabs(provider);
|
|
3472
3329
|
}
|
|
3473
3330
|
|
|
3474
3331
|
/**
|
|
@@ -3492,7 +3349,6 @@ function setAgentPickerOpen(instance, open) {
|
|
|
3492
3349
|
if (open) {
|
|
3493
3350
|
instance.activeIndex = -1;
|
|
3494
3351
|
instance.search.value = '';
|
|
3495
|
-
syncAgentPickerTabs(currentLlmProviderSelection());
|
|
3496
3352
|
renderAgentPickerModelList(instance, currentModel);
|
|
3497
3353
|
focusWithoutScroll(instance.search);
|
|
3498
3354
|
} else {
|
|
@@ -3629,19 +3485,6 @@ function initAgentPicker(opts) {
|
|
|
3629
3485
|
}
|
|
3630
3486
|
});
|
|
3631
3487
|
|
|
3632
|
-
for (const tab of instance.tabs) {
|
|
3633
|
-
tab.addEventListener('click', () => {
|
|
3634
|
-
if (tab.disabled) return;
|
|
3635
|
-
const provider =
|
|
3636
|
-
tab.dataset.provider === 'openrouter' ? 'openrouter' : 'claude';
|
|
3637
|
-
if (!isLlmProviderAvailable(provider)) return;
|
|
3638
|
-
if (provider === currentLlmProviderSelection()) return;
|
|
3639
|
-
syncLlmProviderSelects(provider);
|
|
3640
|
-
syncAgentPickerTabs(provider);
|
|
3641
|
-
void onLlmProviderChange(provider);
|
|
3642
|
-
});
|
|
3643
|
-
}
|
|
3644
|
-
|
|
3645
3488
|
return instance;
|
|
3646
3489
|
}
|
|
3647
3490
|
|
|
@@ -3674,33 +3517,10 @@ function reconcileModelForProvider(models, candidate = currentModel, storedCandi
|
|
|
3674
3517
|
return NO_MODEL;
|
|
3675
3518
|
}
|
|
3676
3519
|
|
|
3677
|
-
/**
|
|
3678
|
-
* @param {'claude' | 'openrouter'} provider
|
|
3679
|
-
* @returns {string | undefined}
|
|
3680
|
-
*/
|
|
3681
|
-
function lastModelForProvider(provider) {
|
|
3682
|
-
const stored = appConfig.lastModelsByProvider?.[provider];
|
|
3683
|
-
return typeof stored === 'string' && !isNoModelSelection(stored) ? stored : undefined;
|
|
3684
|
-
}
|
|
3685
|
-
|
|
3686
3520
|
/**
|
|
3687
3521
|
* @returns {Array<{id:string,label?:string,name?:string}>}
|
|
3688
3522
|
*/
|
|
3689
|
-
function
|
|
3690
|
-
if (provider === 'openrouter') {
|
|
3691
|
-
return [
|
|
3692
|
-
{ id: 'anthropic/claude-sonnet-4', label: 'Claude Sonnet 4 (Anthropic)', agentCompatible: true },
|
|
3693
|
-
{ id: 'anthropic/claude-opus-4', label: 'Claude Opus 4 (Anthropic)', agentCompatible: true },
|
|
3694
|
-
{ id: 'anthropic/claude-3.5-sonnet', label: 'Claude 3.5 Sonnet (Anthropic)', agentCompatible: true },
|
|
3695
|
-
{ id: 'anthropic/claude-3.7-sonnet', label: 'Claude 3.7 Sonnet (Anthropic)', agentCompatible: true },
|
|
3696
|
-
{ id: 'openai/gpt-4o', label: 'GPT-4o (OpenAI)', agentCompatible: false },
|
|
3697
|
-
{ id: 'openai/gpt-4o-mini', label: 'GPT-4o Mini (OpenAI)', agentCompatible: false },
|
|
3698
|
-
{ id: 'google/gemini-2.5-pro-preview', label: 'Gemini 2.5 Pro Preview (Google)', agentCompatible: false },
|
|
3699
|
-
{ id: 'qwen/qwen3-235b-a22b', label: 'Qwen3 235B (Qwen)', agentCompatible: false },
|
|
3700
|
-
{ id: 'deepseek/deepseek-chat-v3-0324', label: 'DeepSeek Chat V3 (DeepSeek)', agentCompatible: false },
|
|
3701
|
-
{ id: 'meta-llama/llama-3.3-70b-instruct', label: 'Llama 3.3 70B Instruct (Meta)', agentCompatible: false },
|
|
3702
|
-
];
|
|
3703
|
-
}
|
|
3523
|
+
function defaultModels() {
|
|
3704
3524
|
return [
|
|
3705
3525
|
{ id: 'claude-sonnet-5', label: 'Sonnet 5' },
|
|
3706
3526
|
{ id: 'claude-opus-5', label: 'Opus 5' },
|
|
@@ -3712,28 +3532,18 @@ function defaultModelsForProvider(provider = 'claude') {
|
|
|
3712
3532
|
}
|
|
3713
3533
|
|
|
3714
3534
|
/**
|
|
3715
|
-
* @param {'anthropic' | '
|
|
3716
|
-
* @param {'claude' | 'openrouter'} [provider]
|
|
3535
|
+
* @param {'anthropic' | 'fallback' | string | undefined} source
|
|
3717
3536
|
*/
|
|
3718
|
-
function updateModelSourceHint(source
|
|
3537
|
+
function updateModelSourceHint(source) {
|
|
3719
3538
|
if (!els.settingsModelHint) return;
|
|
3720
|
-
const name = provider === 'openrouter' ? 'OpenRouter' : 'Claude';
|
|
3721
3539
|
if (source === 'anthropic') {
|
|
3722
3540
|
modelSourceHintBase =
|
|
3723
|
-
|
|
3724
|
-
} else if (source === 'openrouter') {
|
|
3725
|
-
modelSourceHintBase =
|
|
3726
|
-
'OpenRouter models for agent runs. Full catalog from OpenRouter Models API. Agent jobs require anthropic/* models (marked “Agent” in the list).';
|
|
3541
|
+
'Claude model for agent runs. List loaded from Anthropic Models API.';
|
|
3727
3542
|
} else if (source === 'fallback') {
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
'OpenRouter models for agent runs. Showing curated catalog (live list unavailable — set OPENROUTER_API_KEY in Authentication). Agent jobs require anthropic/* models.';
|
|
3731
|
-
} else {
|
|
3732
|
-
modelSourceHintBase =
|
|
3733
|
-
'Claude model for agent runs. Showing curated Claude Code models (live list unavailable — set an API key or use claude auth login).';
|
|
3734
|
-
}
|
|
3543
|
+
modelSourceHintBase =
|
|
3544
|
+
'Claude model for agent runs. Showing curated Claude Code models (live list unavailable — set an API key or use claude auth login).';
|
|
3735
3545
|
} else {
|
|
3736
|
-
modelSourceHintBase =
|
|
3546
|
+
modelSourceHintBase = 'Claude model for agent runs.';
|
|
3737
3547
|
}
|
|
3738
3548
|
refreshModelSourceHint();
|
|
3739
3549
|
}
|
|
@@ -3759,92 +3569,55 @@ function modelMatchesSearchQuery(model, query) {
|
|
|
3759
3569
|
.some((part) => String(part).toLowerCase().includes(q));
|
|
3760
3570
|
}
|
|
3761
3571
|
|
|
3762
|
-
/**
|
|
3763
|
-
* @returns {'claude' | 'openrouter'}
|
|
3764
|
-
*/
|
|
3765
|
-
function currentLlmProviderSelection() {
|
|
3766
|
-
const fromOverview = els.overviewLlmProvider?.value;
|
|
3767
|
-
if (fromOverview === 'openrouter' || fromOverview === 'claude') return fromOverview;
|
|
3768
|
-
const fromForm = els.settingsLlmProvider?.value;
|
|
3769
|
-
if (fromForm === 'openrouter' || fromForm === 'claude') return fromForm;
|
|
3770
|
-
return appConfig.llmProvider === 'openrouter' ? 'openrouter' : 'claude';
|
|
3771
|
-
}
|
|
3772
|
-
|
|
3773
|
-
/**
|
|
3774
|
-
* @param {'claude' | 'openrouter'} provider
|
|
3775
|
-
*/
|
|
3776
|
-
function syncLlmProviderSelects(provider) {
|
|
3777
|
-
const value = provider === 'openrouter' ? 'openrouter' : 'claude';
|
|
3778
|
-
if (els.settingsLlmProvider) els.settingsLlmProvider.value = value;
|
|
3779
|
-
if (els.overviewLlmProvider) els.overviewLlmProvider.value = value;
|
|
3780
|
-
syncAgentPickerTabs(value);
|
|
3781
|
-
if (els.settingsLlmProviderHint) {
|
|
3782
|
-
els.settingsLlmProviderHint.textContent =
|
|
3783
|
-
value === 'openrouter'
|
|
3784
|
-
? 'Agent runs route through OpenRouter (requires OPENROUTER_API_KEY). All models are listed; agent jobs need anthropic/* models.'
|
|
3785
|
-
: 'Agent runs use Claude via Anthropic API, OAuth token, or claude auth login.';
|
|
3786
|
-
}
|
|
3787
|
-
}
|
|
3788
|
-
|
|
3789
|
-
/**
|
|
3790
|
-
* @param {{ models?: Array<{id:string,label?:string,name?:string}>, selected?: string, source?: string, provider?: string }} data
|
|
3791
|
-
*/
|
|
3792
3572
|
function populateSettingsModels(data) {
|
|
3793
|
-
const provider = data?.provider || currentLlmProviderSelection();
|
|
3794
|
-
const cached = getAvailableModels(provider);
|
|
3795
3573
|
let models = Array.isArray(data?.models)
|
|
3796
3574
|
? data.models
|
|
3797
|
-
: data?.
|
|
3575
|
+
: data?.reconcile
|
|
3798
3576
|
? []
|
|
3799
|
-
:
|
|
3800
|
-
?
|
|
3801
|
-
:
|
|
3577
|
+
: getAvailableModels().length
|
|
3578
|
+
? getAvailableModels()
|
|
3579
|
+
: defaultModels();
|
|
3802
3580
|
if (!models.length && data?.source) {
|
|
3803
|
-
models =
|
|
3581
|
+
models = defaultModels();
|
|
3804
3582
|
}
|
|
3805
|
-
setAvailableModels(
|
|
3583
|
+
setAvailableModels(models);
|
|
3806
3584
|
|
|
3807
|
-
if (data?.
|
|
3585
|
+
if (data?.reconcile) {
|
|
3808
3586
|
currentModel = reconcileModelForProvider(
|
|
3809
3587
|
models,
|
|
3810
|
-
data.reconcileCandidate
|
|
3811
|
-
lastModelForProvider(provider)
|
|
3588
|
+
data.reconcileCandidate
|
|
3812
3589
|
);
|
|
3813
3590
|
} else if (data?.selected != null) {
|
|
3814
3591
|
const next = isNoModelSelection(data.selected) ? NO_MODEL : data.selected;
|
|
3815
|
-
currentModel = reconcileModelForProvider(models, next
|
|
3592
|
+
currentModel = reconcileModelForProvider(models, next);
|
|
3816
3593
|
}
|
|
3817
|
-
if (data?.source) updateModelSourceHint(data.source
|
|
3594
|
+
if (data?.source) updateModelSourceHint(data.source);
|
|
3818
3595
|
|
|
3819
3596
|
syncAllModelComboboxValues(currentModel);
|
|
3820
3597
|
renderAllModelComboboxLists(currentModel);
|
|
3821
3598
|
updateModelLabel();
|
|
3822
3599
|
}
|
|
3823
3600
|
|
|
3824
|
-
function modelsForFetchFallback(
|
|
3825
|
-
|
|
3826
|
-
if (availableModelsProvider === provider && cached.length) return cached;
|
|
3827
|
-
return defaultModelsForProvider(provider);
|
|
3601
|
+
function modelsForFetchFallback() {
|
|
3602
|
+
return getAvailableModels().length ? getAvailableModels() : defaultModels();
|
|
3828
3603
|
}
|
|
3829
3604
|
|
|
3830
3605
|
async function fetchModels(opts = {}) {
|
|
3831
3606
|
const refresh = opts.refresh === true;
|
|
3832
|
-
const
|
|
3833
|
-
const reconcileProvider = opts.reconcileProvider === true;
|
|
3607
|
+
const reconcile = opts.reconcile === true;
|
|
3834
3608
|
const reconcileCandidate = opts.reconcileCandidate;
|
|
3835
|
-
if (
|
|
3609
|
+
if (reconcile) {
|
|
3836
3610
|
clearAvailableModels();
|
|
3837
3611
|
}
|
|
3838
|
-
const query = new URLSearchParams(
|
|
3612
|
+
const query = new URLSearchParams();
|
|
3839
3613
|
if (refresh) query.set('refresh', '1');
|
|
3840
3614
|
try {
|
|
3841
3615
|
const res = await fetch(`/api/models?${query.toString()}`);
|
|
3842
3616
|
if (!res.ok) {
|
|
3843
3617
|
populateSettingsModels({
|
|
3844
|
-
models: modelsForFetchFallback(
|
|
3618
|
+
models: modelsForFetchFallback(),
|
|
3845
3619
|
selected: currentModel,
|
|
3846
|
-
|
|
3847
|
-
reconcileProvider,
|
|
3620
|
+
reconcile,
|
|
3848
3621
|
reconcileCandidate,
|
|
3849
3622
|
});
|
|
3850
3623
|
return;
|
|
@@ -3852,17 +3625,15 @@ async function fetchModels(opts = {}) {
|
|
|
3852
3625
|
const data = await readJson(res);
|
|
3853
3626
|
populateSettingsModels({
|
|
3854
3627
|
...data,
|
|
3855
|
-
|
|
3856
|
-
reconcileProvider,
|
|
3628
|
+
reconcile,
|
|
3857
3629
|
reconcileCandidate,
|
|
3858
3630
|
});
|
|
3859
3631
|
} catch (err) {
|
|
3860
3632
|
console.error('Failed to fetch models:', err);
|
|
3861
3633
|
populateSettingsModels({
|
|
3862
|
-
models: modelsForFetchFallback(
|
|
3634
|
+
models: modelsForFetchFallback(),
|
|
3863
3635
|
selected: currentModel,
|
|
3864
|
-
|
|
3865
|
-
reconcileProvider,
|
|
3636
|
+
reconcile,
|
|
3866
3637
|
reconcileCandidate,
|
|
3867
3638
|
});
|
|
3868
3639
|
}
|
|
@@ -3982,13 +3753,6 @@ function claudeStatusText(cfg) {
|
|
|
3982
3753
|
}
|
|
3983
3754
|
}
|
|
3984
3755
|
|
|
3985
|
-
function openrouterStatusText(cfg) {
|
|
3986
|
-
if (cfg.openrouterAuthOk) {
|
|
3987
|
-
return 'Authenticated via OPENROUTER_API_KEY';
|
|
3988
|
-
}
|
|
3989
|
-
return 'Not authenticated — add an OpenRouter API key below';
|
|
3990
|
-
}
|
|
3991
|
-
|
|
3992
3756
|
function fillAuthSettings(cfg) {
|
|
3993
3757
|
if (els.settingsGhStatus) {
|
|
3994
3758
|
els.settingsGhStatus.textContent = ghStatusText(cfg);
|
|
@@ -4047,25 +3811,6 @@ function fillAuthSettings(cfg) {
|
|
|
4047
3811
|
? 'OAuth token stored in <code>.acdev/.env</code> as <code>CLAUDE_CODE_OAUTH_TOKEN</code>. Settings cannot complete browser OAuth — that still needs <code>claude auth login</code> on this host.'
|
|
4048
3812
|
: 'From <code>claude setup-token</code> for non-interactive subscription auth. Stored as <code>CLAUDE_CODE_OAUTH_TOKEN</code>. Settings cannot complete browser OAuth — that still needs <code>claude auth login</code> on this host.';
|
|
4049
3813
|
}
|
|
4050
|
-
|
|
4051
|
-
if (els.settingsOpenrouterStatus) {
|
|
4052
|
-
els.settingsOpenrouterStatus.textContent = openrouterStatusText(cfg);
|
|
4053
|
-
els.settingsOpenrouterStatus.className = cfg.openrouterAuthOk
|
|
4054
|
-
? 'auth-status ok'
|
|
4055
|
-
: 'auth-status err';
|
|
4056
|
-
}
|
|
4057
|
-
fillSecretInput(
|
|
4058
|
-
els.settingsOpenrouterKey,
|
|
4059
|
-
els.settingsOpenrouterKeyClear,
|
|
4060
|
-
Boolean(cfg.openrouterApiKeySet),
|
|
4061
|
-
cfg.openrouterApiKeyMasked,
|
|
4062
|
-
'Paste an OpenRouter API key'
|
|
4063
|
-
);
|
|
4064
|
-
if (els.settingsOpenrouterKeyHint) {
|
|
4065
|
-
els.settingsOpenrouterKeyHint.innerHTML = cfg.openrouterApiKeySet
|
|
4066
|
-
? 'API key stored in <code>.acdev/.env</code> as <code>OPENROUTER_API_KEY</code> (not committed).'
|
|
4067
|
-
: 'Required when LLM provider is OpenRouter. Get a key at <a href="https://openrouter.ai/keys" target="_blank" rel="noopener noreferrer">openrouter.ai/keys</a>. Stored as <code>OPENROUTER_API_KEY</code> in <code>.acdev/.env</code>.';
|
|
4068
|
-
}
|
|
4069
3814
|
}
|
|
4070
3815
|
|
|
4071
3816
|
function fillSettingsForm(cfg) {
|
|
@@ -4145,23 +3890,17 @@ function fillSettingsForm(cfg) {
|
|
|
4145
3890
|
els.settingsBaseBranch.value = cfg.baseBranch || '';
|
|
4146
3891
|
}
|
|
4147
3892
|
|
|
4148
|
-
if (els.settingsLlmProvider) {
|
|
4149
|
-
syncLlmProviderSelects(cfg.llmProvider === 'openrouter' ? 'openrouter' : 'claude');
|
|
4150
|
-
}
|
|
4151
|
-
|
|
4152
3893
|
if (els.settingsModelPicker) {
|
|
4153
|
-
|
|
4154
|
-
if (getAvailableModels(provider).length) {
|
|
3894
|
+
if (getAvailableModels().length) {
|
|
4155
3895
|
populateSettingsModels({
|
|
4156
|
-
models: getAvailableModels(
|
|
3896
|
+
models: getAvailableModels(),
|
|
4157
3897
|
selected: cfg.model ?? currentModel,
|
|
4158
|
-
provider,
|
|
4159
3898
|
});
|
|
4160
3899
|
} else if (cfg.model != null) {
|
|
4161
3900
|
currentModel = isNoModelSelection(cfg.model) ? NO_MODEL : cfg.model;
|
|
4162
3901
|
syncModelPickerValue(currentModel);
|
|
4163
3902
|
}
|
|
4164
|
-
void fetchModels(
|
|
3903
|
+
void fetchModels();
|
|
4165
3904
|
}
|
|
4166
3905
|
|
|
4167
3906
|
if (els.settingsMaxTurns) {
|
|
@@ -4354,16 +4093,14 @@ function setOverviewLlmFeedback(message, kind = 'ok') {
|
|
|
4354
4093
|
*/
|
|
4355
4094
|
function fillOverviewLlmControls(cfg) {
|
|
4356
4095
|
if (!cfg) return;
|
|
4357
|
-
const provider = cfg.llmProvider === 'openrouter' ? 'openrouter' : 'claude';
|
|
4358
|
-
syncLlmProviderSelects(provider);
|
|
4359
4096
|
syncAllModelComboboxValues(currentModel);
|
|
4360
4097
|
}
|
|
4361
4098
|
|
|
4362
4099
|
let llmConfigSaving = false;
|
|
4363
4100
|
|
|
4364
4101
|
/**
|
|
4365
|
-
* PATCH
|
|
4366
|
-
* @param {{
|
|
4102
|
+
* PATCH model immediately (Overview switcher).
|
|
4103
|
+
* @param {{ model?: string }} patch
|
|
4367
4104
|
*/
|
|
4368
4105
|
async function patchLlmConfig(patch) {
|
|
4369
4106
|
if (llmConfigSaving) return;
|
|
@@ -4385,7 +4122,7 @@ async function patchLlmConfig(patch) {
|
|
|
4385
4122
|
if (currentView === 'settings') {
|
|
4386
4123
|
fillSettingsForm(data);
|
|
4387
4124
|
}
|
|
4388
|
-
setOverviewLlmFeedback('
|
|
4125
|
+
setOverviewLlmFeedback('Model updated. Next jobs will use this value.', 'ok');
|
|
4389
4126
|
window.setTimeout(() => setOverviewLlmFeedback(null), 2500);
|
|
4390
4127
|
} catch (err) {
|
|
4391
4128
|
setOverviewLlmFeedback(err.message || 'Update failed', 'error');
|
|
@@ -4394,29 +4131,6 @@ async function patchLlmConfig(patch) {
|
|
|
4394
4131
|
}
|
|
4395
4132
|
}
|
|
4396
4133
|
|
|
4397
|
-
/**
|
|
4398
|
-
* @param {'claude' | 'openrouter'} provider
|
|
4399
|
-
*/
|
|
4400
|
-
async function onLlmProviderChange(provider) {
|
|
4401
|
-
const previousModel = currentModel;
|
|
4402
|
-
syncLlmProviderSelects(provider);
|
|
4403
|
-
clearAvailableModels();
|
|
4404
|
-
currentModel = NO_MODEL;
|
|
4405
|
-
syncAllModelComboboxValues(currentModel);
|
|
4406
|
-
renderAllModelComboboxLists(currentModel);
|
|
4407
|
-
updateModelLabel();
|
|
4408
|
-
await fetchModels({
|
|
4409
|
-
refresh: true,
|
|
4410
|
-
provider,
|
|
4411
|
-
reconcileProvider: true,
|
|
4412
|
-
reconcileCandidate: previousModel,
|
|
4413
|
-
});
|
|
4414
|
-
await patchLlmConfig({
|
|
4415
|
-
llmProvider: provider,
|
|
4416
|
-
model: currentModel,
|
|
4417
|
-
});
|
|
4418
|
-
}
|
|
4419
|
-
|
|
4420
4134
|
function applyConfigSnapshot(data) {
|
|
4421
4135
|
appConfig = data || {};
|
|
4422
4136
|
if (data?.model != null) {
|
|
@@ -4432,7 +4146,6 @@ function applyConfigSnapshot(data) {
|
|
|
4432
4146
|
if (els.repoBranch) {
|
|
4433
4147
|
els.repoBranch.textContent = data?.baseBranch || '—';
|
|
4434
4148
|
}
|
|
4435
|
-
syncLlmProviderAvailability(appConfig);
|
|
4436
4149
|
if (currentView === 'settings') {
|
|
4437
4150
|
fillSettingsForm(appConfig);
|
|
4438
4151
|
}
|
|
@@ -4468,7 +4181,6 @@ function readSettingsForm() {
|
|
|
4468
4181
|
const patch = {
|
|
4469
4182
|
baseBranch,
|
|
4470
4183
|
model,
|
|
4471
|
-
llmProvider: els.settingsLlmProvider?.value === 'openrouter' ? 'openrouter' : 'claude',
|
|
4472
4184
|
maxAgentTurns,
|
|
4473
4185
|
agentTimeoutMs: Math.round(timeoutMinutes * 60_000),
|
|
4474
4186
|
testCommand: testTrimmed === '' ? null : testTrimmed,
|
|
@@ -4482,8 +4194,6 @@ function readSettingsForm() {
|
|
|
4482
4194
|
if (anthropicApiKey !== undefined) patch.anthropicApiKey = anthropicApiKey;
|
|
4483
4195
|
const claudeOauthToken = secretPatchValue(els.settingsClaudeOauth);
|
|
4484
4196
|
if (claudeOauthToken !== undefined) patch.claudeOauthToken = claudeOauthToken;
|
|
4485
|
-
const openrouterApiKey = secretPatchValue(els.settingsOpenrouterKey);
|
|
4486
|
-
if (openrouterApiKey !== undefined) patch.openrouterApiKey = openrouterApiKey;
|
|
4487
4197
|
|
|
4488
4198
|
if (ticketSource === 'jira' || els.settingsJiraBaseUrl?.value) {
|
|
4489
4199
|
patch.jiraBaseUrl = els.settingsJiraBaseUrl?.value?.trim() || '';
|
|
@@ -4651,12 +4361,6 @@ bindSecretClear(
|
|
|
4651
4361
|
els.settingsClaudeOauthHint,
|
|
4652
4362
|
'Saved CLAUDE_CODE_OAUTH_TOKEN will be removed when you click Save settings.'
|
|
4653
4363
|
);
|
|
4654
|
-
bindSecretClear(
|
|
4655
|
-
els.settingsOpenrouterKeyClear,
|
|
4656
|
-
els.settingsOpenrouterKey,
|
|
4657
|
-
els.settingsOpenrouterKeyHint,
|
|
4658
|
-
'Saved OPENROUTER_API_KEY will be removed when you click Save settings.'
|
|
4659
|
-
);
|
|
4660
4364
|
|
|
4661
4365
|
|
|
4662
4366
|
document.addEventListener('click', (ev) => {
|
|
@@ -4689,38 +4393,6 @@ document.addEventListener('keydown', (ev) => {
|
|
|
4689
4393
|
}
|
|
4690
4394
|
});
|
|
4691
4395
|
|
|
4692
|
-
els.settingsLlmProvider?.addEventListener('change', () => {
|
|
4693
|
-
const provider = currentLlmProviderSelection();
|
|
4694
|
-
if (!isLlmProviderAvailable(provider)) {
|
|
4695
|
-
const fallback =
|
|
4696
|
-
appConfig.llmProvider === 'openrouter' ? 'openrouter' : 'claude';
|
|
4697
|
-
syncLlmProviderSelects(fallback);
|
|
4698
|
-
return;
|
|
4699
|
-
}
|
|
4700
|
-
for (const cb of modelComboboxes) {
|
|
4701
|
-
setModelComboboxOpen(cb, false);
|
|
4702
|
-
}
|
|
4703
|
-
void onLlmProviderChange(provider);
|
|
4704
|
-
});
|
|
4705
|
-
|
|
4706
|
-
els.overviewLlmProvider?.addEventListener('change', () => {
|
|
4707
|
-
const provider =
|
|
4708
|
-
els.overviewLlmProvider.value === 'openrouter' ? 'openrouter' : 'claude';
|
|
4709
|
-
if (!isLlmProviderAvailable(provider)) {
|
|
4710
|
-
const fallback =
|
|
4711
|
-
appConfig.llmProvider === 'openrouter' ? 'openrouter' : 'claude';
|
|
4712
|
-
syncLlmProviderSelects(fallback);
|
|
4713
|
-
return;
|
|
4714
|
-
}
|
|
4715
|
-
for (const cb of modelComboboxes) {
|
|
4716
|
-
setModelComboboxOpen(cb, false);
|
|
4717
|
-
}
|
|
4718
|
-
for (const ap of agentPickers) {
|
|
4719
|
-
setAgentPickerOpen(ap, false);
|
|
4720
|
-
}
|
|
4721
|
-
void onLlmProviderChange(provider);
|
|
4722
|
-
});
|
|
4723
|
-
|
|
4724
4396
|
if (
|
|
4725
4397
|
els.settingsModelPicker &&
|
|
4726
4398
|
els.settingsModelTrigger &&
|
|
@@ -4763,9 +4435,6 @@ if (
|
|
|
4763
4435
|
list: els.overviewAgentModelList,
|
|
4764
4436
|
valueEl: els.overviewAgentValue,
|
|
4765
4437
|
iconEl: els.overviewAgentIcon,
|
|
4766
|
-
tabs: [
|
|
4767
|
-
...(els.overviewAgentPanel.querySelectorAll('.agent-picker-tab') || []),
|
|
4768
|
-
],
|
|
4769
4438
|
hiddenInput: els.overviewModel,
|
|
4770
4439
|
optionIdPrefix: 'overview-agent-model-opt',
|
|
4771
4440
|
onSelect: (modelId) => {
|
|
@@ -4784,18 +4453,32 @@ els.issueUrls?.addEventListener('keydown', (ev) => {
|
|
|
4784
4453
|
els.addBtn?.click();
|
|
4785
4454
|
});
|
|
4786
4455
|
|
|
4456
|
+
els.preferredBranch?.addEventListener('keydown', (ev) => {
|
|
4457
|
+
if (ev.key !== 'Enter' || ev.shiftKey || ev.metaKey || ev.ctrlKey || ev.altKey) return;
|
|
4458
|
+
ev.preventDefault();
|
|
4459
|
+
els.addBtn?.click();
|
|
4460
|
+
});
|
|
4461
|
+
|
|
4787
4462
|
els.addBtn.addEventListener('click', async () => {
|
|
4788
4463
|
els.enqueueFeedback.classList.add('hidden');
|
|
4789
4464
|
const text = els.issueUrls.value.trim();
|
|
4790
4465
|
if (!text) return;
|
|
4791
4466
|
|
|
4792
4467
|
const urls = splitIssueUrls(text);
|
|
4468
|
+
const branchName = els.preferredBranch?.value?.trim() || '';
|
|
4469
|
+
// #region agent log
|
|
4470
|
+
fetch('http://127.0.0.1:7258/ingest/377aa5e2-15ea-4447-a68b-7ce215882bc3',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'473a78'},body:JSON.stringify({sessionId:'473a78',runId:'pre-fix',hypothesisId:'B',location:'public/app.js:addBtn',message:'enqueue submit',data:{hasCustom:Boolean(branchName),branchName:branchName||null,urlCount:urls.length},timestamp:Date.now()})}).catch(()=>{});
|
|
4471
|
+
// #endregion
|
|
4793
4472
|
|
|
4794
4473
|
try {
|
|
4795
4474
|
const res = await fetch('/api/issues', {
|
|
4796
4475
|
method: 'POST',
|
|
4797
4476
|
headers: { 'Content-Type': 'application/json' },
|
|
4798
|
-
body: JSON.stringify({
|
|
4477
|
+
body: JSON.stringify({
|
|
4478
|
+
urls,
|
|
4479
|
+
ticketSource,
|
|
4480
|
+
...(branchName ? { branchName } : {}),
|
|
4481
|
+
}),
|
|
4799
4482
|
});
|
|
4800
4483
|
const data = await res.json();
|
|
4801
4484
|
if (!res.ok) {
|
|
@@ -4806,6 +4489,7 @@ els.addBtn.addEventListener('click', async () => {
|
|
|
4806
4489
|
return;
|
|
4807
4490
|
}
|
|
4808
4491
|
els.issueUrls.value = '';
|
|
4492
|
+
if (els.preferredBranch) els.preferredBranch.value = '';
|
|
4809
4493
|
const parts = [];
|
|
4810
4494
|
if (data.jobs?.length) parts.push(`Added ${data.jobs.length}`);
|
|
4811
4495
|
if (data.skipped?.length) {
|
|
@@ -5096,9 +4780,8 @@ els.logJobFilter.addEventListener('change', () => {
|
|
|
5096
4780
|
|
|
5097
4781
|
applyTheme(loadTheme());
|
|
5098
4782
|
void (async () => {
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
await fetchModels({ provider });
|
|
4783
|
+
await fetchConfig();
|
|
4784
|
+
await fetchModels();
|
|
5102
4785
|
})();
|
|
5103
4786
|
fetchJobs();
|
|
5104
4787
|
setInterval(fetchJobs, 3000);
|