@kaitranntt/ccs 7.65.3 → 7.66.0-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -750
- package/dist/api/services/profile-lifecycle-service.d.ts.map +1 -1
- package/dist/api/services/profile-lifecycle-service.js +4 -0
- package/dist/api/services/profile-lifecycle-service.js.map +1 -1
- package/dist/api/services/profile-writer.d.ts.map +1 -1
- package/dist/api/services/profile-writer.js +3 -0
- package/dist/api/services/profile-writer.js.map +1 -1
- package/dist/auth/auth-commands.d.ts +1 -0
- package/dist/auth/auth-commands.d.ts.map +1 -1
- package/dist/auth/auth-commands.js +11 -0
- package/dist/auth/auth-commands.js.map +1 -1
- package/dist/auth/commands/backup-command.d.ts +3 -0
- package/dist/auth/commands/backup-command.d.ts.map +1 -0
- package/dist/auth/commands/backup-command.js +126 -0
- package/dist/auth/commands/backup-command.js.map +1 -0
- package/dist/auth/commands/index.d.ts +1 -0
- package/dist/auth/commands/index.d.ts.map +1 -1
- package/dist/auth/commands/index.js +3 -1
- package/dist/auth/commands/index.js.map +1 -1
- package/dist/auth/profile-continuity-inheritance.d.ts +1 -0
- package/dist/auth/profile-continuity-inheritance.d.ts.map +1 -1
- package/dist/auth/profile-continuity-inheritance.js +10 -6
- package/dist/auth/profile-continuity-inheritance.js.map +1 -1
- package/dist/auth/profile-detector.d.ts +9 -1
- package/dist/auth/profile-detector.d.ts.map +1 -1
- package/dist/auth/profile-detector.js +35 -0
- package/dist/auth/profile-detector.js.map +1 -1
- package/dist/auth/resume-lane-diagnostics.d.ts +21 -0
- package/dist/auth/resume-lane-diagnostics.d.ts.map +1 -0
- package/dist/auth/resume-lane-diagnostics.js +146 -0
- package/dist/auth/resume-lane-diagnostics.js.map +1 -0
- package/dist/auth/resume-lane-warning.d.ts +9 -0
- package/dist/auth/resume-lane-warning.d.ts.map +1 -0
- package/dist/auth/resume-lane-warning.js +60 -0
- package/dist/auth/resume-lane-warning.js.map +1 -0
- package/dist/ccs.js +82 -7
- package/dist/ccs.js.map +1 -1
- package/dist/cliproxy/auth/auth-types.d.ts +29 -6
- package/dist/cliproxy/auth/auth-types.d.ts.map +1 -1
- package/dist/cliproxy/auth/auth-types.js +58 -10
- package/dist/cliproxy/auth/auth-types.js.map +1 -1
- package/dist/cliproxy/auth/oauth-handler.d.ts +9 -1
- package/dist/cliproxy/auth/oauth-handler.d.ts.map +1 -1
- package/dist/cliproxy/auth/oauth-handler.js +196 -45
- package/dist/cliproxy/auth/oauth-handler.js.map +1 -1
- package/dist/cliproxy/auth/oauth-process.d.ts +13 -1
- package/dist/cliproxy/auth/oauth-process.d.ts.map +1 -1
- package/dist/cliproxy/auth/oauth-process.js +262 -17
- package/dist/cliproxy/auth/oauth-process.js.map +1 -1
- package/dist/cliproxy/executor/env-resolver.d.ts +3 -0
- package/dist/cliproxy/executor/env-resolver.d.ts.map +1 -1
- package/dist/cliproxy/executor/env-resolver.js +19 -1
- package/dist/cliproxy/executor/env-resolver.js.map +1 -1
- package/dist/cliproxy/executor/index.d.ts +5 -0
- package/dist/cliproxy/executor/index.d.ts.map +1 -1
- package/dist/cliproxy/executor/index.js +138 -12
- package/dist/cliproxy/executor/index.js.map +1 -1
- package/dist/cliproxy/gemini-cli-quota-normalizer.d.ts +10 -0
- package/dist/cliproxy/gemini-cli-quota-normalizer.d.ts.map +1 -0
- package/dist/cliproxy/gemini-cli-quota-normalizer.js +122 -0
- package/dist/cliproxy/gemini-cli-quota-normalizer.js.map +1 -0
- package/dist/cliproxy/quota-fetcher-gemini-cli.d.ts.map +1 -1
- package/dist/cliproxy/quota-fetcher-gemini-cli.js +133 -92
- package/dist/cliproxy/quota-fetcher-gemini-cli.js.map +1 -1
- package/dist/cliproxy/quota-types.d.ts +8 -0
- package/dist/cliproxy/quota-types.d.ts.map +1 -1
- package/dist/cliproxy/services/variant-settings.d.ts.map +1 -1
- package/dist/cliproxy/services/variant-settings.js +11 -0
- package/dist/cliproxy/services/variant-settings.js.map +1 -1
- package/dist/commands/cliproxy/quota-subcommand.d.ts.map +1 -1
- package/dist/commands/cliproxy/quota-subcommand.js +10 -1
- package/dist/commands/cliproxy/quota-subcommand.js.map +1 -1
- package/dist/commands/command-catalog.d.ts +39 -0
- package/dist/commands/command-catalog.d.ts.map +1 -0
- package/dist/commands/command-catalog.js +299 -0
- package/dist/commands/command-catalog.js.map +1 -0
- package/dist/commands/completion-backend.d.ts +14 -0
- package/dist/commands/completion-backend.d.ts.map +1 -0
- package/dist/commands/completion-backend.js +216 -0
- package/dist/commands/completion-backend.js.map +1 -0
- package/dist/commands/cursor-command-display.d.ts.map +1 -1
- package/dist/commands/cursor-command-display.js +25 -5
- package/dist/commands/cursor-command-display.js.map +1 -1
- package/dist/commands/cursor-command.d.ts +1 -3
- package/dist/commands/cursor-command.d.ts.map +1 -1
- package/dist/commands/cursor-command.js +3 -15
- package/dist/commands/cursor-command.js.map +1 -1
- package/dist/commands/help-command.d.ts +4 -3
- package/dist/commands/help-command.d.ts.map +1 -1
- package/dist/commands/help-command.js +209 -503
- package/dist/commands/help-command.js.map +1 -1
- package/dist/commands/install-command.d.ts.map +1 -1
- package/dist/commands/install-command.js +16 -3
- package/dist/commands/install-command.js.map +1 -1
- package/dist/commands/root-command-router.d.ts +2 -0
- package/dist/commands/root-command-router.d.ts.map +1 -1
- package/dist/commands/root-command-router.js +13 -13
- package/dist/commands/root-command-router.js.map +1 -1
- package/dist/commands/shell-completion-command.d.ts +1 -0
- package/dist/commands/shell-completion-command.d.ts.map +1 -1
- package/dist/commands/shell-completion-command.js +27 -11
- package/dist/commands/shell-completion-command.js.map +1 -1
- package/dist/copilot/copilot-executor.d.ts +2 -0
- package/dist/copilot/copilot-executor.d.ts.map +1 -1
- package/dist/copilot/copilot-executor.js +36 -4
- package/dist/copilot/copilot-executor.js.map +1 -1
- package/dist/cursor/constants.d.ts +3 -0
- package/dist/cursor/constants.d.ts.map +1 -0
- package/dist/cursor/constants.js +20 -0
- package/dist/cursor/constants.js.map +1 -0
- package/dist/cursor/cursor-models.d.ts.map +1 -1
- package/dist/cursor/cursor-models.js +2 -0
- package/dist/cursor/cursor-models.js.map +1 -1
- package/dist/cursor/cursor-profile-executor.d.ts +10 -0
- package/dist/cursor/cursor-profile-executor.d.ts.map +1 -0
- package/dist/cursor/cursor-profile-executor.js +158 -0
- package/dist/cursor/cursor-profile-executor.js.map +1 -0
- package/dist/cursor/cursor-translator.d.ts +22 -11
- package/dist/cursor/cursor-translator.d.ts.map +1 -1
- package/dist/cursor/cursor-translator.js +254 -75
- package/dist/cursor/cursor-translator.js.map +1 -1
- package/dist/cursor/index.d.ts +1 -0
- package/dist/cursor/index.d.ts.map +1 -1
- package/dist/cursor/index.js +4 -1
- package/dist/cursor/index.js.map +1 -1
- package/dist/delegation/headless-executor.d.ts.map +1 -1
- package/dist/delegation/headless-executor.js +79 -2
- package/dist/delegation/headless-executor.js.map +1 -1
- package/dist/management/checks/image-analysis-check.d.ts.map +1 -1
- package/dist/management/checks/image-analysis-check.js +4 -5
- package/dist/management/checks/image-analysis-check.js.map +1 -1
- package/dist/management/instance-manager.js +1 -1
- package/dist/management/instance-manager.js.map +1 -1
- package/dist/shared/claude-extension-setup.d.ts.map +1 -1
- package/dist/shared/claude-extension-setup.js +36 -16
- package/dist/shared/claude-extension-setup.js.map +1 -1
- package/dist/targets/target-runtime-compatibility.d.ts.map +1 -1
- package/dist/targets/target-runtime-compatibility.js +6 -0
- package/dist/targets/target-runtime-compatibility.js.map +1 -1
- package/dist/types/profile.d.ts +1 -1
- package/dist/types/profile.d.ts.map +1 -1
- package/dist/ui/assets/accounts-l__hcQUq.js +1 -0
- package/dist/ui/assets/{alert-dialog-D0EFRcfB.js → alert-dialog-BtM3eP43.js} +1 -1
- package/dist/ui/assets/{api-DhM3BYXr.js → api-Ddwn7EXC.js} +1 -1
- package/dist/ui/assets/{auth-section-DVp8FQGm.js → auth-section-BnYn1EA3.js} +1 -1
- package/dist/ui/assets/{backups-section-CRo0NZkA.js → backups-section-M4jx3HzK.js} +1 -1
- package/dist/ui/assets/{channels-uZ_9CBqO.js → channels-D9ywyAj3.js} +1 -1
- package/dist/ui/assets/{checkbox-32DNqW_Q.js → checkbox-Df-yoPbe.js} +1 -1
- package/dist/ui/assets/{claude-extension-BfXlz5gV.js → claude-extension-Cxg0a-Ee.js} +1 -1
- package/dist/ui/assets/cliproxy-DPFdpIHy.js +3 -0
- package/dist/ui/assets/{cliproxy-ai-providers-5SHLMHiy.js → cliproxy-ai-providers-DtW-6cPc.js} +6 -6
- package/dist/ui/assets/cliproxy-control-panel-0TyLDMf7.js +1 -0
- package/dist/ui/assets/{codex-CRUSpjsu.js → codex-BtPojOrJ.js} +3 -3
- package/dist/ui/assets/{confirm-dialog-DVf5ZmCZ.js → confirm-dialog-BMwFBq5G.js} +1 -1
- package/dist/ui/assets/{copilot-BZrihl_Z.js → copilot-BG3BnHxa.js} +2 -2
- package/dist/ui/assets/{cursor-BP4nbEk_.js → cursor-BfDO73Lu.js} +1 -1
- package/dist/ui/assets/{droid-BG92rdM2.js → droid-iW-Vtuhy.js} +2 -2
- package/dist/ui/assets/{globalenv-section-Cf6dKgSf.js → globalenv-section-d9h23hIf.js} +1 -1
- package/dist/ui/assets/{health-BTy1UZs3.js → health-BPPMFQMR.js} +1 -1
- package/dist/ui/assets/icons-BxSzn11s.js +1 -0
- package/dist/ui/assets/index-B6Y8wFQ0.css +1 -0
- package/dist/ui/assets/{index-wg7UtkFv.js → index-BPyp9RJE.js} +1 -1
- package/dist/ui/assets/{index-DHrTq-0n.js → index-CctYE17X.js} +1 -1
- package/dist/ui/assets/{index-BVeN0dIB.js → index-CeP2Pr2w.js} +1 -1
- package/dist/ui/assets/{index-DuRYaONg.js → index-DYY3wcwk.js} +1 -1
- package/dist/ui/assets/{index-N2ZSJurX.js → index-DusmDwkD.js} +1 -1
- package/dist/ui/assets/index-wIy8qzU9.js +69 -0
- package/dist/ui/assets/{masked-input-DX9bedLy.js → masked-input-DWNtAEKs.js} +1 -1
- package/dist/ui/assets/{proxy-status-widget-DVDMuZK5.js → proxy-status-widget-GS3qroms.js} +1 -1
- package/dist/ui/assets/{radix-ui-C98W0NRG.js → radix-ui-Zb8sVEtn.js} +1 -1
- package/dist/ui/assets/{raw-json-settings-editor-panel-Dkt5E6Z_.js → raw-json-settings-editor-panel-e3Ziu5B1.js} +1 -1
- package/dist/ui/assets/{searchable-select-BP3Q1-Yn.js → searchable-select-D2G6gg3h.js} +1 -1
- package/dist/ui/assets/{separator-BLGGUlh9.js → separator-CR1SGLxF.js} +1 -1
- package/dist/ui/assets/{shared-G0XRyLig.js → shared-BpB5GiWS.js} +1 -1
- package/dist/ui/assets/{table-B4lRrWC-.js → table-BMbwptG_.js} +1 -1
- package/dist/ui/assets/{tanstack-CfKik0yL.js → tanstack-DWm6aJ-G.js} +1 -1
- package/dist/ui/assets/{updates--A2Sdo7N.js → updates-iC3kvbKP.js} +1 -1
- package/dist/ui/index.html +5 -5
- package/dist/utils/config-manager.d.ts +5 -0
- package/dist/utils/config-manager.d.ts.map +1 -1
- package/dist/utils/config-manager.js +10 -1
- package/dist/utils/config-manager.js.map +1 -1
- package/dist/utils/hooks/get-image-analysis-hook-env.d.ts +26 -0
- package/dist/utils/hooks/get-image-analysis-hook-env.d.ts.map +1 -1
- package/dist/utils/hooks/get-image-analysis-hook-env.js +79 -1
- package/dist/utils/hooks/get-image-analysis-hook-env.js.map +1 -1
- package/dist/utils/hooks/image-analysis-backend-resolver.d.ts.map +1 -1
- package/dist/utils/hooks/image-analysis-backend-resolver.js +13 -5
- package/dist/utils/hooks/image-analysis-backend-resolver.js.map +1 -1
- package/dist/utils/hooks/image-analysis-runtime-status.d.ts +2 -0
- package/dist/utils/hooks/image-analysis-runtime-status.d.ts.map +1 -1
- package/dist/utils/hooks/image-analysis-runtime-status.js +15 -11
- package/dist/utils/hooks/image-analysis-runtime-status.js.map +1 -1
- package/dist/utils/hooks/image-analyzer-hook-installer.d.ts.map +1 -1
- package/dist/utils/hooks/image-analyzer-hook-installer.js +60 -27
- package/dist/utils/hooks/image-analyzer-hook-installer.js.map +1 -1
- package/dist/utils/hooks/image-analyzer-profile-hook-injector.d.ts.map +1 -1
- package/dist/utils/hooks/image-analyzer-profile-hook-injector.js +3 -0
- package/dist/utils/hooks/image-analyzer-profile-hook-injector.js.map +1 -1
- package/dist/utils/hooks/index.d.ts +2 -1
- package/dist/utils/hooks/index.d.ts.map +1 -1
- package/dist/utils/hooks/index.js +14 -7
- package/dist/utils/hooks/index.js.map +1 -1
- package/dist/utils/image-analysis/claude-tool-args.d.ts +6 -0
- package/dist/utils/image-analysis/claude-tool-args.d.ts.map +1 -0
- package/dist/utils/image-analysis/claude-tool-args.js +65 -0
- package/dist/utils/image-analysis/claude-tool-args.js.map +1 -0
- package/dist/utils/image-analysis/index.d.ts +4 -0
- package/dist/utils/image-analysis/index.d.ts.map +1 -1
- package/dist/utils/image-analysis/index.js +20 -1
- package/dist/utils/image-analysis/index.js.map +1 -1
- package/dist/utils/image-analysis/mcp-installer.d.ts +18 -0
- package/dist/utils/image-analysis/mcp-installer.d.ts.map +1 -0
- package/dist/utils/image-analysis/mcp-installer.js +447 -0
- package/dist/utils/image-analysis/mcp-installer.js.map +1 -0
- package/dist/web-server/routes/account-routes.d.ts.map +1 -1
- package/dist/web-server/routes/account-routes.js +14 -2
- package/dist/web-server/routes/account-routes.js.map +1 -1
- package/dist/web-server/routes/auth-routes.d.ts +10 -0
- package/dist/web-server/routes/auth-routes.d.ts.map +1 -1
- package/dist/web-server/routes/auth-routes.js +33 -6
- package/dist/web-server/routes/auth-routes.js.map +1 -1
- package/dist/web-server/routes/cliproxy-auth-routes.d.ts +8 -0
- package/dist/web-server/routes/cliproxy-auth-routes.d.ts.map +1 -1
- package/dist/web-server/routes/cliproxy-auth-routes.js +141 -11
- package/dist/web-server/routes/cliproxy-auth-routes.js.map +1 -1
- package/dist/web-server/routes/image-analysis-routes.d.ts.map +1 -1
- package/dist/web-server/routes/image-analysis-routes.js +30 -5
- package/dist/web-server/routes/image-analysis-routes.js.map +1 -1
- package/lib/hooks/image-analysis-runtime.cjs +469 -0
- package/lib/hooks/image-analyzer-transformer.cjs +27 -418
- package/lib/hooks/websearch-transformer.cjs +37 -2
- package/lib/mcp/ccs-image-analysis-server.cjs +440 -0
- package/package.json +1 -1
- package/scripts/completion/README.md +55 -131
- package/scripts/completion/ccs.bash +22 -190
- package/scripts/completion/ccs.fish +19 -245
- package/scripts/completion/ccs.ps1 +37 -427
- package/scripts/completion/ccs.zsh +27 -305
- package/dist/ui/assets/accounts-BHEYnq6b.js +0 -1
- package/dist/ui/assets/cliproxy-DjNY9H-U.js +0 -3
- package/dist/ui/assets/cliproxy-control-panel-Zax_m1AC.js +0 -1
- package/dist/ui/assets/icons-CeH5899d.js +0 -1
- package/dist/ui/assets/index-B6SrL1O-.css +0 -1
- package/dist/ui/assets/index-Corv1lSo.js +0 -69
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
function loadRuntimeModule() {
|
|
7
|
+
const candidates = [
|
|
8
|
+
path.join(__dirname, 'image-analysis-runtime.cjs'),
|
|
9
|
+
path.join(__dirname, '../hooks/image-analysis-runtime.cjs'),
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
for (const candidate of candidates) {
|
|
13
|
+
if (fs.existsSync(candidate)) {
|
|
14
|
+
return require(candidate);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
throw new Error(
|
|
19
|
+
`ccs-image-analysis runtime not found. Checked: ${candidates.map((candidate) => path.basename(candidate)).join(', ')}`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { analyzeFile, isAnalyzableFile } = loadRuntimeModule();
|
|
24
|
+
|
|
25
|
+
const PROTOCOL_VERSION = '2024-11-05';
|
|
26
|
+
const SERVER_NAME = 'ccs-image-analysis';
|
|
27
|
+
const SERVER_VERSION = '1.0.0';
|
|
28
|
+
const TOOL_NAME = 'ImageAnalysis';
|
|
29
|
+
const TOOL_ALIASES = ['AnalyzeImage', 'ReadImage'];
|
|
30
|
+
const TEMPLATE_NAMES = ['default', 'screenshot', 'document'];
|
|
31
|
+
const TOOL_DESCRIPTION =
|
|
32
|
+
'Analyze a local image or PDF file with CCS provider-backed vision. Prefer this tool over Read for image and PDF paths. Use Read for text, code, and other plain files.';
|
|
33
|
+
|
|
34
|
+
function isSupportedToolName(name) {
|
|
35
|
+
return name === TOOL_NAME || TOOL_ALIASES.includes(name);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function shouldExposeTools() {
|
|
39
|
+
return (
|
|
40
|
+
process.env.CCS_IMAGE_ANALYSIS_ENABLED === '1' &&
|
|
41
|
+
process.env.CCS_IMAGE_ANALYSIS_SKIP !== '1' &&
|
|
42
|
+
Boolean(process.env.CCS_CURRENT_PROVIDER || process.env.CCS_IMAGE_ANALYSIS_MODEL)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getTools() {
|
|
47
|
+
if (!shouldExposeTools()) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return [
|
|
52
|
+
{
|
|
53
|
+
name: TOOL_NAME,
|
|
54
|
+
description: TOOL_DESCRIPTION,
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
properties: {
|
|
58
|
+
filePath: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description:
|
|
61
|
+
'Workspace-relative path, or an absolute path inside the current workspace, to a local image or PDF file to analyze.',
|
|
62
|
+
},
|
|
63
|
+
focus: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description:
|
|
66
|
+
'Optional question or area of focus, for example "explain the error dialog" or "transcribe the visible text".',
|
|
67
|
+
},
|
|
68
|
+
template: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
enum: TEMPLATE_NAMES,
|
|
71
|
+
description:
|
|
72
|
+
'Optional prompt template override. Use screenshot for UI captures, document for PDFs/docs, or default for general images.',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
required: ['filePath'],
|
|
76
|
+
additionalProperties: false,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function writeMessage(message) {
|
|
83
|
+
process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function writeResponse(id, result) {
|
|
87
|
+
writeMessage({
|
|
88
|
+
jsonrpc: '2.0',
|
|
89
|
+
id,
|
|
90
|
+
result,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function writeError(id, code, message) {
|
|
95
|
+
writeMessage({
|
|
96
|
+
jsonrpc: '2.0',
|
|
97
|
+
id,
|
|
98
|
+
error: {
|
|
99
|
+
code,
|
|
100
|
+
message,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function formatResult(filePath, result, focus) {
|
|
106
|
+
const lines = [
|
|
107
|
+
'[Image Analysis via CCS]',
|
|
108
|
+
'',
|
|
109
|
+
`File: ${path.basename(filePath)} (${(result.fileSize / 1024).toFixed(1)} KB)`,
|
|
110
|
+
`Model: ${result.model}`,
|
|
111
|
+
`Template: ${result.template}`,
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
if (focus && focus.trim()) {
|
|
115
|
+
lines.push(`Focus: ${focus.trim()}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
lines.push('', '---', '', result.description);
|
|
119
|
+
return lines.join('\n');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function normalizeTemplate(value) {
|
|
123
|
+
if (typeof value !== 'string') {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const normalized = value.trim().toLowerCase();
|
|
128
|
+
return TEMPLATE_NAMES.includes(normalized) ? normalized : undefined;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function normalizePathForComparison(value) {
|
|
132
|
+
return process.platform === 'win32' ? value.toLowerCase() : value;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function isPathWithinWorkspace(workspaceRoot, candidatePath) {
|
|
136
|
+
const relativePath = path.relative(workspaceRoot, candidatePath);
|
|
137
|
+
return (
|
|
138
|
+
relativePath === '' ||
|
|
139
|
+
(!relativePath.startsWith('..') &&
|
|
140
|
+
!relativePath.startsWith(`..${path.sep}`) &&
|
|
141
|
+
!path.isAbsolute(relativePath))
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function resolveFilePath(toolArgs) {
|
|
146
|
+
if (!toolArgs || typeof toolArgs !== 'object') {
|
|
147
|
+
return '';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const candidates = [toolArgs.filePath, toolArgs.file_path, toolArgs.path];
|
|
151
|
+
for (const candidate of candidates) {
|
|
152
|
+
if (typeof candidate === 'string' && candidate.trim().length > 0) {
|
|
153
|
+
return candidate.trim();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return '';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function resolveWorkspaceFilePath(toolArgs) {
|
|
161
|
+
const requestedPath = resolveFilePath(toolArgs);
|
|
162
|
+
if (!requestedPath) {
|
|
163
|
+
return { filePath: '', error: null };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const workspaceRoot = (() => {
|
|
167
|
+
try {
|
|
168
|
+
return fs.realpathSync(process.cwd());
|
|
169
|
+
} catch {
|
|
170
|
+
return path.resolve(process.cwd());
|
|
171
|
+
}
|
|
172
|
+
})();
|
|
173
|
+
const absolutePath = path.resolve(process.cwd(), requestedPath);
|
|
174
|
+
const comparisonPath = (() => {
|
|
175
|
+
if (fs.existsSync(absolutePath)) {
|
|
176
|
+
return fs.realpathSync(absolutePath);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const suffixSegments = [];
|
|
180
|
+
let currentPath = absolutePath;
|
|
181
|
+
while (!fs.existsSync(currentPath)) {
|
|
182
|
+
const parentPath = path.dirname(currentPath);
|
|
183
|
+
if (parentPath === currentPath) {
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
suffixSegments.unshift(path.basename(currentPath));
|
|
187
|
+
currentPath = parentPath;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const resolvedExistingPath = fs.existsSync(currentPath)
|
|
191
|
+
? fs.realpathSync(currentPath)
|
|
192
|
+
: path.resolve(currentPath);
|
|
193
|
+
return path.join(resolvedExistingPath, ...suffixSegments);
|
|
194
|
+
})();
|
|
195
|
+
|
|
196
|
+
if (
|
|
197
|
+
!isPathWithinWorkspace(
|
|
198
|
+
normalizePathForComparison(workspaceRoot),
|
|
199
|
+
normalizePathForComparison(comparisonPath)
|
|
200
|
+
)
|
|
201
|
+
) {
|
|
202
|
+
return {
|
|
203
|
+
filePath: '',
|
|
204
|
+
error: 'ImageAnalysis only allows files inside the current workspace.',
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
filePath: absolutePath,
|
|
210
|
+
error: null,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function resolveFocus(toolArgs) {
|
|
215
|
+
return typeof toolArgs.focus === 'string' && toolArgs.focus.trim().length > 0
|
|
216
|
+
? toolArgs.focus.trim()
|
|
217
|
+
: undefined;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function formatErrorDetail(filePath, error) {
|
|
221
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
222
|
+
|
|
223
|
+
if (message.startsWith('FILE_TOO_LARGE:')) {
|
|
224
|
+
const fileSizeBytes = Number.parseInt(message.split(':')[1], 10);
|
|
225
|
+
const sizeMb = Number.isFinite(fileSizeBytes) ? (fileSizeBytes / 1024 / 1024).toFixed(1) : '?';
|
|
226
|
+
return `ImageAnalysis cannot process ${path.basename(filePath)} because it is too large (${sizeMb} MB). The limit is 10 MB.`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (message.startsWith('AUTH_ERROR:')) {
|
|
230
|
+
return `ImageAnalysis failed because CCS vision auth for this provider is unavailable (${message.split(':')[1]}).`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (message.startsWith('RATE_LIMIT:')) {
|
|
234
|
+
return `ImageAnalysis hit a provider rate limit while analyzing ${path.basename(filePath)}.`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (message.startsWith('API_ERROR:')) {
|
|
238
|
+
return `ImageAnalysis failed at the CCS provider route while analyzing ${path.basename(filePath)}.`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (
|
|
242
|
+
message === 'TIMEOUT' ||
|
|
243
|
+
message.includes('timed out') ||
|
|
244
|
+
message.includes('timeout') ||
|
|
245
|
+
message.includes('ECONNREFUSED') ||
|
|
246
|
+
message.includes('ENOTFOUND') ||
|
|
247
|
+
message.includes('ENETUNREACH') ||
|
|
248
|
+
message.includes('EAI_AGAIN')
|
|
249
|
+
) {
|
|
250
|
+
return `ImageAnalysis could not reach the configured CCS provider route for ${path.basename(filePath)}.`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (message.includes('EACCES') || message.includes('EPERM')) {
|
|
254
|
+
return `ImageAnalysis could not read ${filePath} because access was denied.`;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return `ImageAnalysis failed for ${path.basename(filePath)}: ${message}`;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function handleToolCall(message) {
|
|
261
|
+
const id = message.id;
|
|
262
|
+
const params = message.params || {};
|
|
263
|
+
const toolArgs = params.arguments || {};
|
|
264
|
+
const toolName = params.name || '<missing>';
|
|
265
|
+
|
|
266
|
+
if (!isSupportedToolName(toolName)) {
|
|
267
|
+
writeError(id, -32602, `Unknown tool: ${toolName}`);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (!shouldExposeTools()) {
|
|
272
|
+
writeResponse(id, {
|
|
273
|
+
content: [
|
|
274
|
+
{
|
|
275
|
+
type: 'text',
|
|
276
|
+
text: 'CCS ImageAnalysis is unavailable for this profile or no provider-backed vision route is ready.',
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
isError: true,
|
|
280
|
+
});
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const { filePath, error: filePathError } = resolveWorkspaceFilePath(toolArgs);
|
|
285
|
+
if (!filePath) {
|
|
286
|
+
writeError(
|
|
287
|
+
id,
|
|
288
|
+
-32602,
|
|
289
|
+
filePathError || `Tool "${TOOL_NAME}" requires a non-empty filePath.`
|
|
290
|
+
);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (!fs.existsSync(filePath)) {
|
|
295
|
+
writeResponse(id, {
|
|
296
|
+
content: [{ type: 'text', text: `ImageAnalysis could not find file: ${filePath}` }],
|
|
297
|
+
isError: true,
|
|
298
|
+
});
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (!isAnalyzableFile(filePath)) {
|
|
303
|
+
writeResponse(id, {
|
|
304
|
+
content: [
|
|
305
|
+
{
|
|
306
|
+
type: 'text',
|
|
307
|
+
text: `ImageAnalysis only supports image and PDF files. Use Read for ${path.basename(filePath)} instead.`,
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
isError: true,
|
|
311
|
+
});
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const focus = resolveFocus(toolArgs);
|
|
316
|
+
const template = normalizeTemplate(toolArgs.template);
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
const result = await analyzeFile(filePath, { focus, template });
|
|
320
|
+
writeResponse(id, {
|
|
321
|
+
content: [{ type: 'text', text: formatResult(filePath, result, focus) }],
|
|
322
|
+
});
|
|
323
|
+
} catch (error) {
|
|
324
|
+
writeResponse(id, {
|
|
325
|
+
content: [{ type: 'text', text: formatErrorDetail(filePath, error) }],
|
|
326
|
+
isError: true,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async function handleMessage(message) {
|
|
332
|
+
if (!message || message.jsonrpc !== '2.0' || typeof message.method !== 'string') {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
switch (message.method) {
|
|
337
|
+
case 'initialize':
|
|
338
|
+
writeResponse(message.id, {
|
|
339
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
340
|
+
capabilities: {
|
|
341
|
+
tools: {},
|
|
342
|
+
},
|
|
343
|
+
serverInfo: {
|
|
344
|
+
name: SERVER_NAME,
|
|
345
|
+
version: SERVER_VERSION,
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
return;
|
|
349
|
+
case 'notifications/initialized':
|
|
350
|
+
return;
|
|
351
|
+
case 'ping':
|
|
352
|
+
writeResponse(message.id, {});
|
|
353
|
+
return;
|
|
354
|
+
case 'tools/list':
|
|
355
|
+
writeResponse(message.id, { tools: getTools() });
|
|
356
|
+
return;
|
|
357
|
+
case 'tools/call':
|
|
358
|
+
await handleToolCall(message);
|
|
359
|
+
return;
|
|
360
|
+
default:
|
|
361
|
+
if (message.id !== undefined) {
|
|
362
|
+
writeError(message.id, -32601, `Method not found: ${message.method}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
let inputBuffer = Buffer.alloc(0);
|
|
368
|
+
|
|
369
|
+
function processIncomingBuffer() {
|
|
370
|
+
while (true) {
|
|
371
|
+
let body;
|
|
372
|
+
const startsWithLegacyHeaders = inputBuffer
|
|
373
|
+
.slice(0, Math.min(inputBuffer.length, 32))
|
|
374
|
+
.toString('utf8')
|
|
375
|
+
.toLowerCase()
|
|
376
|
+
.startsWith('content-length:');
|
|
377
|
+
|
|
378
|
+
if (startsWithLegacyHeaders) {
|
|
379
|
+
const headerEnd = inputBuffer.indexOf('\r\n\r\n');
|
|
380
|
+
if (headerEnd === -1) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const headerText = inputBuffer.slice(0, headerEnd).toString('utf8');
|
|
385
|
+
const contentLengthMatch = headerText.match(/content-length:\s*(\d+)/i);
|
|
386
|
+
if (!contentLengthMatch) {
|
|
387
|
+
inputBuffer = Buffer.alloc(0);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const contentLength = Number.parseInt(contentLengthMatch[1], 10);
|
|
392
|
+
const messageEnd = headerEnd + 4 + contentLength;
|
|
393
|
+
if (inputBuffer.length < messageEnd) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
body = inputBuffer.slice(headerEnd + 4, messageEnd).toString('utf8');
|
|
398
|
+
inputBuffer = inputBuffer.slice(messageEnd);
|
|
399
|
+
} else {
|
|
400
|
+
const newlineIndex = inputBuffer.indexOf('\n');
|
|
401
|
+
if (newlineIndex === -1) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
body = inputBuffer.slice(0, newlineIndex).toString('utf8').replace(/\r$/, '').trim();
|
|
406
|
+
inputBuffer = inputBuffer.slice(newlineIndex + 1);
|
|
407
|
+
if (!body) {
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
try {
|
|
413
|
+
const message = JSON.parse(body);
|
|
414
|
+
Promise.resolve(handleMessage(message)).catch((error) => {
|
|
415
|
+
if (message && message.id !== undefined) {
|
|
416
|
+
writeError(message.id, -32603, (error && error.message) || 'Internal error');
|
|
417
|
+
} else if (process.env.CCS_DEBUG) {
|
|
418
|
+
console.error(`[ccs-image-analysis] ${error instanceof Error ? error.stack : error}`);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
} catch (error) {
|
|
422
|
+
if (process.env.CCS_DEBUG) {
|
|
423
|
+
console.error(
|
|
424
|
+
`[ccs-image-analysis] Failed to parse JSON-RPC message: ${
|
|
425
|
+
error instanceof Error ? error.message : error
|
|
426
|
+
}`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
process.stdin.on('data', (chunk) => {
|
|
434
|
+
inputBuffer = Buffer.concat([inputBuffer, chunk]);
|
|
435
|
+
processIncomingBuffer();
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
process.stdin.on('end', () => {
|
|
439
|
+
process.exit(0);
|
|
440
|
+
});
|