replicas-cli 0.2.423 → 0.2.425
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/index.mjs +179 -47
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -24492,7 +24492,7 @@ function formatTurnElapsed(ms) {
|
|
|
24492
24492
|
}
|
|
24493
24493
|
|
|
24494
24494
|
// ../shared/src/cli-version.ts
|
|
24495
|
-
var CLI_VERSION = "0.2.
|
|
24495
|
+
var CLI_VERSION = "0.2.425";
|
|
24496
24496
|
|
|
24497
24497
|
// ../shared/src/version.ts
|
|
24498
24498
|
function compareVersions(v1, v2) {
|
|
@@ -24671,6 +24671,15 @@ function lifecyclePolicySupportsAutoStop(policy) {
|
|
|
24671
24671
|
return policy === "default";
|
|
24672
24672
|
}
|
|
24673
24673
|
|
|
24674
|
+
// ../shared/src/automations/github-checks.ts
|
|
24675
|
+
var AUTOMATION_CHECK_CONCLUSIONS = ["success", "failure"];
|
|
24676
|
+
function isAutomationCheckConclusion(value) {
|
|
24677
|
+
return AUTOMATION_CHECK_CONCLUSIONS.some((conclusion) => conclusion === value);
|
|
24678
|
+
}
|
|
24679
|
+
function normalizeAutomationGithubCheckNames(names) {
|
|
24680
|
+
return names.map((name) => name.trim()).filter(Boolean);
|
|
24681
|
+
}
|
|
24682
|
+
|
|
24674
24683
|
// ../shared/src/automations/agent-validation.ts
|
|
24675
24684
|
function validateAgentSelection(body, existing) {
|
|
24676
24685
|
const agentProvider = body.agent_provider;
|
|
@@ -29153,6 +29162,9 @@ function printAutomation(automation2) {
|
|
|
29153
29162
|
if (automation2.triggers.length > 0) {
|
|
29154
29163
|
console.log(chalk17.gray(` Triggers: ${automation2.triggers.map(formatTrigger).join(", ")}`));
|
|
29155
29164
|
}
|
|
29165
|
+
if (automation2.github_check_names.length > 0) {
|
|
29166
|
+
console.log(chalk17.gray(` GitHub checks: ${automation2.github_check_names.join(", ")}`));
|
|
29167
|
+
}
|
|
29156
29168
|
console.log(chalk17.gray(` Prompt: ${truncate2(automation2.prompt, 80)}`));
|
|
29157
29169
|
if (automation2.cron_next_fire_at) {
|
|
29158
29170
|
console.log(chalk17.gray(` Next Run: ${formatDate2(automation2.cron_next_fire_at)}`));
|
|
@@ -29179,6 +29191,9 @@ async function automationListCommand(options) {
|
|
|
29179
29191
|
if (options.limit) params.set("limit", options.limit);
|
|
29180
29192
|
const scope = parseScopeFilter(options.owner);
|
|
29181
29193
|
if (scope) params.set("scope", scope);
|
|
29194
|
+
if (options.triggerType) params.set("trigger_type", options.triggerType);
|
|
29195
|
+
if (options.enabled) params.set("enabled", options.enabled);
|
|
29196
|
+
if (options.search) params.set("search", options.search);
|
|
29182
29197
|
const query = params.toString();
|
|
29183
29198
|
const response = await orgAuthenticatedFetch(
|
|
29184
29199
|
`/v1/automations${query ? "?" + query : ""}`
|
|
@@ -29217,6 +29232,9 @@ Automation: ${automation2.name}
|
|
|
29217
29232
|
console.log(chalk17.gray(` - ${formatTrigger(trigger)}`));
|
|
29218
29233
|
}
|
|
29219
29234
|
}
|
|
29235
|
+
if (automation2.github_check_names.length > 0) {
|
|
29236
|
+
console.log(chalk17.gray(` GitHub checks: ${automation2.github_check_names.join(", ")}`));
|
|
29237
|
+
}
|
|
29220
29238
|
console.log(chalk17.gray(` Prompt: ${automation2.prompt}`));
|
|
29221
29239
|
console.log(chalk17.gray(` Environment: ${automation2.environment_id}`));
|
|
29222
29240
|
if (automation2.cron_expression) {
|
|
@@ -29510,6 +29528,7 @@ async function automationCreateCommand(name, options) {
|
|
|
29510
29528
|
environment_id: selectedEnvironmentId,
|
|
29511
29529
|
triggers,
|
|
29512
29530
|
enabled: options.enabled !== false,
|
|
29531
|
+
...options.githubChecks !== void 0 ? { github_check_names: normalizeAutomationGithubCheckNames(options.githubChecks.split(",")) } : {},
|
|
29513
29532
|
config: workspaceConfigWithPrFollowups(void 0, prFollowups),
|
|
29514
29533
|
...lifecyclePolicy ? { workspace_lifecycle_policy: lifecyclePolicy } : {},
|
|
29515
29534
|
...options.autoStopMinutes ? { workspace_auto_stop_minutes: parseInt(options.autoStopMinutes, 10) } : {},
|
|
@@ -29576,7 +29595,7 @@ async function automationEditCommand(id, options) {
|
|
|
29576
29595
|
model: existing.automation.model
|
|
29577
29596
|
});
|
|
29578
29597
|
const body = {};
|
|
29579
|
-
const hasOptions = options.name || options.prompt || options.enabled !== void 0 || options.prFollowups !== void 0 || options.triggerCron || options.triggerGithub || options.triggerGitlab || options.environment || options.triggerGithubExcludeUsers !== void 0 || options.triggerGitlabExcludeUsers !== void 0 || options.lifecycle || options.sleepWhenDone || options.autoStopMinutes || options.agentProvider !== void 0 || options.model !== void 0 || options.thinkingLevel !== void 0 || options.planMode !== void 0 || options.goalMode !== void 0 || options.fastMode !== void 0;
|
|
29598
|
+
const hasOptions = options.name || options.prompt || options.enabled !== void 0 || options.prFollowups !== void 0 || options.triggerCron || options.triggerGithub || options.triggerGitlab || options.environment || options.triggerGithubExcludeUsers !== void 0 || options.triggerGitlabExcludeUsers !== void 0 || options.githubChecks !== void 0 || options.addGithubChecks !== void 0 || options.lifecycle || options.sleepWhenDone || options.autoStopMinutes || options.agentProvider !== void 0 || options.model !== void 0 || options.thinkingLevel !== void 0 || options.planMode !== void 0 || options.goalMode !== void 0 || options.fastMode !== void 0;
|
|
29580
29599
|
if (!hasOptions) {
|
|
29581
29600
|
const nameResponse = await prompts5({
|
|
29582
29601
|
type: "text",
|
|
@@ -29636,6 +29655,15 @@ async function automationEditCommand(id, options) {
|
|
|
29636
29655
|
if (options.prFollowups !== void 0) {
|
|
29637
29656
|
body.config = workspaceConfigWithPrFollowups(existing.automation.config, options.prFollowups);
|
|
29638
29657
|
}
|
|
29658
|
+
if (options.githubChecks !== void 0) {
|
|
29659
|
+
body.github_check_names = normalizeAutomationGithubCheckNames(options.githubChecks.split(","));
|
|
29660
|
+
}
|
|
29661
|
+
if (options.addGithubChecks !== void 0) {
|
|
29662
|
+
body.github_check_names = [.../* @__PURE__ */ new Set([
|
|
29663
|
+
...existing.automation.github_check_names,
|
|
29664
|
+
...normalizeAutomationGithubCheckNames(options.addGithubChecks.split(","))
|
|
29665
|
+
])];
|
|
29666
|
+
}
|
|
29639
29667
|
if (options.triggerCron || options.triggerGithub || options.triggerGitlab) {
|
|
29640
29668
|
const triggers = [];
|
|
29641
29669
|
if (options.triggerCron) {
|
|
@@ -29816,6 +29844,46 @@ Automation "${automationName}" (${id}) deleted.
|
|
|
29816
29844
|
process.exit(1);
|
|
29817
29845
|
}
|
|
29818
29846
|
}
|
|
29847
|
+
async function automationCheckCommand(checkRunId, options) {
|
|
29848
|
+
const id = Number(checkRunId);
|
|
29849
|
+
if (!Number.isSafeInteger(id)) {
|
|
29850
|
+
console.error(chalk17.red("Error: check run ID must be an integer"));
|
|
29851
|
+
process.exit(1);
|
|
29852
|
+
}
|
|
29853
|
+
if (!options.token) {
|
|
29854
|
+
console.error(chalk17.red("Error: --token is required"));
|
|
29855
|
+
process.exit(1);
|
|
29856
|
+
}
|
|
29857
|
+
const { conclusion } = options;
|
|
29858
|
+
if (!isAutomationCheckConclusion(conclusion)) {
|
|
29859
|
+
console.error(chalk17.red(`Error: --conclusion must be one of ${AUTOMATION_CHECK_CONCLUSIONS.join(", ")}`));
|
|
29860
|
+
process.exit(1);
|
|
29861
|
+
}
|
|
29862
|
+
if (!options.title?.trim()) {
|
|
29863
|
+
console.error(chalk17.red("Error: --title is required"));
|
|
29864
|
+
process.exit(1);
|
|
29865
|
+
}
|
|
29866
|
+
const body = {
|
|
29867
|
+
token: options.token,
|
|
29868
|
+
conclusion,
|
|
29869
|
+
title: options.title.trim(),
|
|
29870
|
+
...options.summary?.trim() ? { summary: options.summary.trim() } : {}
|
|
29871
|
+
};
|
|
29872
|
+
try {
|
|
29873
|
+
const response = await orgAuthenticatedFetch(
|
|
29874
|
+
`/v1/automations/checks/${id}`,
|
|
29875
|
+
{ method: "POST", body }
|
|
29876
|
+
);
|
|
29877
|
+
console.log(response.reported ? chalk17.green(`
|
|
29878
|
+
Reported ${conclusion} for "${response.name}".
|
|
29879
|
+
`) : chalk17.yellow(`
|
|
29880
|
+
Skipped "${response.name}" \u2014 a newer run of this automation owns it and will report the verdict.
|
|
29881
|
+
`));
|
|
29882
|
+
} catch (error51) {
|
|
29883
|
+
console.error(chalk17.red(`Error: ${error51 instanceof Error ? error51.message : "Unknown error"}`));
|
|
29884
|
+
process.exit(1);
|
|
29885
|
+
}
|
|
29886
|
+
}
|
|
29819
29887
|
|
|
29820
29888
|
// src/commands/preview.ts
|
|
29821
29889
|
import chalk18 from "chalk";
|
|
@@ -30297,6 +30365,42 @@ async function serviceLogsCommand(name, options) {
|
|
|
30297
30365
|
}
|
|
30298
30366
|
|
|
30299
30367
|
// src/commands/learnings.ts
|
|
30368
|
+
import { createHash as createHash2 } from "crypto";
|
|
30369
|
+
import { mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
30370
|
+
import { join as join2 } from "path";
|
|
30371
|
+
var SESSIONS_DIR = join2(CONFIG_DIR, "learnings-sessions");
|
|
30372
|
+
var REPRINT_AFTER_READS = 50;
|
|
30373
|
+
var SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
30374
|
+
function sessionFilePath() {
|
|
30375
|
+
const key = process.env.REPLICAS_CHAT_ID || process.env.CLAUDE_CODE_SESSION_ID;
|
|
30376
|
+
return key ? join2(SESSIONS_DIR, `${key.replace(/[^a-zA-Z0-9_-]/g, "-")}.json`) : null;
|
|
30377
|
+
}
|
|
30378
|
+
function readSession(path6) {
|
|
30379
|
+
const session = { reads: 0, shown: {} };
|
|
30380
|
+
try {
|
|
30381
|
+
const parsed = JSON.parse(readFileSync2(path6, "utf-8"));
|
|
30382
|
+
if (typeof parsed?.reads === "number") session.reads = parsed.reads;
|
|
30383
|
+
const shown = parsed?.shown ?? {};
|
|
30384
|
+
for (const [id, entry] of Object.entries(shown)) {
|
|
30385
|
+
if (typeof entry?.read === "number" && typeof entry?.hash === "string") {
|
|
30386
|
+
session.shown[id] = { read: entry.read, hash: entry.hash };
|
|
30387
|
+
}
|
|
30388
|
+
}
|
|
30389
|
+
} catch {
|
|
30390
|
+
}
|
|
30391
|
+
return session;
|
|
30392
|
+
}
|
|
30393
|
+
function writeSession(path6, session) {
|
|
30394
|
+
try {
|
|
30395
|
+
mkdirSync2(SESSIONS_DIR, { recursive: true, mode: 448 });
|
|
30396
|
+
writeFileSync2(path6, JSON.stringify(session), { mode: 384 });
|
|
30397
|
+
for (const entry of readdirSync2(SESSIONS_DIR)) {
|
|
30398
|
+
const file2 = join2(SESSIONS_DIR, entry);
|
|
30399
|
+
if (Date.now() - statSync(file2).mtimeMs > SESSION_TTL_MS) unlinkSync(file2);
|
|
30400
|
+
}
|
|
30401
|
+
} catch {
|
|
30402
|
+
}
|
|
30403
|
+
}
|
|
30300
30404
|
function printBlocks(learnings) {
|
|
30301
30405
|
for (const learning of learnings) {
|
|
30302
30406
|
const labels = [
|
|
@@ -30317,14 +30421,30 @@ async function learningsReadCommand(options) {
|
|
|
30317
30421
|
method: "POST",
|
|
30318
30422
|
body: { query }
|
|
30319
30423
|
});
|
|
30424
|
+
const path6 = sessionFilePath();
|
|
30425
|
+
const session = path6 ? readSession(path6) : null;
|
|
30426
|
+
if (session) session.reads += 1;
|
|
30427
|
+
const unseen = response.learnings.filter((learning) => {
|
|
30428
|
+
if (!session) return true;
|
|
30429
|
+
const hash2 = createHash2("sha256").update(learning.content).digest("hex").slice(0, 16);
|
|
30430
|
+
const shown = session.shown[learning.id];
|
|
30431
|
+
if (!options.fresh && shown && shown.hash === hash2 && session.reads - shown.read < REPRINT_AFTER_READS) {
|
|
30432
|
+
return false;
|
|
30433
|
+
}
|
|
30434
|
+
session.shown[learning.id] = { read: session.reads, hash: hash2 };
|
|
30435
|
+
return true;
|
|
30436
|
+
});
|
|
30320
30437
|
if (response.learnings.length === 0) {
|
|
30321
30438
|
console.log("No learnings matched this query.");
|
|
30439
|
+
} else if (unseen.length === 0) {
|
|
30440
|
+
console.log("No new learnings \u2014 the matching ones were already shown earlier in this session.");
|
|
30322
30441
|
} else {
|
|
30323
|
-
printBlocks(
|
|
30442
|
+
printBlocks(unseen);
|
|
30324
30443
|
}
|
|
30325
30444
|
if (response.matching_degraded) {
|
|
30326
30445
|
console.log("Note: trigger matching was unavailable \u2014 only always-apply learnings are shown.");
|
|
30327
30446
|
}
|
|
30447
|
+
if (path6 && session) writeSession(path6, session);
|
|
30328
30448
|
}
|
|
30329
30449
|
async function propose(request) {
|
|
30330
30450
|
const { proposal } = await agentFetch("/v1/agent/learnings/proposals", {
|
|
@@ -30374,14 +30494,14 @@ async function learningsWithdrawCommand(id) {
|
|
|
30374
30494
|
|
|
30375
30495
|
// src/commands/computer/index.ts
|
|
30376
30496
|
import { spawn as spawn5, spawnSync as spawnSync3 } from "child_process";
|
|
30377
|
-
import { createHash as
|
|
30378
|
-
import { closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as
|
|
30497
|
+
import { createHash as createHash3 } from "crypto";
|
|
30498
|
+
import { closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync5, openSync as openSync2, readFileSync as readFileSync5, readSync, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
30379
30499
|
import { dirname as dirname3 } from "path";
|
|
30380
30500
|
import chalk21 from "chalk";
|
|
30381
30501
|
|
|
30382
30502
|
// src/commands/computer/desktop.ts
|
|
30383
30503
|
import { spawnSync } from "child_process";
|
|
30384
|
-
import { existsSync, mkdirSync as
|
|
30504
|
+
import { existsSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3 } from "fs";
|
|
30385
30505
|
import { dirname, isAbsolute, resolve as resolve2 } from "path";
|
|
30386
30506
|
var STATE_DIR = process.env.REPLICAS_DESKTOP_STATE_DIR || "/tmp/replicas-computer";
|
|
30387
30507
|
var DEFAULT_DISPLAY = process.env.REPLICAS_DESKTOP_DISPLAY || ":99";
|
|
@@ -30417,7 +30537,7 @@ function desktopStackHealthy() {
|
|
|
30417
30537
|
const pids = {};
|
|
30418
30538
|
for (const name of ["openbox", "tint2", "x11vnc", "novnc"]) {
|
|
30419
30539
|
try {
|
|
30420
|
-
const pid = Number.parseInt(
|
|
30540
|
+
const pid = Number.parseInt(readFileSync3(`${STATE_DIR}/${name}.pid`, "utf8").trim(), 10);
|
|
30421
30541
|
if (!Number.isFinite(pid)) return false;
|
|
30422
30542
|
process.kill(pid, 0);
|
|
30423
30543
|
pids[name] = pid;
|
|
@@ -30460,7 +30580,7 @@ function runDisplayCmd(bin, args) {
|
|
|
30460
30580
|
return r.stdout?.toString() ?? "";
|
|
30461
30581
|
}
|
|
30462
30582
|
function runDesktopInputCmd(args) {
|
|
30463
|
-
|
|
30583
|
+
mkdirSync3(dirname(INPUT_LOCK_FILE), { recursive: true });
|
|
30464
30584
|
return runDisplayCmd("flock", [
|
|
30465
30585
|
"--exclusive",
|
|
30466
30586
|
"--wait",
|
|
@@ -30535,12 +30655,12 @@ var sleep2 = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
|
30535
30655
|
|
|
30536
30656
|
// src/commands/computer/recording.ts
|
|
30537
30657
|
import { spawn as spawn4 } from "child_process";
|
|
30538
|
-
import { appendFileSync, existsSync as existsSync2, mkdirSync as
|
|
30658
|
+
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
30539
30659
|
import { dirname as dirname2 } from "path";
|
|
30540
30660
|
|
|
30541
30661
|
// src/commands/computer/recording/render.ts
|
|
30542
30662
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
30543
|
-
import { copyFileSync, rmSync as rmSync2, writeFileSync as
|
|
30663
|
+
import { copyFileSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
30544
30664
|
|
|
30545
30665
|
// src/commands/computer/recording/config.ts
|
|
30546
30666
|
var cameraMotion = {
|
|
@@ -30945,7 +31065,7 @@ function renderRecording(rawPath, target, actions, fps, size) {
|
|
|
30945
31065
|
}
|
|
30946
31066
|
const stamp = `${process.pid}-${Date.now()}`;
|
|
30947
31067
|
const cursor = cursorAssets(stamp);
|
|
30948
|
-
|
|
31068
|
+
writeFileSync3(cursor.path, cursorSvg(cursor.size));
|
|
30949
31069
|
const spans = renderedSegmentSpans(segments);
|
|
30950
31070
|
const renderedDuration = spans.length ? spans[spans.length - 1].outputEnd : duration3;
|
|
30951
31071
|
const renderedActions = actionsOnRenderedTimeline(actions, spans);
|
|
@@ -30968,7 +31088,7 @@ function renderRecording(rawPath, target, actions, fps, size) {
|
|
|
30968
31088
|
const concatInputs = segments.map((_, index) => `[v${index}]`).join("");
|
|
30969
31089
|
const filter = `${screenSplitFilter};${filters.join(";")};${concatInputs}concat=n=${segments.length}:v=1:a=0[screen];${cursorFilter}`;
|
|
30970
31090
|
const filterPath = `/tmp/replicas-recording-filter-${stamp}.ffgraph`;
|
|
30971
|
-
|
|
31091
|
+
writeFileSync3(filterPath, filter);
|
|
30972
31092
|
try {
|
|
30973
31093
|
const r = spawnSync2("ffmpeg", [
|
|
30974
31094
|
"-y",
|
|
@@ -31030,7 +31150,7 @@ function clearRecordingState() {
|
|
|
31030
31150
|
}
|
|
31031
31151
|
function recordingStartedAt() {
|
|
31032
31152
|
if (!existsSync2(RECORD_STARTED_AT_FILE)) return null;
|
|
31033
|
-
const startedAt = Number.parseInt(
|
|
31153
|
+
const startedAt = Number.parseInt(readFileSync4(RECORD_STARTED_AT_FILE, "utf8").trim(), 10);
|
|
31034
31154
|
return Number.isFinite(startedAt) ? startedAt : null;
|
|
31035
31155
|
}
|
|
31036
31156
|
function logRecordingAction(action) {
|
|
@@ -31043,7 +31163,7 @@ function logRecordingAction(action) {
|
|
|
31043
31163
|
function readRecordingDimensions() {
|
|
31044
31164
|
if (!existsSync2(RECORD_DIMENSIONS_FILE)) return configuredDesktopDimensions();
|
|
31045
31165
|
try {
|
|
31046
|
-
const dimensions = JSON.parse(
|
|
31166
|
+
const dimensions = JSON.parse(readFileSync4(RECORD_DIMENSIONS_FILE, "utf8"));
|
|
31047
31167
|
const width = dimensions?.width;
|
|
31048
31168
|
const height = dimensions?.height;
|
|
31049
31169
|
if (typeof width === "number" && Number.isFinite(width) && width > 0 && typeof height === "number" && Number.isFinite(height) && height > 0) {
|
|
@@ -31061,7 +31181,7 @@ function isOptionalNumber(value) {
|
|
|
31061
31181
|
}
|
|
31062
31182
|
function readRecordingActions() {
|
|
31063
31183
|
if (!existsSync2(RECORD_ACTIONS_FILE)) return [];
|
|
31064
|
-
return
|
|
31184
|
+
return readFileSync4(RECORD_ACTIONS_FILE, "utf8").split("\n").filter(Boolean).flatMap((line) => {
|
|
31065
31185
|
try {
|
|
31066
31186
|
const value = JSON.parse(line);
|
|
31067
31187
|
if (typeof value !== "object" || value === null) return [];
|
|
@@ -31083,7 +31203,7 @@ function readRecordingActions() {
|
|
|
31083
31203
|
async function computerRecordStartCommand(path6, options) {
|
|
31084
31204
|
ensureServicesRunning();
|
|
31085
31205
|
if (existsSync2(RECORD_PID_FILE)) {
|
|
31086
|
-
const pid = parseInt(
|
|
31206
|
+
const pid = parseInt(readFileSync4(RECORD_PID_FILE, "utf8").trim(), 10);
|
|
31087
31207
|
if (Number.isFinite(pid)) {
|
|
31088
31208
|
let alive2 = false;
|
|
31089
31209
|
try {
|
|
@@ -31095,10 +31215,10 @@ async function computerRecordStartCommand(path6, options) {
|
|
|
31095
31215
|
}
|
|
31096
31216
|
}
|
|
31097
31217
|
const target = resolvePath(path6);
|
|
31098
|
-
|
|
31218
|
+
mkdirSync4(dirname2(target), { recursive: true });
|
|
31099
31219
|
const fps = options.fps ? parseCoord(options.fps, "--fps") : 60;
|
|
31100
31220
|
const { width, height } = configuredDesktopDimensions();
|
|
31101
|
-
|
|
31221
|
+
mkdirSync4(STATE_DIR, { recursive: true });
|
|
31102
31222
|
const rawTarget = `${target}.raw-${Date.now()}.mp4`;
|
|
31103
31223
|
rmSync3(RECORD_ACTIONS_FILE, { force: true });
|
|
31104
31224
|
const child = spawn4("ffmpeg", [
|
|
@@ -31132,17 +31252,17 @@ async function computerRecordStartCommand(path6, options) {
|
|
|
31132
31252
|
], { detached: true, stdio: "ignore" });
|
|
31133
31253
|
child.unref();
|
|
31134
31254
|
if (!child.pid) fail("failed to launch ffmpeg");
|
|
31135
|
-
|
|
31136
|
-
|
|
31137
|
-
|
|
31138
|
-
|
|
31139
|
-
|
|
31140
|
-
|
|
31255
|
+
writeFileSync4(RECORD_PID_FILE, String(child.pid));
|
|
31256
|
+
writeFileSync4(RECORD_PATH_FILE, target);
|
|
31257
|
+
writeFileSync4(RECORD_RAW_PATH_FILE, rawTarget);
|
|
31258
|
+
writeFileSync4(RECORD_STARTED_AT_FILE, String(Date.now()));
|
|
31259
|
+
writeFileSync4(RECORD_FPS_FILE, String(fps));
|
|
31260
|
+
writeFileSync4(RECORD_DIMENSIONS_FILE, JSON.stringify({ width, height }));
|
|
31141
31261
|
const startedAt = Date.now();
|
|
31142
31262
|
while (Date.now() - startedAt < 5e3) {
|
|
31143
31263
|
try {
|
|
31144
31264
|
process.kill(child.pid, 0);
|
|
31145
|
-
if (existsSync2(rawTarget) &&
|
|
31265
|
+
if (existsSync2(rawTarget) && statSync2(rawTarget).size > 0) {
|
|
31146
31266
|
console.log(`${target} (recording ready in ${Date.now() - startedAt}ms)`);
|
|
31147
31267
|
return;
|
|
31148
31268
|
}
|
|
@@ -31165,7 +31285,7 @@ async function computerRecordStartCommand(path6, options) {
|
|
|
31165
31285
|
async function computerRecordStopCommand() {
|
|
31166
31286
|
if (!existsSync2(RECORD_PID_FILE) && !existsSync2(RECORD_PATH_FILE)) fail("no recording in progress");
|
|
31167
31287
|
if (existsSync2(RECORD_PID_FILE)) {
|
|
31168
|
-
const pid = parseInt(
|
|
31288
|
+
const pid = parseInt(readFileSync4(RECORD_PID_FILE, "utf8").trim(), 10);
|
|
31169
31289
|
if (!Number.isFinite(pid)) fail("invalid recording pidfile");
|
|
31170
31290
|
try {
|
|
31171
31291
|
process.kill(pid, "SIGINT");
|
|
@@ -31184,9 +31304,9 @@ async function computerRecordStopCommand() {
|
|
|
31184
31304
|
if (alive) fail(`ffmpeg did not finalize recording within 30 seconds (pid ${pid})`);
|
|
31185
31305
|
}
|
|
31186
31306
|
if (existsSync2(RECORD_PATH_FILE)) {
|
|
31187
|
-
const target =
|
|
31188
|
-
const rawPath = existsSync2(RECORD_RAW_PATH_FILE) ?
|
|
31189
|
-
const fps = existsSync2(RECORD_FPS_FILE) ? parseInt(
|
|
31307
|
+
const target = readFileSync4(RECORD_PATH_FILE, "utf8").trim();
|
|
31308
|
+
const rawPath = existsSync2(RECORD_RAW_PATH_FILE) ? readFileSync4(RECORD_RAW_PATH_FILE, "utf8").trim() : target;
|
|
31309
|
+
const fps = existsSync2(RECORD_FPS_FILE) ? parseInt(readFileSync4(RECORD_FPS_FILE, "utf8").trim(), 10) : 60;
|
|
31190
31310
|
const size = readRecordingDimensions();
|
|
31191
31311
|
const actions = readRecordingActions();
|
|
31192
31312
|
if (rawPath !== target) {
|
|
@@ -31278,7 +31398,7 @@ function loadBrandSvg(canvasW, canvasH) {
|
|
|
31278
31398
|
`Brand wallpaper SVG missing at ${path6}. The workspace image is out of date \u2014 \`desktop/brand-wallpaper.svg\` must be installed at $REPLICAS_DESKTOP_TEMPLATES.`
|
|
31279
31399
|
);
|
|
31280
31400
|
}
|
|
31281
|
-
return
|
|
31401
|
+
return readFileSync5(path6, "utf8").replace(/(<svg[^>]*\s)width="\d+"/, `$1width="${canvasW}"`).replace(/(<svg[^>]*\s)height="\d+"/, `$1height="${canvasH}"`);
|
|
31282
31402
|
}
|
|
31283
31403
|
var BRAND_PAD_FRACTION = 0.06;
|
|
31284
31404
|
var SCREENSHOT_CORNER_FRACTION = 0.022;
|
|
@@ -31313,7 +31433,7 @@ ${labels.join("\n")}
|
|
|
31313
31433
|
</svg>`;
|
|
31314
31434
|
}
|
|
31315
31435
|
function overlayGrid(rawPath, target, width, height, gridSize, gridPath) {
|
|
31316
|
-
|
|
31436
|
+
writeFileSync5(gridPath, buildGridSvg(width, height, gridSize));
|
|
31317
31437
|
const r = spawnSync3(
|
|
31318
31438
|
"ffmpeg",
|
|
31319
31439
|
[
|
|
@@ -31341,7 +31461,7 @@ function overlayGrid(rawPath, target, width, height, gridSize, gridPath) {
|
|
|
31341
31461
|
}
|
|
31342
31462
|
async function computerScreenshotCommand(path6, options = {}) {
|
|
31343
31463
|
const target = resolvePath(path6);
|
|
31344
|
-
|
|
31464
|
+
mkdirSync5(dirname3(target), { recursive: true });
|
|
31345
31465
|
const stamp = `${process.pid}-${Date.now()}`;
|
|
31346
31466
|
const rawPath = `/tmp/replicas-screenshot-${stamp}.raw.png`;
|
|
31347
31467
|
const svgPath = `/tmp/replicas-screenshot-${stamp}.brand.svg`;
|
|
@@ -31373,12 +31493,12 @@ async function computerScreenshotCommand(path6, options = {}) {
|
|
|
31373
31493
|
const shadowMargin = shadowSigma * 3;
|
|
31374
31494
|
const shadowW = width + shadowMargin * 2;
|
|
31375
31495
|
const shadowH = height + shadowMargin * 2;
|
|
31376
|
-
|
|
31377
|
-
|
|
31496
|
+
writeFileSync5(svgPath, loadBrandSvg(canvasW, canvasH));
|
|
31497
|
+
writeFileSync5(
|
|
31378
31498
|
maskPath,
|
|
31379
31499
|
SCREENSHOT_MASK_TEMPLATE.replace(/__W__/g, String(width)).replace(/__H__/g, String(height)).replace(/__R__/g, String(cornerR))
|
|
31380
31500
|
);
|
|
31381
|
-
|
|
31501
|
+
writeFileSync5(
|
|
31382
31502
|
shadowPath,
|
|
31383
31503
|
SHADOW_MASK_TEMPLATE.replace(/__SW__/g, String(shadowW)).replace(/__SH__/g, String(shadowH)).replace(/__M__/g, String(shadowMargin)).replace(/__W__/g, String(width)).replace(/__H__/g, String(height)).replace(/__R__/g, String(cornerR))
|
|
31384
31504
|
);
|
|
@@ -31420,7 +31540,7 @@ async function computerScreenshotCommand(path6, options = {}) {
|
|
|
31420
31540
|
console.log(target);
|
|
31421
31541
|
}
|
|
31422
31542
|
function hashFile(path6) {
|
|
31423
|
-
return
|
|
31543
|
+
return createHash3("sha256").update(readFileSync5(path6)).digest("hex");
|
|
31424
31544
|
}
|
|
31425
31545
|
async function captureStableRawScreenshot(target, options) {
|
|
31426
31546
|
const start = Date.now();
|
|
@@ -31471,7 +31591,7 @@ function recordingMousePosition() {
|
|
|
31471
31591
|
}
|
|
31472
31592
|
async function computerObserveCommand(path6, options = {}) {
|
|
31473
31593
|
const target = resolvePath(path6);
|
|
31474
|
-
|
|
31594
|
+
mkdirSync5(dirname3(target), { recursive: true });
|
|
31475
31595
|
const timeoutMs = options.timeout ? parseCoord(options.timeout, "--timeout") : 3e3;
|
|
31476
31596
|
const stableMs = options.stableMs ? parseCoord(options.stableMs, "--stable-ms") : 600;
|
|
31477
31597
|
const pollMs = options.pollMs ? parseCoord(options.pollMs, "--poll-ms") : 200;
|
|
@@ -31706,7 +31826,7 @@ function browserStateProperties(node) {
|
|
|
31706
31826
|
function readBrowserStateCache(path6) {
|
|
31707
31827
|
let value;
|
|
31708
31828
|
try {
|
|
31709
|
-
value = JSON.parse(
|
|
31829
|
+
value = JSON.parse(readFileSync5(path6, "utf8"));
|
|
31710
31830
|
} catch {
|
|
31711
31831
|
return null;
|
|
31712
31832
|
}
|
|
@@ -32052,7 +32172,7 @@ async function captureBrowserSnapshot(page, options) {
|
|
|
32052
32172
|
visibleTextLength += Math.min(name.length, remaining) + 1;
|
|
32053
32173
|
}
|
|
32054
32174
|
}
|
|
32055
|
-
const revision =
|
|
32175
|
+
const revision = createHash3("sha256").update(JSON.stringify([page.url, entries.map(({ key, semantic }) => [key, semantic])])).digest("hex").slice(0, 16);
|
|
32056
32176
|
const snapshot = {
|
|
32057
32177
|
title: (page.title ?? "").slice(0, 1e3),
|
|
32058
32178
|
url: (page.url ?? "").slice(0, 2e3),
|
|
@@ -32232,7 +32352,7 @@ async function computerBrowserStateCommand(path6, options = {}) {
|
|
|
32232
32352
|
const page = await selectChromePage(options);
|
|
32233
32353
|
const stability = await waitForBrowserStability(page, { timeoutMs, stableMs, pollMs });
|
|
32234
32354
|
const target = resolvePath(path6);
|
|
32235
|
-
|
|
32355
|
+
mkdirSync5(dirname3(target), { recursive: true });
|
|
32236
32356
|
const [{ snapshot, entries }, screenshotResult] = await Promise.all([
|
|
32237
32357
|
captureBrowserSnapshot(page, { textLimit, elementLimit }),
|
|
32238
32358
|
sendChromeCommand(page.webSocketDebuggerUrl, "Page.captureScreenshot", {
|
|
@@ -32243,7 +32363,7 @@ async function computerBrowserStateCommand(path6, options = {}) {
|
|
|
32243
32363
|
]);
|
|
32244
32364
|
const data = screenshotResult.data;
|
|
32245
32365
|
if (typeof data !== "string") fail("Chrome did not return screenshot data");
|
|
32246
|
-
|
|
32366
|
+
writeFileSync5(target, Buffer.from(data, "base64"));
|
|
32247
32367
|
const screenshot = readPngDimensions(target);
|
|
32248
32368
|
const targetId = page.id ?? "unknown";
|
|
32249
32369
|
const cachePath = browserStateCachePath(targetId);
|
|
@@ -32259,8 +32379,8 @@ async function computerBrowserStateCommand(path6, options = {}) {
|
|
|
32259
32379
|
changes = { added: diff.added, changed: diff.changed, removed: diff.removed };
|
|
32260
32380
|
}
|
|
32261
32381
|
}
|
|
32262
|
-
|
|
32263
|
-
|
|
32382
|
+
mkdirSync5(STATE_DIR, { recursive: true });
|
|
32383
|
+
writeFileSync5(cachePath, JSON.stringify({ url: snapshot.url, documentId: snapshot.documentId, entries }));
|
|
32264
32384
|
const stableState = isRecord(stability.state) ? stability.state : {};
|
|
32265
32385
|
const state = {
|
|
32266
32386
|
title: snapshot.title,
|
|
@@ -37127,7 +37247,7 @@ program.command("read <id>").description("Read conversation history of a replica
|
|
|
37127
37247
|
}
|
|
37128
37248
|
});
|
|
37129
37249
|
var automation = program.command("automation").alias("auto").description("Manage automations");
|
|
37130
|
-
automation.command("list").description("List all automations").option("-p, --page <page>", "Page number").option("-l, --limit <limit>", "Number of items per page").option("--owner <owner>", "Ownership filter: org, user, all").action(async (options) => {
|
|
37250
|
+
automation.command("list").description("List all automations").option("-p, --page <page>", "Page number").option("-l, --limit <limit>", "Number of items per page").option("--owner <owner>", "Ownership filter: org, user, all").option("--trigger-type <type>", "Only automations with a trigger of this type (cron, github, gitlab, slack, sentry, custom)").option("--enabled <enabled>", "Only enabled or disabled automations (true/false)").option("--search <search>", "Case-insensitive substring match on the name").action(async (options) => {
|
|
37131
37251
|
try {
|
|
37132
37252
|
await automationListCommand(options);
|
|
37133
37253
|
} catch (error51) {
|
|
@@ -37151,7 +37271,7 @@ automation.command("get <id>").description("Get automation details by ID").actio
|
|
|
37151
37271
|
process.exit(1);
|
|
37152
37272
|
}
|
|
37153
37273
|
});
|
|
37154
|
-
automation.command("create [name]").description("Create a new automation").option("-p, --prompt <prompt>", "Prompt for the automation").option("-e, --environment <environment>", "Environment name or ID").option("--trigger-cron <schedule>", 'Cron schedule expression (e.g. "0 9 * * 1-5")').option("--trigger-cron-timezone <timezone>", "Timezone for cron trigger (default: UTC)").option("--trigger-github <event>", 'GitHub event (e.g. "pull_request.opened")').option("--trigger-github-repos <repos>", "Comma-separated repo names to filter GitHub trigger").option("--trigger-github-exclude-users <users>", "Comma-separated GitHub usernames to exclude from triggering (pass empty string to clear the list)").option("--trigger-gitlab <event>", 'GitLab event (e.g. "merge_request.opened")').option("--trigger-gitlab-repos <repos>", "Comma-separated repo names to filter GitLab trigger").option("--trigger-gitlab-exclude-users <users>", "Comma-separated GitLab usernames to exclude from triggering (pass empty string to clear the list)").option("--lifecycle <policy>", "Workspace lifecycle: archive_when_done, sleep_when_done, default").option("--sleep-when-done", "Sleep automation workspaces as soon as the agent finishes").option("--auto-stop-minutes <minutes>", "Inactivity timeout in minutes (3-1440, requires --lifecycle default)").option("--pr-followups", "Allow follow-up actions on matching PRs").option("--agent-provider <provider>", 'Coding agent to use: claude, codex, cursor, relay (or "none" to inherit org default)').option("--model <model>", 'Model identifier (must be valid for --agent-provider; pass "none" to clear)').option("--thinking-level <level>", 'Thinking/reasoning level: low, medium, high, xhigh, max (or "none" to clear)').option("--plan-mode", "Run automation messages in plan mode").option("--goal-mode", "Set automation messages as Codex goals").option("--fast-mode", "Run automation messages in fast mode").option("--personal", "Create a personal automation owned by the authenticated user").option("--disabled", "Create in disabled state").action(async (name, options) => {
|
|
37274
|
+
automation.command("create [name]").description("Create a new automation").option("-p, --prompt <prompt>", "Prompt for the automation").option("-e, --environment <environment>", "Environment name or ID").option("--trigger-cron <schedule>", 'Cron schedule expression (e.g. "0 9 * * 1-5")').option("--trigger-cron-timezone <timezone>", "Timezone for cron trigger (default: UTC)").option("--trigger-github <event>", 'GitHub event (e.g. "pull_request.opened")').option("--trigger-github-repos <repos>", "Comma-separated repo names to filter GitHub trigger").option("--trigger-github-exclude-users <users>", "Comma-separated GitHub usernames to exclude from triggering (pass empty string to clear the list)").option("--trigger-gitlab <event>", 'GitLab event (e.g. "merge_request.opened")').option("--trigger-gitlab-repos <repos>", "Comma-separated repo names to filter GitLab trigger").option("--trigger-gitlab-exclude-users <users>", "Comma-separated GitLab usernames to exclude from triggering (pass empty string to clear the list)").option("--github-checks <names>", "Comma-separated GitHub check names to create on matching PRs (requires a PR opened/updated trigger)").option("--lifecycle <policy>", "Workspace lifecycle: archive_when_done, sleep_when_done, default").option("--sleep-when-done", "Sleep automation workspaces as soon as the agent finishes").option("--auto-stop-minutes <minutes>", "Inactivity timeout in minutes (3-1440, requires --lifecycle default)").option("--pr-followups", "Allow follow-up actions on matching PRs").option("--agent-provider <provider>", 'Coding agent to use: claude, codex, cursor, relay (or "none" to inherit org default)').option("--model <model>", 'Model identifier (must be valid for --agent-provider; pass "none" to clear)').option("--thinking-level <level>", 'Thinking/reasoning level: low, medium, high, xhigh, max (or "none" to clear)').option("--plan-mode", "Run automation messages in plan mode").option("--goal-mode", "Set automation messages as Codex goals").option("--fast-mode", "Run automation messages in fast mode").option("--personal", "Create a personal automation owned by the authenticated user").option("--disabled", "Create in disabled state").action(async (name, options) => {
|
|
37155
37275
|
try {
|
|
37156
37276
|
await automationCreateCommand(name, {
|
|
37157
37277
|
...options,
|
|
@@ -37166,7 +37286,7 @@ automation.command("create [name]").description("Create a new automation").optio
|
|
|
37166
37286
|
process.exit(1);
|
|
37167
37287
|
}
|
|
37168
37288
|
});
|
|
37169
|
-
automation.command("edit <id>").description("Edit an existing automation").option("-n, --name <name>", "New name").option("-p, --prompt <prompt>", "New prompt").option("-e, --enabled <enabled>", "Enable or disable (true/false)").option("--trigger-cron <schedule>", "Set cron schedule (replaces existing triggers)").option("--trigger-cron-timezone <timezone>", "Timezone for cron trigger").option("--trigger-github <event>", "Set GitHub event (replaces existing triggers)").option("--trigger-github-repos <repos>", "Comma-separated repo names to filter GitHub trigger").option("--trigger-github-exclude-users <users>", "Comma-separated GitHub usernames to exclude from triggering (pass empty string to clear the list)").option("--trigger-gitlab <event>", "Set GitLab event (replaces existing triggers)").option("--trigger-gitlab-repos <repos>", "Comma-separated repo names to filter GitLab trigger").option("--trigger-gitlab-exclude-users <users>", "Comma-separated GitLab usernames to exclude from triggering (pass empty string to clear the list)").option("--environment <environment>", "Environment name or ID").option("--lifecycle <policy>", "Workspace lifecycle: archive_when_done, sleep_when_done, default").option("--sleep-when-done", "Sleep automation workspaces as soon as the agent finishes").option("--auto-stop-minutes <minutes>", "Inactivity timeout in minutes (3-1440, requires --lifecycle default)").option("--pr-followups <enabled>", "Allow follow-up actions on matching PRs (true/false)", parseBooleanOption).option("--agent-provider <provider>", 'Coding agent to use: claude, codex, cursor, relay (or "none" to inherit org default)').option("--model <model>", 'Model identifier (must be valid for --agent-provider; pass "none" to clear)').option("--thinking-level <level>", 'Thinking/reasoning level: low, medium, high, xhigh, max (or "none" to clear)').option("--plan-mode <enabled>", "Run automation messages in plan mode (true/false)", parseBooleanOption).option("--goal-mode <enabled>", "Set automation messages as Codex goals (true/false)", parseBooleanOption).option("--fast-mode <enabled>", "Run automation messages in fast mode (true/false)", parseBooleanOption).action(async (id, options) => {
|
|
37289
|
+
automation.command("edit <id>").description("Edit an existing automation").option("-n, --name <name>", "New name").option("-p, --prompt <prompt>", "New prompt").option("-e, --enabled <enabled>", "Enable or disable (true/false)").option("--trigger-cron <schedule>", "Set cron schedule (replaces existing triggers)").option("--trigger-cron-timezone <timezone>", "Timezone for cron trigger").option("--trigger-github <event>", "Set GitHub event (replaces existing triggers)").option("--trigger-github-repos <repos>", "Comma-separated repo names to filter GitHub trigger").option("--trigger-github-exclude-users <users>", "Comma-separated GitHub usernames to exclude from triggering (pass empty string to clear the list)").option("--trigger-gitlab <event>", "Set GitLab event (replaces existing triggers)").option("--trigger-gitlab-repos <repos>", "Comma-separated repo names to filter GitLab trigger").option("--trigger-gitlab-exclude-users <users>", "Comma-separated GitLab usernames to exclude from triggering (pass empty string to clear the list)").option("--github-checks <names>", "Comma-separated GitHub check names to create on matching PRs (replaces the current list; pass empty string to remove them)").option("--add-github-checks <names>", "Comma-separated GitHub check names to add, keeping the ones already configured").option("--environment <environment>", "Environment name or ID").option("--lifecycle <policy>", "Workspace lifecycle: archive_when_done, sleep_when_done, default").option("--sleep-when-done", "Sleep automation workspaces as soon as the agent finishes").option("--auto-stop-minutes <minutes>", "Inactivity timeout in minutes (3-1440, requires --lifecycle default)").option("--pr-followups <enabled>", "Allow follow-up actions on matching PRs (true/false)", parseBooleanOption).option("--agent-provider <provider>", 'Coding agent to use: claude, codex, cursor, relay (or "none" to inherit org default)').option("--model <model>", 'Model identifier (must be valid for --agent-provider; pass "none" to clear)').option("--thinking-level <level>", 'Thinking/reasoning level: low, medium, high, xhigh, max (or "none" to clear)').option("--plan-mode <enabled>", "Run automation messages in plan mode (true/false)", parseBooleanOption).option("--goal-mode <enabled>", "Set automation messages as Codex goals (true/false)", parseBooleanOption).option("--fast-mode <enabled>", "Run automation messages in fast mode (true/false)", parseBooleanOption).action(async (id, options) => {
|
|
37170
37290
|
try {
|
|
37171
37291
|
await automationEditCommand(id, options);
|
|
37172
37292
|
} catch (error51) {
|
|
@@ -37202,6 +37322,18 @@ automation.command("delete <id>").description("Delete an automation").option("-f
|
|
|
37202
37322
|
process.exit(1);
|
|
37203
37323
|
}
|
|
37204
37324
|
});
|
|
37325
|
+
automation.command("check <checkRunId>").description("Report this automation run's verdict on one of its GitHub checks").requiredOption("--token <token>", "Ownership token for this check, exactly as given in the prompt").requiredOption("--conclusion <conclusion>", "success or failure").requiredOption("--title <title>", "One-line verdict shown on the check").option("--summary <summary>", "What was checked and why it passed or failed").action(async (checkRunId, options) => {
|
|
37326
|
+
try {
|
|
37327
|
+
await automationCheckCommand(checkRunId, options);
|
|
37328
|
+
} catch (error51) {
|
|
37329
|
+
if (error51 instanceof Error) {
|
|
37330
|
+
console.error(chalk24.red(`
|
|
37331
|
+
\u2717 ${error51.message}
|
|
37332
|
+
`));
|
|
37333
|
+
}
|
|
37334
|
+
process.exit(1);
|
|
37335
|
+
}
|
|
37336
|
+
});
|
|
37205
37337
|
automation.action(async () => {
|
|
37206
37338
|
try {
|
|
37207
37339
|
await automationListCommand({});
|
|
@@ -37616,7 +37748,7 @@ if (isAgentMode()) {
|
|
|
37616
37748
|
}
|
|
37617
37749
|
});
|
|
37618
37750
|
const learnings = program.command("learnings").description("Fetch curated org knowledge and propose changes for human review");
|
|
37619
|
-
learnings.command("read").description("Fetch learnings relevant to a task. Enrich the query with codebase specifics, not the raw user request.").requiredOption("-q, --query <query>", "Enriched task description to match against learning triggers").action(async (options) => {
|
|
37751
|
+
learnings.command("read").description("Fetch learnings relevant to a task. Enrich the query with codebase specifics, not the raw user request.").requiredOption("-q, --query <query>", "Enriched task description to match against learning triggers").option("-f, --fresh", "Print full text even for learnings already shown in this session").action(async (options) => {
|
|
37620
37752
|
try {
|
|
37621
37753
|
await learningsReadCommand(options);
|
|
37622
37754
|
} catch (error51) {
|