@codemieai/code 0.0.57 → 0.0.58
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 +124 -19
- package/bin/proxy-daemon.js +10 -0
- package/dist/agents/core/session/SessionStore.d.ts +4 -0
- package/dist/agents/core/session/SessionStore.d.ts.map +1 -1
- package/dist/agents/core/session/SessionStore.js +40 -1
- package/dist/agents/core/session/SessionStore.js.map +1 -1
- package/dist/agents/core/session/types.d.ts +7 -0
- package/dist/agents/core/session/types.d.ts.map +1 -1
- package/dist/agents/plugins/claude/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/agents/plugins/claude/session/processors/claude.conversations-processor.d.ts +1 -0
- package/dist/agents/plugins/claude/session/processors/claude.conversations-processor.d.ts.map +1 -1
- package/dist/agents/plugins/claude/session/processors/claude.conversations-processor.js +32 -20
- package/dist/agents/plugins/claude/session/processors/claude.conversations-processor.js.map +1 -1
- package/dist/agents/plugins/claude/session/processors/claude.metrics-processor.d.ts +2 -0
- package/dist/agents/plugins/claude/session/processors/claude.metrics-processor.d.ts.map +1 -1
- package/dist/agents/plugins/claude/session/processors/claude.metrics-processor.js +24 -1
- package/dist/agents/plugins/claude/session/processors/claude.metrics-processor.js.map +1 -1
- package/dist/bin/proxy-daemon.d.ts +2 -0
- package/dist/bin/proxy-daemon.d.ts.map +1 -0
- package/dist/bin/proxy-daemon.js +138 -0
- package/dist/bin/proxy-daemon.js.map +1 -0
- package/dist/cli/commands/proxy/connectors/desktop.d.ts +55 -0
- package/dist/cli/commands/proxy/connectors/desktop.d.ts.map +1 -0
- package/dist/cli/commands/proxy/connectors/desktop.js +175 -0
- package/dist/cli/commands/proxy/connectors/desktop.js.map +1 -0
- package/dist/cli/commands/proxy/daemon-manager.d.ts +35 -0
- package/dist/cli/commands/proxy/daemon-manager.d.ts.map +1 -0
- package/dist/cli/commands/proxy/daemon-manager.js +107 -0
- package/dist/cli/commands/proxy/daemon-manager.js.map +1 -0
- package/dist/cli/commands/proxy/index.d.ts +3 -0
- package/dist/cli/commands/proxy/index.d.ts.map +1 -0
- package/dist/cli/commands/proxy/index.js +221 -0
- package/dist/cli/commands/proxy/index.js.map +1 -0
- package/dist/cli/commands/proxy/inspect-desktop.d.ts +28 -0
- package/dist/cli/commands/proxy/inspect-desktop.d.ts.map +1 -0
- package/dist/cli/commands/proxy/inspect-desktop.js +134 -0
- package/dist/cli/commands/proxy/inspect-desktop.js.map +1 -0
- package/dist/cli/index.js +12 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/gateway-key.plugin.d.ts +9 -0
- package/dist/providers/plugins/sso/proxy/plugins/gateway-key.plugin.d.ts.map +1 -0
- package/dist/providers/plugins/sso/proxy/plugins/gateway-key.plugin.js +36 -0
- package/dist/providers/plugins/sso/proxy/plugins/gateway-key.plugin.js.map +1 -0
- package/dist/providers/plugins/sso/proxy/plugins/index.d.ts +2 -1
- package/dist/providers/plugins/sso/proxy/plugins/index.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/plugins/index.js +3 -1
- package/dist/providers/plugins/sso/proxy/plugins/index.js.map +1 -1
- package/dist/providers/plugins/sso/proxy/proxy-types.d.ts +4 -0
- package/dist/providers/plugins/sso/proxy/proxy-types.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/sso.proxy.d.ts.map +1 -1
- package/dist/providers/plugins/sso/proxy/sso.proxy.js +4 -3
- package/dist/providers/plugins/sso/proxy/sso.proxy.js.map +1 -1
- package/dist/telemetry/clients/claude-desktop/ClaudeDesktopTelemetryAdapter.d.ts +15 -0
- package/dist/telemetry/clients/claude-desktop/ClaudeDesktopTelemetryAdapter.d.ts.map +1 -0
- package/dist/telemetry/clients/claude-desktop/ClaudeDesktopTelemetryAdapter.js +117 -0
- package/dist/telemetry/clients/claude-desktop/ClaudeDesktopTelemetryAdapter.js.map +1 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.discovery.d.ts +3 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.discovery.d.ts.map +1 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.discovery.js +138 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.discovery.js.map +1 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.metrics.d.ts +4 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.metrics.d.ts.map +1 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.metrics.js +53 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.metrics.js.map +1 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.parser.d.ts +4 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.parser.d.ts.map +1 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.parser.js +60 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.parser.js.map +1 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.paths.d.ts +4 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.paths.d.ts.map +1 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.paths.js +19 -0
- package/dist/telemetry/clients/claude-desktop/claude-desktop.paths.js.map +1 -0
- package/dist/telemetry/runtime/DesktopTelemetryRuntime.d.ts +23 -0
- package/dist/telemetry/runtime/DesktopTelemetryRuntime.d.ts.map +1 -0
- package/dist/telemetry/runtime/DesktopTelemetryRuntime.js +243 -0
- package/dist/telemetry/runtime/DesktopTelemetryRuntime.js.map +1 -0
- package/dist/telemetry/runtime/checkpoints.d.ts +10 -0
- package/dist/telemetry/runtime/checkpoints.d.ts.map +1 -0
- package/dist/telemetry/runtime/checkpoints.js +8 -0
- package/dist/telemetry/runtime/checkpoints.js.map +1 -0
- package/dist/telemetry/runtime/types.d.ts +32 -0
- package/dist/telemetry/runtime/types.d.ts.map +1 -0
- package/dist/telemetry/runtime/types.js +2 -0
- package/dist/telemetry/runtime/types.js.map +1 -0
- package/dist/utils/processes.d.ts +6 -0
- package/dist/utils/processes.d.ts.map +1 -1
- package/dist/utils/processes.js +13 -3
- package/dist/utils/processes.js.map +1 -1
- package/package.json +4 -2
- package/scripts/README.md +15 -1
- package/scripts/prepare-install-artifacts.mjs +66 -0
- package/scripts/test-desktop-telemetry.js +229 -0
- package/scripts/test-proxy-endpoint.js +173 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
|
+
import { randomUUID } from 'crypto';
|
|
5
|
+
import { resolve } from 'path';
|
|
6
|
+
|
|
7
|
+
function printUsage() {
|
|
8
|
+
console.log(`Usage: node scripts/test-desktop-telemetry.js [options]
|
|
9
|
+
|
|
10
|
+
Options:
|
|
11
|
+
--latest Use the most recently updated Desktop session (default)
|
|
12
|
+
--session <local_id> Use a specific Desktop local session id
|
|
13
|
+
--dry-run Build and sync through processors without remote writes (default)
|
|
14
|
+
--live Attempt real sync using stored SSO credentials
|
|
15
|
+
--target-url <url> Override API base URL for sync
|
|
16
|
+
--codemie-url <url> Override CodeMie URL for credential lookup
|
|
17
|
+
--codemie-home <dir> Override CODEMIE_HOME for isolated local output
|
|
18
|
+
--limit <count> Number of recent sessions to search while resolving --session (default: 20)
|
|
19
|
+
--help Show this help
|
|
20
|
+
`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parseArgs(argv) {
|
|
24
|
+
const args = {
|
|
25
|
+
latest: true,
|
|
26
|
+
sessionId: undefined,
|
|
27
|
+
dryRun: true,
|
|
28
|
+
targetUrl: undefined,
|
|
29
|
+
codeMieUrl: undefined,
|
|
30
|
+
codemieHome: undefined,
|
|
31
|
+
limit: 20
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
35
|
+
const arg = argv[index];
|
|
36
|
+
|
|
37
|
+
switch (arg) {
|
|
38
|
+
case '--latest':
|
|
39
|
+
args.latest = true;
|
|
40
|
+
break;
|
|
41
|
+
case '--session':
|
|
42
|
+
args.sessionId = argv[index + 1];
|
|
43
|
+
args.latest = false;
|
|
44
|
+
index += 1;
|
|
45
|
+
break;
|
|
46
|
+
case '--dry-run':
|
|
47
|
+
args.dryRun = true;
|
|
48
|
+
break;
|
|
49
|
+
case '--live':
|
|
50
|
+
args.dryRun = false;
|
|
51
|
+
break;
|
|
52
|
+
case '--target-url':
|
|
53
|
+
args.targetUrl = argv[index + 1];
|
|
54
|
+
index += 1;
|
|
55
|
+
break;
|
|
56
|
+
case '--codemie-url':
|
|
57
|
+
args.codeMieUrl = argv[index + 1];
|
|
58
|
+
index += 1;
|
|
59
|
+
break;
|
|
60
|
+
case '--codemie-home':
|
|
61
|
+
args.codemieHome = argv[index + 1];
|
|
62
|
+
index += 1;
|
|
63
|
+
break;
|
|
64
|
+
case '--limit':
|
|
65
|
+
args.limit = Number.parseInt(argv[index + 1], 10);
|
|
66
|
+
index += 1;
|
|
67
|
+
break;
|
|
68
|
+
case '--help':
|
|
69
|
+
printUsage();
|
|
70
|
+
process.exit(0);
|
|
71
|
+
default:
|
|
72
|
+
console.error(`Unknown argument: ${arg}`);
|
|
73
|
+
printUsage();
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return args;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const args = parseArgs(process.argv.slice(2));
|
|
82
|
+
if (args.codemieHome) {
|
|
83
|
+
process.env.CODEMIE_HOME = resolve(args.codemieHome);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const distRoot = resolve(process.cwd(), 'dist');
|
|
87
|
+
if (!existsSync(distRoot)) {
|
|
88
|
+
console.error('dist/ is missing. Run: rtk npm run build');
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const [
|
|
93
|
+
{ discoverClaudeDesktopSessions },
|
|
94
|
+
{ ClaudeDesktopTelemetryAdapter },
|
|
95
|
+
{ SessionStore },
|
|
96
|
+
{ getSessionConversationPath, getSessionMetricsPath },
|
|
97
|
+
{ SessionSyncer },
|
|
98
|
+
{ CodeMieSSO },
|
|
99
|
+
{ detectGitBranch, detectGitRemoteRepo },
|
|
100
|
+
] = await Promise.all([
|
|
101
|
+
import('../dist/telemetry/clients/claude-desktop/claude-desktop.discovery.js'),
|
|
102
|
+
import('../dist/telemetry/clients/claude-desktop/ClaudeDesktopTelemetryAdapter.js'),
|
|
103
|
+
import('../dist/agents/core/session/SessionStore.js'),
|
|
104
|
+
import('../dist/agents/core/session/session-config.js'),
|
|
105
|
+
import('../dist/providers/plugins/sso/session/SessionSyncer.js'),
|
|
106
|
+
import('../dist/providers/plugins/sso/sso.auth.js'),
|
|
107
|
+
import('../dist/utils/processes.js')
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
const discoveredSessions = await discoverClaudeDesktopSessions(0);
|
|
111
|
+
const recentSessions = discoveredSessions
|
|
112
|
+
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
113
|
+
.slice(0, Number.isFinite(args.limit) && args.limit > 0 ? args.limit : 20);
|
|
114
|
+
|
|
115
|
+
const selected = args.sessionId
|
|
116
|
+
? recentSessions.find((session) => session.externalSessionId === args.sessionId)
|
|
117
|
+
: recentSessions[0];
|
|
118
|
+
|
|
119
|
+
if (!selected) {
|
|
120
|
+
console.error('No matching Claude Desktop local session found.');
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const sessionStore = new SessionStore();
|
|
125
|
+
let session = await sessionStore.findSessionByExternalId('claude-desktop', selected.externalSessionId);
|
|
126
|
+
|
|
127
|
+
if (!session) {
|
|
128
|
+
const [gitBranch, repository] = await Promise.all([
|
|
129
|
+
detectGitBranch(selected.workingDirectory),
|
|
130
|
+
detectGitRemoteRepo(selected.workingDirectory)
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
session = {
|
|
134
|
+
sessionId: randomUUID(),
|
|
135
|
+
agentName: 'claude-desktop',
|
|
136
|
+
provider: 'ai-run-sso',
|
|
137
|
+
startTime: selected.createdAt,
|
|
138
|
+
workingDirectory: selected.workingDirectory,
|
|
139
|
+
gitBranch: gitBranch || undefined,
|
|
140
|
+
repository: repository || undefined,
|
|
141
|
+
status: 'active',
|
|
142
|
+
activeDurationMs: 0,
|
|
143
|
+
correlation: {
|
|
144
|
+
status: 'matched',
|
|
145
|
+
agentSessionId: selected.agentSessionId,
|
|
146
|
+
agentSessionFile: selected.transcriptPath,
|
|
147
|
+
retryCount: 0
|
|
148
|
+
},
|
|
149
|
+
runtimeCheckpoint: {
|
|
150
|
+
externalSessionId: selected.externalSessionId,
|
|
151
|
+
transcriptPath: selected.transcriptPath,
|
|
152
|
+
lastDiscoveredAt: Date.now(),
|
|
153
|
+
lastSeenActivityAt: selected.updatedAt
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
await sessionStore.saveSession(session);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const targetUrl = args.targetUrl
|
|
161
|
+
|| process.env.CODEMIE_TEST_API_URL
|
|
162
|
+
|| process.env.CODEMIE_API_URL
|
|
163
|
+
|| 'http://127.0.0.1:3000';
|
|
164
|
+
const codeMieUrl = args.codeMieUrl
|
|
165
|
+
|| process.env.CODEMIE_TEST_CODEMIE_URL
|
|
166
|
+
|| process.env.CODEMIE_URL
|
|
167
|
+
|| targetUrl;
|
|
168
|
+
|
|
169
|
+
let cookies = '';
|
|
170
|
+
if (!args.dryRun) {
|
|
171
|
+
const credentials = await new CodeMieSSO().getStoredCredentials(codeMieUrl);
|
|
172
|
+
if (!credentials?.cookies) {
|
|
173
|
+
console.error(`No stored SSO credentials found for ${codeMieUrl}`);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
cookies = Object.entries(credentials.cookies)
|
|
177
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
178
|
+
.join('; ');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const adapter = new ClaudeDesktopTelemetryAdapter();
|
|
182
|
+
const parsedSession = await adapter.parseSession(selected, session.sessionId);
|
|
183
|
+
const context = {
|
|
184
|
+
apiBaseUrl: targetUrl,
|
|
185
|
+
cookies,
|
|
186
|
+
clientType: 'claude-desktop',
|
|
187
|
+
version: readCliVersion(),
|
|
188
|
+
dryRun: args.dryRun,
|
|
189
|
+
sessionId: session.sessionId,
|
|
190
|
+
agentSessionId: selected.agentSessionId,
|
|
191
|
+
agentSessionFile: selected.transcriptPath
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const processed = await adapter.processParsedSession(parsedSession, context);
|
|
195
|
+
const syncResult = await new SessionSyncer().sync(session.sessionId, context);
|
|
196
|
+
const metricsPath = getSessionMetricsPath(session.sessionId);
|
|
197
|
+
const conversationPath = getSessionConversationPath(session.sessionId);
|
|
198
|
+
|
|
199
|
+
console.log('Claude Desktop telemetry simulation');
|
|
200
|
+
console.log(` mode: ${args.dryRun ? 'dry-run' : 'live'}`);
|
|
201
|
+
console.log(` local session: ${selected.externalSessionId}`);
|
|
202
|
+
console.log(` cli session: ${selected.agentSessionId}`);
|
|
203
|
+
console.log(` codemie session: ${session.sessionId}`);
|
|
204
|
+
console.log(` updated at: ${new Date(selected.updatedAt).toISOString()}`);
|
|
205
|
+
console.log(` transcript: ${selected.transcriptPath}`);
|
|
206
|
+
console.log(` messages parsed: ${parsedSession.messages.length}`);
|
|
207
|
+
console.log(` metrics path: ${metricsPath}${existsSync(metricsPath) ? '' : ' (missing)'}`);
|
|
208
|
+
console.log(` conversation path: ${conversationPath}${existsSync(conversationPath) ? '' : ' (missing)'}`);
|
|
209
|
+
console.log(` processors: ${processed.success ? 'ok' : 'failed'} (${processed.totalRecords} records)`);
|
|
210
|
+
console.log(` sync: ${syncResult.success ? 'ok' : 'failed'} (${syncResult.message})`);
|
|
211
|
+
|
|
212
|
+
if (existsSync(metricsPath)) {
|
|
213
|
+
const lines = readFileSync(metricsPath, 'utf-8').split('\n').filter(Boolean).length;
|
|
214
|
+
console.log(` metrics lines: ${lines}`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (existsSync(conversationPath)) {
|
|
218
|
+
const lines = readFileSync(conversationPath, 'utf-8').split('\n').filter(Boolean).length;
|
|
219
|
+
console.log(` conversation lines:${String(lines).padStart(2, ' ')}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function readCliVersion() {
|
|
223
|
+
try {
|
|
224
|
+
const pkg = JSON.parse(readFileSync(resolve(process.cwd(), 'package.json'), 'utf-8'));
|
|
225
|
+
return pkg.version || '0.0.0';
|
|
226
|
+
} catch {
|
|
227
|
+
return '0.0.0';
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Send a sample /v1/messages request to the local CodeMie proxy daemon.
|
|
4
|
+
*
|
|
5
|
+
* - Auto-discovers URL and gateway key from ~/.codemie/proxy-daemon.json
|
|
6
|
+
* (override with --url and --gateway-key)
|
|
7
|
+
* - Sends Anthropic-style headers and a tiny payload
|
|
8
|
+
* - Supports both buffered and streaming (SSE) responses
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node scripts/test-proxy-endpoint.js # auto-discover, non-streaming
|
|
12
|
+
* node scripts/test-proxy-endpoint.js --stream # SSE streaming
|
|
13
|
+
* node scripts/test-proxy-endpoint.js --url http://localhost:4001 --gateway-key codemie-proxy
|
|
14
|
+
* node scripts/test-proxy-endpoint.js --model claude-sonnet-4-5 --message "Hello"
|
|
15
|
+
*/
|
|
16
|
+
import { readFile } from 'node:fs/promises';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { homedir } from 'node:os';
|
|
19
|
+
|
|
20
|
+
const STATE_FILE = process.env.CODEMIE_HOME
|
|
21
|
+
? join(process.env.CODEMIE_HOME, 'proxy-daemon.json')
|
|
22
|
+
: join(homedir(), '.codemie', 'proxy-daemon.json');
|
|
23
|
+
|
|
24
|
+
function printUsage() {
|
|
25
|
+
console.log(`Usage: node scripts/test-proxy-endpoint.js [options]
|
|
26
|
+
|
|
27
|
+
Options:
|
|
28
|
+
--url <base-url> Proxy base URL (default: read from state file)
|
|
29
|
+
--gateway-key <key> Static bearer key (default: read from state file)
|
|
30
|
+
--endpoint <path> Endpoint path (default: /v1/messages)
|
|
31
|
+
--model <id> Model ID (default: claude-sonnet-4-5)
|
|
32
|
+
--message <text> Prompt text (default: "Test proxy endpoint")
|
|
33
|
+
--max-tokens <n> Max tokens (default: 32)
|
|
34
|
+
--stream Use SSE streaming
|
|
35
|
+
--no-anthropic-version Don't send anthropic-version header
|
|
36
|
+
-h, --help Show this help`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseArgs(argv) {
|
|
40
|
+
const opts = {
|
|
41
|
+
endpoint: '/v1/messages',
|
|
42
|
+
message: 'Test proxy endpoint',
|
|
43
|
+
model: 'claude-sonnet-4-5',
|
|
44
|
+
maxTokens: 32,
|
|
45
|
+
stream: false,
|
|
46
|
+
sendAnthropicVersion: true,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
50
|
+
const arg = argv[i];
|
|
51
|
+
if (arg === '--help' || arg === '-h') { opts.help = true; return opts; }
|
|
52
|
+
if (arg === '--url') { opts.url = argv[++i]; continue; }
|
|
53
|
+
if (arg === '--gateway-key') { opts.gatewayKey = argv[++i]; continue; }
|
|
54
|
+
if (arg === '--endpoint') { opts.endpoint = argv[++i] || opts.endpoint; continue; }
|
|
55
|
+
if (arg === '--message') { opts.message = argv[++i] || opts.message; continue; }
|
|
56
|
+
if (arg === '--model') { opts.model = argv[++i] || opts.model; continue; }
|
|
57
|
+
if (arg === '--max-tokens') {
|
|
58
|
+
const v = Number.parseInt(argv[++i] || '', 10);
|
|
59
|
+
if (Number.isFinite(v) && v > 0) opts.maxTokens = v;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (arg === '--stream') { opts.stream = true; continue; }
|
|
63
|
+
if (arg === '--no-anthropic-version') { opts.sendAnthropicVersion = false; continue; }
|
|
64
|
+
}
|
|
65
|
+
return opts;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function loadDaemonState() {
|
|
69
|
+
try {
|
|
70
|
+
const raw = await readFile(STATE_FILE, 'utf-8');
|
|
71
|
+
return JSON.parse(raw);
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function main() {
|
|
78
|
+
const args = parseArgs(process.argv.slice(2));
|
|
79
|
+
if (args.help) { printUsage(); process.exit(0); }
|
|
80
|
+
|
|
81
|
+
if (!args.url || !args.gatewayKey) {
|
|
82
|
+
const state = await loadDaemonState();
|
|
83
|
+
if (state) {
|
|
84
|
+
args.url ??= state.url;
|
|
85
|
+
args.gatewayKey ??= state.gatewayKey;
|
|
86
|
+
console.log(`[test] Using daemon at ${args.url} (profile: ${state.profile})`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!args.url) {
|
|
91
|
+
console.error(`No proxy URL. Provide --url or start the daemon: codemie proxy start`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
if (!args.gatewayKey) {
|
|
95
|
+
console.error(`No gateway key. Provide --gateway-key or start the daemon: codemie proxy start`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let baseUrl;
|
|
100
|
+
try { baseUrl = new URL(args.url); }
|
|
101
|
+
catch { console.error(`Invalid URL: ${args.url}`); process.exit(1); }
|
|
102
|
+
|
|
103
|
+
const endpoint = args.endpoint.startsWith('/') ? args.endpoint : `/${args.endpoint}`;
|
|
104
|
+
const targetUrl = new URL(endpoint, baseUrl);
|
|
105
|
+
|
|
106
|
+
const body = {
|
|
107
|
+
model: args.model,
|
|
108
|
+
messages: [{ role: 'user', content: args.message }],
|
|
109
|
+
max_tokens: args.maxTokens,
|
|
110
|
+
stream: args.stream,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const headers = {
|
|
114
|
+
'Content-Type': 'application/json',
|
|
115
|
+
Accept: args.stream ? 'text/event-stream' : 'application/json',
|
|
116
|
+
Authorization: `Bearer ${args.gatewayKey}`,
|
|
117
|
+
};
|
|
118
|
+
if (args.sendAnthropicVersion) headers['anthropic-version'] = '2023-06-01';
|
|
119
|
+
|
|
120
|
+
console.log(`[test] POST ${targetUrl.href}`);
|
|
121
|
+
console.log(`[test] Headers: ${JSON.stringify({ ...headers, Authorization: 'Bearer ***' })}`);
|
|
122
|
+
console.log(`[test] Body: ${JSON.stringify(body)}`);
|
|
123
|
+
|
|
124
|
+
const startMs = Date.now();
|
|
125
|
+
let response;
|
|
126
|
+
try {
|
|
127
|
+
response = await fetch(targetUrl, {
|
|
128
|
+
method: 'POST',
|
|
129
|
+
headers,
|
|
130
|
+
body: JSON.stringify(body),
|
|
131
|
+
});
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error(`[test] Request failed: ${error.message}`);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const ttfbMs = Date.now() - startMs;
|
|
138
|
+
console.log(`\n[test] Status: ${response.status} ${response.statusText} (TTFB ${ttfbMs}ms)`);
|
|
139
|
+
console.log(`[test] Response headers:`);
|
|
140
|
+
for (const [k, v] of response.headers.entries()) {
|
|
141
|
+
console.log(` ${k}: ${v}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (args.stream) {
|
|
145
|
+
if (!response.body) {
|
|
146
|
+
console.error('[test] No response body for streaming');
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
console.log('\n[test] --- SSE stream ---');
|
|
150
|
+
const reader = response.body.getReader();
|
|
151
|
+
const decoder = new TextDecoder();
|
|
152
|
+
let chunkCount = 0;
|
|
153
|
+
let totalBytes = 0;
|
|
154
|
+
while (true) {
|
|
155
|
+
const { value, done } = await reader.read();
|
|
156
|
+
if (done) break;
|
|
157
|
+
chunkCount += 1;
|
|
158
|
+
totalBytes += value.length;
|
|
159
|
+
process.stdout.write(decoder.decode(value, { stream: true }));
|
|
160
|
+
}
|
|
161
|
+
const totalMs = Date.now() - startMs;
|
|
162
|
+
console.log(`\n[test] --- end (${chunkCount} chunks, ${totalBytes} bytes, ${totalMs}ms total) ---`);
|
|
163
|
+
} else {
|
|
164
|
+
const text = await response.text();
|
|
165
|
+
const totalMs = Date.now() - startMs;
|
|
166
|
+
console.log(`\n[test] Body (${text.length} bytes, ${totalMs}ms total):`);
|
|
167
|
+
console.log(text);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!response.ok) process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
main().catch(err => { console.error(err); process.exit(1); });
|