replicas-engine 0.1.231 → 0.1.233
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/src/index.js +213 -140
- package/package.json +1 -1
package/dist/src/index.js
CHANGED
|
@@ -357,18 +357,18 @@ var GIT_PARTIAL_CLONE_FILTER = "blob:none";
|
|
|
357
357
|
|
|
358
358
|
// ../shared/src/default-skills/replicas-agent/abilities/computer.ts
|
|
359
359
|
var SECTION = `### Computer use (Linux desktop control)
|
|
360
|
-
Drive the workspace's Linux desktop - open a browser, click, type, scroll, screenshot, record -
|
|
360
|
+
Drive the workspace's Linux desktop - open a browser, click, type, scroll, screenshot, record - via the \`replicas computer\` CLI. Every Replicas workspace boots with Xvfb / openbox / x11vnc / noVNC pre-installed and the live noVNC viewer is automatically published as an authenticated preview, so a \`Desktop\` tab is always available in the dashboard. Use \`replicas computer info\` to get the viewer URL when you want to share it (e.g. point a user on Slack at the live stream).
|
|
361
361
|
|
|
362
362
|
**Reference:** \`references/COMPUTER-USE.md\`
|
|
363
363
|
|
|
364
364
|
Use this when:
|
|
365
365
|
- A task requires interacting with a website, web app, or desktop application that has no usable API
|
|
366
|
-
- You want the user to watch the agent work -
|
|
366
|
+
- You want the user to watch the agent work - they can open the \`Desktop\` tab, or you can share the URL from \`replicas computer info\`
|
|
367
367
|
- You're testing UI changes in a browser before reporting them as done
|
|
368
368
|
- You want to record a screen capture of a task as proof to share back`;
|
|
369
369
|
var REFERENCE = `# Computer use (Linux desktop control)
|
|
370
370
|
|
|
371
|
-
Every Replicas workspace boots with a full Linux desktop stack - Xvfb (1920\xD71080), openbox, tint2, x11vnc, noVNC, ffmpeg, and Google Chrome. You drive
|
|
371
|
+
Every Replicas workspace boots with a full Linux desktop stack - Xvfb (1920\xD71080), openbox, tint2, x11vnc, noVNC, ffmpeg, and Google Chrome. The engine also auto-registers the noVNC viewer (port 6080) as an authenticated preview at startup, so the dashboard \`Desktop\` tab is live the moment the workspace is up. You drive the desktop through the \`replicas computer\` CLI.
|
|
372
372
|
|
|
373
373
|
Use this for anything the user can't reasonably do via an API - clicking around web apps, filling forms, testing UI changes, dragging files between desktop apps, recording a walkthrough.
|
|
374
374
|
|
|
@@ -376,7 +376,7 @@ Use this for anything the user can't reasonably do via an API - clicking around
|
|
|
376
376
|
|
|
377
377
|
- **Prefer real APIs first.** If a task has a CLI or HTTP API (GitHub, Linear, Slack, Replicas itself), use that. Driving a UI is slower, flakier, and less auditable.
|
|
378
378
|
- **Use it when there's no API**: testing a frontend you just changed, navigating a vendor portal, demonstrating a flow on video.
|
|
379
|
-
- **Use it when the user wants to watch.**
|
|
379
|
+
- **Use it when the user wants to watch.** The dashboard already shows a \`Desktop\` tab. If you're talking to the user somewhere else (e.g. Slack), call \`replicas computer info\` to get the viewer URL and paste it in.
|
|
380
380
|
|
|
381
381
|
## Never use raw \`xdotool\` / \`scrot\` / \`ffmpeg\` directly
|
|
382
382
|
|
|
@@ -395,10 +395,10 @@ scrot /tmp/state.png
|
|
|
395
395
|
## Quickstart
|
|
396
396
|
|
|
397
397
|
\`\`\`bash
|
|
398
|
-
# 1)
|
|
399
|
-
#
|
|
400
|
-
#
|
|
401
|
-
replicas computer
|
|
398
|
+
# 1) (Optional) Get the live viewer URL so you can share it with the user.
|
|
399
|
+
# The desktop is already running and the preview is already registered -
|
|
400
|
+
# this just looks up the URL.
|
|
401
|
+
replicas computer info
|
|
402
402
|
|
|
403
403
|
# 2) Launch a browser on the workspace display.
|
|
404
404
|
replicas computer launch chrome
|
|
@@ -419,20 +419,14 @@ replicas computer record start /tmp/demo.mp4 --fps 60
|
|
|
419
419
|
# ... do stuff ...
|
|
420
420
|
replicas computer record stop
|
|
421
421
|
replicas media upload /tmp/demo.mp4
|
|
422
|
-
|
|
423
|
-
# 6) Tear down the live preview when done (services keep running for next time).
|
|
424
|
-
replicas computer stop
|
|
425
422
|
\`\`\`
|
|
426
423
|
|
|
427
424
|
## Command reference
|
|
428
425
|
|
|
429
|
-
### \`replicas computer
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
Idempotent - safe to call repeatedly. Use it as the first computer-use command in any session.
|
|
426
|
+
### \`replicas computer info\`
|
|
427
|
+
Prints the live noVNC viewer URL (\`https://6080-<hash>.tryreplicas.com/\`) for the workspace desktop. The engine auto-registers this authenticated preview on startup; \`info\` just looks it up. Use it when you want to share the live stream URL with the user out-of-band (e.g. in a Slack reply) - the dashboard \`Desktop\` tab already points at the same URL automatically.
|
|
433
428
|
|
|
434
|
-
|
|
435
|
-
Tears down the noVNC preview (the \`Desktop\` tab disappears). The underlying Xvfb / openbox / x11vnc / browser keep running so the next \`start\` is instant.
|
|
429
|
+
If invoked very early in the workspace lifecycle, \`info\` will poll briefly while the engine finishes registering the preview, then error if it's still not available.
|
|
436
430
|
|
|
437
431
|
### \`replicas computer status\`
|
|
438
432
|
Prints which desktop services are running and the active preview URL (if any). Useful for debugging when a tool call seems to be doing nothing.
|
|
@@ -505,12 +499,11 @@ replicas computer screenshot /tmp/loaded.png
|
|
|
505
499
|
The display is 1920\xD71080. Screenshot pixels map 1:1 to click coordinates - if your Read tool shows a button at pixel (520, 700), click \`replicas computer click 520 700\`. **No translation needed.** Modern image-reading models often imagine the screenshot is at a different resolution; trust the \`xdpyinfo\` value (\`replicas computer status\` shows the real size).
|
|
506
500
|
|
|
507
501
|
### Letting the user watch
|
|
508
|
-
|
|
502
|
+
The Desktop tab is already live in the dashboard - the user can open it any time. If you're communicating with the user somewhere else (Slack, PR comment, etc.), grab the URL with \`replicas computer info\` and share it inline so they can watch you work.
|
|
509
503
|
|
|
510
504
|
### Recording a deliverable
|
|
511
505
|
For tasks the user wants proof of:
|
|
512
506
|
\`\`\`bash
|
|
513
|
-
replicas computer start # makes it visible live too
|
|
514
507
|
replicas computer record start /tmp/walkthrough.mp4
|
|
515
508
|
# ... your work ...
|
|
516
509
|
replicas computer record stop
|
|
@@ -518,15 +511,13 @@ replicas media upload /tmp/walkthrough.mp4
|
|
|
518
511
|
\`\`\`
|
|
519
512
|
Then embed the printed \`\` line in your chat reply. See \`MEDIA.md\`.
|
|
520
513
|
|
|
521
|
-
### Cleaning up
|
|
522
|
-
Call \`replicas computer stop\` when you're done with the visual demo so the live preview URL goes away. The services keep running so the next \`start\` is instant.
|
|
523
|
-
|
|
524
514
|
## Failure modes
|
|
525
515
|
|
|
526
516
|
- **"Desktop services script missing"**: workspace image is older than this skill. Tell the user - nothing you can do from the CLI side.
|
|
527
517
|
- **\`xdotool ... failed: Can't open display\`**: Xvfb didn't come up. \`replicas computer status\` will show which service is dead. Re-running any CLI command auto-attempts to start it.
|
|
528
518
|
- **Browser doesn't appear after \`launch chrome\`**: give it 1-2s, then screenshot. Chrome cold-start on the virtual display takes ~500ms but bigger pages take longer.
|
|
529
519
|
- **Live preview shows static / black screen**: the browser may have crashed. \`replicas computer status\` should show no Chrome process - re-launch.
|
|
520
|
+
- **\`replicas computer info\` errors with "not registered"**: engine couldn't register the preview at startup (transient monolith error, or warming mode). Re-running the engine usually fixes it. Until it's registered, the Desktop tab will show a placeholder.
|
|
530
521
|
|
|
531
522
|
## What gets baked in vs. lazy
|
|
532
523
|
|
|
@@ -534,10 +525,10 @@ Call \`replicas computer stop\` when you're done with the visual demo so the liv
|
|
|
534
525
|
|---|---|
|
|
535
526
|
| \`xvfb\`, \`openbox\`, \`tint2\`, \`x11vnc\`, \`websockify\`, \`xdotool\`, \`scrot\`, \`ffmpeg\`, \`google-chrome\` | Baked into the workspace image |
|
|
536
527
|
| Xvfb / openbox / tint2 / x11vnc / websockify processes | Started at workspace boot (\`replicas-start-desktop-services\`) |
|
|
537
|
-
| noVNC preview URL (port 6080) |
|
|
538
|
-
| Dashboard \`Desktop\` tab |
|
|
528
|
+
| noVNC preview URL (port 6080) | Registered by the engine on startup (authenticated) |
|
|
529
|
+
| Dashboard \`Desktop\` tab | Always available once the engine has registered the preview |
|
|
539
530
|
|
|
540
|
-
You can call any of the input tools (\`click\`, \`type\`, \`screenshot\`, etc.)
|
|
531
|
+
You can call any of the input tools (\`click\`, \`type\`, \`screenshot\`, etc.) immediately - the daemons are already running.
|
|
541
532
|
`;
|
|
542
533
|
var COMPUTER_ABILITY = {
|
|
543
534
|
label: "Computer use",
|
|
@@ -1683,17 +1674,25 @@ function clampWarmHookTimeoutMs(timeoutMs) {
|
|
|
1683
1674
|
}
|
|
1684
1675
|
return Math.min(timeoutMs, MAX_WARM_HOOK_TIMEOUT_MS);
|
|
1685
1676
|
}
|
|
1686
|
-
function
|
|
1677
|
+
function buildOutputPreview(text, {
|
|
1678
|
+
maxChars = DEFAULT_HOOK_OUTPUT_PREVIEW_CHARS,
|
|
1679
|
+
truncateFrom = "head",
|
|
1680
|
+
marker = "\n...[truncated \u2014 download the full log to see the rest]"
|
|
1681
|
+
} = {}) {
|
|
1687
1682
|
if (text.length <= maxChars) {
|
|
1688
1683
|
return { output: text, outputTruncated: false, outputTotalChars: text.length };
|
|
1689
1684
|
}
|
|
1685
|
+
const previewChars = Math.max(0, maxChars - marker.length);
|
|
1686
|
+
const output = previewChars === 0 ? marker.slice(0, maxChars) : truncateFrom === "tail" ? `${marker}${text.slice(-previewChars)}` : `${text.slice(0, previewChars)}${marker}`;
|
|
1690
1687
|
return {
|
|
1691
|
-
output
|
|
1692
|
-
...[truncated \u2014 download the full log to see the rest]`,
|
|
1688
|
+
output,
|
|
1693
1689
|
outputTruncated: true,
|
|
1694
1690
|
outputTotalChars: text.length
|
|
1695
1691
|
};
|
|
1696
1692
|
}
|
|
1693
|
+
function buildHookOutputPreview(text, maxChars = DEFAULT_HOOK_OUTPUT_PREVIEW_CHARS) {
|
|
1694
|
+
return buildOutputPreview(text, { maxChars });
|
|
1695
|
+
}
|
|
1697
1696
|
function parseWarmHookConfig(value, filename = "replicas.json") {
|
|
1698
1697
|
if (typeof value === "string") {
|
|
1699
1698
|
return value;
|
|
@@ -1784,7 +1783,8 @@ function isClaudeAuthErrorText(text) {
|
|
|
1784
1783
|
}
|
|
1785
1784
|
|
|
1786
1785
|
// ../shared/src/engine/environment.ts
|
|
1787
|
-
var DAYTONA_SNAPSHOT_ID = "29-05-2026-royal-york-
|
|
1786
|
+
var DAYTONA_SNAPSHOT_ID = "29-05-2026-royal-york-v4";
|
|
1787
|
+
var DESKTOP_NOVNC_PORT = 6080;
|
|
1788
1788
|
|
|
1789
1789
|
// ../shared/src/engine/types.ts
|
|
1790
1790
|
var DEFAULT_CHAT_TITLES = {
|
|
@@ -2146,21 +2146,48 @@ var BaseRefreshManager = class {
|
|
|
2146
2146
|
}
|
|
2147
2147
|
};
|
|
2148
2148
|
|
|
2149
|
+
// src/services/monolith-service.ts
|
|
2150
|
+
async function monolithRequest(path4, init = {}) {
|
|
2151
|
+
if (!ENGINE_ENV.WORKSPACE_ID) {
|
|
2152
|
+
throw new Error("WORKSPACE_ID is not set; cannot call monolith");
|
|
2153
|
+
}
|
|
2154
|
+
return fetch(`${ENGINE_ENV.MONOLITH_URL}${path4}`, {
|
|
2155
|
+
method: init.method ?? "POST",
|
|
2156
|
+
headers: {
|
|
2157
|
+
Authorization: `Bearer ${ENGINE_ENV.REPLICAS_ENGINE_SECRET}`,
|
|
2158
|
+
"X-Workspace-Id": ENGINE_ENV.WORKSPACE_ID,
|
|
2159
|
+
"Content-Type": "application/json"
|
|
2160
|
+
},
|
|
2161
|
+
body: init.body === void 0 ? void 0 : JSON.stringify(init.body),
|
|
2162
|
+
signal: init.signal
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
2165
|
+
var MonolithService = class {
|
|
2166
|
+
async sendEvent(event) {
|
|
2167
|
+
if (!ENGINE_ENV.WORKSPACE_ID) {
|
|
2168
|
+
return;
|
|
2169
|
+
}
|
|
2170
|
+
try {
|
|
2171
|
+
const response = await monolithRequest("/v1/engine/webhook", { body: event });
|
|
2172
|
+
if (!response.ok) {
|
|
2173
|
+
const errorText = await response.text();
|
|
2174
|
+
console.error(`[MonolithService] Failed to send event: ${response.status} ${errorText}`);
|
|
2175
|
+
}
|
|
2176
|
+
} catch (error) {
|
|
2177
|
+
console.error("[MonolithService] Failed to send event:", error);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
};
|
|
2181
|
+
var monolithService = new MonolithService();
|
|
2182
|
+
|
|
2149
2183
|
// src/managers/github-token-manager.ts
|
|
2150
2184
|
var GitHubTokenManager = class extends BaseRefreshManager {
|
|
2151
2185
|
constructor() {
|
|
2152
2186
|
super("GitHubTokenManager");
|
|
2153
2187
|
}
|
|
2154
|
-
async doRefresh(
|
|
2188
|
+
async doRefresh(_config) {
|
|
2155
2189
|
console.log("[GitHubTokenManager] Refreshing GitHub token...");
|
|
2156
|
-
const response = await
|
|
2157
|
-
method: "POST",
|
|
2158
|
-
headers: {
|
|
2159
|
-
Authorization: `Bearer ${config.engineSecret}`,
|
|
2160
|
-
"X-Workspace-Id": config.workspaceId,
|
|
2161
|
-
"Content-Type": "application/json"
|
|
2162
|
-
}
|
|
2163
|
-
});
|
|
2190
|
+
const response = await monolithRequest("/v1/engine/github/refresh-token");
|
|
2164
2191
|
if (!response.ok) {
|
|
2165
2192
|
const errorText = await response.text();
|
|
2166
2193
|
throw new Error(`Token refresh failed: ${response.status} ${errorText}`);
|
|
@@ -2225,19 +2252,13 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
|
|
|
2225
2252
|
}
|
|
2226
2253
|
return null;
|
|
2227
2254
|
}
|
|
2228
|
-
async doRefresh(
|
|
2229
|
-
await this.refreshWithRequest(
|
|
2255
|
+
async doRefresh(_config) {
|
|
2256
|
+
await this.refreshWithRequest();
|
|
2230
2257
|
}
|
|
2231
|
-
async refreshWithRequest(
|
|
2258
|
+
async refreshWithRequest(request) {
|
|
2232
2259
|
console.log("[ClaudeTokenManager] Refreshing Claude credentials...");
|
|
2233
|
-
const response = await
|
|
2234
|
-
|
|
2235
|
-
headers: {
|
|
2236
|
-
Authorization: `Bearer ${config.engineSecret}`,
|
|
2237
|
-
"X-Workspace-Id": config.workspaceId,
|
|
2238
|
-
"Content-Type": "application/json"
|
|
2239
|
-
},
|
|
2240
|
-
...request ? { body: JSON.stringify(request) } : {}
|
|
2260
|
+
const response = await monolithRequest("/v1/engine/claude/refresh-credentials", {
|
|
2261
|
+
body: request
|
|
2241
2262
|
});
|
|
2242
2263
|
if (!response.ok) {
|
|
2243
2264
|
const errorText = await response.text();
|
|
@@ -2253,7 +2274,7 @@ var ClaudeTokenManager = class extends BaseRefreshManager {
|
|
|
2253
2274
|
try {
|
|
2254
2275
|
console.log("[ClaudeTokenManager] Fetching fresh credentials from monolith after auth failure...");
|
|
2255
2276
|
const failedMethod = ENGINE_ENV.REPLICAS_CLAUDE_AUTH_METHOD;
|
|
2256
|
-
await this.refreshWithRequest(
|
|
2277
|
+
await this.refreshWithRequest(failedMethod && failedMethod !== "none" ? {
|
|
2257
2278
|
failedMethod,
|
|
2258
2279
|
failureReason
|
|
2259
2280
|
} : void 0);
|
|
@@ -2337,19 +2358,13 @@ var CodexTokenManager = class extends BaseRefreshManager {
|
|
|
2337
2358
|
}
|
|
2338
2359
|
return null;
|
|
2339
2360
|
}
|
|
2340
|
-
async doRefresh(
|
|
2341
|
-
await this.refreshWithRequest(
|
|
2361
|
+
async doRefresh(_config) {
|
|
2362
|
+
await this.refreshWithRequest();
|
|
2342
2363
|
}
|
|
2343
|
-
async refreshWithRequest(
|
|
2364
|
+
async refreshWithRequest(request) {
|
|
2344
2365
|
console.log("[CodexTokenManager] Refreshing Codex credentials...");
|
|
2345
|
-
const response = await
|
|
2346
|
-
|
|
2347
|
-
headers: {
|
|
2348
|
-
Authorization: `Bearer ${config.engineSecret}`,
|
|
2349
|
-
"X-Workspace-Id": config.workspaceId,
|
|
2350
|
-
"Content-Type": "application/json"
|
|
2351
|
-
},
|
|
2352
|
-
...request ? { body: JSON.stringify(request) } : {}
|
|
2366
|
+
const response = await monolithRequest("/v1/engine/codex/refresh-credentials", {
|
|
2367
|
+
body: request
|
|
2353
2368
|
});
|
|
2354
2369
|
if (!response.ok) {
|
|
2355
2370
|
const errorText = await response.text();
|
|
@@ -2365,7 +2380,7 @@ var CodexTokenManager = class extends BaseRefreshManager {
|
|
|
2365
2380
|
try {
|
|
2366
2381
|
console.log("[CodexTokenManager] Fetching fresh credentials from monolith after auth failure...");
|
|
2367
2382
|
const failedMethod = ENGINE_ENV.REPLICAS_CODEX_AUTH_METHOD;
|
|
2368
|
-
await this.refreshWithRequest(
|
|
2383
|
+
await this.refreshWithRequest(failedMethod === "oauth" || failedMethod === "api_key" ? {
|
|
2369
2384
|
failedMethod,
|
|
2370
2385
|
failureReason
|
|
2371
2386
|
} : void 0);
|
|
@@ -2435,11 +2450,24 @@ import { execFileSync as execFileSync2, spawnSync } from "child_process";
|
|
|
2435
2450
|
import { join as join5 } from "path";
|
|
2436
2451
|
|
|
2437
2452
|
// src/utils/state.ts
|
|
2438
|
-
import { readFile,
|
|
2453
|
+
import { readFile, mkdir } from "fs/promises";
|
|
2439
2454
|
import { existsSync } from "fs";
|
|
2440
2455
|
import { join as join3 } from "path";
|
|
2441
2456
|
import { homedir as homedir3 } from "os";
|
|
2442
2457
|
|
|
2458
|
+
// src/utils/file.ts
|
|
2459
|
+
import { rename, unlink, writeFile } from "fs/promises";
|
|
2460
|
+
async function atomicWriteFile(path4, data) {
|
|
2461
|
+
const tmpFile = `${path4}.${process.pid}.${Date.now()}.tmp`;
|
|
2462
|
+
try {
|
|
2463
|
+
await writeFile(tmpFile, data, "utf-8");
|
|
2464
|
+
await rename(tmpFile, path4);
|
|
2465
|
+
} catch (error) {
|
|
2466
|
+
await unlink(tmpFile).catch(() => void 0);
|
|
2467
|
+
throw error;
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2443
2471
|
// src/utils/type-guards.ts
|
|
2444
2472
|
function isRecord4(value) {
|
|
2445
2473
|
return typeof value === "object" && value !== null;
|
|
@@ -2462,14 +2490,7 @@ async function updateEngineState(updater) {
|
|
|
2462
2490
|
await mkdir(STATE_DIR, { recursive: true });
|
|
2463
2491
|
const currentState = await loadEngineState();
|
|
2464
2492
|
const nextState = updater(currentState);
|
|
2465
|
-
|
|
2466
|
-
try {
|
|
2467
|
-
await writeFile(tmpFile, JSON.stringify(nextState, null, 2), "utf-8");
|
|
2468
|
-
await rename(tmpFile, STATE_FILE);
|
|
2469
|
-
} catch (err) {
|
|
2470
|
-
await unlink(tmpFile).catch(() => void 0);
|
|
2471
|
-
throw err;
|
|
2472
|
-
}
|
|
2493
|
+
await atomicWriteFile(STATE_FILE, JSON.stringify(nextState, null, 2));
|
|
2473
2494
|
});
|
|
2474
2495
|
}
|
|
2475
2496
|
function isEngineRepoDiff(value) {
|
|
@@ -3059,14 +3080,14 @@ var EngineLogger = class {
|
|
|
3059
3080
|
var engineLogger = new EngineLogger();
|
|
3060
3081
|
|
|
3061
3082
|
// src/services/replicas-config-service.ts
|
|
3062
|
-
import { readFile as readFile4, appendFile as appendFile2, writeFile as
|
|
3083
|
+
import { readFile as readFile4, appendFile as appendFile2, writeFile as writeFile4, mkdir as mkdir5 } from "fs/promises";
|
|
3063
3084
|
import { existsSync as existsSync4 } from "fs";
|
|
3064
3085
|
import { join as join9 } from "path";
|
|
3065
3086
|
import { homedir as homedir7 } from "os";
|
|
3066
3087
|
import { spawn } from "child_process";
|
|
3067
3088
|
|
|
3068
3089
|
// src/services/environment-details-service.ts
|
|
3069
|
-
import { mkdir as mkdir3, readFile as readFile2
|
|
3090
|
+
import { mkdir as mkdir3, readFile as readFile2 } from "fs/promises";
|
|
3070
3091
|
import { existsSync as existsSync3 } from "fs";
|
|
3071
3092
|
import { homedir as homedir5 } from "os";
|
|
3072
3093
|
import { join as join7 } from "path";
|
|
@@ -3154,8 +3175,8 @@ async function readDetails() {
|
|
|
3154
3175
|
}
|
|
3155
3176
|
async function writeDetails(details) {
|
|
3156
3177
|
await mkdir3(REPLICAS_DIR, { recursive: true });
|
|
3157
|
-
await
|
|
3158
|
-
|
|
3178
|
+
await atomicWriteFile(DETAILS_FILE, `${JSON.stringify(details, null, 2)}
|
|
3179
|
+
`);
|
|
3159
3180
|
}
|
|
3160
3181
|
var EnvironmentDetailsService = class {
|
|
3161
3182
|
async getDetails() {
|
|
@@ -3240,7 +3261,7 @@ var environmentDetailsService = new EnvironmentDetailsService();
|
|
|
3240
3261
|
|
|
3241
3262
|
// src/services/start-hook-logs-service.ts
|
|
3242
3263
|
import { createHash } from "crypto";
|
|
3243
|
-
import { mkdir as mkdir4, readFile as readFile3, writeFile as
|
|
3264
|
+
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile3, readdir as readdir2 } from "fs/promises";
|
|
3244
3265
|
import { homedir as homedir6 } from "os";
|
|
3245
3266
|
import { join as join8 } from "path";
|
|
3246
3267
|
var LOGS_DIR = join8(homedir6(), ".replicas", "start-hook-logs");
|
|
@@ -3263,7 +3284,7 @@ var StartHookLogsService = class {
|
|
|
3263
3284
|
async saveRepoLog(repoName, entry) {
|
|
3264
3285
|
await this.ensureDir();
|
|
3265
3286
|
const log = { repoName, ...entry };
|
|
3266
|
-
await
|
|
3287
|
+
await writeFile3(join8(LOGS_DIR, repoFilename(repoName)), `${JSON.stringify(log, null, 2)}
|
|
3267
3288
|
`, "utf-8");
|
|
3268
3289
|
}
|
|
3269
3290
|
async getAllLogs() {
|
|
@@ -3527,7 +3548,7 @@ var ReplicasConfigService = class {
|
|
|
3527
3548
|
this.hooksFailed = false;
|
|
3528
3549
|
try {
|
|
3529
3550
|
await mkdir5(join9(homedir7(), ".replicas"), { recursive: true });
|
|
3530
|
-
await
|
|
3551
|
+
await writeFile4(
|
|
3531
3552
|
START_HOOKS_LOG,
|
|
3532
3553
|
`=== Start Hooks Execution Log ===
|
|
3533
3554
|
Started: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
@@ -3753,7 +3774,7 @@ var EventService = class {
|
|
|
3753
3774
|
var eventService = new EventService();
|
|
3754
3775
|
|
|
3755
3776
|
// src/services/preview-service.ts
|
|
3756
|
-
import { mkdir as mkdir7, readFile as readFile5
|
|
3777
|
+
import { mkdir as mkdir7, readFile as readFile5 } from "fs/promises";
|
|
3757
3778
|
import { existsSync as existsSync5 } from "fs";
|
|
3758
3779
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3759
3780
|
import { homedir as homedir9 } from "os";
|
|
@@ -3773,8 +3794,8 @@ async function readPreviewsFile() {
|
|
|
3773
3794
|
async function writePreviewsFile(data) {
|
|
3774
3795
|
const dir = dirname(PREVIEW_PORTS_FILE);
|
|
3775
3796
|
await mkdir7(dir, { recursive: true });
|
|
3776
|
-
await
|
|
3777
|
-
|
|
3797
|
+
await atomicWriteFile(PREVIEW_PORTS_FILE, `${JSON.stringify(data, null, 2)}
|
|
3798
|
+
`);
|
|
3778
3799
|
}
|
|
3779
3800
|
var PreviewService = class {
|
|
3780
3801
|
async initialize() {
|
|
@@ -3824,9 +3845,52 @@ var PreviewService = class {
|
|
|
3824
3845
|
};
|
|
3825
3846
|
var previewService = new PreviewService();
|
|
3826
3847
|
|
|
3848
|
+
// src/services/desktop-preview-service.ts
|
|
3849
|
+
var REGISTRATION_TIMEOUT_MS = 3e4;
|
|
3850
|
+
var RETRY_DELAY_MS = 2e3;
|
|
3851
|
+
var ATTEMPT_TIMEOUT_MS = 8e3;
|
|
3852
|
+
async function attemptRegistration() {
|
|
3853
|
+
try {
|
|
3854
|
+
const response = await monolithRequest("/v1/previews", {
|
|
3855
|
+
body: { port: DESKTOP_NOVNC_PORT, authenticated: true },
|
|
3856
|
+
signal: AbortSignal.timeout(ATTEMPT_TIMEOUT_MS)
|
|
3857
|
+
});
|
|
3858
|
+
if (response.ok || response.status === 409) {
|
|
3859
|
+
console.log(
|
|
3860
|
+
`[DesktopPreview] Registered port ${DESKTOP_NOVNC_PORT} (status ${response.status}).`
|
|
3861
|
+
);
|
|
3862
|
+
return true;
|
|
3863
|
+
}
|
|
3864
|
+
const errorText = await response.text().catch(() => "");
|
|
3865
|
+
console.error(
|
|
3866
|
+
`[DesktopPreview] Registration failed: ${response.status} ${errorText}`
|
|
3867
|
+
);
|
|
3868
|
+
return false;
|
|
3869
|
+
} catch (error) {
|
|
3870
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3871
|
+
console.error(`[DesktopPreview] Registration error: ${message}`);
|
|
3872
|
+
return false;
|
|
3873
|
+
}
|
|
3874
|
+
}
|
|
3875
|
+
async function registerDesktopPreview() {
|
|
3876
|
+
if (IS_WARMING_MODE || !ENGINE_ENV.WORKSPACE_ID) {
|
|
3877
|
+
return;
|
|
3878
|
+
}
|
|
3879
|
+
const deadline = Date.now() + REGISTRATION_TIMEOUT_MS;
|
|
3880
|
+
while (Date.now() < deadline) {
|
|
3881
|
+
if (await attemptRegistration()) {
|
|
3882
|
+
return;
|
|
3883
|
+
}
|
|
3884
|
+
await new Promise((resolve3) => setTimeout(resolve3, RETRY_DELAY_MS));
|
|
3885
|
+
}
|
|
3886
|
+
console.error(
|
|
3887
|
+
`[DesktopPreview] Gave up registering port ${DESKTOP_NOVNC_PORT} after ${REGISTRATION_TIMEOUT_MS}ms`
|
|
3888
|
+
);
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3827
3891
|
// src/services/chat/chat-service.ts
|
|
3828
3892
|
import { existsSync as existsSync7 } from "fs";
|
|
3829
|
-
import { appendFile as appendFile5, mkdir as mkdir11, readFile as readFile8,
|
|
3893
|
+
import { appendFile as appendFile5, copyFile, mkdir as mkdir11, readFile as readFile8, rename as rename2, rm } from "fs/promises";
|
|
3830
3894
|
import { homedir as homedir13 } from "os";
|
|
3831
3895
|
import { join as join15 } from "path";
|
|
3832
3896
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
@@ -4379,7 +4443,7 @@ function extractPlanFromCodexAspNotification(notification) {
|
|
|
4379
4443
|
|
|
4380
4444
|
// src/utils/image-utils.ts
|
|
4381
4445
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
4382
|
-
import { mkdir as mkdir8, unlink as unlink2, writeFile as
|
|
4446
|
+
import { mkdir as mkdir8, unlink as unlink2, writeFile as writeFile5 } from "fs/promises";
|
|
4383
4447
|
import { homedir as homedir10 } from "os";
|
|
4384
4448
|
import { join as join12 } from "path";
|
|
4385
4449
|
function isImageMediaType(value) {
|
|
@@ -4467,7 +4531,7 @@ async function saveNormalizedImagesToTempFiles(images, tempImageDir = join12(hom
|
|
|
4467
4531
|
const ext = image.source.media_type.split("/")[1] || "png";
|
|
4468
4532
|
const filename = `img_${randomUUID3()}.${ext}`;
|
|
4469
4533
|
const filepath = join12(tempImageDir, filename);
|
|
4470
|
-
await
|
|
4534
|
+
await writeFile5(filepath, Buffer.from(image.source.data, "base64"));
|
|
4471
4535
|
tempPaths.push(filepath);
|
|
4472
4536
|
}
|
|
4473
4537
|
} catch (error) {
|
|
@@ -4617,33 +4681,6 @@ var MessageQueueService = class {
|
|
|
4617
4681
|
}
|
|
4618
4682
|
};
|
|
4619
4683
|
|
|
4620
|
-
// src/services/monolith-service.ts
|
|
4621
|
-
var MonolithService = class {
|
|
4622
|
-
async sendEvent(event) {
|
|
4623
|
-
if (!ENGINE_ENV.WORKSPACE_ID) {
|
|
4624
|
-
return;
|
|
4625
|
-
}
|
|
4626
|
-
try {
|
|
4627
|
-
const response = await fetch(`${ENGINE_ENV.MONOLITH_URL}/v1/engine/webhook`, {
|
|
4628
|
-
method: "POST",
|
|
4629
|
-
headers: {
|
|
4630
|
-
Authorization: `Bearer ${ENGINE_ENV.REPLICAS_ENGINE_SECRET}`,
|
|
4631
|
-
"X-Workspace-Id": ENGINE_ENV.WORKSPACE_ID,
|
|
4632
|
-
"Content-Type": "application/json"
|
|
4633
|
-
},
|
|
4634
|
-
body: JSON.stringify(event)
|
|
4635
|
-
});
|
|
4636
|
-
if (!response.ok) {
|
|
4637
|
-
const errorText = await response.text();
|
|
4638
|
-
console.error(`[MonolithService] Failed to send event: ${response.status} ${errorText}`);
|
|
4639
|
-
}
|
|
4640
|
-
} catch (error) {
|
|
4641
|
-
console.error("[MonolithService] Failed to send event:", error);
|
|
4642
|
-
}
|
|
4643
|
-
}
|
|
4644
|
-
};
|
|
4645
|
-
var monolithService = new MonolithService();
|
|
4646
|
-
|
|
4647
4684
|
// src/managers/coding-agent-manager.ts
|
|
4648
4685
|
var MAX_INTERRUPT_QUEUE_ITEMS = 1e3;
|
|
4649
4686
|
var MAX_INTERRUPT_QUEUE_CHARS = 2e5;
|
|
@@ -5965,6 +6002,7 @@ var THREAD_GOAL_CLEAR_METHOD = "thread/goal/clear";
|
|
|
5965
6002
|
var TURN_START_METHOD = "turn/start";
|
|
5966
6003
|
var TURN_INTERRUPT_METHOD = "turn/interrupt";
|
|
5967
6004
|
var ACCOUNT_RATE_LIMITS_READ_METHOD = "account/rateLimits/read";
|
|
6005
|
+
var MAX_CODEX_ASP_TRANSCRIPT_OUTPUT_CHARS = DEFAULT_HOOK_OUTPUT_PREVIEW_CHARS;
|
|
5968
6006
|
function toReasoningEffort(thinkingLevel) {
|
|
5969
6007
|
return codexReasoningEffortForThinkingLevel(thinkingLevel);
|
|
5970
6008
|
}
|
|
@@ -5986,7 +6024,7 @@ function timestampFromMilliseconds(value) {
|
|
|
5986
6024
|
}
|
|
5987
6025
|
function stringifyToolOutput(value) {
|
|
5988
6026
|
if (value === null || value === void 0) return void 0;
|
|
5989
|
-
if (typeof value === "string") return value;
|
|
6027
|
+
if (typeof value === "string") return truncateCodexAspTranscriptOutput(value);
|
|
5990
6028
|
if (typeof value === "object" && "content" in value && Array.isArray(value.content)) {
|
|
5991
6029
|
const text = value.content.map((item) => {
|
|
5992
6030
|
if (typeof item === "string") return item;
|
|
@@ -5995,14 +6033,24 @@ function stringifyToolOutput(value) {
|
|
|
5995
6033
|
}
|
|
5996
6034
|
return "";
|
|
5997
6035
|
}).filter(Boolean).join("\n");
|
|
5998
|
-
if (text) return text;
|
|
6036
|
+
if (text) return truncateCodexAspTranscriptOutput(text);
|
|
5999
6037
|
}
|
|
6000
6038
|
try {
|
|
6001
|
-
return JSON.stringify(value);
|
|
6039
|
+
return truncateCodexAspTranscriptOutput(JSON.stringify(value));
|
|
6002
6040
|
} catch {
|
|
6003
|
-
return String(value);
|
|
6041
|
+
return truncateCodexAspTranscriptOutput(String(value));
|
|
6004
6042
|
}
|
|
6005
6043
|
}
|
|
6044
|
+
function truncateCodexAspTranscriptOutput(output, maxChars = MAX_CODEX_ASP_TRANSCRIPT_OUTPUT_CHARS) {
|
|
6045
|
+
return buildOutputPreview(output, {
|
|
6046
|
+
maxChars,
|
|
6047
|
+
truncateFrom: "tail",
|
|
6048
|
+
marker: "\n[output truncated: showing tail of output]\n"
|
|
6049
|
+
}).output;
|
|
6050
|
+
}
|
|
6051
|
+
function appendCodexAspTranscriptOutput(current, delta, maxChars = MAX_CODEX_ASP_TRANSCRIPT_OUTPUT_CHARS) {
|
|
6052
|
+
return truncateCodexAspTranscriptOutput(`${current ?? ""}${delta}`, maxChars);
|
|
6053
|
+
}
|
|
6006
6054
|
function transcriptPatchOperation(change) {
|
|
6007
6055
|
return {
|
|
6008
6056
|
action: change.kind.type,
|
|
@@ -6049,7 +6097,7 @@ function itemToTranscriptItem(item, timestamp, status) {
|
|
|
6049
6097
|
type: "commandExecution",
|
|
6050
6098
|
id: item.id,
|
|
6051
6099
|
command: item.command,
|
|
6052
|
-
...item.aggregatedOutput ? { output: item.aggregatedOutput } : {},
|
|
6100
|
+
...item.aggregatedOutput ? { output: truncateCodexAspTranscriptOutput(item.aggregatedOutput) } : {},
|
|
6053
6101
|
exitCode,
|
|
6054
6102
|
timestamp,
|
|
6055
6103
|
status: normalizeCodexAspTranscriptStatus(item.status, typeof exitCode === "number" && exitCode !== 0)
|
|
@@ -6073,7 +6121,7 @@ function itemToTranscriptItem(item, timestamp, status) {
|
|
|
6073
6121
|
server: item.server,
|
|
6074
6122
|
tool: item.tool,
|
|
6075
6123
|
input: item.arguments,
|
|
6076
|
-
output: item.error?.message
|
|
6124
|
+
output: item.error?.message ? truncateCodexAspTranscriptOutput(item.error.message) : stringifyToolOutput(item.result),
|
|
6077
6125
|
timestamp,
|
|
6078
6126
|
status: normalizeCodexAspTranscriptStatus(item.status, item.status === "failed")
|
|
6079
6127
|
};
|
|
@@ -7087,7 +7135,7 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
7087
7135
|
if (item.type !== "commandExecution" && item.type !== "fileChange") return;
|
|
7088
7136
|
turn.items[itemIndex] = {
|
|
7089
7137
|
...item,
|
|
7090
|
-
output:
|
|
7138
|
+
output: appendCodexAspTranscriptOutput(item.output, delta),
|
|
7091
7139
|
status: "in_progress"
|
|
7092
7140
|
};
|
|
7093
7141
|
this.codexAspTranscript.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -7189,7 +7237,7 @@ var CodexAspManager = class extends CodingAgentManager {
|
|
|
7189
7237
|
|
|
7190
7238
|
// src/managers/codex-manager.ts
|
|
7191
7239
|
import { Codex } from "@openai/codex-sdk";
|
|
7192
|
-
import { readdir as readdir3, stat as stat2, writeFile as
|
|
7240
|
+
import { readdir as readdir3, stat as stat2, writeFile as writeFile6, mkdir as mkdir10, readFile as readFile7 } from "fs/promises";
|
|
7193
7241
|
import { existsSync as existsSync6 } from "fs";
|
|
7194
7242
|
import { join as join14 } from "path";
|
|
7195
7243
|
import { homedir as homedir12 } from "os";
|
|
@@ -7293,7 +7341,7 @@ var CodexManager = class extends CodingAgentManager {
|
|
|
7293
7341
|
delete config.developer_instructions;
|
|
7294
7342
|
}
|
|
7295
7343
|
const tomlContent = stringifyToml(config);
|
|
7296
|
-
await
|
|
7344
|
+
await writeFile6(CODEX_CONFIG_PATH, tomlContent, "utf-8");
|
|
7297
7345
|
console.log("[CodexManager] Updated config.toml with developer_instructions");
|
|
7298
7346
|
} catch (error) {
|
|
7299
7347
|
console.error("[CodexManager] Failed to update config.toml:", error);
|
|
@@ -8152,15 +8200,7 @@ var KeepAliveService = class _KeepAliveService {
|
|
|
8152
8200
|
return;
|
|
8153
8201
|
}
|
|
8154
8202
|
try {
|
|
8155
|
-
const response = await
|
|
8156
|
-
method: "POST",
|
|
8157
|
-
headers: {
|
|
8158
|
-
Authorization: `Bearer ${ENGINE_ENV.REPLICAS_ENGINE_SECRET}`,
|
|
8159
|
-
"X-Workspace-Id": ENGINE_ENV.WORKSPACE_ID,
|
|
8160
|
-
"Content-Type": "application/json"
|
|
8161
|
-
},
|
|
8162
|
-
body: JSON.stringify({})
|
|
8163
|
-
});
|
|
8203
|
+
const response = await monolithRequest("/v1/engine/keep-alive", { body: {} });
|
|
8164
8204
|
if (!response.ok) {
|
|
8165
8205
|
console.warn(`[KeepAlive] Ping returned ${response.status}`);
|
|
8166
8206
|
}
|
|
@@ -8204,6 +8244,7 @@ var CLAUDE_HISTORY_DIR = join15(ENGINE_DIR2, "claude-histories");
|
|
|
8204
8244
|
var RELAY_HISTORY_DIR = join15(ENGINE_DIR2, "relay-histories");
|
|
8205
8245
|
var CHAT_SENDERS_DIR = join15(ENGINE_DIR2, "chat-senders");
|
|
8206
8246
|
var CODEX_AUTH_PATH2 = join15(homedir13(), ".codex", "auth.json");
|
|
8247
|
+
var CHATS_BACKUP_FILE = `${CHATS_FILE}.bak`;
|
|
8207
8248
|
function isChatMessageSender(value) {
|
|
8208
8249
|
if (!isRecord4(value)) return false;
|
|
8209
8250
|
return typeof value.senderUserId === "string" && typeof value.senderEmail === "string" && typeof value.recordedAt === "string";
|
|
@@ -8258,6 +8299,16 @@ function normalizePersistedChat(chat) {
|
|
|
8258
8299
|
...chat.provider === "codex" ? { codexBackend: codexBackendForChat(chat) } : {}
|
|
8259
8300
|
};
|
|
8260
8301
|
}
|
|
8302
|
+
function parsePersistedChatsContent(content) {
|
|
8303
|
+
const parsed = JSON.parse(content);
|
|
8304
|
+
if (!Array.isArray(parsed)) {
|
|
8305
|
+
return [];
|
|
8306
|
+
}
|
|
8307
|
+
return parsed.filter((entry) => isPersistedChat(entry)).map((entry) => normalizePersistedChat(entry));
|
|
8308
|
+
}
|
|
8309
|
+
function corruptChatsFilePath() {
|
|
8310
|
+
return `${CHATS_FILE}.corrupt-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
8311
|
+
}
|
|
8261
8312
|
function createUserMessageEvent(message, messageId) {
|
|
8262
8313
|
return {
|
|
8263
8314
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -8749,23 +8800,44 @@ var ChatService = class {
|
|
|
8749
8800
|
async loadChats() {
|
|
8750
8801
|
try {
|
|
8751
8802
|
const content = await readFile8(CHATS_FILE, "utf-8");
|
|
8752
|
-
|
|
8753
|
-
if (!Array.isArray(parsed)) {
|
|
8754
|
-
return [];
|
|
8755
|
-
}
|
|
8756
|
-
return parsed.filter((entry) => isPersistedChat(entry)).map((entry) => normalizePersistedChat(entry));
|
|
8803
|
+
return parsePersistedChatsContent(content);
|
|
8757
8804
|
} catch (error) {
|
|
8758
8805
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
8759
8806
|
return [];
|
|
8760
8807
|
}
|
|
8761
|
-
|
|
8808
|
+
const quarantinePath = corruptChatsFilePath();
|
|
8809
|
+
console.error(`[ChatService] Failed to load ${CHATS_FILE}; quarantining and trying backup:`, error);
|
|
8810
|
+
try {
|
|
8811
|
+
await rename2(CHATS_FILE, quarantinePath);
|
|
8812
|
+
console.error(`[ChatService] Quarantined corrupt chats file at ${quarantinePath}`);
|
|
8813
|
+
} catch (renameError) {
|
|
8814
|
+
console.error("[ChatService] Failed to quarantine corrupt chats file:", renameError);
|
|
8815
|
+
}
|
|
8816
|
+
try {
|
|
8817
|
+
const backupContent = await readFile8(CHATS_BACKUP_FILE, "utf-8");
|
|
8818
|
+
return parsePersistedChatsContent(backupContent);
|
|
8819
|
+
} catch (backupError) {
|
|
8820
|
+
if (backupError && typeof backupError === "object" && "code" in backupError && backupError.code === "ENOENT") {
|
|
8821
|
+
return [];
|
|
8822
|
+
}
|
|
8823
|
+
console.error(`[ChatService] Failed to load backup ${CHATS_BACKUP_FILE}; starting with defaults:`, backupError);
|
|
8824
|
+
return [];
|
|
8825
|
+
}
|
|
8762
8826
|
}
|
|
8763
8827
|
}
|
|
8764
8828
|
async persistAllChats() {
|
|
8765
8829
|
this.writeChain = this.writeChain.catch(() => {
|
|
8766
8830
|
}).then(async () => {
|
|
8767
8831
|
const payload = Array.from(this.chats.values()).map((chat) => chat.persisted);
|
|
8768
|
-
|
|
8832
|
+
try {
|
|
8833
|
+
await copyFile(CHATS_FILE, CHATS_BACKUP_FILE);
|
|
8834
|
+
} catch (error) {
|
|
8835
|
+
if (!(error && typeof error === "object" && "code" in error && error.code === "ENOENT")) {
|
|
8836
|
+
console.error("[ChatService] Failed to update chats backup:", error);
|
|
8837
|
+
}
|
|
8838
|
+
}
|
|
8839
|
+
await atomicWriteFile(CHATS_FILE, `${JSON.stringify(payload, null, 2)}
|
|
8840
|
+
`);
|
|
8769
8841
|
});
|
|
8770
8842
|
await this.writeChain;
|
|
8771
8843
|
}
|
|
@@ -9152,7 +9224,7 @@ import { join as join19 } from "path";
|
|
|
9152
9224
|
|
|
9153
9225
|
// src/services/warm-hook-logs-service.ts
|
|
9154
9226
|
import { createHash as createHash2 } from "crypto";
|
|
9155
|
-
import { mkdir as mkdir12, readFile as readFile11, writeFile as
|
|
9227
|
+
import { mkdir as mkdir12, readFile as readFile11, writeFile as writeFile7, readdir as readdir5, appendFile as appendFile6, unlink as unlink3 } from "fs/promises";
|
|
9156
9228
|
import { homedir as homedir15 } from "os";
|
|
9157
9229
|
import { join as join18 } from "path";
|
|
9158
9230
|
var LOGS_DIR2 = join18(homedir15(), ".replicas", "warm-hook-logs");
|
|
@@ -9186,7 +9258,7 @@ var WarmHookLogsService = class {
|
|
|
9186
9258
|
hookName: "organization",
|
|
9187
9259
|
...entry
|
|
9188
9260
|
};
|
|
9189
|
-
await
|
|
9261
|
+
await writeFile7(join18(LOGS_DIR2, globalFilename()), `${JSON.stringify(log, null, 2)}
|
|
9190
9262
|
`, "utf-8");
|
|
9191
9263
|
}
|
|
9192
9264
|
async saveEnvironmentHookLog(entry) {
|
|
@@ -9196,7 +9268,7 @@ var WarmHookLogsService = class {
|
|
|
9196
9268
|
hookName: "environment",
|
|
9197
9269
|
...entry
|
|
9198
9270
|
};
|
|
9199
|
-
await
|
|
9271
|
+
await writeFile7(join18(LOGS_DIR2, environmentFilename()), `${JSON.stringify(log, null, 2)}
|
|
9200
9272
|
`, "utf-8");
|
|
9201
9273
|
}
|
|
9202
9274
|
async saveRepoHookLog(repoName, entry) {
|
|
@@ -9206,7 +9278,7 @@ var WarmHookLogsService = class {
|
|
|
9206
9278
|
hookName: repoName,
|
|
9207
9279
|
...entry
|
|
9208
9280
|
};
|
|
9209
|
-
await
|
|
9281
|
+
await writeFile7(join18(LOGS_DIR2, repoFilename2(repoName)), `${JSON.stringify(log, null, 2)}
|
|
9210
9282
|
`, "utf-8");
|
|
9211
9283
|
}
|
|
9212
9284
|
async getAllLogs() {
|
|
@@ -10453,6 +10525,7 @@ serve(
|
|
|
10453
10525
|
await chatService.initialize();
|
|
10454
10526
|
await previewService.initialize();
|
|
10455
10527
|
engineReady = true;
|
|
10528
|
+
void registerDesktopPreview();
|
|
10456
10529
|
if (!IS_WARMING_MODE) {
|
|
10457
10530
|
await githubTokenManager.start();
|
|
10458
10531
|
await claudeTokenManager.start();
|