@kaitranntt/ccs 7.63.1-dev.2 → 7.64.0-dev.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 (136) hide show
  1. package/README.md +2 -0
  2. package/dist/api/services/profile-types.d.ts +17 -0
  3. package/dist/api/services/profile-types.d.ts.map +1 -1
  4. package/dist/ccs.js +61 -6
  5. package/dist/ccs.js.map +1 -1
  6. package/dist/cliproxy/executor/env-resolver.d.ts +27 -0
  7. package/dist/cliproxy/executor/env-resolver.d.ts.map +1 -1
  8. package/dist/cliproxy/executor/env-resolver.js +87 -3
  9. package/dist/cliproxy/executor/env-resolver.js.map +1 -1
  10. package/dist/cliproxy/executor/index.d.ts.map +1 -1
  11. package/dist/cliproxy/executor/index.js +30 -0
  12. package/dist/cliproxy/executor/index.js.map +1 -1
  13. package/dist/cliproxy/model-catalog.d.ts +6 -0
  14. package/dist/cliproxy/model-catalog.d.ts.map +1 -1
  15. package/dist/cliproxy/model-catalog.js +38 -1
  16. package/dist/cliproxy/model-catalog.js.map +1 -1
  17. package/dist/cliproxy/proxy-config-resolver.d.ts +2 -1
  18. package/dist/cliproxy/proxy-config-resolver.d.ts.map +1 -1
  19. package/dist/cliproxy/proxy-config-resolver.js +1 -0
  20. package/dist/cliproxy/proxy-config-resolver.js.map +1 -1
  21. package/dist/cliproxy/proxy-target-resolver.d.ts +2 -0
  22. package/dist/cliproxy/proxy-target-resolver.d.ts.map +1 -1
  23. package/dist/cliproxy/proxy-target-resolver.js +3 -0
  24. package/dist/cliproxy/proxy-target-resolver.js.map +1 -1
  25. package/dist/cliproxy/remote-auth-fetcher.d.ts.map +1 -1
  26. package/dist/cliproxy/remote-auth-fetcher.js +89 -8
  27. package/dist/cliproxy/remote-auth-fetcher.js.map +1 -1
  28. package/dist/cliproxy/services/variant-settings.d.ts.map +1 -1
  29. package/dist/cliproxy/services/variant-settings.js +19 -4
  30. package/dist/cliproxy/services/variant-settings.js.map +1 -1
  31. package/dist/cliproxy/types.d.ts +2 -0
  32. package/dist/cliproxy/types.d.ts.map +1 -1
  33. package/dist/commands/config-image-analysis-command.d.ts.map +1 -1
  34. package/dist/commands/config-image-analysis-command.js +87 -1
  35. package/dist/commands/config-image-analysis-command.js.map +1 -1
  36. package/dist/config/unified-config-loader.d.ts.map +1 -1
  37. package/dist/config/unified-config-loader.js +9 -4
  38. package/dist/config/unified-config-loader.js.map +1 -1
  39. package/dist/config/unified-config-types.d.ts +4 -0
  40. package/dist/config/unified-config-types.d.ts.map +1 -1
  41. package/dist/config/unified-config-types.js +4 -2
  42. package/dist/config/unified-config-types.js.map +1 -1
  43. package/dist/copilot/copilot-executor.d.ts +13 -0
  44. package/dist/copilot/copilot-executor.d.ts.map +1 -1
  45. package/dist/copilot/copilot-executor.js +52 -2
  46. package/dist/copilot/copilot-executor.js.map +1 -1
  47. package/dist/management/checks/image-analysis-check.js +1 -1
  48. package/dist/management/checks/image-analysis-check.js.map +1 -1
  49. package/dist/types/config.d.ts +5 -0
  50. package/dist/types/config.d.ts.map +1 -1
  51. package/dist/types/config.js.map +1 -1
  52. package/dist/ui/assets/{accounts-CUUJb7DN.js → accounts-BHEYnq6b.js} +1 -1
  53. package/dist/ui/assets/{alert-dialog-B3irFOWw.js → alert-dialog-D0EFRcfB.js} +1 -1
  54. package/dist/ui/assets/api-DhM3BYXr.js +4 -0
  55. package/dist/ui/assets/{auth-section-DIo3G68G.js → auth-section-DVp8FQGm.js} +1 -1
  56. package/dist/ui/assets/{backups-section-AFO82gIq.js → backups-section-CRo0NZkA.js} +1 -1
  57. package/dist/ui/assets/channels-uZ_9CBqO.js +1 -0
  58. package/dist/ui/assets/checkbox-32DNqW_Q.js +1 -0
  59. package/dist/ui/assets/{claude-extension-BNiAud6L.js → claude-extension-BfXlz5gV.js} +1 -1
  60. package/dist/ui/assets/cliproxy-DjNY9H-U.js +3 -0
  61. package/dist/ui/assets/{cliproxy-ai-providers-C9jXuZcV.js → cliproxy-ai-providers-5SHLMHiy.js} +5 -5
  62. package/dist/ui/assets/{cliproxy-control-panel-DRSjNcrR.js → cliproxy-control-panel-Zax_m1AC.js} +1 -1
  63. package/dist/ui/assets/{codex-DSAAz_k8.js → codex-CRUSpjsu.js} +3 -3
  64. package/dist/ui/assets/{confirm-dialog-Dn7GsgSZ.js → confirm-dialog-DVf5ZmCZ.js} +1 -1
  65. package/dist/ui/assets/copilot-BZrihl_Z.js +3 -0
  66. package/dist/ui/assets/cursor-BP4nbEk_.js +1 -0
  67. package/dist/ui/assets/{droid-C5ArrKfL.js → droid-BG92rdM2.js} +2 -2
  68. package/dist/ui/assets/globalenv-section-Cf6dKgSf.js +1 -0
  69. package/dist/ui/assets/{health-7Wp3Ar7n.js → health-BTy1UZs3.js} +1 -1
  70. package/dist/ui/assets/icons-CeH5899d.js +1 -0
  71. package/dist/ui/assets/index-B6SrL1O-.css +1 -0
  72. package/dist/ui/assets/index-BVeN0dIB.js +1 -0
  73. package/dist/ui/assets/{index-C7OpByHy.js → index-Corv1lSo.js} +30 -30
  74. package/dist/ui/assets/{index-CBwOAp7P.js → index-DHrTq-0n.js} +1 -1
  75. package/dist/ui/assets/index-DuRYaONg.js +1 -0
  76. package/dist/ui/assets/index-N2ZSJurX.js +1 -0
  77. package/dist/ui/assets/index-wg7UtkFv.js +1 -0
  78. package/dist/ui/assets/{masked-input-BlP9jFt5.js → masked-input-DX9bedLy.js} +1 -1
  79. package/dist/ui/assets/{proxy-status-widget-CFyrt5Yw.js → proxy-status-widget-DVDMuZK5.js} +1 -1
  80. package/dist/ui/assets/{radix-ui-Dt3edmE5.js → radix-ui-C98W0NRG.js} +1 -1
  81. package/dist/ui/assets/{raw-json-settings-editor-panel-CfcTOhs4.js → raw-json-settings-editor-panel-Dkt5E6Z_.js} +1 -1
  82. package/dist/ui/assets/{searchable-select-CcCH2Ug3.js → searchable-select-BP3Q1-Yn.js} +1 -1
  83. package/dist/ui/assets/separator-BLGGUlh9.js +1 -0
  84. package/dist/ui/assets/{shared-ByRk7nxS.js → shared-G0XRyLig.js} +1 -1
  85. package/dist/ui/assets/{table-84HxfWMM.js → table-B4lRrWC-.js} +1 -1
  86. package/dist/ui/assets/{tanstack-CkjseTWE.js → tanstack-CfKik0yL.js} +1 -1
  87. package/dist/ui/assets/{updates-BgPdUQoA.js → updates--A2Sdo7N.js} +1 -1
  88. package/dist/ui/index.html +5 -5
  89. package/dist/utils/hooks/get-image-analysis-hook-env.d.ts +3 -2
  90. package/dist/utils/hooks/get-image-analysis-hook-env.d.ts.map +1 -1
  91. package/dist/utils/hooks/get-image-analysis-hook-env.js +15 -6
  92. package/dist/utils/hooks/get-image-analysis-hook-env.js.map +1 -1
  93. package/dist/utils/hooks/image-analysis-backend-resolver.d.ts +53 -0
  94. package/dist/utils/hooks/image-analysis-backend-resolver.d.ts.map +1 -0
  95. package/dist/utils/hooks/image-analysis-backend-resolver.js +376 -0
  96. package/dist/utils/hooks/image-analysis-backend-resolver.js.map +1 -0
  97. package/dist/utils/hooks/image-analysis-runtime-status.d.ts +17 -0
  98. package/dist/utils/hooks/image-analysis-runtime-status.d.ts.map +1 -0
  99. package/dist/utils/hooks/image-analysis-runtime-status.js +132 -0
  100. package/dist/utils/hooks/image-analysis-runtime-status.js.map +1 -0
  101. package/dist/utils/hooks/image-analyzer-profile-hook-injector.d.ts +6 -5
  102. package/dist/utils/hooks/image-analyzer-profile-hook-injector.d.ts.map +1 -1
  103. package/dist/utils/hooks/image-analyzer-profile-hook-injector.js +37 -17
  104. package/dist/utils/hooks/image-analyzer-profile-hook-injector.js.map +1 -1
  105. package/dist/utils/hooks/index.d.ts +2 -0
  106. package/dist/utils/hooks/index.d.ts.map +1 -1
  107. package/dist/utils/hooks/index.js +8 -1
  108. package/dist/utils/hooks/index.js.map +1 -1
  109. package/dist/web-server/index.d.ts.map +1 -1
  110. package/dist/web-server/index.js +6 -1
  111. package/dist/web-server/index.js.map +1 -1
  112. package/dist/web-server/routes/image-analysis-routes.d.ts +3 -0
  113. package/dist/web-server/routes/image-analysis-routes.d.ts.map +1 -0
  114. package/dist/web-server/routes/image-analysis-routes.js +362 -0
  115. package/dist/web-server/routes/image-analysis-routes.js.map +1 -0
  116. package/dist/web-server/routes/index.d.ts.map +1 -1
  117. package/dist/web-server/routes/index.js +2 -0
  118. package/dist/web-server/routes/index.js.map +1 -1
  119. package/dist/web-server/routes/settings-routes.d.ts.map +1 -1
  120. package/dist/web-server/routes/settings-routes.js +67 -5
  121. package/dist/web-server/routes/settings-routes.js.map +1 -1
  122. package/package.json +3 -3
  123. package/dist/ui/assets/api-D-Qqy1yz.js +0 -4
  124. package/dist/ui/assets/channels-BCTKZ7gt.js +0 -1
  125. package/dist/ui/assets/checkbox-jPTeVbcz.js +0 -1
  126. package/dist/ui/assets/cliproxy-CoYvAxcj.js +0 -3
  127. package/dist/ui/assets/copilot-Ddvc59py.js +0 -3
  128. package/dist/ui/assets/cursor-CPLxNsYY.js +0 -1
  129. package/dist/ui/assets/globalenv-section-CddBw-9L.js +0 -1
  130. package/dist/ui/assets/icons-C7Np5k44.js +0 -1
  131. package/dist/ui/assets/index-1GqjYVWP.js +0 -1
  132. package/dist/ui/assets/index-BPIA8mAF.js +0 -1
  133. package/dist/ui/assets/index-BsTZB-Im.css +0 -1
  134. package/dist/ui/assets/index-DSgKWnG8.js +0 -1
  135. package/dist/ui/assets/separator-DRULo1eG.js +0 -1
  136. package/dist/ui/assets/switch-BO_9wSB8.js +0 -1
@@ -0,0 +1,362 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ const express_1 = require("express");
27
+ const fs = __importStar(require("fs"));
28
+ const unified_config_loader_1 = require("../../config/unified-config-loader");
29
+ const provider_capabilities_1 = require("../../cliproxy/provider-capabilities");
30
+ const services_1 = require("../../api/services");
31
+ const auth_middleware_1 = require("../middleware/auth-middleware");
32
+ const helpers_1 = require("../../utils/helpers");
33
+ const config_manager_1 = require("../../utils/config-manager");
34
+ const model_id_normalizer_1 = require("../../cliproxy/model-id-normalizer");
35
+ const hooks_1 = require("../../utils/hooks");
36
+ const image_analyzer_hook_installer_1 = require("../../utils/hooks/image-analyzer-hook-installer");
37
+ const image_analyzer_profile_hook_injector_1 = require("../../utils/hooks/image-analyzer-profile-hook-injector");
38
+ const router = (0, express_1.Router)();
39
+ const IMAGE_ANALYSIS_LOCAL_ACCESS_ERROR = 'Image Analysis endpoints require localhost access when dashboard auth is disabled.';
40
+ function safeLoadSettings(settingsPath) {
41
+ if (!settingsPath)
42
+ return null;
43
+ try {
44
+ const expandedPath = (0, helpers_1.expandPath)(settingsPath);
45
+ if (!fs.existsSync(expandedPath))
46
+ return null;
47
+ return (0, config_manager_1.loadSettings)(expandedPath);
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ function resolveProviderFromBaseUrl(baseUrl) {
54
+ if (typeof baseUrl !== 'string' || baseUrl.trim().length === 0) {
55
+ return null;
56
+ }
57
+ try {
58
+ const parsed = new URL(baseUrl);
59
+ const extracted = (0, model_id_normalizer_1.extractProviderFromPathname)(parsed.pathname);
60
+ return extracted ? (0, provider_capabilities_1.mapExternalProviderName)(extracted) : null;
61
+ }
62
+ catch {
63
+ const extracted = (0, model_id_normalizer_1.extractProviderFromPathname)(baseUrl);
64
+ return extracted ? (0, provider_capabilities_1.mapExternalProviderName)(extracted) : null;
65
+ }
66
+ }
67
+ function resolveTarget(target) {
68
+ if (target === 'droid' || target === 'codex')
69
+ return target;
70
+ return 'claude';
71
+ }
72
+ function resolveCurrentTargetMode(target, status) {
73
+ if (!status.enabled)
74
+ return 'disabled';
75
+ if (target !== 'claude')
76
+ return 'bypassed';
77
+ if (status.nativeReadPreference)
78
+ return 'native';
79
+ if (!status.backendId)
80
+ return 'unresolved';
81
+ if (status.status === 'hook-missing')
82
+ return 'setup';
83
+ if (status.effectiveRuntimeMode === 'native-read')
84
+ return 'fallback';
85
+ return 'active';
86
+ }
87
+ function resolveBackendState(status) {
88
+ if (status.authReadiness === 'missing')
89
+ return 'needs_auth';
90
+ if (status.proxyReadiness === 'unavailable')
91
+ return 'needs_proxy';
92
+ if (status.proxyReadiness === 'stopped')
93
+ return 'starts_on_launch';
94
+ if (status.effectiveRuntimeMode === 'native-read' || status.status === 'attention')
95
+ return 'review';
96
+ return 'ready';
97
+ }
98
+ function getKnownBackends() {
99
+ return Array.from(new Set(provider_capabilities_1.CLIPROXY_PROVIDER_IDS)).sort((left, right) => left.localeCompare(right));
100
+ }
101
+ async function buildDashboardPayload() {
102
+ const config = (0, unified_config_loader_1.getImageAnalysisConfig)();
103
+ const { profiles, variants } = (0, services_1.listApiProfiles)();
104
+ const sharedHookInstalled = (0, image_analyzer_hook_installer_1.hasImageAnalyzerHook)();
105
+ const profileRows = await Promise.all(profiles.map(async (profile) => {
106
+ const settingsPath = profile.settingsPath || null;
107
+ const settings = safeLoadSettings(settingsPath);
108
+ const cliproxyProvider = (0, provider_capabilities_1.mapExternalProviderName)(profile.name) ??
109
+ (0, services_1.resolveCliproxyBridgeMetadata)(settings ?? undefined)?.provider ??
110
+ resolveProviderFromBaseUrl(settings?.env?.ANTHROPIC_BASE_URL);
111
+ const status = await (0, hooks_1.resolveImageAnalysisRuntimeStatus)({
112
+ profileName: profile.name,
113
+ profileType: 'settings',
114
+ cliproxyProvider,
115
+ settingsPath,
116
+ settings,
117
+ cliproxyBridge: (0, services_1.resolveCliproxyBridgeMetadata)(settings ?? undefined),
118
+ hookInstalled: settingsPath
119
+ ? (0, image_analyzer_profile_hook_injector_1.hasImageAnalysisProfileHook)(profile.name, settingsPath)
120
+ : undefined,
121
+ sharedHookInstalled,
122
+ }, config);
123
+ return {
124
+ name: profile.name,
125
+ kind: 'profile',
126
+ target: resolveTarget(profile.target),
127
+ configured: profile.isConfigured,
128
+ settingsPath,
129
+ backendId: status.backendId,
130
+ backendDisplayName: status.backendDisplayName,
131
+ resolutionSource: status.resolutionSource,
132
+ status: status.status,
133
+ effectiveRuntimeMode: status.effectiveRuntimeMode,
134
+ effectiveRuntimeReason: status.effectiveRuntimeReason,
135
+ currentTargetMode: resolveCurrentTargetMode(resolveTarget(profile.target), status),
136
+ profileModel: status.profileModel,
137
+ nativeReadPreference: status.nativeReadPreference,
138
+ nativeImageCapable: status.nativeImageCapable,
139
+ nativeImageReason: status.nativeImageReason,
140
+ };
141
+ }));
142
+ const variantRows = await Promise.all(variants.map(async (variant) => {
143
+ const settingsPath = typeof variant.settings === 'string' && variant.settings !== '-' ? variant.settings : null;
144
+ const settings = safeLoadSettings(settingsPath);
145
+ const cliproxyProvider = (0, provider_capabilities_1.mapExternalProviderName)(variant.provider);
146
+ const status = await (0, hooks_1.resolveImageAnalysisRuntimeStatus)({
147
+ profileName: variant.name,
148
+ profileType: 'cliproxy',
149
+ cliproxyProvider,
150
+ isComposite: variant.provider === 'composite',
151
+ settingsPath,
152
+ settings,
153
+ cliproxyBridge: (0, services_1.resolveCliproxyBridgeMetadata)(settings ?? undefined),
154
+ hookInstalled: settingsPath
155
+ ? (0, image_analyzer_profile_hook_injector_1.hasImageAnalysisProfileHook)(variant.name, settingsPath)
156
+ : undefined,
157
+ sharedHookInstalled,
158
+ }, config);
159
+ return {
160
+ name: variant.name,
161
+ kind: 'variant',
162
+ target: resolveTarget(variant.target),
163
+ configured: true,
164
+ settingsPath,
165
+ backendId: status.backendId,
166
+ backendDisplayName: status.backendDisplayName,
167
+ resolutionSource: status.resolutionSource,
168
+ status: status.status,
169
+ effectiveRuntimeMode: status.effectiveRuntimeMode,
170
+ effectiveRuntimeReason: status.effectiveRuntimeReason,
171
+ currentTargetMode: resolveCurrentTargetMode(resolveTarget(variant.target), status),
172
+ profileModel: status.profileModel,
173
+ nativeReadPreference: status.nativeReadPreference,
174
+ nativeImageCapable: status.nativeImageCapable,
175
+ nativeImageReason: status.nativeImageReason,
176
+ };
177
+ }));
178
+ const allProfileRows = [...profileRows, ...variantRows].sort((left, right) => left.name.localeCompare(right.name));
179
+ const backendRows = await Promise.all(Object.entries(config.provider_models)
180
+ .sort(([left], [right]) => left.localeCompare(right))
181
+ .map(async ([backendId, model]) => {
182
+ const status = await (0, hooks_1.resolveImageAnalysisRuntimeStatus)({
183
+ profileName: backendId,
184
+ profileType: 'cliproxy',
185
+ cliproxyProvider: (0, provider_capabilities_1.mapExternalProviderName)(backendId),
186
+ hookInstalled: true,
187
+ sharedHookInstalled: true,
188
+ }, config);
189
+ return {
190
+ backendId,
191
+ displayName: (0, provider_capabilities_1.getProviderDisplayName)(backendId),
192
+ model,
193
+ state: resolveBackendState(status),
194
+ authReadiness: status.authReadiness,
195
+ authReason: status.authReason,
196
+ proxyReadiness: status.proxyReadiness,
197
+ proxyReason: status.proxyReason,
198
+ profilesUsing: allProfileRows.filter((profile) => profile.backendId === backendId && !profile.nativeReadPreference).length,
199
+ };
200
+ }));
201
+ const activeProfileCount = allProfileRows.filter((row) => row.currentTargetMode === 'active').length;
202
+ const bypassedProfileCount = allProfileRows.filter((row) => row.currentTargetMode === 'bypassed').length;
203
+ const mappedProfileCount = allProfileRows.filter((row) => row.resolutionSource === 'profile-backend').length;
204
+ const nativeProfileCount = allProfileRows.filter((row) => row.nativeReadPreference).length;
205
+ const blockerCount = backendRows.filter((row) => row.state === 'needs_auth' || row.state === 'needs_proxy' || row.state === 'review').length;
206
+ let summaryState = 'ready';
207
+ let title = 'Ready';
208
+ let detail = `${activeProfileCount} profile${activeProfileCount === 1 ? '' : 's'} route through Image on the current Claude target path.`;
209
+ if (nativeProfileCount > 0) {
210
+ detail += ` ${nativeProfileCount} prefer native image reading.`;
211
+ }
212
+ if (!config.enabled) {
213
+ summaryState = 'disabled';
214
+ title = 'Disabled';
215
+ detail = 'Image is turned off globally. Images and PDFs fall back to native file access.';
216
+ }
217
+ else if (backendRows.length === 0) {
218
+ summaryState = 'needs_setup';
219
+ title = 'Needs provider models';
220
+ detail = 'Add at least one provider model before turning Image on for profiles.';
221
+ }
222
+ else if (blockerCount > 0) {
223
+ summaryState = activeProfileCount > 0 ? 'partial' : 'needs_setup';
224
+ title = activeProfileCount > 0 ? 'Partially ready' : 'Needs setup';
225
+ detail = `${blockerCount} backend${blockerCount === 1 ? '' : 's'} still need auth, runtime, or review before every profile path is healthy.`;
226
+ }
227
+ return {
228
+ config: {
229
+ enabled: config.enabled,
230
+ timeout: config.timeout,
231
+ providerModels: config.provider_models,
232
+ fallbackBackend: config.fallback_backend ?? null,
233
+ profileBackends: config.profile_backends ?? {},
234
+ },
235
+ summary: {
236
+ state: summaryState,
237
+ title,
238
+ detail,
239
+ backendCount: backendRows.length,
240
+ mappedProfileCount,
241
+ activeProfileCount,
242
+ bypassedProfileCount,
243
+ nativeProfileCount,
244
+ },
245
+ backends: backendRows,
246
+ profiles: allProfileRows,
247
+ catalog: {
248
+ knownBackends: getKnownBackends(),
249
+ profileNames: allProfileRows.map((row) => row.name),
250
+ },
251
+ };
252
+ }
253
+ router.use((req, res, next) => {
254
+ if ((0, auth_middleware_1.requireLocalAccessWhenAuthDisabled)(req, res, IMAGE_ANALYSIS_LOCAL_ACCESS_ERROR)) {
255
+ next();
256
+ }
257
+ });
258
+ router.get('/', async (_req, res) => {
259
+ try {
260
+ res.json(await buildDashboardPayload());
261
+ }
262
+ catch (error) {
263
+ res.status(500).json({ error: error.message });
264
+ }
265
+ });
266
+ router.put('/', async (req, res) => {
267
+ const body = req.body;
268
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
269
+ res.status(400).json({ error: 'Invalid request body. Must be an object.' });
270
+ return;
271
+ }
272
+ if (body.enabled !== undefined && typeof body.enabled !== 'boolean') {
273
+ res.status(400).json({ error: 'Invalid value for enabled. Must be a boolean.' });
274
+ return;
275
+ }
276
+ if (body.timeout !== undefined) {
277
+ if (!Number.isInteger(body.timeout) || body.timeout < 10 || body.timeout > 600) {
278
+ res.status(400).json({ error: 'Timeout must be an integer between 10 and 600 seconds.' });
279
+ return;
280
+ }
281
+ }
282
+ if (body.providerModels !== undefined &&
283
+ (body.providerModels === null ||
284
+ Array.isArray(body.providerModels) ||
285
+ typeof body.providerModels !== 'object')) {
286
+ res.status(400).json({ error: 'Invalid value for providerModels. Must be an object.' });
287
+ return;
288
+ }
289
+ if (body.profileBackends !== undefined &&
290
+ (body.profileBackends === null ||
291
+ Array.isArray(body.profileBackends) ||
292
+ typeof body.profileBackends !== 'object')) {
293
+ res.status(400).json({ error: 'Invalid value for profileBackends. Must be an object.' });
294
+ return;
295
+ }
296
+ if (body.fallbackBackend !== undefined &&
297
+ body.fallbackBackend !== null &&
298
+ typeof body.fallbackBackend !== 'string') {
299
+ res.status(400).json({ error: 'Invalid value for fallbackBackend. Must be a string or null.' });
300
+ return;
301
+ }
302
+ try {
303
+ const currentConfig = (0, unified_config_loader_1.getImageAnalysisConfig)();
304
+ const knownBackends = new Set([
305
+ ...getKnownBackends(),
306
+ ...Object.keys(currentConfig.provider_models),
307
+ ]);
308
+ const nextProviderModels = Object.entries(body.providerModels ?? currentConfig.provider_models).reduce((acc, [backendId, model]) => {
309
+ const normalizedBackend = (0, hooks_1.normalizeImageAnalysisBackendId)(backendId, knownBackends);
310
+ const normalizedModel = typeof model === 'string' ? model.trim() : '';
311
+ if (!normalizedBackend || normalizedModel.length === 0) {
312
+ return acc;
313
+ }
314
+ acc[normalizedBackend] = normalizedModel;
315
+ return acc;
316
+ }, {});
317
+ if (Object.keys(nextProviderModels).length === 0) {
318
+ res.status(400).json({ error: 'At least one provider model must remain configured.' });
319
+ return;
320
+ }
321
+ const requestedFallback = typeof body.fallbackBackend === 'string'
322
+ ? body.fallbackBackend
323
+ : currentConfig.fallback_backend;
324
+ const normalizedFallback = (0, hooks_1.normalizeImageAnalysisBackendId)(requestedFallback, Object.keys(nextProviderModels));
325
+ if (!normalizedFallback || !nextProviderModels[normalizedFallback]) {
326
+ res
327
+ .status(400)
328
+ .json({ error: 'Fallback backend must reference a configured provider model.' });
329
+ return;
330
+ }
331
+ const nextProfileBackends = {};
332
+ for (const [profileName, backendId] of Object.entries(body.profileBackends ?? currentConfig.profile_backends ?? {})) {
333
+ const trimmedProfileName = profileName.trim();
334
+ if (!trimmedProfileName) {
335
+ continue;
336
+ }
337
+ const normalizedBackend = (0, hooks_1.normalizeImageAnalysisBackendId)(backendId, Object.keys(nextProviderModels));
338
+ if (!normalizedBackend || !nextProviderModels[normalizedBackend]) {
339
+ res.status(400).json({
340
+ error: `Profile mapping for "${trimmedProfileName}" references an unknown backend.`,
341
+ });
342
+ return;
343
+ }
344
+ nextProfileBackends[trimmedProfileName] = normalizedBackend;
345
+ }
346
+ (0, unified_config_loader_1.mutateUnifiedConfig)((config) => {
347
+ config.image_analysis = {
348
+ enabled: body.enabled ?? currentConfig.enabled,
349
+ timeout: body.timeout ?? currentConfig.timeout,
350
+ provider_models: nextProviderModels,
351
+ fallback_backend: normalizedFallback,
352
+ profile_backends: nextProfileBackends,
353
+ };
354
+ });
355
+ res.json(await buildDashboardPayload());
356
+ }
357
+ catch (error) {
358
+ res.status(500).json({ error: error.message });
359
+ }
360
+ });
361
+ exports.default = router;
362
+ //# sourceMappingURL=image-analysis-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-analysis-routes.js","sourceRoot":"","sources":["../../../src/web-server/routes/image-analysis-routes.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/web-server/routes/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA8BH,eAAO,MAAM,SAAS,4CAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/web-server/routes/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA+BH,eAAO,MAAM,SAAS,4CAAW,CAAC"}
@@ -21,6 +21,7 @@ const variant_routes_1 = __importDefault(require("./variant-routes"));
21
21
  const settings_routes_1 = __importDefault(require("./settings-routes"));
22
22
  const channels_routes_1 = __importDefault(require("./channels-routes"));
23
23
  const websearch_routes_1 = __importDefault(require("./websearch-routes"));
24
+ const image_analysis_routes_1 = __importDefault(require("./image-analysis-routes"));
24
25
  const cliproxy_auth_routes_1 = __importDefault(require("./cliproxy-auth-routes"));
25
26
  const cliproxy_stats_routes_1 = __importDefault(require("./cliproxy-stats-routes"));
26
27
  const cliproxy_sync_routes_1 = __importDefault(require("./cliproxy-sync-routes"));
@@ -64,6 +65,7 @@ exports.apiRoutes.use('/cliproxy/ai-providers', ai_provider_routes_1.default);
64
65
  exports.apiRoutes.use('/cliproxy/openai-compat', provider_routes_1.default);
65
66
  // ==================== WebSearch ====================
66
67
  exports.apiRoutes.use('/websearch', websearch_routes_1.default);
68
+ exports.apiRoutes.use('/image-analysis', image_analysis_routes_1.default);
67
69
  // ==================== Copilot ====================
68
70
  exports.apiRoutes.use('/copilot', copilot_routes_1.default);
69
71
  // ==================== Cursor ====================
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/web-server/routes/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;AAEH,qCAAiC;AAEjC,wBAAwB;AACxB,sEAA6C;AAC7C,sEAA6C;AAC7C,oEAA2C;AAC3C,oEAA2C;AAC3C,wEAA+C;AAC/C,sEAA6C;AAC7C,wEAA+C;AAC/C,wEAA+C;AAC/C,0EAAiD;AACjD,kFAAwD;AACxD,oFAA0D;AAC1D,kFAAwD;AACxD,8EAAoD;AACpD,sEAA6C;AAC7C,oEAA2C;AAC3C,kEAAyC;AACzC,kEAAyC;AACzC,gEAAuC;AACvC,kEAAkD;AAClD,gEAAuC;AACvC,sEAA6C;AAC7C,sEAA6C;AAC7C,wFAA8D;AAE9D,6BAA6B;AAChB,QAAA,SAAS,GAAG,IAAA,gBAAM,GAAE,CAAC;AAElC,+DAA+D;AAC/D,uDAAuD;AACvD,iBAAS,CAAC,GAAG,CAAC,WAAW,EAAE,wBAAa,CAAC,CAAC;AAC1C,iBAAS,CAAC,GAAG,CAAC,WAAW,EAAE,yBAAc,CAAC,CAAC;AAC3C,iBAAS,CAAC,GAAG,CAAC,WAAW,EAAE,yBAAc,CAAC,CAAC;AAC3C,iBAAS,CAAC,GAAG,CAAC,WAAW,EAAE,wBAAa,CAAC,CAAC;AAE1C,2DAA2D;AAC3D,2BAA2B;AAC3B,iBAAS,CAAC,GAAG,CAAC,SAAS,EAAE,uBAAY,CAAC,CAAC;AAEvC,0DAA0D;AAC1D,iBAAS,CAAC,GAAG,CAAC,SAAS,EAAE,uBAAY,CAAC,CAAC;AAEvC,2DAA2D;AAC3D,iBAAS,CAAC,GAAG,CAAC,OAAO,EAAE,qBAAU,CAAC,CAAC;AAEnC,wEAAwE;AACxE,iBAAS,CAAC,GAAG,CAAC,UAAU,EAAE,wBAAa,CAAC,CAAC;AACzC,iBAAS,CAAC,GAAG,CAAC,mBAAmB,EAAE,iCAAqB,CAAC,CAAC;AAE1D,qDAAqD;AACrD,8DAA8D;AAC9D,iBAAS,CAAC,GAAG,CAAC,WAAW,EAAE,wBAAa,CAAC,CAAC;AAC1C,iBAAS,CAAC,GAAG,CAAC,gBAAgB,EAAE,8BAAkB,CAAC,CAAC;AACpD,iBAAS,CAAC,GAAG,CAAC,WAAW,EAAE,+BAAmB,CAAC,CAAC;AAChD,iBAAS,CAAC,GAAG,CAAC,gBAAgB,EAAE,8BAAkB,CAAC,CAAC;AACpD,iBAAS,CAAC,GAAG,CAAC,mBAAmB,EAAE,wBAAa,CAAC,CAAC;AAClD,iBAAS,CAAC,GAAG,CAAC,wBAAwB,EAAE,4BAAgB,CAAC,CAAC;AAC1D,iBAAS,CAAC,GAAG,CAAC,yBAAyB,EAAE,yBAAc,CAAC,CAAC;AAEzD,sDAAsD;AACtD,iBAAS,CAAC,GAAG,CAAC,YAAY,EAAE,0BAAe,CAAC,CAAC;AAE7C,oDAAoD;AACpD,iBAAS,CAAC,GAAG,CAAC,UAAU,EAAE,wBAAa,CAAC,CAAC;AAEzC,mDAAmD;AACnD,iBAAS,CAAC,GAAG,CAAC,SAAS,EAAE,uBAAY,CAAC,CAAC;AAEvC,kDAAkD;AAClD,iBAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,sBAAW,CAAC,CAAC;AAErC,kDAAkD;AAClD,iBAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,sBAAW,CAAC,CAAC;AAErC,qEAAqE;AACrE,iBAAS,CAAC,GAAG,CAAC,kBAAkB,EAAE,sBAAoB,CAAC,CAAC;AAExD,wEAAwE;AACxE,iBAAS,CAAC,GAAG,CAAC,GAAG,EAAE,qBAAU,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/web-server/routes/index.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;AAEH,qCAAiC;AAEjC,wBAAwB;AACxB,sEAA6C;AAC7C,sEAA6C;AAC7C,oEAA2C;AAC3C,oEAA2C;AAC3C,wEAA+C;AAC/C,sEAA6C;AAC7C,wEAA+C;AAC/C,wEAA+C;AAC/C,0EAAiD;AACjD,oFAA0D;AAC1D,kFAAwD;AACxD,oFAA0D;AAC1D,kFAAwD;AACxD,8EAAoD;AACpD,sEAA6C;AAC7C,oEAA2C;AAC3C,kEAAyC;AACzC,kEAAyC;AACzC,gEAAuC;AACvC,kEAAkD;AAClD,gEAAuC;AACvC,sEAA6C;AAC7C,sEAA6C;AAC7C,wFAA8D;AAE9D,6BAA6B;AAChB,QAAA,SAAS,GAAG,IAAA,gBAAM,GAAE,CAAC;AAElC,+DAA+D;AAC/D,uDAAuD;AACvD,iBAAS,CAAC,GAAG,CAAC,WAAW,EAAE,wBAAa,CAAC,CAAC;AAC1C,iBAAS,CAAC,GAAG,CAAC,WAAW,EAAE,yBAAc,CAAC,CAAC;AAC3C,iBAAS,CAAC,GAAG,CAAC,WAAW,EAAE,yBAAc,CAAC,CAAC;AAC3C,iBAAS,CAAC,GAAG,CAAC,WAAW,EAAE,wBAAa,CAAC,CAAC;AAE1C,2DAA2D;AAC3D,2BAA2B;AAC3B,iBAAS,CAAC,GAAG,CAAC,SAAS,EAAE,uBAAY,CAAC,CAAC;AAEvC,0DAA0D;AAC1D,iBAAS,CAAC,GAAG,CAAC,SAAS,EAAE,uBAAY,CAAC,CAAC;AAEvC,2DAA2D;AAC3D,iBAAS,CAAC,GAAG,CAAC,OAAO,EAAE,qBAAU,CAAC,CAAC;AAEnC,wEAAwE;AACxE,iBAAS,CAAC,GAAG,CAAC,UAAU,EAAE,wBAAa,CAAC,CAAC;AACzC,iBAAS,CAAC,GAAG,CAAC,mBAAmB,EAAE,iCAAqB,CAAC,CAAC;AAE1D,qDAAqD;AACrD,8DAA8D;AAC9D,iBAAS,CAAC,GAAG,CAAC,WAAW,EAAE,wBAAa,CAAC,CAAC;AAC1C,iBAAS,CAAC,GAAG,CAAC,gBAAgB,EAAE,8BAAkB,CAAC,CAAC;AACpD,iBAAS,CAAC,GAAG,CAAC,WAAW,EAAE,+BAAmB,CAAC,CAAC;AAChD,iBAAS,CAAC,GAAG,CAAC,gBAAgB,EAAE,8BAAkB,CAAC,CAAC;AACpD,iBAAS,CAAC,GAAG,CAAC,mBAAmB,EAAE,wBAAa,CAAC,CAAC;AAClD,iBAAS,CAAC,GAAG,CAAC,wBAAwB,EAAE,4BAAgB,CAAC,CAAC;AAC1D,iBAAS,CAAC,GAAG,CAAC,yBAAyB,EAAE,yBAAc,CAAC,CAAC;AAEzD,sDAAsD;AACtD,iBAAS,CAAC,GAAG,CAAC,YAAY,EAAE,0BAAe,CAAC,CAAC;AAC7C,iBAAS,CAAC,GAAG,CAAC,iBAAiB,EAAE,+BAAmB,CAAC,CAAC;AAEtD,oDAAoD;AACpD,iBAAS,CAAC,GAAG,CAAC,UAAU,EAAE,wBAAa,CAAC,CAAC;AAEzC,mDAAmD;AACnD,iBAAS,CAAC,GAAG,CAAC,SAAS,EAAE,uBAAY,CAAC,CAAC;AAEvC,kDAAkD;AAClD,iBAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,sBAAW,CAAC,CAAC;AAErC,kDAAkD;AAClD,iBAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,sBAAW,CAAC,CAAC;AAErC,qEAAqE;AACrE,iBAAS,CAAC,GAAG,CAAC,kBAAkB,EAAE,sBAAoB,CAAC,CAAC;AAExD,wEAAwE;AACxE,iBAAS,CAAC,GAAG,CAAC,GAAG,EAAE,qBAAU,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"settings-routes.d.ts","sourceRoot":"","sources":["../../../src/web-server/routes/settings-routes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAiCH,QAAA,MAAM,MAAM,4CAAW,CAAC;AAkxBxB,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"settings-routes.d.ts","sourceRoot":"","sources":["../../../src/web-server/routes/settings-routes.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2CH,QAAA,MAAM,MAAM,4CAAW,CAAC;AAu2BxB,eAAe,MAAM,CAAC"}
@@ -28,7 +28,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const express_1 = require("express");
30
30
  const fs = __importStar(require("fs"));
31
- const os = __importStar(require("os"));
32
31
  const path = __importStar(require("path"));
33
32
  const lockfile = __importStar(require("proper-lockfile"));
34
33
  const config_manager_1 = require("../../utils/config-manager");
@@ -41,8 +40,12 @@ const services_1 = require("../../api/services");
41
40
  const unified_config_loader_1 = require("../../config/unified-config-loader");
42
41
  const auth_middleware_1 = require("../middleware/auth-middleware");
43
42
  const provider_capabilities_1 = require("../../cliproxy/provider-capabilities");
43
+ const helpers_1 = require("../../utils/helpers");
44
44
  const model_id_normalizer_1 = require("../../cliproxy/model-id-normalizer");
45
45
  const route_helpers_1 = require("./route-helpers");
46
+ const image_analyzer_profile_hook_injector_1 = require("../../utils/hooks/image-analyzer-profile-hook-injector");
47
+ const image_analyzer_hook_installer_1 = require("../../utils/hooks/image-analyzer-hook-installer");
48
+ const hooks_1 = require("../../utils/hooks");
46
49
  const router = (0, express_1.Router)();
47
50
  const MODEL_ENV_KEYS = [
48
51
  'ANTHROPIC_MODEL',
@@ -91,8 +94,16 @@ function resolveSettingsPath(profileOrVariant) {
91
94
  const variants = (0, variant_service_1.listVariants)();
92
95
  const variant = variants[profileOrVariant];
93
96
  if (variant?.settings) {
94
- // Variant settings path (e.g., ~/.ccs/agy-g3.settings.json)
95
- return resolvePathWithin(resolvedCcsDir, variant.settings.replace(/^~/, os.homedir()));
97
+ return path.resolve((0, helpers_1.expandPath)(variant.settings));
98
+ }
99
+ try {
100
+ const configuredSettingsPath = (0, config_manager_1.loadConfigSafe)().profiles[profileOrVariant];
101
+ if (typeof configuredSettingsPath === 'string' && configuredSettingsPath.trim().length > 0) {
102
+ return path.resolve((0, helpers_1.expandPath)(configuredSettingsPath));
103
+ }
104
+ }
105
+ catch {
106
+ // Fall back to the conventional ~/.ccs/<profile>.settings.json path below.
96
107
  }
97
108
  // Regular profile settings
98
109
  return resolvePathWithin(resolvedCcsDir, path.join(resolvedCcsDir, `${profileOrVariant}.settings.json`));
@@ -218,6 +229,34 @@ function canonicalizeProfileSettings(profileOrVariant, settings) {
218
229
  }
219
230
  return changed ? next : settings;
220
231
  }
232
+ async function resolveImageAnalysisStatusForProfile(profileOrVariant, settings, settingsPath) {
233
+ const variants = (0, variant_service_1.listVariants)();
234
+ const variant = variants[profileOrVariant];
235
+ const cliproxyProvider = resolveProviderForProfile(profileOrVariant);
236
+ const cliproxyBridge = (0, services_1.resolveCliproxyBridgeMetadata)(settings);
237
+ const status = await (0, hooks_1.resolveImageAnalysisRuntimeStatus)({
238
+ profileName: profileOrVariant,
239
+ profileType: cliproxyProvider ? 'cliproxy' : 'settings',
240
+ cliproxyProvider,
241
+ isComposite: Boolean(variant && 'type' in variant && variant.type === 'composite'),
242
+ settingsPath,
243
+ settings,
244
+ cliproxyBridge,
245
+ hookInstalled: (0, image_analyzer_profile_hook_injector_1.hasImageAnalysisProfileHook)(profileOrVariant, settingsPath),
246
+ sharedHookInstalled: (0, image_analyzer_hook_installer_1.hasImageAnalyzerHook)(),
247
+ }, (0, unified_config_loader_1.getImageAnalysisConfig)());
248
+ return {
249
+ ...status,
250
+ persistencePath: status.shouldPersistHook
251
+ ? (0, image_analyzer_profile_hook_injector_1.getImageAnalysisProfileSettingsPath)(profileOrVariant, settingsPath)
252
+ : null,
253
+ };
254
+ }
255
+ async function resolvePreviewImageAnalysisStatus(profileOrVariant, settings) {
256
+ const normalizedSettings = canonicalizeProfileSettings(profileOrVariant, settings);
257
+ const settingsPath = resolveSettingsPath(profileOrVariant);
258
+ return resolveImageAnalysisStatusForProfile(profileOrVariant, normalizedSettings, settingsPath);
259
+ }
221
260
  function writeSettingsAtomically(settingsPath, settings) {
222
261
  const tempPath = `${settingsPath}.tmp.${process.pid}`;
223
262
  fs.writeFileSync(tempPath, JSON.stringify(settings, null, 2) + '\n');
@@ -274,7 +313,7 @@ function maskApiKeys(settings) {
274
313
  /**
275
314
  * GET /api/settings/:profile - Get settings with masked API keys
276
315
  */
277
- router.get('/:profile', (req, res) => {
316
+ router.get('/:profile', async (req, res) => {
278
317
  try {
279
318
  const { profile } = req.params;
280
319
  const settingsPath = resolveSettingsPath(profile);
@@ -291,6 +330,7 @@ router.get('/:profile', (req, res) => {
291
330
  mtime: stat.mtime.getTime(),
292
331
  path: settingsPath,
293
332
  cliproxyBridge: (0, services_1.resolveCliproxyBridgeMetadata)(settings),
333
+ imageAnalysisStatus: await resolveImageAnalysisStatusForProfile(profile, settings, settingsPath),
294
334
  });
295
335
  }
296
336
  catch (error) {
@@ -300,7 +340,7 @@ router.get('/:profile', (req, res) => {
300
340
  /**
301
341
  * GET /api/settings/:profile/raw - Get full settings (for editing)
302
342
  */
303
- router.get('/:profile/raw', (req, res) => {
343
+ router.get('/:profile/raw', async (req, res) => {
304
344
  if (!requireSensitiveLocalAccess(req, res))
305
345
  return;
306
346
  try {
@@ -318,6 +358,28 @@ router.get('/:profile/raw', (req, res) => {
318
358
  mtime: stat.mtime.getTime(),
319
359
  path: settingsPath,
320
360
  cliproxyBridge: (0, services_1.resolveCliproxyBridgeMetadata)(settings),
361
+ imageAnalysisStatus: await resolveImageAnalysisStatusForProfile(profile, settings, settingsPath),
362
+ });
363
+ }
364
+ catch (error) {
365
+ respondInternalError(res, error, 'Internal server error.');
366
+ }
367
+ });
368
+ /**
369
+ * POST /api/settings/:profile/image-analysis-status - Preview image analysis status from editor JSON
370
+ */
371
+ router.post('/:profile/image-analysis-status', async (req, res) => {
372
+ if (!requireSensitiveLocalAccess(req, res))
373
+ return;
374
+ try {
375
+ const { profile } = req.params;
376
+ const { settings } = req.body;
377
+ if (!settings || typeof settings !== 'object') {
378
+ res.status(400).json({ error: 'settings object is required in request body' });
379
+ return;
380
+ }
381
+ res.json({
382
+ imageAnalysisStatus: await resolvePreviewImageAnalysisStatus(profile, settings),
321
383
  });
322
384
  }
323
385
  catch (error) {