@jsonstudio/rcc 0.89.524 → 0.89.552
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/dist/build-info.js +2 -2
- package/dist/cli.js +53 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/auth/oauth-lifecycle.js +97 -24
- package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
- package/dist/providers/core/runtime/base-provider.d.ts +2 -0
- package/dist/providers/core/runtime/base-provider.js +35 -1
- package/dist/providers/core/runtime/base-provider.js.map +1 -1
- package/dist/providers/core/runtime/http-request-executor.js +34 -0
- package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
- package/dist/providers/core/runtime/http-transport-provider.js +23 -0
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/runtime/iflow-http-provider.d.ts +4 -0
- package/dist/providers/core/runtime/iflow-http-provider.js +28 -0
- package/dist/providers/core/runtime/iflow-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/rate-limit-manager.d.ts +30 -0
- package/dist/providers/core/runtime/rate-limit-manager.js +136 -0
- package/dist/providers/core/runtime/rate-limit-manager.js.map +1 -0
- package/dist/providers/core/utils/provider-error-reporter.js +31 -5
- package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
- package/dist/providers/core/utils/provider-type-utils.js +1 -1
- package/dist/providers/core/utils/provider-type-utils.js.map +1 -1
- package/dist/server/runtime/http-server/provider-utils.js +1 -1
- package/dist/server/runtime/http-server/provider-utils.js.map +1 -1
- package/dist/server/runtime/http-server/routes.js +10 -50
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/runtime-manager.js +15 -0
- package/dist/server/runtime/http-server/runtime-manager.js.map +1 -1
- package/dist/token-daemon/history-store.d.ts +75 -0
- package/dist/token-daemon/history-store.js +207 -0
- package/dist/token-daemon/history-store.js.map +1 -0
- package/dist/token-daemon/index.js +104 -10
- package/dist/token-daemon/index.js.map +1 -1
- package/dist/token-daemon/token-daemon.d.ts +3 -0
- package/dist/token-daemon/token-daemon.js +115 -10
- package/dist/token-daemon/token-daemon.js.map +1 -1
- package/dist/token-portal/local-token-portal.d.ts +1 -0
- package/dist/token-portal/local-token-portal.js +89 -0
- package/dist/token-portal/local-token-portal.js.map +1 -0
- package/dist/token-portal/render.d.ts +10 -0
- package/dist/token-portal/render.js +56 -0
- package/dist/token-portal/render.js.map +1 -0
- package/package.json +2 -1
- package/scripts/test-iflow-web-search.mjs +141 -0
- package/scripts/test-iflow.mjs +93 -1
|
@@ -7,6 +7,7 @@ export interface TokenDaemonOptions {
|
|
|
7
7
|
export declare class TokenDaemon {
|
|
8
8
|
private readonly intervalMs;
|
|
9
9
|
private readonly refreshAheadMinutes;
|
|
10
|
+
private readonly historyStore;
|
|
10
11
|
private timer;
|
|
11
12
|
private lastRefreshAttempt;
|
|
12
13
|
constructor(options?: Partial<TokenDaemonOptions>);
|
|
@@ -17,4 +18,6 @@ export declare class TokenDaemon {
|
|
|
17
18
|
static getSnapshot(): Promise<TokenDaemonSnapshot>;
|
|
18
19
|
static findTokenBySelector(selector: string): Promise<TokenDescriptor | null>;
|
|
19
20
|
private logDebug;
|
|
21
|
+
private ensurePortalEnvironment;
|
|
22
|
+
private recordHistoryEvent;
|
|
20
23
|
}
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { homedir } from 'os';
|
|
1
4
|
import chalk from 'chalk';
|
|
2
5
|
import { ensureValidOAuthToken } from '../providers/auth/oauth-lifecycle.js';
|
|
3
6
|
import { collectTokenSnapshot } from './token-utils.js';
|
|
4
7
|
import { buildTokenKey, SUPPORTED_OAUTH_PROVIDERS } from './token-types.js';
|
|
8
|
+
import { TokenHistoryStore } from './history-store.js';
|
|
9
|
+
import { ensureLocalTokenPortalEnv } from '../token-portal/local-token-portal.js';
|
|
5
10
|
const DEBUG_FLAG = String(process.env.ROUTECODEX_TOKEN_DAEMON_DEBUG || '').trim().toLowerCase();
|
|
6
11
|
const DEBUG_ENABLED = DEBUG_FLAG === '1' || DEBUG_FLAG === 'true';
|
|
7
12
|
const DEFAULT_INTERVAL_MS = 60_000;
|
|
8
13
|
const DEFAULT_REFRESH_AHEAD_MINUTES = 30;
|
|
9
14
|
const MIN_REFRESH_INTERVAL_MS = 5 * 60_000;
|
|
15
|
+
const GEMINI_PROVIDER_IDS = new Set(['gemini-cli', 'antigravity']);
|
|
10
16
|
export class TokenDaemon {
|
|
11
17
|
intervalMs;
|
|
12
18
|
refreshAheadMinutes;
|
|
19
|
+
historyStore;
|
|
13
20
|
timer = null;
|
|
14
21
|
lastRefreshAttempt = new Map();
|
|
15
22
|
constructor(options) {
|
|
@@ -18,6 +25,7 @@ export class TokenDaemon {
|
|
|
18
25
|
options?.refreshAheadMinutes && options.refreshAheadMinutes > 0
|
|
19
26
|
? options.refreshAheadMinutes
|
|
20
27
|
: DEFAULT_REFRESH_AHEAD_MINUTES;
|
|
28
|
+
this.historyStore = new TokenHistoryStore();
|
|
21
29
|
}
|
|
22
30
|
async start() {
|
|
23
31
|
console.log(chalk.blue('ℹ'), 'Token Refresh Daemon started');
|
|
@@ -63,10 +71,13 @@ export class TokenDaemon {
|
|
|
63
71
|
continue;
|
|
64
72
|
}
|
|
65
73
|
// Only attempt auto-refresh when token is valid/expiring and within refresh window
|
|
66
|
-
if (msLeft
|
|
74
|
+
if (msLeft > refreshAheadMs) {
|
|
67
75
|
this.logDebug(`[daemon] skip token outside refresh window alias=${token.alias} remainingMs=${msLeft} window=${refreshAheadMs}`);
|
|
68
76
|
continue;
|
|
69
77
|
}
|
|
78
|
+
if (msLeft <= 0) {
|
|
79
|
+
this.logDebug(`[daemon] token already expired alias=${token.alias} - forcing immediate refresh`);
|
|
80
|
+
}
|
|
70
81
|
const last = this.lastRefreshAttempt.get(key) || 0;
|
|
71
82
|
if (now - last < MIN_REFRESH_INTERVAL_MS) {
|
|
72
83
|
this.logDebug(`[daemon] skip token throttle alias=${token.alias} sinceLast=${now - last}ms minInterval=${MIN_REFRESH_INTERVAL_MS}`);
|
|
@@ -83,17 +94,41 @@ export class TokenDaemon {
|
|
|
83
94
|
async trySilentRefresh(token) {
|
|
84
95
|
const providerType = token.provider;
|
|
85
96
|
const rawType = `${providerType}-oauth`;
|
|
97
|
+
const tokenMtimeBefore = await getTokenFileMtime(token.filePath);
|
|
98
|
+
if (await this.historyStore.isAutoSuspended(token, tokenMtimeBefore)) {
|
|
99
|
+
this.logDebug(`[daemon] skip refresh provider=${providerType} alias=${token.alias} reason=auto-suspended`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const portalReady = await this.ensurePortalEnvironment();
|
|
103
|
+
if (!portalReady) {
|
|
104
|
+
this.logDebug(`[daemon] skip refresh provider=${providerType} alias=${token.alias} reason=portal-unavailable`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const startedAt = Date.now();
|
|
86
108
|
console.log(chalk.gray('◉'), `Auto-refresh token for ${providerType} (${token.displayName}), file=${token.filePath}`);
|
|
87
109
|
this.logDebug(`[daemon] trigger refresh provider=${providerType} alias=${token.alias} file=${token.filePath}`);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
110
|
+
try {
|
|
111
|
+
await ensureValidOAuthToken(providerType, {
|
|
112
|
+
type: rawType,
|
|
113
|
+
tokenFile: token.filePath
|
|
114
|
+
}, {
|
|
115
|
+
openBrowser: true,
|
|
116
|
+
forceReacquireIfRefreshFails: true
|
|
117
|
+
});
|
|
118
|
+
console.log(chalk.green('✓'), `Token refreshed for ${providerType} (${token.displayName})`);
|
|
119
|
+
this.logDebug(`[daemon] refresh success provider=${providerType} alias=${token.alias}`);
|
|
120
|
+
const tokenMtimeAfter = await getTokenFileMtime(token.filePath);
|
|
121
|
+
await this.recordHistoryEvent(token, 'success', startedAt, {
|
|
122
|
+
tokenFileMtime: tokenMtimeAfter
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
await this.recordHistoryEvent(token, 'failure', startedAt, {
|
|
127
|
+
error,
|
|
128
|
+
tokenFileMtime: tokenMtimeBefore
|
|
129
|
+
});
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
97
132
|
}
|
|
98
133
|
static async getSnapshot() {
|
|
99
134
|
return collectTokenSnapshot();
|
|
@@ -131,6 +166,7 @@ export class TokenDaemon {
|
|
|
131
166
|
if (first) {
|
|
132
167
|
return first;
|
|
133
168
|
}
|
|
169
|
+
return createSyntheticTokenDescriptor(providerMatch);
|
|
134
170
|
}
|
|
135
171
|
return null;
|
|
136
172
|
}
|
|
@@ -140,5 +176,74 @@ export class TokenDaemon {
|
|
|
140
176
|
}
|
|
141
177
|
console.log(chalk.gray('[token-daemon-debug]'), message);
|
|
142
178
|
}
|
|
179
|
+
async ensurePortalEnvironment() {
|
|
180
|
+
try {
|
|
181
|
+
await ensureLocalTokenPortalEnv();
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
186
|
+
this.logDebug(`[daemon] portal init failed: ${msg}`);
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async recordHistoryEvent(token, outcome, startedAt, meta) {
|
|
191
|
+
try {
|
|
192
|
+
const completedAt = Date.now();
|
|
193
|
+
await this.historyStore.recordRefreshResult(token, outcome, {
|
|
194
|
+
startedAt,
|
|
195
|
+
completedAt,
|
|
196
|
+
durationMs: completedAt - startedAt,
|
|
197
|
+
mode: 'auto',
|
|
198
|
+
error: meta?.error ? (meta.error instanceof Error ? meta.error.message : String(meta.error)) : undefined,
|
|
199
|
+
tokenFileMtime: meta?.tokenFileMtime ?? null
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
catch (historyError) {
|
|
203
|
+
this.logDebug(`[daemon] history persistence failed: ${historyError instanceof Error ? historyError.message : String(historyError)}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async function getTokenFileMtime(filePath) {
|
|
208
|
+
try {
|
|
209
|
+
const stats = await fs.stat(filePath);
|
|
210
|
+
return stats.mtimeMs;
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function defaultTokenFilePath(provider) {
|
|
217
|
+
const home = homedir();
|
|
218
|
+
if (provider === 'iflow') {
|
|
219
|
+
return path.join(home, '.iflow', 'oauth_creds.json');
|
|
220
|
+
}
|
|
221
|
+
if (provider === 'qwen') {
|
|
222
|
+
return path.join(home, '.routecodex', 'auth', 'qwen-oauth.json');
|
|
223
|
+
}
|
|
224
|
+
if (GEMINI_PROVIDER_IDS.has(provider)) {
|
|
225
|
+
const file = provider === 'antigravity' ? 'antigravity-oauth.json' : 'gemini-oauth.json';
|
|
226
|
+
return path.join(home, '.routecodex', 'auth', file);
|
|
227
|
+
}
|
|
228
|
+
return path.join(home, '.routecodex', 'auth', `${provider}-oauth-1-default.json`);
|
|
229
|
+
}
|
|
230
|
+
function createSyntheticTokenDescriptor(provider) {
|
|
231
|
+
const filePath = defaultTokenFilePath(provider);
|
|
232
|
+
return {
|
|
233
|
+
provider,
|
|
234
|
+
filePath,
|
|
235
|
+
sequence: 0,
|
|
236
|
+
alias: 'default',
|
|
237
|
+
state: {
|
|
238
|
+
hasAccessToken: false,
|
|
239
|
+
hasRefreshToken: false,
|
|
240
|
+
hasApiKey: false,
|
|
241
|
+
expiresAt: null,
|
|
242
|
+
msUntilExpiry: null,
|
|
243
|
+
status: 'invalid',
|
|
244
|
+
noRefresh: false
|
|
245
|
+
},
|
|
246
|
+
displayName: path.basename(filePath)
|
|
247
|
+
};
|
|
143
248
|
}
|
|
144
249
|
//# sourceMappingURL=token-daemon.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-daemon.js","sourceRoot":"","sources":["../../src/token-daemon/token-daemon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EACL,oBAAoB,EAErB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,aAAa,EACb,yBAAyB,EAG1B,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"token-daemon.js","sourceRoot":"","sources":["../../src/token-daemon/token-daemon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ensureLocalTokenPortalEnv(): Promise<string>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import { URL } from 'url';
|
|
3
|
+
import { HTTP_PROTOCOLS, LOCAL_HOSTS } from '../constants/index.js';
|
|
4
|
+
import { renderTokenPortalPage } from './render.js';
|
|
5
|
+
class LocalTokenPortalServer {
|
|
6
|
+
server = null;
|
|
7
|
+
port = null;
|
|
8
|
+
baseUrl = null;
|
|
9
|
+
get currentPort() {
|
|
10
|
+
return this.port;
|
|
11
|
+
}
|
|
12
|
+
async ensureRunning() {
|
|
13
|
+
if (this.server && this.port && this.baseUrl) {
|
|
14
|
+
return this.baseUrl;
|
|
15
|
+
}
|
|
16
|
+
await this.startServer();
|
|
17
|
+
if (!this.baseUrl) {
|
|
18
|
+
throw new Error('Failed to initialize local token portal');
|
|
19
|
+
}
|
|
20
|
+
return this.baseUrl;
|
|
21
|
+
}
|
|
22
|
+
async startServer() {
|
|
23
|
+
if (this.server) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
await new Promise((resolve, reject) => {
|
|
27
|
+
const srv = http.createServer((req, res) => {
|
|
28
|
+
this.handleRequest(req, res).catch((error) => {
|
|
29
|
+
res.statusCode = 500;
|
|
30
|
+
res.end(`Token portal error: ${error instanceof Error ? error.message : String(error)}`);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
srv.on('error', (error) => {
|
|
34
|
+
reject(error);
|
|
35
|
+
});
|
|
36
|
+
srv.listen(0, '127.0.0.1', () => {
|
|
37
|
+
const address = srv.address();
|
|
38
|
+
if (!address?.port) {
|
|
39
|
+
reject(new Error('Failed to obtain token portal port'));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
this.server = srv;
|
|
43
|
+
this.port = address.port;
|
|
44
|
+
this.baseUrl = `${HTTP_PROTOCOLS.HTTP}${LOCAL_HOSTS.IPV4}:${address.port}/token-auth/demo`;
|
|
45
|
+
resolve();
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async handleRequest(req, res) {
|
|
50
|
+
if (!req.url) {
|
|
51
|
+
res.statusCode = 404;
|
|
52
|
+
res.end('Not Found');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const url = new URL(req.url, `${HTTP_PROTOCOLS.HTTP}${LOCAL_HOSTS.IPV4}`);
|
|
56
|
+
if (url.pathname !== '/token-auth/demo') {
|
|
57
|
+
res.statusCode = 404;
|
|
58
|
+
res.end('Not Found');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const provider = url.searchParams.get('provider') || 'unknown-provider';
|
|
62
|
+
const alias = url.searchParams.get('alias') || 'default';
|
|
63
|
+
const tokenFile = url.searchParams.get('tokenFile') || '~/.routecodex/auth/unknown-token.json';
|
|
64
|
+
const oauthUrl = url.searchParams.get('oauthUrl') || 'https://accounts.google.com/o/oauth2/v2/auth';
|
|
65
|
+
const sessionId = url.searchParams.get('sessionId') || 'local-session';
|
|
66
|
+
const displayName = url.searchParams.get('displayName') || undefined;
|
|
67
|
+
const html = renderTokenPortalPage({
|
|
68
|
+
provider,
|
|
69
|
+
alias,
|
|
70
|
+
tokenFile,
|
|
71
|
+
oauthUrl,
|
|
72
|
+
sessionId,
|
|
73
|
+
displayName
|
|
74
|
+
});
|
|
75
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
76
|
+
res.end(html);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const localPortal = new LocalTokenPortalServer();
|
|
80
|
+
export async function ensureLocalTokenPortalEnv() {
|
|
81
|
+
const baseUrl = await localPortal.ensureRunning();
|
|
82
|
+
process.env.ROUTECODEX_TOKEN_PORTAL_BASE = baseUrl;
|
|
83
|
+
process.env.ROUTECODEX_HTTP_HOST = LOCAL_HOSTS.IPV4;
|
|
84
|
+
if (localPortal.currentPort) {
|
|
85
|
+
process.env.ROUTECODEX_HTTP_PORT = String(localPortal.currentPort);
|
|
86
|
+
}
|
|
87
|
+
return baseUrl;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=local-token-portal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-token-portal.js","sourceRoot":"","sources":["../../src/token-portal/local-token-portal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
function escapeHtml(value) {
|
|
2
|
+
return value
|
|
3
|
+
.replace(/&/g, '&')
|
|
4
|
+
.replace(/</g, '<')
|
|
5
|
+
.replace(/>/g, '>')
|
|
6
|
+
.replace(/"/g, '"')
|
|
7
|
+
.replace(/'/g, ''');
|
|
8
|
+
}
|
|
9
|
+
export function renderTokenPortalPage(props) {
|
|
10
|
+
const { provider, alias, tokenFile, sessionId, oauthUrl, displayName } = props;
|
|
11
|
+
const providerLabel = displayName ? `${displayName} (${provider})` : provider;
|
|
12
|
+
return `<!DOCTYPE html>
|
|
13
|
+
<html lang="en">
|
|
14
|
+
<head>
|
|
15
|
+
<meta charset="utf-8" />
|
|
16
|
+
<title>RouteCodex Token Auth</title>
|
|
17
|
+
<style>
|
|
18
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 40px; color: #1f2933; background:#f8fafc; }
|
|
19
|
+
.card { border: 1px solid #e2e8f0; border-radius: 16px; padding: 28px; max-width: 640px; background:#fff; box-shadow: 0 25px 50px -12px rgba(15,23,42,0.25); }
|
|
20
|
+
.title { font-size: 26px; margin-bottom: 16px; font-weight: 600; color:#0f172a; }
|
|
21
|
+
.meta { margin-bottom: 18px; line-height: 1.7; }
|
|
22
|
+
.meta div { margin-bottom:4px; }
|
|
23
|
+
.meta span { font-weight: 600; color:#475569; margin-right:6px; }
|
|
24
|
+
button { background: linear-gradient(135deg,#2563eb,#4f46e5); color: white; border: none; border-radius: 10px; padding: 14px 26px; font-size: 16px; font-weight:600; cursor: pointer; transition: transform .1s ease; }
|
|
25
|
+
button:hover { transform: translateY(-1px); }
|
|
26
|
+
button:disabled { opacity: 0.6; cursor: not-allowed; }
|
|
27
|
+
.hint { color: #475569; margin-top: 18px; font-size: 14px; }
|
|
28
|
+
</style>
|
|
29
|
+
</head>
|
|
30
|
+
<body>
|
|
31
|
+
<div class="card">
|
|
32
|
+
<div class="title">Authorize Token</div>
|
|
33
|
+
<div class="meta">
|
|
34
|
+
<div><span>Provider:</span> ${escapeHtml(providerLabel)}</div>
|
|
35
|
+
<div><span>Alias:</span> ${escapeHtml(alias || 'default')}</div>
|
|
36
|
+
<div><span>Token file:</span> ${escapeHtml(tokenFile)}</div>
|
|
37
|
+
<div><span>Session ID:</span> ${escapeHtml(sessionId)}</div>
|
|
38
|
+
</div>
|
|
39
|
+
<button id="continue-btn">Continue to OAuth</button>
|
|
40
|
+
<div class="hint">
|
|
41
|
+
RouteCodex shows this page before contacting the upstream OAuth portal so you know exactly which credential is being refreshed.
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<script>
|
|
45
|
+
const btn = document.getElementById('continue-btn');
|
|
46
|
+
const oauthUrl = ${JSON.stringify(oauthUrl)};
|
|
47
|
+
btn.addEventListener('click', () => {
|
|
48
|
+
btn.disabled = true;
|
|
49
|
+
btn.innerText = 'Opening OAuth…';
|
|
50
|
+
window.location.href = oauthUrl;
|
|
51
|
+
});
|
|
52
|
+
</script>
|
|
53
|
+
</body>
|
|
54
|
+
</html>`;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/token-portal/render.ts"],"names":[],"mappings":"AASA,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAA2B;IAC/D,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;IAC/E,MAAM,aAAa,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,WAAW,KAAK,QAAQ,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;IAE9E,OAAO;;;;;;;;;;;;;;;;;;;;;;sCAsB6B,UAAU,CAAC,aAAa,CAAC;mCAC5B,UAAU,CAAC,KAAK,IAAI,SAAS,CAAC;wCACzB,UAAU,CAAC,SAAS,CAAC;wCACrB,UAAU,CAAC,SAAS,CAAC;;;;;;;;;yBASpC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;;;;;;;;QAQzC,CAAC;AACT,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsonstudio/rcc",
|
|
3
|
-
"version": "0.89.
|
|
3
|
+
"version": "0.89.552",
|
|
4
4
|
"description": "Multi-provider OpenAI proxy server with anthropic/responses/chat support (dev)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -127,6 +127,7 @@
|
|
|
127
127
|
"dependencies": {
|
|
128
128
|
"@anthropic-ai/sdk": "^0.65.0",
|
|
129
129
|
"@jsonstudio/llms": "^0.6.375",
|
|
130
|
+
"@jsonstudio/rcc": "^0.89.524",
|
|
130
131
|
"@lmstudio/sdk": "^1.5.0",
|
|
131
132
|
"@radix-ui/react-switch": "^1.2.6",
|
|
132
133
|
"@types/socket.io": "^3.0.1",
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* IFlow web_search 直连测试脚本(不经过 RouteCodex pipeline)
|
|
4
|
+
*
|
|
5
|
+
* 目标:完全复用 iFlow CLI 内置 WebSearchTool 的请求协议,验证
|
|
6
|
+
* `https://apis.iflow.cn/v1/chat/retrieve` 能否正常返回搜索结果。
|
|
7
|
+
*
|
|
8
|
+
* 用法:
|
|
9
|
+
* node scripts/test-iflow-web-search.mjs
|
|
10
|
+
*
|
|
11
|
+
* 可选环境变量:
|
|
12
|
+
* IFLOW_SETTINGS_PATH 自定义 settings.json 路径(默认 ~/.iflow/settings.json)
|
|
13
|
+
* IFLOW_SEARCH_QUERY 搜索关键词(默认: "today international news 2025")
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import fs from 'node:fs/promises';
|
|
17
|
+
import os from 'node:os';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
|
|
20
|
+
async function main() {
|
|
21
|
+
const settingsPath =
|
|
22
|
+
process.env.IFLOW_SETTINGS_PATH ||
|
|
23
|
+
path.join(os.homedir(), '.iflow', 'settings.json');
|
|
24
|
+
const query =
|
|
25
|
+
process.env.IFLOW_SEARCH_QUERY || 'today international news 2025';
|
|
26
|
+
|
|
27
|
+
console.log(
|
|
28
|
+
`[iflow-web-search] Using settings: ${settingsPath}, query="${query}"`
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const settingsRaw = await fs.readFile(settingsPath, 'utf-8');
|
|
32
|
+
const settings = JSON.parse(settingsRaw);
|
|
33
|
+
|
|
34
|
+
const baseUrl =
|
|
35
|
+
(typeof settings.baseUrl === 'string' && settings.baseUrl.trim()) ||
|
|
36
|
+
'https://apis.iflow.cn/v1';
|
|
37
|
+
const searchApiKey =
|
|
38
|
+
(typeof settings.searchApiKey === 'string' &&
|
|
39
|
+
settings.searchApiKey.trim()) ||
|
|
40
|
+
(typeof settings.apiKey === 'string' && settings.apiKey.trim()) ||
|
|
41
|
+
null;
|
|
42
|
+
|
|
43
|
+
if (!searchApiKey) {
|
|
44
|
+
console.error(
|
|
45
|
+
'[iflow-web-search] ERROR: searchApiKey/apiKey not found in settings.json'
|
|
46
|
+
);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const url = `${baseUrl.replace(/\/$/, '')}/chat/retrieve`;
|
|
51
|
+
|
|
52
|
+
// Body 对齐 iFlow CLI WebSearchTool.executeInternal
|
|
53
|
+
const body = {
|
|
54
|
+
query,
|
|
55
|
+
history: {},
|
|
56
|
+
userId: 2,
|
|
57
|
+
userIp: '42.120.74.197',
|
|
58
|
+
appCode: 'SEARCH_CHATBOT',
|
|
59
|
+
chatId: Date.now(), // 使用时间戳作为 chatId 即可
|
|
60
|
+
phase: 'UNIFY',
|
|
61
|
+
enableQueryRewrite: false,
|
|
62
|
+
enableRetrievalSecurity: false,
|
|
63
|
+
enableIntention: false,
|
|
64
|
+
searchEngineList: ['GOOGLE', 'BING', 'SCHOLAR', 'AIPGC', 'PDF']
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
console.log(`[iflow-web-search] POST ${url}`);
|
|
68
|
+
|
|
69
|
+
const controller = new AbortController();
|
|
70
|
+
const timeoutMs = Number(process.env.IFLOW_SEARCH_TIMEOUT_MS || '30000');
|
|
71
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const res = await fetch(url, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: {
|
|
77
|
+
Authorization: `Bearer ${searchApiKey}`,
|
|
78
|
+
'Content-Type': 'application/json'
|
|
79
|
+
},
|
|
80
|
+
body: JSON.stringify(body),
|
|
81
|
+
signal: controller.signal
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
clearTimeout(timeoutId);
|
|
85
|
+
|
|
86
|
+
console.log(`[iflow-web-search] HTTP ${res.status}`);
|
|
87
|
+
const json = await res.json().catch(async () => {
|
|
88
|
+
const text = await res.text();
|
|
89
|
+
console.error('[iflow-web-search] Non-JSON response:', text.slice(0, 500));
|
|
90
|
+
throw new Error('Response is not valid JSON');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const dataArray = Array.isArray(json?.data) ? json.data : [];
|
|
94
|
+
console.log(
|
|
95
|
+
`[iflow-web-search] data.length = ${dataArray.length}, keys=${Object.keys(
|
|
96
|
+
json || {}
|
|
97
|
+
).join(',')}`
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
console.error(
|
|
102
|
+
'[iflow-web-search] ERROR: upstream returned non-200 status',
|
|
103
|
+
JSON.stringify(json, null, 2).slice(0, 2000)
|
|
104
|
+
);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!dataArray.length) {
|
|
109
|
+
console.warn(
|
|
110
|
+
'[iflow-web-search] WARN: data array is empty, check query or account permissions.'
|
|
111
|
+
);
|
|
112
|
+
} else {
|
|
113
|
+
const first = dataArray[0] || {};
|
|
114
|
+
console.log(
|
|
115
|
+
'[iflow-web-search] First item:',
|
|
116
|
+
JSON.stringify(
|
|
117
|
+
{
|
|
118
|
+
title: first.title,
|
|
119
|
+
url: first.url,
|
|
120
|
+
time: first.time,
|
|
121
|
+
abstractInfo: first.abstractInfo
|
|
122
|
+
},
|
|
123
|
+
null,
|
|
124
|
+
2
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log('[iflow-web-search] ✅ connectivity OK');
|
|
130
|
+
} catch (error) {
|
|
131
|
+
clearTimeout(timeoutId);
|
|
132
|
+
console.error('[iflow-web-search] ❌ request failed:', error);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
main().catch((error) => {
|
|
138
|
+
console.error('[iflow-web-search] ❌ unexpected error:', error);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
});
|
|
141
|
+
|