pi-harness-runtime 1.1.428 → 1.1.430
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/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/dist/index.js +1 -1
- package/packages/clipboard-plugin/dist/index.js.map +1 -1
- package/packages/clipboard-plugin/package.json +1 -2
- package/packages/clipboard-plugin/src/index.ts +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/connection-runtime/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/durable-review/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-sink/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/logging/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/permission-mode/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/relay/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/dist/runtime-api.d.ts +61 -6
- package/packages/runtime/dist/runtime-api.d.ts.map +1 -1
- package/packages/runtime/dist/runtime-api.js +83 -30
- package/packages/runtime/dist/runtime-api.js.map +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/runtime/src/runtime-api.ts +168 -52
- package/packages/runtime/test/runtime-api.test.ts +169 -0
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/tooling-preference/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/two-phase-review/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/voice-api/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/wiki/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
- package/scripts/lib/runtime-imports.ts +90 -26
- package/scripts/typecheck-packages.mjs +1 -1
- package/scripts/verify-package-contents.ts +42 -0
- package/harness/task-graph.d.ts +0 -51
- package/packages/runtime/command-executor.ts +0 -523
- package/packages/runtime/index.ts +0 -50
- package/packages/runtime/local-runtime-agent.ts +0 -480
- package/packages/runtime/policy-engine.ts +0 -813
- package/packages/runtime/runtime-api.ts +0 -665
- package/packages/runtime/workspace-manager.ts +0 -591
- package/packages/types/src/ai-providers.js +0 -66
- package/packages/types/src/runtime-types.js +0 -3
|
@@ -1,480 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Local Runtime Agent — RFC-0024
|
|
3
|
-
*
|
|
4
|
-
* Server-side coordinator for local browser agent (RFC-0023).
|
|
5
|
-
* Runs on the server, communicates with browser agent on user's machine.
|
|
6
|
-
*
|
|
7
|
-
* Architecture:
|
|
8
|
-
* Server Runtime -> HTTP/RPC request -> Local Browser Agent on iMac
|
|
9
|
-
* -> Playwright persistent profile -> Human login if needed
|
|
10
|
-
* -> scrape usage/reset time -> return safe quota summary
|
|
11
|
-
*
|
|
12
|
-
* Security Rules:
|
|
13
|
-
* - Never return raw cookies
|
|
14
|
-
* - Never return localStorage
|
|
15
|
-
* - Bind to localhost by default
|
|
16
|
-
* - If remote access needed, require VPN/Tailscale and token
|
|
17
|
-
* - Log only safe summaries
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { EventEmitter } from "node:events";
|
|
21
|
-
import { createServer, type Server } from "node:http";
|
|
22
|
-
|
|
23
|
-
export interface QuotaSummary {
|
|
24
|
-
provider: string;
|
|
25
|
-
authenticated: boolean;
|
|
26
|
-
checkedAt: string;
|
|
27
|
-
usageTextSample?: string;
|
|
28
|
-
resetAt?: string;
|
|
29
|
-
quotaUsedPct?: number;
|
|
30
|
-
quotaResetIn?: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface AuthSession {
|
|
34
|
-
provider: string;
|
|
35
|
-
startedAt: string;
|
|
36
|
-
lastCheckedAt: string;
|
|
37
|
-
authenticated: boolean;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface LocalRuntimeAgentConfig {
|
|
41
|
-
/** Port to listen on (default: 3847) */
|
|
42
|
-
port?: number;
|
|
43
|
-
/** Host to bind to (default: localhost) */
|
|
44
|
-
host?: string;
|
|
45
|
-
/** Secret token for authentication (optional) */
|
|
46
|
-
authToken?: string;
|
|
47
|
-
/** Local browser agent URL (default: http://localhost:3848) */
|
|
48
|
-
browserAgentUrl?: string;
|
|
49
|
-
/** Request timeout in ms (default: 30000) */
|
|
50
|
-
requestTimeoutMs?: number;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface LocalRuntimeAgentEvents {
|
|
54
|
-
onQuotaChecked: (summary: QuotaSummary) => void;
|
|
55
|
-
onAuthStarted: (provider: string) => void;
|
|
56
|
-
onAuthCompleted: (provider: string) => void;
|
|
57
|
-
onError: (error: Error) => void;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const DEFAULT_CONFIG: Required<Omit<LocalRuntimeAgentConfig, "authToken">> = {
|
|
61
|
-
port: 3847,
|
|
62
|
-
host: "localhost",
|
|
63
|
-
browserAgentUrl: "http://localhost:3848",
|
|
64
|
-
requestTimeoutMs: 30000,
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export class LocalRuntimeAgent extends EventEmitter {
|
|
68
|
-
private readonly config: LocalRuntimeAgentConfig & Required<Omit<LocalRuntimeAgentConfig, "authToken">>;
|
|
69
|
-
private server: Server | null = null;
|
|
70
|
-
private authSessions: Map<string, AuthSession> = new Map();
|
|
71
|
-
private isRunning = false;
|
|
72
|
-
|
|
73
|
-
constructor(config: LocalRuntimeAgentConfig = {}) {
|
|
74
|
-
super();
|
|
75
|
-
this.config = {
|
|
76
|
-
port: DEFAULT_CONFIG.port,
|
|
77
|
-
host: DEFAULT_CONFIG.host,
|
|
78
|
-
browserAgentUrl: DEFAULT_CONFIG.browserAgentUrl,
|
|
79
|
-
requestTimeoutMs: DEFAULT_CONFIG.requestTimeoutMs,
|
|
80
|
-
...config,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Start the Local Runtime Agent server
|
|
86
|
-
*/
|
|
87
|
-
async start(): Promise<void> {
|
|
88
|
-
if (this.isRunning) {
|
|
89
|
-
throw new Error("Local Runtime Agent is already running");
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return new Promise((resolve, reject) => {
|
|
93
|
-
this.server = createServer(async (req, res) => {
|
|
94
|
-
try {
|
|
95
|
-
await this.handleRequest(req, res);
|
|
96
|
-
} catch (error) {
|
|
97
|
-
this.emitError(error);
|
|
98
|
-
this.sendJson(res, 500, { error: "Internal server error" });
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
this.server.on("error", (error) => {
|
|
103
|
-
this.emitError(error);
|
|
104
|
-
reject(error);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
this.server.listen(this.config.port, this.config.host, () => {
|
|
108
|
-
this.isRunning = true;
|
|
109
|
-
console.log(
|
|
110
|
-
`[LocalRuntimeAgent] Listening on ${this.config.host}:${this.config.port}`,
|
|
111
|
-
);
|
|
112
|
-
resolve();
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Stop the Local Runtime Agent server
|
|
119
|
-
*/
|
|
120
|
-
async stop(): Promise<void> {
|
|
121
|
-
return new Promise((resolve) => {
|
|
122
|
-
if (!this.server) {
|
|
123
|
-
resolve();
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
this.server.close(() => {
|
|
128
|
-
this.isRunning = false;
|
|
129
|
-
this.server = null;
|
|
130
|
-
console.log("[LocalRuntimeAgent] Stopped");
|
|
131
|
-
resolve();
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Check if the agent is running
|
|
138
|
-
*/
|
|
139
|
-
isListening(): boolean {
|
|
140
|
-
return this.isRunning;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Get quota summary for a provider via local browser agent
|
|
145
|
-
*/
|
|
146
|
-
async getQuota(provider: string): Promise<QuotaSummary> {
|
|
147
|
-
const url = `${this.config.browserAgentUrl}/quota/${provider}`;
|
|
148
|
-
const response = await this.fetchWithTimeout(url);
|
|
149
|
-
|
|
150
|
-
if (!response.ok) {
|
|
151
|
-
throw new Error(
|
|
152
|
-
`Failed to get quota: ${response.status} ${response.statusText}`,
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const data = (await response.json()) as QuotaSummary;
|
|
157
|
-
|
|
158
|
-
// Ensure safe response (redact any sensitive data)
|
|
159
|
-
const safeData = this.redactQuotaResponse(data);
|
|
160
|
-
|
|
161
|
-
// Track session
|
|
162
|
-
this.trackQuotaCheck(provider, safeData);
|
|
163
|
-
|
|
164
|
-
return safeData;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Start authentication flow for a provider
|
|
169
|
-
*/
|
|
170
|
-
async startAuth(
|
|
171
|
-
provider: string,
|
|
172
|
-
): Promise<{ started: boolean; message: string }> {
|
|
173
|
-
const session: AuthSession = {
|
|
174
|
-
provider,
|
|
175
|
-
startedAt: new Date().toISOString(),
|
|
176
|
-
lastCheckedAt: new Date().toISOString(),
|
|
177
|
-
authenticated: false,
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
this.authSessions.set(provider, session);
|
|
181
|
-
this.emit("authStarted", provider);
|
|
182
|
-
|
|
183
|
-
const url = `${this.config.browserAgentUrl}/auth/${provider}/start`;
|
|
184
|
-
const response = await this.fetchWithTimeout(url);
|
|
185
|
-
|
|
186
|
-
if (!response.ok) {
|
|
187
|
-
return {
|
|
188
|
-
started: false,
|
|
189
|
-
message: `Failed to start auth: ${response.statusText}`,
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
started: true,
|
|
195
|
-
message: `Auth flow started for ${provider}. Please complete login on your local browser.`,
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Check authentication status for a provider
|
|
201
|
-
*/
|
|
202
|
-
async checkAuth(
|
|
203
|
-
provider: string,
|
|
204
|
-
): Promise<{ authenticated: boolean; since?: string }> {
|
|
205
|
-
const session = this.authSessions.get(provider);
|
|
206
|
-
const url = `${this.config.browserAgentUrl}/auth/${provider}/status`;
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
const response = await this.fetchWithTimeout(url);
|
|
210
|
-
if (response.ok) {
|
|
211
|
-
const data = (await response.json()) as { authenticated: boolean };
|
|
212
|
-
return {
|
|
213
|
-
authenticated: data.authenticated,
|
|
214
|
-
since: session?.startedAt,
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
} catch {
|
|
218
|
-
// Browser agent not reachable
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return {
|
|
222
|
-
authenticated: session?.authenticated ?? false,
|
|
223
|
-
since: session?.startedAt,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Get all active auth sessions
|
|
229
|
-
*/
|
|
230
|
-
getAuthSessions(): AuthSession[] {
|
|
231
|
-
return Array.from(this.authSessions.values());
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// ─── Private Methods ────────────────────────────────────────────────
|
|
235
|
-
|
|
236
|
-
private async handleRequest(
|
|
237
|
-
req: import("node:http").IncomingMessage,
|
|
238
|
-
res: import("node:http").ServerResponse,
|
|
239
|
-
): Promise<void> {
|
|
240
|
-
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
241
|
-
const path = url.pathname;
|
|
242
|
-
const method = req.method ?? "GET";
|
|
243
|
-
|
|
244
|
-
// Authenticate request
|
|
245
|
-
if (!this.authenticateRequest(req, res)) {
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Route requests
|
|
250
|
-
switch (path) {
|
|
251
|
-
case "/health":
|
|
252
|
-
this.handleHealth(res);
|
|
253
|
-
break;
|
|
254
|
-
|
|
255
|
-
case `/quota/${url.pathname.split("/")[2]}`:
|
|
256
|
-
if (method === "GET") {
|
|
257
|
-
await this.handleQuota(res, url.pathname.split("/")[2]);
|
|
258
|
-
} else {
|
|
259
|
-
this.sendJson(res, 405, { error: "Method not allowed" });
|
|
260
|
-
}
|
|
261
|
-
break;
|
|
262
|
-
|
|
263
|
-
case `/auth/${url.pathname.split("/")[2]}/start`:
|
|
264
|
-
if (method === "POST") {
|
|
265
|
-
await this.handleAuthStart(res, url.pathname.split("/")[2]);
|
|
266
|
-
} else {
|
|
267
|
-
this.sendJson(res, 405, { error: "Method not allowed" });
|
|
268
|
-
}
|
|
269
|
-
break;
|
|
270
|
-
|
|
271
|
-
case `/auth/${url.pathname.split("/")[2]}/reset`:
|
|
272
|
-
if (method === "POST") {
|
|
273
|
-
await this.handleAuthReset(res, url.pathname.split("/")[2]);
|
|
274
|
-
} else {
|
|
275
|
-
this.sendJson(res, 405, { error: "Method not allowed" });
|
|
276
|
-
}
|
|
277
|
-
break;
|
|
278
|
-
|
|
279
|
-
case "/sessions":
|
|
280
|
-
this.handleSessions(res);
|
|
281
|
-
break;
|
|
282
|
-
|
|
283
|
-
default:
|
|
284
|
-
this.sendJson(res, 404, { error: "Not found" });
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
private authenticateRequest(
|
|
289
|
-
req: import("node:http").IncomingMessage,
|
|
290
|
-
res: import("node:http").ServerResponse,
|
|
291
|
-
): boolean {
|
|
292
|
-
// No auth token configured - allow all
|
|
293
|
-
if (!this.config.authToken) {
|
|
294
|
-
return true;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const authHeader = req.headers.authorization;
|
|
298
|
-
if (!authHeader) {
|
|
299
|
-
this.sendJson(res, 401, { error: "Authorization required" });
|
|
300
|
-
return false;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const token = authHeader.replace(/^Bearer\s+/i, "");
|
|
304
|
-
if (token !== this.config.authToken) {
|
|
305
|
-
this.sendJson(res, 403, { error: "Invalid token" });
|
|
306
|
-
return false;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return true;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
private async handleHealth(
|
|
313
|
-
res: import("node:http").ServerResponse,
|
|
314
|
-
): Promise<void> {
|
|
315
|
-
const healthy = this.isRunning;
|
|
316
|
-
|
|
317
|
-
// Check if browser agent is reachable
|
|
318
|
-
let browserAgentHealthy = false;
|
|
319
|
-
try {
|
|
320
|
-
const response = await this.fetchWithTimeout(
|
|
321
|
-
`${this.config.browserAgentUrl}/health`,
|
|
322
|
-
);
|
|
323
|
-
browserAgentHealthy = response.ok;
|
|
324
|
-
} catch {
|
|
325
|
-
// Browser agent not reachable
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
this.sendJson(res, healthy ? 200 : 503, {
|
|
329
|
-
status: healthy ? "healthy" : "unhealthy",
|
|
330
|
-
localRuntimeAgent: healthy,
|
|
331
|
-
browserAgent: browserAgentHealthy ? "reachable" : "unreachable",
|
|
332
|
-
timestamp: new Date().toISOString(),
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
private async handleQuota(
|
|
337
|
-
res: import("node:http").ServerResponse,
|
|
338
|
-
provider: string,
|
|
339
|
-
): Promise<void> {
|
|
340
|
-
try {
|
|
341
|
-
const summary = await this.getQuota(provider);
|
|
342
|
-
this.sendJson(res, 200, summary);
|
|
343
|
-
} catch (error) {
|
|
344
|
-
this.sendJson(res, 500, {
|
|
345
|
-
error: error instanceof Error ? error.message : "Failed to get quota",
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
private async handleAuthStart(
|
|
351
|
-
res: import("node:http").ServerResponse,
|
|
352
|
-
provider: string,
|
|
353
|
-
): Promise<void> {
|
|
354
|
-
const result = await this.startAuth(provider);
|
|
355
|
-
this.sendJson(res, result.started ? 200 : 500, result);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
private async handleAuthReset(
|
|
359
|
-
res: import("node:http").ServerResponse,
|
|
360
|
-
provider: string,
|
|
361
|
-
): Promise<void> {
|
|
362
|
-
try {
|
|
363
|
-
const url = `${this.config.browserAgentUrl}/auth/${provider}/reset`;
|
|
364
|
-
const response = await this.fetchWithTimeout(url, { method: "POST" });
|
|
365
|
-
|
|
366
|
-
if (response.ok) {
|
|
367
|
-
this.authSessions.delete(provider);
|
|
368
|
-
this.sendJson(res, 200, { message: "Auth session reset" });
|
|
369
|
-
} else {
|
|
370
|
-
this.sendJson(res, response.status, { error: "Failed to reset auth" });
|
|
371
|
-
}
|
|
372
|
-
} catch (error) {
|
|
373
|
-
this.sendJson(res, 500, {
|
|
374
|
-
error: error instanceof Error ? error.message : "Failed to reset auth",
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
private handleSessions(res: import("node:http").ServerResponse): void {
|
|
380
|
-
this.sendJson(res, 200, {
|
|
381
|
-
sessions: this.getAuthSessions(),
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
private async fetchWithTimeout(
|
|
386
|
-
url: string,
|
|
387
|
-
options: RequestInit = {},
|
|
388
|
-
): Promise<Response> {
|
|
389
|
-
const controller = new AbortController();
|
|
390
|
-
const timeout = setTimeout(
|
|
391
|
-
() => controller.abort(),
|
|
392
|
-
this.config.requestTimeoutMs,
|
|
393
|
-
);
|
|
394
|
-
|
|
395
|
-
try {
|
|
396
|
-
const response = await fetch(url, {
|
|
397
|
-
...options,
|
|
398
|
-
signal: controller.signal,
|
|
399
|
-
});
|
|
400
|
-
return response;
|
|
401
|
-
} finally {
|
|
402
|
-
clearTimeout(timeout);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
private redactQuotaResponse(data: QuotaSummary): QuotaSummary {
|
|
407
|
-
// Ensure no raw cookies, tokens, or sensitive data are included
|
|
408
|
-
const safe: QuotaSummary = {
|
|
409
|
-
provider: data.provider,
|
|
410
|
-
authenticated: data.authenticated,
|
|
411
|
-
checkedAt: data.checkedAt,
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
// Only include safe fields
|
|
415
|
-
if (data.usageTextSample) {
|
|
416
|
-
// Truncate and redact any potential sensitive patterns
|
|
417
|
-
safe.usageTextSample = this.redactText(data.usageTextSample);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
if (data.resetAt) {
|
|
421
|
-
safe.resetAt = data.resetAt;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (data.quotaUsedPct !== undefined) {
|
|
425
|
-
safe.quotaUsedPct = data.quotaUsedPct;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if (data.quotaResetIn) {
|
|
429
|
-
safe.quotaResetIn = data.quotaResetIn;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return safe;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
private redactText(text: string): string {
|
|
436
|
-
// Redact potential tokens, cookies, session IDs
|
|
437
|
-
return text
|
|
438
|
-
.replace(/[A-Za-z0-9+/]{32,}={0,2}/g, "[REDACTED]") // Long base64 strings
|
|
439
|
-
.replace(/session[_-]?id["\s:=]+[^\s&"]+/gi, "session_id=[REDACTED]")
|
|
440
|
-
.replace(/token["\s:=]+[^\s&"]+/gi, "token=[REDACTED]")
|
|
441
|
-
.replace(/cookie["\s:=]+[^\s&"]+/gi, "cookie=[REDACTED]");
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
private trackQuotaCheck(provider: string, summary: QuotaSummary): void {
|
|
445
|
-
const existing = this.authSessions.get(provider);
|
|
446
|
-
if (existing) {
|
|
447
|
-
existing.lastCheckedAt = new Date().toISOString();
|
|
448
|
-
existing.authenticated = summary.authenticated;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
this.emit("quotaChecked", summary);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
private emitError(error: unknown): void {
|
|
455
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
456
|
-
this.emit("error", err);
|
|
457
|
-
console.error("[LocalRuntimeAgent] Error:", err.message);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
private sendJson(
|
|
461
|
-
res: import("node:http").ServerResponse,
|
|
462
|
-
status: number,
|
|
463
|
-
data: unknown,
|
|
464
|
-
): void {
|
|
465
|
-
res.writeHead(status, { "Content-Type": "application/json" });
|
|
466
|
-
res.end(JSON.stringify(data));
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/**
|
|
471
|
-
* Create a LocalRuntimeAgent with default config for harness runtime
|
|
472
|
-
*/
|
|
473
|
-
export function createHarnessRuntimeAgent(): LocalRuntimeAgent {
|
|
474
|
-
return new LocalRuntimeAgent({
|
|
475
|
-
port: 3847,
|
|
476
|
-
host: "localhost",
|
|
477
|
-
browserAgentUrl: "http://localhost:3848",
|
|
478
|
-
requestTimeoutMs: 30000,
|
|
479
|
-
});
|
|
480
|
-
}
|