@gemslibe/rbo 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/config/agent.json +34 -0
- package/config/controller.json +20 -0
- package/dist/rbo.js +643 -262
- package/package.json +8 -3
- package/scripts/stop-running-rbo.mjs +306 -0
package/dist/rbo.js
CHANGED
|
@@ -204,11 +204,11 @@ async function resolveContainedCwd(projectPath, cwd) {
|
|
|
204
204
|
if (!isPathContained(projectPath, joined)) {
|
|
205
205
|
throw new Error(`cwd escapes project root: ${cwd}`);
|
|
206
206
|
}
|
|
207
|
-
const { access: access4, mkdir:
|
|
207
|
+
const { access: access4, mkdir: mkdir15 } = await import("node:fs/promises");
|
|
208
208
|
try {
|
|
209
209
|
await access4(joined);
|
|
210
210
|
} catch {
|
|
211
|
-
await
|
|
211
|
+
await mkdir15(joined, { recursive: true });
|
|
212
212
|
}
|
|
213
213
|
await assertRealPathContained(projectPath, joined);
|
|
214
214
|
return resolveRealPath(joined);
|
|
@@ -2028,7 +2028,7 @@ var init_runtime_env = __esm({
|
|
|
2028
2028
|
});
|
|
2029
2029
|
|
|
2030
2030
|
// ../../packages/executor/dist/windows-executor-path.js
|
|
2031
|
-
import { existsSync as
|
|
2031
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
2032
2032
|
import { createRequire } from "node:module";
|
|
2033
2033
|
import { dirname as dirname3, join as join11 } from "node:path";
|
|
2034
2034
|
import { fileURLToPath } from "node:url";
|
|
@@ -2048,7 +2048,7 @@ function describeWindowsExecutorResolution(options = {}) {
|
|
|
2048
2048
|
const env = options.env ?? process.env;
|
|
2049
2049
|
const platform3 = options.platform ?? process.platform;
|
|
2050
2050
|
const arch2 = options.arch ?? process.arch;
|
|
2051
|
-
const exists = options.existsSyncFn ??
|
|
2051
|
+
const exists = options.existsSyncFn ?? existsSync3;
|
|
2052
2052
|
const moduleDir = options.moduleDir ?? dirname3(fileURLToPath(import.meta.url));
|
|
2053
2053
|
const fromEnv = env.RBO_WINDOWS_EXECUTOR;
|
|
2054
2054
|
if (fromEnv && exists(fromEnv)) {
|
|
@@ -4238,7 +4238,7 @@ var init_materialize = __esm({
|
|
|
4238
4238
|
});
|
|
4239
4239
|
|
|
4240
4240
|
// ../../packages/snapshot/dist/index.js
|
|
4241
|
-
import { z as
|
|
4241
|
+
import { z as z7 } from "zod";
|
|
4242
4242
|
var Sha256HexSchema, ContentIdSchema, SafeRelativePathSchema, SnapshotFileEntrySchema, SnapshotWorkspaceSchema, SnapshotAdditionalRootSchema, SnapshotRepoSchema, payloadBase, FullSnapshotManifestSchema, GitOverlaySnapshotManifestSchema, SnapshotManifestSchema, SnapshotInstanceSchema;
|
|
4243
4243
|
var init_dist3 = __esm({
|
|
4244
4244
|
"../../packages/snapshot/dist/index.js"() {
|
|
@@ -4252,88 +4252,88 @@ var init_dist3 = __esm({
|
|
|
4252
4252
|
init_capture();
|
|
4253
4253
|
init_overlay();
|
|
4254
4254
|
init_materialize();
|
|
4255
|
-
Sha256HexSchema =
|
|
4256
|
-
ContentIdSchema =
|
|
4257
|
-
SafeRelativePathSchema =
|
|
4255
|
+
Sha256HexSchema = z7.string().regex(/^[0-9a-f]{64}$/i);
|
|
4256
|
+
ContentIdSchema = z7.string().regex(/^sha256:[0-9a-f]{64}$/i);
|
|
4257
|
+
SafeRelativePathSchema = z7.string().min(1).refine((value) => isSafeRelativePath(value), {
|
|
4258
4258
|
message: "must be a relative path without '..', absolute, or UNC segments"
|
|
4259
4259
|
});
|
|
4260
|
-
SnapshotFileEntrySchema =
|
|
4261
|
-
|
|
4262
|
-
path:
|
|
4263
|
-
type:
|
|
4264
|
-
mode:
|
|
4265
|
-
size:
|
|
4260
|
+
SnapshotFileEntrySchema = z7.discriminatedUnion("type", [
|
|
4261
|
+
z7.object({
|
|
4262
|
+
path: z7.string().min(1),
|
|
4263
|
+
type: z7.literal("file"),
|
|
4264
|
+
mode: z7.enum(["100644", "100755"]),
|
|
4265
|
+
size: z7.number().int().nonnegative(),
|
|
4266
4266
|
sha256: Sha256HexSchema
|
|
4267
4267
|
}),
|
|
4268
|
-
|
|
4269
|
-
path:
|
|
4270
|
-
type:
|
|
4271
|
-
mode:
|
|
4272
|
-
target:
|
|
4268
|
+
z7.object({
|
|
4269
|
+
path: z7.string().min(1),
|
|
4270
|
+
type: z7.literal("symlink"),
|
|
4271
|
+
mode: z7.literal("120000"),
|
|
4272
|
+
target: z7.string().min(1)
|
|
4273
4273
|
})
|
|
4274
4274
|
]);
|
|
4275
|
-
SnapshotWorkspaceSchema =
|
|
4275
|
+
SnapshotWorkspaceSchema = z7.object({
|
|
4276
4276
|
main_mount: SafeRelativePathSchema,
|
|
4277
|
-
cwd:
|
|
4277
|
+
cwd: z7.string().min(1).refine((value) => isSafeRelativePath(value, { allowDot: true }), {
|
|
4278
4278
|
message: "must be a relative path without '..', absolute, or UNC segments"
|
|
4279
4279
|
})
|
|
4280
4280
|
});
|
|
4281
|
-
SnapshotAdditionalRootSchema =
|
|
4282
|
-
id:
|
|
4281
|
+
SnapshotAdditionalRootSchema = z7.object({
|
|
4282
|
+
id: z7.string().min(1),
|
|
4283
4283
|
mount: SafeRelativePathSchema,
|
|
4284
|
-
file_count:
|
|
4285
|
-
total_size:
|
|
4284
|
+
file_count: z7.number().int().nonnegative(),
|
|
4285
|
+
total_size: z7.number().int().nonnegative(),
|
|
4286
4286
|
tree_sha256: Sha256HexSchema,
|
|
4287
|
-
mode:
|
|
4288
|
-
});
|
|
4289
|
-
SnapshotRepoSchema =
|
|
4290
|
-
canonical_id:
|
|
4291
|
-
url:
|
|
4292
|
-
branch:
|
|
4293
|
-
base_commit:
|
|
4294
|
-
head_is_pushed:
|
|
4287
|
+
mode: z7.enum(["read_only", "read_write"]).default("read_only")
|
|
4288
|
+
});
|
|
4289
|
+
SnapshotRepoSchema = z7.object({
|
|
4290
|
+
canonical_id: z7.string().min(1),
|
|
4291
|
+
url: z7.string().min(1),
|
|
4292
|
+
branch: z7.string().nullable(),
|
|
4293
|
+
base_commit: z7.string().min(1),
|
|
4294
|
+
head_is_pushed: z7.boolean(),
|
|
4295
4295
|
/** Optional; Controller derives from branch when absent (§10.6). */
|
|
4296
|
-
fetch_refs:
|
|
4296
|
+
fetch_refs: z7.array(z7.string().min(1)).optional()
|
|
4297
4297
|
});
|
|
4298
4298
|
payloadBase = {
|
|
4299
|
-
format:
|
|
4300
|
-
compression:
|
|
4299
|
+
format: z7.literal("tar"),
|
|
4300
|
+
compression: z7.literal("zstd"),
|
|
4301
4301
|
sha256: Sha256HexSchema,
|
|
4302
|
-
size:
|
|
4302
|
+
size: z7.number().int().nonnegative()
|
|
4303
4303
|
};
|
|
4304
|
-
FullSnapshotManifestSchema =
|
|
4305
|
-
schema_version:
|
|
4304
|
+
FullSnapshotManifestSchema = z7.object({
|
|
4305
|
+
schema_version: z7.literal(1),
|
|
4306
4306
|
content_id: ContentIdSchema,
|
|
4307
4307
|
repo: SnapshotRepoSchema.partial({ base_commit: true }).optional(),
|
|
4308
4308
|
workspace: SnapshotWorkspaceSchema,
|
|
4309
|
-
source:
|
|
4310
|
-
files:
|
|
4311
|
-
empty_directories:
|
|
4309
|
+
source: z7.object({
|
|
4310
|
+
files: z7.array(SnapshotFileEntrySchema),
|
|
4311
|
+
empty_directories: z7.array(z7.string()).default([])
|
|
4312
4312
|
}),
|
|
4313
|
-
additional_roots:
|
|
4314
|
-
payload:
|
|
4313
|
+
additional_roots: z7.array(SnapshotAdditionalRootSchema).default([]),
|
|
4314
|
+
payload: z7.object({ mode: z7.literal("full"), ...payloadBase })
|
|
4315
4315
|
}).strict();
|
|
4316
|
-
GitOverlaySnapshotManifestSchema =
|
|
4317
|
-
schema_version:
|
|
4316
|
+
GitOverlaySnapshotManifestSchema = z7.object({
|
|
4317
|
+
schema_version: z7.literal(1),
|
|
4318
4318
|
content_id: ContentIdSchema,
|
|
4319
4319
|
repo: SnapshotRepoSchema,
|
|
4320
4320
|
workspace: SnapshotWorkspaceSchema,
|
|
4321
|
-
overlay:
|
|
4322
|
-
files:
|
|
4323
|
-
deletions:
|
|
4324
|
-
empty_directories:
|
|
4321
|
+
overlay: z7.object({
|
|
4322
|
+
files: z7.array(SnapshotFileEntrySchema),
|
|
4323
|
+
deletions: z7.array(z7.string()),
|
|
4324
|
+
empty_directories: z7.array(z7.string()).default([])
|
|
4325
4325
|
}),
|
|
4326
|
-
additional_roots:
|
|
4327
|
-
payload:
|
|
4326
|
+
additional_roots: z7.array(SnapshotAdditionalRootSchema).default([]),
|
|
4327
|
+
payload: z7.object({ mode: z7.literal("git_overlay"), ...payloadBase })
|
|
4328
4328
|
}).strict();
|
|
4329
|
-
SnapshotManifestSchema =
|
|
4329
|
+
SnapshotManifestSchema = z7.union([
|
|
4330
4330
|
FullSnapshotManifestSchema,
|
|
4331
4331
|
GitOverlaySnapshotManifestSchema
|
|
4332
4332
|
]);
|
|
4333
|
-
SnapshotInstanceSchema =
|
|
4334
|
-
snapshot_id:
|
|
4333
|
+
SnapshotInstanceSchema = z7.object({
|
|
4334
|
+
snapshot_id: z7.string().min(1),
|
|
4335
4335
|
content_id: ContentIdSchema,
|
|
4336
|
-
captured_at:
|
|
4336
|
+
captured_at: z7.string().min(1)
|
|
4337
4337
|
});
|
|
4338
4338
|
}
|
|
4339
4339
|
});
|
|
@@ -4723,21 +4723,48 @@ var init_dist4 = __esm({
|
|
|
4723
4723
|
});
|
|
4724
4724
|
|
|
4725
4725
|
// ../controller/dist/config.js
|
|
4726
|
-
import { mkdirSync as mkdirSync5 } from "node:fs";
|
|
4727
|
-
import { join as join25 } from "node:path";
|
|
4726
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
|
|
4727
|
+
import { isAbsolute as isAbsolute2, join as join25 } from "node:path";
|
|
4728
|
+
import { z as z8 } from "zod";
|
|
4728
4729
|
function parseCsv2(raw) {
|
|
4729
4730
|
if (!raw?.trim()) {
|
|
4730
4731
|
return [];
|
|
4731
4732
|
}
|
|
4732
4733
|
return raw.split(",").map((part) => part.trim()).filter(Boolean);
|
|
4733
4734
|
}
|
|
4734
|
-
function
|
|
4735
|
-
|
|
4736
|
-
|
|
4735
|
+
function envSet2(key, env = process.env) {
|
|
4736
|
+
return env[key] !== void 0;
|
|
4737
|
+
}
|
|
4738
|
+
function parseEnvNumber2(key, raw) {
|
|
4739
|
+
const n = Number(raw);
|
|
4740
|
+
if (!Number.isFinite(n)) {
|
|
4741
|
+
throw new Error(`Invalid ${key}=${JSON.stringify(raw)}: expected a finite number`);
|
|
4742
|
+
}
|
|
4743
|
+
return n;
|
|
4744
|
+
}
|
|
4745
|
+
function parseEnvInt2(key, raw) {
|
|
4746
|
+
const n = parseEnvNumber2(key, raw);
|
|
4747
|
+
if (!Number.isInteger(n)) {
|
|
4748
|
+
throw new Error(`Invalid ${key}=${JSON.stringify(raw)}: expected an integer`);
|
|
4749
|
+
}
|
|
4750
|
+
return n;
|
|
4751
|
+
}
|
|
4752
|
+
function assertAbsoluteAllowlistPaths(paths, label) {
|
|
4753
|
+
return paths.map((path, index) => {
|
|
4754
|
+
const parsed = AbsoluteAllowlistPathSchema.safeParse(path);
|
|
4755
|
+
if (!parsed.success) {
|
|
4756
|
+
throw new Error(`Invalid ${label}[${index}]=${JSON.stringify(path)}: must be a non-empty absolute path`);
|
|
4757
|
+
}
|
|
4758
|
+
return parsed.data;
|
|
4759
|
+
});
|
|
4760
|
+
}
|
|
4761
|
+
function parseGitAllowlistFromEnv2(env = process.env) {
|
|
4762
|
+
if (!envSet2("RBO_GIT_ALLOWLIST_SCHEMES", env) && !envSet2("RBO_GIT_ALLOWLIST_HOSTS", env) && !envSet2("RBO_GIT_ALLOWLIST_PREFIXES", env)) {
|
|
4763
|
+
return void 0;
|
|
4737
4764
|
}
|
|
4738
|
-
const schemes = parseCsv2(
|
|
4739
|
-
const hosts = parseCsv2(
|
|
4740
|
-
const prefixesRaw =
|
|
4765
|
+
const schemes = parseCsv2(env.RBO_GIT_ALLOWLIST_SCHEMES);
|
|
4766
|
+
const hosts = parseCsv2(env.RBO_GIT_ALLOWLIST_HOSTS);
|
|
4767
|
+
const prefixesRaw = env.RBO_GIT_ALLOWLIST_PREFIXES?.trim();
|
|
4741
4768
|
let repository_prefixes;
|
|
4742
4769
|
if (prefixesRaw) {
|
|
4743
4770
|
try {
|
|
@@ -4756,34 +4783,124 @@ function parseGitAllowlist2(overrides) {
|
|
|
4756
4783
|
...repository_prefixes ? { repository_prefixes } : {}
|
|
4757
4784
|
};
|
|
4758
4785
|
}
|
|
4786
|
+
function mergeGitAllowlist2(file, fromEnv, override) {
|
|
4787
|
+
if (override) {
|
|
4788
|
+
return override;
|
|
4789
|
+
}
|
|
4790
|
+
if (fromEnv) {
|
|
4791
|
+
return fromEnv;
|
|
4792
|
+
}
|
|
4793
|
+
if (file) {
|
|
4794
|
+
return {
|
|
4795
|
+
schemes: file.schemes && file.schemes.length > 0 ? file.schemes : [...DEFAULT_GIT_ALLOWLIST_SCHEMES2],
|
|
4796
|
+
hosts: file.hosts && file.hosts.length > 0 ? file.hosts : [...DEFAULT_GIT_ALLOWLIST_HOSTS2],
|
|
4797
|
+
...file.repository_prefixes ? { repository_prefixes: file.repository_prefixes } : {}
|
|
4798
|
+
};
|
|
4799
|
+
}
|
|
4800
|
+
return {
|
|
4801
|
+
schemes: [...DEFAULT_GIT_ALLOWLIST_SCHEMES2],
|
|
4802
|
+
hosts: [...DEFAULT_GIT_ALLOWLIST_HOSTS2]
|
|
4803
|
+
};
|
|
4804
|
+
}
|
|
4805
|
+
function defaultControllerConfigFile() {
|
|
4806
|
+
return {
|
|
4807
|
+
mcp_host: "127.0.0.1",
|
|
4808
|
+
mcp_port: 7410,
|
|
4809
|
+
agent_plane_port: 7411,
|
|
4810
|
+
controller_public_host: "127.0.0.1",
|
|
4811
|
+
data_plane_base_url: null,
|
|
4812
|
+
allowed_project_roots: [],
|
|
4813
|
+
allowed_artifact_destinations: [],
|
|
4814
|
+
git_allowlist: {
|
|
4815
|
+
schemes: [...DEFAULT_GIT_ALLOWLIST_SCHEMES2],
|
|
4816
|
+
hosts: [...DEFAULT_GIT_ALLOWLIST_HOSTS2]
|
|
4817
|
+
},
|
|
4818
|
+
local_max_concurrent_jobs: 1,
|
|
4819
|
+
disconnect_grace_seconds: 60,
|
|
4820
|
+
orphan_timeout_seconds: 300,
|
|
4821
|
+
reconcile_deadline_seconds: 120,
|
|
4822
|
+
allow_local_fallback: true,
|
|
4823
|
+
max_git_bundle_bytes: DEFAULT_MAX_GIT_BUNDLE_BYTES,
|
|
4824
|
+
local_fallback_max_host_cpu_percent: 80
|
|
4825
|
+
};
|
|
4826
|
+
}
|
|
4827
|
+
function resolveControllerConfigPath(dataDir) {
|
|
4828
|
+
return join25(dataDir, CONTROLLER_CONFIG_FILENAME);
|
|
4829
|
+
}
|
|
4830
|
+
function readControllerConfigFile(configPath) {
|
|
4831
|
+
if (!existsSync8(configPath)) {
|
|
4832
|
+
return void 0;
|
|
4833
|
+
}
|
|
4834
|
+
let raw;
|
|
4835
|
+
try {
|
|
4836
|
+
raw = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
4837
|
+
} catch (error) {
|
|
4838
|
+
throw new Error(`Invalid controller config JSON at ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
4839
|
+
}
|
|
4840
|
+
const parsed = ControllerConfigFileSchema.safeParse(raw);
|
|
4841
|
+
if (!parsed.success) {
|
|
4842
|
+
throw new Error(`Invalid controller config at ${configPath}: ${parsed.error.message}`);
|
|
4843
|
+
}
|
|
4844
|
+
return parsed.data;
|
|
4845
|
+
}
|
|
4846
|
+
function writeDefaultControllerConfigFile(dataDir, options = {}) {
|
|
4847
|
+
mkdirSync5(dataDir, { recursive: true });
|
|
4848
|
+
const path = resolveControllerConfigPath(dataDir);
|
|
4849
|
+
if (existsSync8(path) && !options.force) {
|
|
4850
|
+
return { path, written: false };
|
|
4851
|
+
}
|
|
4852
|
+
const body = `${JSON.stringify(defaultControllerConfigFile(), null, 2)}
|
|
4853
|
+
`;
|
|
4854
|
+
writeFileSync5(path, body, "utf8");
|
|
4855
|
+
return { path, written: true };
|
|
4856
|
+
}
|
|
4759
4857
|
function loadControllerConfig(overrides = {}) {
|
|
4760
|
-
const
|
|
4858
|
+
const { configPath: configPathOption, ...fieldOverrides } = overrides;
|
|
4859
|
+
const dataDir = fieldOverrides.dataDir ?? resolveControllerDataDir();
|
|
4860
|
+
const configPath = configPathOption === void 0 ? resolveControllerConfigPath(dataDir) : configPathOption;
|
|
4861
|
+
const file = configPath === null ? void 0 : readControllerConfigFile(configPath);
|
|
4862
|
+
const mcpHost = fieldOverrides.mcpHost ?? (envSet2("RBO_MCP_HOST") ? process.env.RBO_MCP_HOST : void 0) ?? file?.mcp_host ?? "127.0.0.1";
|
|
4863
|
+
const mcpPort = fieldOverrides.mcpPort ?? (envSet2("RBO_MCP_PORT") ? parseEnvInt2("RBO_MCP_PORT", process.env.RBO_MCP_PORT) : void 0) ?? file?.mcp_port ?? 7410;
|
|
4864
|
+
const agentPlanePort = fieldOverrides.agentPlanePort ?? (envSet2("RBO_AGENT_PORT") ? parseEnvInt2("RBO_AGENT_PORT", process.env.RBO_AGENT_PORT) : void 0) ?? file?.agent_plane_port ?? 7411;
|
|
4865
|
+
const controllerPublicHost = fieldOverrides.controllerPublicHost ?? (envSet2("RBO_CONTROLLER_PUBLIC_HOST") ? process.env.RBO_CONTROLLER_PUBLIC_HOST : void 0) ?? file?.controller_public_host ?? "127.0.0.1";
|
|
4866
|
+
let dataPlaneBaseUrl = fieldOverrides.dataPlaneBaseUrl;
|
|
4867
|
+
if (dataPlaneBaseUrl === void 0) {
|
|
4868
|
+
if (envSet2("RBO_DATA_PLANE_BASE_URL")) {
|
|
4869
|
+
dataPlaneBaseUrl = process.env.RBO_DATA_PLANE_BASE_URL;
|
|
4870
|
+
} else if (file?.data_plane_base_url !== void 0) {
|
|
4871
|
+
dataPlaneBaseUrl = file.data_plane_base_url ?? void 0;
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
const allowedProjectRoots = assertAbsoluteAllowlistPaths(fieldOverrides.allowedProjectRoots ?? (envSet2("RBO_ALLOWED_PROJECT_ROOTS") ? parseCsv2(process.env.RBO_ALLOWED_PROJECT_ROOTS) : void 0) ?? file?.allowed_project_roots ?? [], "allowed_project_roots");
|
|
4875
|
+
const allowedArtifactDestinations = assertAbsoluteAllowlistPaths(fieldOverrides.allowedArtifactDestinations ?? (envSet2("RBO_ALLOWED_ARTIFACT_DESTINATIONS") ? parseCsv2(process.env.RBO_ALLOWED_ARTIFACT_DESTINATIONS) : void 0) ?? file?.allowed_artifact_destinations ?? [], "allowed_artifact_destinations");
|
|
4876
|
+
const allowLocalFallback = fieldOverrides.allowLocalFallback ?? (envSet2("RBO_ALLOW_LOCAL_FALLBACK") ? process.env.RBO_ALLOW_LOCAL_FALLBACK !== "false" : void 0) ?? file?.allow_local_fallback ?? true;
|
|
4877
|
+
const maxHostCpuBusyFraction = fieldOverrides.maxHostCpuBusyFraction ?? (envSet2("RBO_LOCAL_FALLBACK_MAX_HOST_CPU_PERCENT") ? parseEnvNumber2("RBO_LOCAL_FALLBACK_MAX_HOST_CPU_PERCENT", process.env.RBO_LOCAL_FALLBACK_MAX_HOST_CPU_PERCENT) / 100 : void 0) ?? (file?.local_fallback_max_host_cpu_percent !== void 0 ? file.local_fallback_max_host_cpu_percent / 100 : void 0) ?? 0.8;
|
|
4761
4878
|
return {
|
|
4762
|
-
mcpHost
|
|
4763
|
-
mcpPort
|
|
4764
|
-
agentPlanePort
|
|
4765
|
-
controllerPublicHost
|
|
4766
|
-
dataPlaneBaseUrl
|
|
4879
|
+
mcpHost,
|
|
4880
|
+
mcpPort,
|
|
4881
|
+
agentPlanePort,
|
|
4882
|
+
controllerPublicHost,
|
|
4883
|
+
dataPlaneBaseUrl,
|
|
4767
4884
|
dataDir,
|
|
4768
|
-
databasePath:
|
|
4769
|
-
allowedProjectRoots
|
|
4770
|
-
allowedArtifactDestinations
|
|
4771
|
-
gitAllowlist:
|
|
4885
|
+
databasePath: fieldOverrides.databasePath ?? join25(dataDir, "controller.db"),
|
|
4886
|
+
allowedProjectRoots,
|
|
4887
|
+
allowedArtifactDestinations,
|
|
4888
|
+
gitAllowlist: mergeGitAllowlist2(file?.git_allowlist, parseGitAllowlistFromEnv2(), fieldOverrides.gitAllowlist),
|
|
4772
4889
|
localExecutor: {
|
|
4773
|
-
maxConcurrentJobs:
|
|
4890
|
+
maxConcurrentJobs: fieldOverrides.localExecutor?.maxConcurrentJobs ?? (envSet2("RBO_LOCAL_MAX_CONCURRENT_JOBS") ? parseEnvInt2("RBO_LOCAL_MAX_CONCURRENT_JOBS", process.env.RBO_LOCAL_MAX_CONCURRENT_JOBS) : void 0) ?? file?.local_max_concurrent_jobs ?? 1
|
|
4774
4891
|
},
|
|
4775
|
-
disconnectGraceSeconds:
|
|
4776
|
-
orphanTimeoutSeconds:
|
|
4777
|
-
reconcileDeadlineSeconds:
|
|
4778
|
-
allowLocalFallback
|
|
4779
|
-
maxGitBundleBytes:
|
|
4780
|
-
maxHostCpuBusyFraction
|
|
4892
|
+
disconnectGraceSeconds: fieldOverrides.disconnectGraceSeconds ?? (envSet2("RBO_DISCONNECT_GRACE_SECONDS") ? parseEnvInt2("RBO_DISCONNECT_GRACE_SECONDS", process.env.RBO_DISCONNECT_GRACE_SECONDS) : void 0) ?? file?.disconnect_grace_seconds ?? 60,
|
|
4893
|
+
orphanTimeoutSeconds: fieldOverrides.orphanTimeoutSeconds ?? (envSet2("RBO_ORPHAN_TIMEOUT_SECONDS") ? parseEnvInt2("RBO_ORPHAN_TIMEOUT_SECONDS", process.env.RBO_ORPHAN_TIMEOUT_SECONDS) : void 0) ?? file?.orphan_timeout_seconds ?? 300,
|
|
4894
|
+
reconcileDeadlineSeconds: fieldOverrides.reconcileDeadlineSeconds ?? (envSet2("RBO_RECONCILE_DEADLINE_SECONDS") ? parseEnvInt2("RBO_RECONCILE_DEADLINE_SECONDS", process.env.RBO_RECONCILE_DEADLINE_SECONDS) : void 0) ?? file?.reconcile_deadline_seconds ?? 120,
|
|
4895
|
+
allowLocalFallback,
|
|
4896
|
+
maxGitBundleBytes: fieldOverrides.maxGitBundleBytes ?? (envSet2("RBO_MAX_GIT_BUNDLE_BYTES") ? parseEnvInt2("RBO_MAX_GIT_BUNDLE_BYTES", process.env.RBO_MAX_GIT_BUNDLE_BYTES) : void 0) ?? file?.max_git_bundle_bytes ?? DEFAULT_MAX_GIT_BUNDLE_BYTES,
|
|
4897
|
+
maxHostCpuBusyFraction
|
|
4781
4898
|
};
|
|
4782
4899
|
}
|
|
4783
4900
|
function ensureDataDir(config) {
|
|
4784
4901
|
mkdirSync5(config.dataDir, { recursive: true });
|
|
4785
4902
|
}
|
|
4786
|
-
var DEFAULT_MAX_GIT_BUNDLE_BYTES, DEFAULT_GIT_ALLOWLIST_SCHEMES2, DEFAULT_GIT_ALLOWLIST_HOSTS2;
|
|
4903
|
+
var DEFAULT_MAX_GIT_BUNDLE_BYTES, DEFAULT_GIT_ALLOWLIST_SCHEMES2, DEFAULT_GIT_ALLOWLIST_HOSTS2, CONTROLLER_CONFIG_FILENAME, GitAllowlistFileSchema2, AbsoluteAllowlistPathSchema, ControllerConfigFileSchema;
|
|
4787
4904
|
var init_config = __esm({
|
|
4788
4905
|
"../controller/dist/config.js"() {
|
|
4789
4906
|
"use strict";
|
|
@@ -4791,6 +4908,32 @@ var init_config = __esm({
|
|
|
4791
4908
|
DEFAULT_MAX_GIT_BUNDLE_BYTES = 512 * 1024 * 1024;
|
|
4792
4909
|
DEFAULT_GIT_ALLOWLIST_SCHEMES2 = ["https", "ssh"];
|
|
4793
4910
|
DEFAULT_GIT_ALLOWLIST_HOSTS2 = ["github.com"];
|
|
4911
|
+
CONTROLLER_CONFIG_FILENAME = "controller.json";
|
|
4912
|
+
GitAllowlistFileSchema2 = z8.object({
|
|
4913
|
+
schemes: z8.array(z8.string().min(1)).optional(),
|
|
4914
|
+
hosts: z8.array(z8.string().min(1)).optional(),
|
|
4915
|
+
repository_prefixes: z8.array(z8.string().min(1)).optional()
|
|
4916
|
+
});
|
|
4917
|
+
AbsoluteAllowlistPathSchema = z8.string().refine((value) => value.length > 0 && value === value.trim() && isAbsolute2(value), {
|
|
4918
|
+
message: 'must be a non-empty absolute path (relative paths, ".", and whitespace are rejected)'
|
|
4919
|
+
});
|
|
4920
|
+
ControllerConfigFileSchema = z8.object({
|
|
4921
|
+
mcp_host: z8.string().min(1).optional(),
|
|
4922
|
+
mcp_port: z8.number().int().positive().optional(),
|
|
4923
|
+
agent_plane_port: z8.number().int().positive().optional(),
|
|
4924
|
+
controller_public_host: z8.string().min(1).optional(),
|
|
4925
|
+
data_plane_base_url: z8.string().min(1).nullable().optional(),
|
|
4926
|
+
allowed_project_roots: z8.array(AbsoluteAllowlistPathSchema).optional(),
|
|
4927
|
+
allowed_artifact_destinations: z8.array(AbsoluteAllowlistPathSchema).optional(),
|
|
4928
|
+
git_allowlist: GitAllowlistFileSchema2.optional(),
|
|
4929
|
+
local_max_concurrent_jobs: z8.number().int().positive().optional(),
|
|
4930
|
+
disconnect_grace_seconds: z8.number().int().nonnegative().optional(),
|
|
4931
|
+
orphan_timeout_seconds: z8.number().int().nonnegative().optional(),
|
|
4932
|
+
reconcile_deadline_seconds: z8.number().int().nonnegative().optional(),
|
|
4933
|
+
allow_local_fallback: z8.boolean().optional(),
|
|
4934
|
+
max_git_bundle_bytes: z8.number().int().positive().optional(),
|
|
4935
|
+
local_fallback_max_host_cpu_percent: z8.number().min(0).max(100).optional()
|
|
4936
|
+
}).strict();
|
|
4794
4937
|
}
|
|
4795
4938
|
});
|
|
4796
4939
|
|
|
@@ -5028,7 +5171,7 @@ var init_database = __esm({
|
|
|
5028
5171
|
});
|
|
5029
5172
|
|
|
5030
5173
|
// ../controller/dist/execution/artifacts.js
|
|
5031
|
-
import { appendFile as appendFile3, mkdir as
|
|
5174
|
+
import { appendFile as appendFile3, mkdir as mkdir11, writeFile as writeFile11 } from "node:fs/promises";
|
|
5032
5175
|
import { join as join26 } from "node:path";
|
|
5033
5176
|
async function persistCollectedArtifacts(input) {
|
|
5034
5177
|
const results = [];
|
|
@@ -5073,7 +5216,7 @@ async function materializeArtifactToDestination(input) {
|
|
|
5073
5216
|
if (!allowed) {
|
|
5074
5217
|
throw new Error("Destination path is outside allowed artifact destinations");
|
|
5075
5218
|
}
|
|
5076
|
-
const { access: access4, readFile:
|
|
5219
|
+
const { access: access4, readFile: readFile19, rename: rename6 } = await import("node:fs/promises");
|
|
5077
5220
|
try {
|
|
5078
5221
|
await access4(input.destinationPath);
|
|
5079
5222
|
if (!input.overwrite) {
|
|
@@ -5084,20 +5227,20 @@ async function materializeArtifactToDestination(input) {
|
|
|
5084
5227
|
throw error;
|
|
5085
5228
|
}
|
|
5086
5229
|
}
|
|
5087
|
-
const content = await
|
|
5230
|
+
const content = await readFile19(row.path);
|
|
5088
5231
|
if (sha256(content) !== row.sha256) {
|
|
5089
5232
|
throw new Error("Stored artifact hash mismatch");
|
|
5090
5233
|
}
|
|
5091
5234
|
const tempPath = `${input.destinationPath}.rbo.tmp`;
|
|
5092
|
-
await
|
|
5093
|
-
const writtenHash = sha256(await
|
|
5235
|
+
await writeFile11(tempPath, content);
|
|
5236
|
+
const writtenHash = sha256(await readFile19(tempPath));
|
|
5094
5237
|
if (writtenHash !== row.sha256) {
|
|
5095
5238
|
throw new Error("Temporary artifact hash mismatch");
|
|
5096
5239
|
}
|
|
5097
5240
|
await rename6(tempPath, input.destinationPath);
|
|
5098
5241
|
if (input.dataDir) {
|
|
5099
5242
|
const auditDir = join26(input.dataDir, "audit");
|
|
5100
|
-
await
|
|
5243
|
+
await mkdir11(auditDir, { recursive: true });
|
|
5101
5244
|
const auditLine = JSON.stringify({
|
|
5102
5245
|
type: "artifact_materialize",
|
|
5103
5246
|
created_at: nowIso(),
|
|
@@ -5284,7 +5427,7 @@ var init_lifecycle = __esm({
|
|
|
5284
5427
|
});
|
|
5285
5428
|
|
|
5286
5429
|
// ../controller/dist/execution/runner.js
|
|
5287
|
-
import { mkdir as
|
|
5430
|
+
import { mkdir as mkdir12, readFile as readFile14, rm as rm10, writeFile as writeFile12 } from "node:fs/promises";
|
|
5288
5431
|
import { dirname as dirname7, join as join27 } from "node:path";
|
|
5289
5432
|
function getLocalRunningJobsCount() {
|
|
5290
5433
|
return admissionActive;
|
|
@@ -5410,9 +5553,9 @@ async function runLocalJob(ctx, jobId) {
|
|
|
5410
5553
|
const persistentLogs = attemptLogDir(ctx.dataDir, attempt.id);
|
|
5411
5554
|
const controlDir = attemptControlDir(ctx.dataDir, attempt.id);
|
|
5412
5555
|
const artifactsDir = attemptArtifactsDir(ctx.dataDir, attempt.id);
|
|
5413
|
-
await
|
|
5414
|
-
await
|
|
5415
|
-
await
|
|
5556
|
+
await mkdir12(artifactsDir, { recursive: true });
|
|
5557
|
+
await mkdir12(persistentLogs, { recursive: true });
|
|
5558
|
+
await mkdir12(controlDir, { recursive: true });
|
|
5416
5559
|
let timedOut = false;
|
|
5417
5560
|
let cancelled = false;
|
|
5418
5561
|
let durationComplete = false;
|
|
@@ -5433,7 +5576,7 @@ async function runLocalJob(ctx, jobId) {
|
|
|
5433
5576
|
if (!snapshotRow?.payload_path) {
|
|
5434
5577
|
throw new RboError("internal", "Job is missing snapshot payload");
|
|
5435
5578
|
}
|
|
5436
|
-
const manifest = JSON.parse(await
|
|
5579
|
+
const manifest = JSON.parse(await readFile14(snapshotRow.manifest_path, "utf8"));
|
|
5437
5580
|
const materialized = await materializeFullSnapshot({
|
|
5438
5581
|
manifest,
|
|
5439
5582
|
archivePath: snapshotRow.payload_path,
|
|
@@ -5451,7 +5594,7 @@ async function runLocalJob(ctx, jobId) {
|
|
|
5451
5594
|
});
|
|
5452
5595
|
const warningsPath = join27(dirname7(snapshotRow.manifest_path), "secret-warnings.json");
|
|
5453
5596
|
try {
|
|
5454
|
-
const warnings = JSON.parse(await
|
|
5597
|
+
const warnings = JSON.parse(await readFile14(warningsPath, "utf8"));
|
|
5455
5598
|
for (const warning of warnings) {
|
|
5456
5599
|
await emitJobEvent(ctx, logs, {
|
|
5457
5600
|
type: "secret_warning",
|
|
@@ -5698,9 +5841,9 @@ async function captureAndPersistSnapshot(ctx, jobId, request) {
|
|
|
5698
5841
|
contentStorageDir: storageDir
|
|
5699
5842
|
});
|
|
5700
5843
|
const manifestPath = join27(storageDir, "manifest.json");
|
|
5701
|
-
await
|
|
5702
|
-
await
|
|
5703
|
-
await
|
|
5844
|
+
await writeFile12(manifestPath, JSON.stringify(captured.manifest, null, 2));
|
|
5845
|
+
await writeFile12(join27(storageDir, "secret-warnings.json"), JSON.stringify(captured.secretWarnings));
|
|
5846
|
+
await writeFile12(join27(storageDir, "git-source-requirements.json"), JSON.stringify(captured.gitSourceRequirements));
|
|
5704
5847
|
persistSnapshot(ctx.db, {
|
|
5705
5848
|
snapshotId: captured.instance.snapshot_id,
|
|
5706
5849
|
contentId: captured.manifest.content_id,
|
|
@@ -5746,7 +5889,7 @@ function mergeGitSourceToolRequirements(request, gitSourceRequirements) {
|
|
|
5746
5889
|
}
|
|
5747
5890
|
async function readGitSourceRequirements(dataDir, jobId) {
|
|
5748
5891
|
try {
|
|
5749
|
-
const raw = await
|
|
5892
|
+
const raw = await readFile14(join27(dataDir, "snapshots", jobId, "git-source-requirements.json"), "utf8");
|
|
5750
5893
|
const parsed = JSON.parse(raw);
|
|
5751
5894
|
return {
|
|
5752
5895
|
submodules: parsed.submodules === true,
|
|
@@ -5838,7 +5981,7 @@ var init_data_tokens = __esm({
|
|
|
5838
5981
|
// ../controller/dist/http/data-plane.js
|
|
5839
5982
|
import { createHash as createHash7 } from "node:crypto";
|
|
5840
5983
|
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2 } from "node:fs";
|
|
5841
|
-
import { access as access3, mkdir as
|
|
5984
|
+
import { access as access3, mkdir as mkdir13, readFile as readFile15, rename as rename5, rm as rm11, stat as stat8 } from "node:fs/promises";
|
|
5842
5985
|
import { dirname as dirname8, join as join28 } from "node:path";
|
|
5843
5986
|
function artifactUploadStreamLimit(declaredSizeBytes, globalCapBytes = DEFAULT_ARTIFACT_CAP_BYTES) {
|
|
5844
5987
|
return Math.min(globalCapBytes, declaredSizeBytes);
|
|
@@ -5868,7 +6011,7 @@ async function filterMissingArtifacts(dataDir, attemptId, artifacts) {
|
|
|
5868
6011
|
for (const art of artifacts) {
|
|
5869
6012
|
const destPath = join28(artifactsDir, art.logical_name);
|
|
5870
6013
|
try {
|
|
5871
|
-
const buf = await
|
|
6014
|
+
const buf = await readFile15(destPath);
|
|
5872
6015
|
const hash = createHash7("sha256").update(buf).digest("hex");
|
|
5873
6016
|
if (hash === art.sha256 && buf.length === art.size_bytes) {
|
|
5874
6017
|
continue;
|
|
@@ -5988,7 +6131,7 @@ async function handleDataPlaneRequest(req, res, options) {
|
|
|
5988
6131
|
const transferStats = await stat8(transferSnapshot);
|
|
5989
6132
|
payloadPath = transferSnapshot;
|
|
5990
6133
|
sizeBytes = transferStats.size;
|
|
5991
|
-
const transferData = await
|
|
6134
|
+
const transferData = await readFile15(transferSnapshot);
|
|
5992
6135
|
sha2562 = createHash7("sha256").update(transferData).digest("hex");
|
|
5993
6136
|
} catch {
|
|
5994
6137
|
}
|
|
@@ -6087,8 +6230,8 @@ async function handleDataPlaneRequest(req, res, options) {
|
|
|
6087
6230
|
return true;
|
|
6088
6231
|
}
|
|
6089
6232
|
const partPath = join28(artifactsDir, `.upload-${createHash7("sha256").update(logicalName).digest("hex").slice(0, 16)}.part`);
|
|
6090
|
-
await
|
|
6091
|
-
await
|
|
6233
|
+
await mkdir13(dirname8(destPath), { recursive: true });
|
|
6234
|
+
await mkdir13(artifactsDir, { recursive: true });
|
|
6092
6235
|
const hasher = createHash7("sha256");
|
|
6093
6236
|
const writeStream = createWriteStream2(partPath);
|
|
6094
6237
|
let sizeCounter = 0;
|
|
@@ -6297,7 +6440,7 @@ async function handleDataPlaneRequest(req, res, options) {
|
|
|
6297
6440
|
}
|
|
6298
6441
|
const bundlePath = join28(attemptTransferDir(options.dataDir, attemptId), "bundle.gitbundle");
|
|
6299
6442
|
try {
|
|
6300
|
-
const data = await
|
|
6443
|
+
const data = await readFile15(bundlePath);
|
|
6301
6444
|
const sha2562 = createHash7("sha256").update(data).digest("hex");
|
|
6302
6445
|
res.writeHead(200, {
|
|
6303
6446
|
"content-type": "application/octet-stream",
|
|
@@ -6696,8 +6839,8 @@ var init_coordinator = __esm({
|
|
|
6696
6839
|
// ../controller/dist/execution/remote-execution.js
|
|
6697
6840
|
import { execFile as execFile8 } from "node:child_process";
|
|
6698
6841
|
import { createHash as createHash8 } from "node:crypto";
|
|
6699
|
-
import { readFileSync as
|
|
6700
|
-
import { mkdir as
|
|
6842
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
6843
|
+
import { mkdir as mkdir14, readFile as readFile16, rm as rm12, stat as stat9, writeFile as writeFile13 } from "node:fs/promises";
|
|
6701
6844
|
import { dirname as dirname9, join as join29 } from "node:path";
|
|
6702
6845
|
import { promisify as promisify8 } from "node:util";
|
|
6703
6846
|
function snapshotManifestMode(manifest) {
|
|
@@ -6804,7 +6947,7 @@ function failAttemptPrepare(opts, attemptId, jobId, category, message) {
|
|
|
6804
6947
|
});
|
|
6805
6948
|
}
|
|
6806
6949
|
async function createGitBundle(projectRoot, baseCommit, bundlePath, maxBytes = DEFAULT_MAX_GIT_BUNDLE_BYTES) {
|
|
6807
|
-
await
|
|
6950
|
+
await mkdir14(dirname9(bundlePath), { recursive: true });
|
|
6808
6951
|
const { stdout: head } = await execFileAsync8("git", ["rev-parse", "HEAD"], {
|
|
6809
6952
|
cwd: projectRoot,
|
|
6810
6953
|
windowsHide: true
|
|
@@ -6833,7 +6976,7 @@ async function createGitBundle(projectRoot, baseCommit, bundlePath, maxBytes = D
|
|
|
6833
6976
|
await rm12(bundlePath, { force: true });
|
|
6834
6977
|
throw new GitBundleSizeExceededError(sizeBytes, maxBytes);
|
|
6835
6978
|
}
|
|
6836
|
-
const data = await
|
|
6979
|
+
const data = await readFile16(bundlePath);
|
|
6837
6980
|
return {
|
|
6838
6981
|
sizeBytes: data.length,
|
|
6839
6982
|
sha256: createHash8("sha256").update(data).digest("hex")
|
|
@@ -6849,9 +6992,9 @@ async function ensureFullFallbackArchive(opts, attemptId, jobId) {
|
|
|
6849
6992
|
const manifestPath = join29(transferDir, "snapshot.manifest.json");
|
|
6850
6993
|
try {
|
|
6851
6994
|
const existing = await stat9(archivePath);
|
|
6852
|
-
const data = await
|
|
6995
|
+
const data = await readFile16(archivePath);
|
|
6853
6996
|
const sha2562 = createHash8("sha256").update(data).digest("hex");
|
|
6854
|
-
const manifestRaw = await
|
|
6997
|
+
const manifestRaw = await readFile16(manifestPath, "utf8");
|
|
6855
6998
|
const manifest = JSON.parse(manifestRaw);
|
|
6856
6999
|
if (manifest.payload?.sha256 === sha2562 && manifest.payload?.size === existing.size) {
|
|
6857
7000
|
return {
|
|
@@ -6863,7 +7006,7 @@ async function ensureFullFallbackArchive(opts, attemptId, jobId) {
|
|
|
6863
7006
|
}
|
|
6864
7007
|
} catch {
|
|
6865
7008
|
}
|
|
6866
|
-
await
|
|
7009
|
+
await mkdir14(transferDir, { recursive: true });
|
|
6867
7010
|
const captured = await captureFullSnapshot({
|
|
6868
7011
|
projectRoot: request.source.project_root,
|
|
6869
7012
|
allowedProjectRoots: opts.allowedProjectRoots ?? [request.source.project_root],
|
|
@@ -6876,8 +7019,8 @@ async function ensureFullFallbackArchive(opts, attemptId, jobId) {
|
|
|
6876
7019
|
additionalRoots: request.source.additional_roots,
|
|
6877
7020
|
contentStorageDir: transferDir
|
|
6878
7021
|
});
|
|
6879
|
-
await
|
|
6880
|
-
await
|
|
7022
|
+
await writeFile13(archivePath, await readFile16(captured.archivePath));
|
|
7023
|
+
await writeFile13(manifestPath, JSON.stringify(captured.manifest, null, 2));
|
|
6881
7024
|
return {
|
|
6882
7025
|
archivePath,
|
|
6883
7026
|
sizeBytes: captured.manifest.payload.size,
|
|
@@ -7023,7 +7166,7 @@ function handleRemoteLeaseAccept(opts, agentId, payload) {
|
|
|
7023
7166
|
let manifest;
|
|
7024
7167
|
if (snapshotRow.manifest_path) {
|
|
7025
7168
|
try {
|
|
7026
|
-
manifest = JSON.parse(
|
|
7169
|
+
manifest = JSON.parse(readFileSync8(snapshotRow.manifest_path, "utf8"));
|
|
7027
7170
|
} catch {
|
|
7028
7171
|
}
|
|
7029
7172
|
}
|
|
@@ -7050,7 +7193,7 @@ async function handleRemoteSourceNeed(opts, agentId, payload) {
|
|
|
7050
7193
|
}
|
|
7051
7194
|
let manifest;
|
|
7052
7195
|
try {
|
|
7053
|
-
manifest = JSON.parse(
|
|
7196
|
+
manifest = JSON.parse(readFileSync8(snapshotRow.manifest_path, "utf8"));
|
|
7054
7197
|
} catch {
|
|
7055
7198
|
failAttemptPrepare(opts, attempt.id, attempt.job_id, "materialization", "Cannot create bundle: manifest unreadable");
|
|
7056
7199
|
return;
|
|
@@ -7097,7 +7240,7 @@ async function handleRemoteSourceNeed(opts, agentId, payload) {
|
|
|
7097
7240
|
}
|
|
7098
7241
|
let manifest;
|
|
7099
7242
|
try {
|
|
7100
|
-
manifest = JSON.parse(
|
|
7243
|
+
manifest = JSON.parse(readFileSync8(snapshotRow.manifest_path, "utf8"));
|
|
7101
7244
|
} catch {
|
|
7102
7245
|
manifest = void 0;
|
|
7103
7246
|
}
|
|
@@ -7714,7 +7857,7 @@ function selectAgentForJob(agents, request, options = {}) {
|
|
|
7714
7857
|
const busyAgentRunningJobs = [];
|
|
7715
7858
|
for (const candidate of agents) {
|
|
7716
7859
|
const caps = candidate.capabilities;
|
|
7717
|
-
const effectiveCapacity =
|
|
7860
|
+
const effectiveCapacity = caps.execution.max_jobs;
|
|
7718
7861
|
if (effectiveCapacity <= 0 || candidate.activeJobsCount >= effectiveCapacity) {
|
|
7719
7862
|
if (effectiveCapacity > 0) {
|
|
7720
7863
|
busyAgentRunningJobs.push(candidate.activeJobsCount);
|
|
@@ -8303,22 +8446,18 @@ var init_submit = __esm({
|
|
|
8303
8446
|
|
|
8304
8447
|
// src/main.ts
|
|
8305
8448
|
init_dist();
|
|
8306
|
-
import { readFile as
|
|
8449
|
+
import { readFile as readFile18 } from "node:fs/promises";
|
|
8307
8450
|
|
|
8308
8451
|
// src/commands/agent.ts
|
|
8309
|
-
import { existsSync as
|
|
8310
|
-
import { mkdir as mkdir11, readFile as readFile14, writeFile as writeFile11 } from "node:fs/promises";
|
|
8452
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
8311
8453
|
import { join as join24 } from "node:path";
|
|
8312
8454
|
|
|
8313
|
-
// ../agent/dist/
|
|
8455
|
+
// ../agent/dist/config.js
|
|
8456
|
+
init_dist2();
|
|
8314
8457
|
init_dist();
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
import {
|
|
8318
|
-
import { readFile as readFile5, readdir as readdir5, statfs } from "node:fs/promises";
|
|
8319
|
-
import { arch, cpus as cpus2, freemem, hostname, loadavg, platform as platform2, release, totalmem } from "node:os";
|
|
8320
|
-
import { join as join8 } from "node:path";
|
|
8321
|
-
import { promisify as promisify2 } from "node:util";
|
|
8458
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
8459
|
+
import { dirname as dirname2, join as join7 } from "node:path";
|
|
8460
|
+
import { z as z6 } from "zod";
|
|
8322
8461
|
|
|
8323
8462
|
// ../agent/dist/build-cache/index.js
|
|
8324
8463
|
import { access, readdir as readdir3 } from "node:fs/promises";
|
|
@@ -9021,12 +9160,6 @@ function resolveBuildCachesDir(stateDir) {
|
|
|
9021
9160
|
return join5(stateDir, "build-caches");
|
|
9022
9161
|
}
|
|
9023
9162
|
|
|
9024
|
-
// ../agent/dist/config.js
|
|
9025
|
-
init_dist2();
|
|
9026
|
-
init_dist();
|
|
9027
|
-
import { mkdirSync as mkdirSync2 } from "node:fs";
|
|
9028
|
-
import { dirname as dirname2, join as join7 } from "node:path";
|
|
9029
|
-
|
|
9030
9163
|
// ../agent/dist/repos/mirror.js
|
|
9031
9164
|
init_dist();
|
|
9032
9165
|
import { execFile } from "node:child_process";
|
|
@@ -9383,19 +9516,77 @@ var DEFAULT_LOG_SPOOL_MAX_BYTES = 536870912;
|
|
|
9383
9516
|
var DEFAULT_LOG_SEND_QUEUE_MAX = 64;
|
|
9384
9517
|
var DEFAULT_GIT_ALLOWLIST_SCHEMES = ["https", "ssh"];
|
|
9385
9518
|
var DEFAULT_GIT_ALLOWLIST_HOSTS = ["github.com"];
|
|
9519
|
+
var AGENT_CONFIG_FILENAME = "agent.json";
|
|
9520
|
+
var AGENT_CONFIG_SCHEMA_VERSION = 1;
|
|
9521
|
+
var RiskLevelSchema2 = z6.enum(["safe", "normal", "destructive", "hardware"]);
|
|
9522
|
+
var GitAllowlistFileSchema = z6.object({
|
|
9523
|
+
schemes: z6.array(z6.string().min(1)).optional(),
|
|
9524
|
+
hosts: z6.array(z6.string().min(1)).optional(),
|
|
9525
|
+
repository_prefixes: z6.array(z6.string().min(1)).optional()
|
|
9526
|
+
});
|
|
9527
|
+
var RepoCacheFileSchema = z6.object({
|
|
9528
|
+
max_size_gb: z6.number().positive().optional(),
|
|
9529
|
+
min_free_disk_gb: z6.number().nonnegative().optional(),
|
|
9530
|
+
retention_days: z6.number().nonnegative().optional()
|
|
9531
|
+
});
|
|
9532
|
+
var BuildCacheFileSchema = z6.object({
|
|
9533
|
+
enabled_kinds: z6.array(BuildCacheKindSchema).optional(),
|
|
9534
|
+
max_size_gb: z6.number().positive().optional(),
|
|
9535
|
+
min_free_disk_gb: z6.number().nonnegative().optional(),
|
|
9536
|
+
retention_days: z6.number().nonnegative().optional(),
|
|
9537
|
+
allow_read_risk_levels: z6.array(RiskLevelSchema2).optional(),
|
|
9538
|
+
allow_write_risk_levels: z6.array(RiskLevelSchema2).optional()
|
|
9539
|
+
});
|
|
9540
|
+
var AgentConfigFileSchema = z6.object({
|
|
9541
|
+
schema_version: z6.number().int().positive().optional(),
|
|
9542
|
+
initialized_at: z6.string().min(1).optional(),
|
|
9543
|
+
controller_url: z6.string().optional(),
|
|
9544
|
+
controller_fingerprint: z6.string().optional(),
|
|
9545
|
+
display_name: z6.string().min(1).optional(),
|
|
9546
|
+
max_jobs: z6.number().int().positive().optional(),
|
|
9547
|
+
repo_cache_dir: z6.string().min(1).nullable().optional(),
|
|
9548
|
+
secret_map: z6.record(z6.string()).nullable().optional(),
|
|
9549
|
+
git_allowlist: GitAllowlistFileSchema.optional(),
|
|
9550
|
+
repo_cache: RepoCacheFileSchema.optional(),
|
|
9551
|
+
build_cache: BuildCacheFileSchema.optional(),
|
|
9552
|
+
log_spool_max_bytes: z6.number().int().positive().optional(),
|
|
9553
|
+
log_send_queue_max: z6.number().int().positive().optional(),
|
|
9554
|
+
disconnect_grace_seconds: z6.number().int().nonnegative().optional(),
|
|
9555
|
+
orphan_timeout_seconds: z6.number().int().nonnegative().optional(),
|
|
9556
|
+
reconcile_deadline_seconds: z6.number().int().nonnegative().optional(),
|
|
9557
|
+
disk_min_free_bytes: z6.number().int().nonnegative().nullable().optional(),
|
|
9558
|
+
configured_priority: z6.number().nullable().optional()
|
|
9559
|
+
}).strict();
|
|
9386
9560
|
function parseCsv(raw) {
|
|
9387
9561
|
if (!raw?.trim()) {
|
|
9388
9562
|
return [];
|
|
9389
9563
|
}
|
|
9390
9564
|
return raw.split(",").map((part) => part.trim()).filter(Boolean);
|
|
9391
9565
|
}
|
|
9392
|
-
function
|
|
9393
|
-
|
|
9394
|
-
|
|
9566
|
+
function envSet(key, env = process.env) {
|
|
9567
|
+
return env[key] !== void 0;
|
|
9568
|
+
}
|
|
9569
|
+
function parseEnvNumber(key, raw) {
|
|
9570
|
+
const n = Number(raw);
|
|
9571
|
+
if (!Number.isFinite(n)) {
|
|
9572
|
+
throw new Error(`Invalid ${key}=${JSON.stringify(raw)}: expected a finite number`);
|
|
9573
|
+
}
|
|
9574
|
+
return n;
|
|
9575
|
+
}
|
|
9576
|
+
function parseEnvInt(key, raw) {
|
|
9577
|
+
const n = parseEnvNumber(key, raw);
|
|
9578
|
+
if (!Number.isInteger(n)) {
|
|
9579
|
+
throw new Error(`Invalid ${key}=${JSON.stringify(raw)}: expected an integer`);
|
|
9580
|
+
}
|
|
9581
|
+
return n;
|
|
9582
|
+
}
|
|
9583
|
+
function parseGitAllowlistFromEnv(env = process.env) {
|
|
9584
|
+
if (!envSet("RBO_GIT_ALLOWLIST_SCHEMES", env) && !envSet("RBO_GIT_ALLOWLIST_HOSTS", env) && !envSet("RBO_GIT_ALLOWLIST_PREFIXES", env)) {
|
|
9585
|
+
return void 0;
|
|
9395
9586
|
}
|
|
9396
|
-
const schemes = parseCsv(
|
|
9397
|
-
const hosts = parseCsv(
|
|
9398
|
-
const prefixesRaw =
|
|
9587
|
+
const schemes = parseCsv(env.RBO_GIT_ALLOWLIST_SCHEMES);
|
|
9588
|
+
const hosts = parseCsv(env.RBO_GIT_ALLOWLIST_HOSTS);
|
|
9589
|
+
const prefixesRaw = env.RBO_GIT_ALLOWLIST_PREFIXES?.trim();
|
|
9399
9590
|
let repository_prefixes;
|
|
9400
9591
|
if (prefixesRaw) {
|
|
9401
9592
|
try {
|
|
@@ -9414,17 +9605,23 @@ function parseGitAllowlist(overrides) {
|
|
|
9414
9605
|
...repository_prefixes ? { repository_prefixes } : {}
|
|
9415
9606
|
};
|
|
9416
9607
|
}
|
|
9417
|
-
function
|
|
9418
|
-
if (
|
|
9419
|
-
return
|
|
9608
|
+
function mergeGitAllowlist(file, fromEnv, override) {
|
|
9609
|
+
if (override) {
|
|
9610
|
+
return override;
|
|
9611
|
+
}
|
|
9612
|
+
if (fromEnv) {
|
|
9613
|
+
return fromEnv;
|
|
9614
|
+
}
|
|
9615
|
+
if (file) {
|
|
9616
|
+
return {
|
|
9617
|
+
schemes: file.schemes && file.schemes.length > 0 ? file.schemes : [...DEFAULT_GIT_ALLOWLIST_SCHEMES],
|
|
9618
|
+
hosts: file.hosts && file.hosts.length > 0 ? file.hosts : [...DEFAULT_GIT_ALLOWLIST_HOSTS],
|
|
9619
|
+
...file.repository_prefixes ? { repository_prefixes: file.repository_prefixes } : {}
|
|
9620
|
+
};
|
|
9420
9621
|
}
|
|
9421
|
-
const maxSize = Number(process.env.RBO_REPO_CACHE_MAX_SIZE_GB ?? DEFAULT_REPO_CACHE_CONFIG.max_size_gb);
|
|
9422
|
-
const minFree = Number(process.env.RBO_REPO_CACHE_MIN_FREE_DISK_GB ?? DEFAULT_REPO_CACHE_CONFIG.min_free_disk_gb);
|
|
9423
|
-
const retention = Number(process.env.RBO_REPO_CACHE_RETENTION_DAYS ?? DEFAULT_REPO_CACHE_CONFIG.retention_days);
|
|
9424
9622
|
return {
|
|
9425
|
-
|
|
9426
|
-
|
|
9427
|
-
retention_days: retention
|
|
9623
|
+
schemes: [...DEFAULT_GIT_ALLOWLIST_SCHEMES],
|
|
9624
|
+
hosts: [...DEFAULT_GIT_ALLOWLIST_HOSTS]
|
|
9428
9625
|
};
|
|
9429
9626
|
}
|
|
9430
9627
|
function parseBuildCacheKinds(raw) {
|
|
@@ -9455,23 +9652,30 @@ function parseRiskLevels(raw) {
|
|
|
9455
9652
|
}
|
|
9456
9653
|
return levels;
|
|
9457
9654
|
}
|
|
9458
|
-
function
|
|
9459
|
-
if (
|
|
9460
|
-
return
|
|
9655
|
+
function mergeRepoCache(file, override) {
|
|
9656
|
+
if (override) {
|
|
9657
|
+
return override;
|
|
9658
|
+
}
|
|
9659
|
+
const maxSize = envSet("RBO_REPO_CACHE_MAX_SIZE_GB") ? parseEnvNumber("RBO_REPO_CACHE_MAX_SIZE_GB", process.env.RBO_REPO_CACHE_MAX_SIZE_GB) : file?.max_size_gb ?? DEFAULT_REPO_CACHE_CONFIG.max_size_gb;
|
|
9660
|
+
const minFree = envSet("RBO_REPO_CACHE_MIN_FREE_DISK_GB") ? parseEnvNumber("RBO_REPO_CACHE_MIN_FREE_DISK_GB", process.env.RBO_REPO_CACHE_MIN_FREE_DISK_GB) : file?.min_free_disk_gb ?? DEFAULT_REPO_CACHE_CONFIG.min_free_disk_gb;
|
|
9661
|
+
const retention = envSet("RBO_REPO_CACHE_RETENTION_DAYS") ? parseEnvNumber("RBO_REPO_CACHE_RETENTION_DAYS", process.env.RBO_REPO_CACHE_RETENTION_DAYS) : file?.retention_days ?? DEFAULT_REPO_CACHE_CONFIG.retention_days;
|
|
9662
|
+
return {
|
|
9663
|
+
max_size_gb: maxSize,
|
|
9664
|
+
min_free_disk_gb: minFree,
|
|
9665
|
+
retention_days: retention
|
|
9666
|
+
};
|
|
9667
|
+
}
|
|
9668
|
+
function mergeBuildCache(file, override) {
|
|
9669
|
+
if (override) {
|
|
9670
|
+
return override;
|
|
9461
9671
|
}
|
|
9462
9672
|
return {
|
|
9463
|
-
enabledKinds: parseBuildCacheKinds(process.env.RBO_BUILD_CACHE_ENABLED_KINDS) ?? [
|
|
9464
|
-
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
allowReadRiskLevels: parseRiskLevels(process.env.RBO_BUILD_CACHE_ALLOW_READ_RISKS) ?? [
|
|
9470
|
-
...DEFAULT_BUILD_CACHE_CONFIG.allowReadRiskLevels
|
|
9471
|
-
],
|
|
9472
|
-
allowWriteRiskLevels: parseRiskLevels(process.env.RBO_BUILD_CACHE_ALLOW_WRITE_RISKS) ?? [
|
|
9473
|
-
...DEFAULT_BUILD_CACHE_CONFIG.allowWriteRiskLevels
|
|
9474
|
-
]
|
|
9673
|
+
enabledKinds: (envSet("RBO_BUILD_CACHE_ENABLED_KINDS") ? parseBuildCacheKinds(process.env.RBO_BUILD_CACHE_ENABLED_KINDS) : void 0) ?? file?.enabled_kinds ?? [...DEFAULT_BUILD_CACHE_CONFIG.enabledKinds],
|
|
9674
|
+
maxSizeGb: envSet("RBO_BUILD_CACHE_MAX_SIZE_GB") ? parseEnvNumber("RBO_BUILD_CACHE_MAX_SIZE_GB", process.env.RBO_BUILD_CACHE_MAX_SIZE_GB) : file?.max_size_gb ?? DEFAULT_BUILD_CACHE_CONFIG.maxSizeGb,
|
|
9675
|
+
minFreeDiskGb: envSet("RBO_BUILD_CACHE_MIN_FREE_DISK_GB") ? parseEnvNumber("RBO_BUILD_CACHE_MIN_FREE_DISK_GB", process.env.RBO_BUILD_CACHE_MIN_FREE_DISK_GB) : file?.min_free_disk_gb ?? DEFAULT_BUILD_CACHE_CONFIG.minFreeDiskGb,
|
|
9676
|
+
retentionDays: envSet("RBO_BUILD_CACHE_RETENTION_DAYS") ? parseEnvNumber("RBO_BUILD_CACHE_RETENTION_DAYS", process.env.RBO_BUILD_CACHE_RETENTION_DAYS) : file?.retention_days ?? DEFAULT_BUILD_CACHE_CONFIG.retentionDays,
|
|
9677
|
+
allowReadRiskLevels: (envSet("RBO_BUILD_CACHE_ALLOW_READ_RISKS") ? parseRiskLevels(process.env.RBO_BUILD_CACHE_ALLOW_READ_RISKS) : void 0) ?? file?.allow_read_risk_levels ?? [...DEFAULT_BUILD_CACHE_CONFIG.allowReadRiskLevels],
|
|
9678
|
+
allowWriteRiskLevels: (envSet("RBO_BUILD_CACHE_ALLOW_WRITE_RISKS") ? parseRiskLevels(process.env.RBO_BUILD_CACHE_ALLOW_WRITE_RISKS) : void 0) ?? file?.allow_write_risk_levels ?? [...DEFAULT_BUILD_CACHE_CONFIG.allowWriteRiskLevels]
|
|
9475
9679
|
};
|
|
9476
9680
|
}
|
|
9477
9681
|
function parseSecretMap(raw) {
|
|
@@ -9495,50 +9699,157 @@ function parseSecretMap(raw) {
|
|
|
9495
9699
|
throw new Error(`Invalid RBO_SECRET_MAP: ${error instanceof Error ? error.message : String(error)}`);
|
|
9496
9700
|
}
|
|
9497
9701
|
}
|
|
9702
|
+
function defaultAgentConfigFile(options = {}) {
|
|
9703
|
+
return {
|
|
9704
|
+
schema_version: AGENT_CONFIG_SCHEMA_VERSION,
|
|
9705
|
+
initialized_at: options.initializedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
9706
|
+
controller_url: "",
|
|
9707
|
+
controller_fingerprint: "",
|
|
9708
|
+
display_name: "rbo-agent",
|
|
9709
|
+
max_jobs: 1,
|
|
9710
|
+
repo_cache_dir: null,
|
|
9711
|
+
secret_map: null,
|
|
9712
|
+
git_allowlist: {
|
|
9713
|
+
schemes: [...DEFAULT_GIT_ALLOWLIST_SCHEMES],
|
|
9714
|
+
hosts: [...DEFAULT_GIT_ALLOWLIST_HOSTS]
|
|
9715
|
+
},
|
|
9716
|
+
repo_cache: {
|
|
9717
|
+
max_size_gb: DEFAULT_REPO_CACHE_CONFIG.max_size_gb,
|
|
9718
|
+
min_free_disk_gb: DEFAULT_REPO_CACHE_CONFIG.min_free_disk_gb,
|
|
9719
|
+
retention_days: DEFAULT_REPO_CACHE_CONFIG.retention_days
|
|
9720
|
+
},
|
|
9721
|
+
build_cache: {
|
|
9722
|
+
enabled_kinds: [...DEFAULT_BUILD_CACHE_CONFIG.enabledKinds],
|
|
9723
|
+
max_size_gb: DEFAULT_BUILD_CACHE_CONFIG.maxSizeGb,
|
|
9724
|
+
min_free_disk_gb: DEFAULT_BUILD_CACHE_CONFIG.minFreeDiskGb,
|
|
9725
|
+
retention_days: DEFAULT_BUILD_CACHE_CONFIG.retentionDays,
|
|
9726
|
+
allow_read_risk_levels: [...DEFAULT_BUILD_CACHE_CONFIG.allowReadRiskLevels],
|
|
9727
|
+
allow_write_risk_levels: [...DEFAULT_BUILD_CACHE_CONFIG.allowWriteRiskLevels]
|
|
9728
|
+
},
|
|
9729
|
+
log_spool_max_bytes: DEFAULT_LOG_SPOOL_MAX_BYTES,
|
|
9730
|
+
log_send_queue_max: DEFAULT_LOG_SEND_QUEUE_MAX,
|
|
9731
|
+
disconnect_grace_seconds: 60,
|
|
9732
|
+
orphan_timeout_seconds: 300,
|
|
9733
|
+
reconcile_deadline_seconds: 120,
|
|
9734
|
+
disk_min_free_bytes: null,
|
|
9735
|
+
configured_priority: null
|
|
9736
|
+
};
|
|
9737
|
+
}
|
|
9738
|
+
function resolveAgentConfigPath(stateDir) {
|
|
9739
|
+
return join7(stateDir, AGENT_CONFIG_FILENAME);
|
|
9740
|
+
}
|
|
9741
|
+
function readAgentConfigFile(configPath) {
|
|
9742
|
+
if (!existsSync2(configPath)) {
|
|
9743
|
+
return void 0;
|
|
9744
|
+
}
|
|
9745
|
+
let raw;
|
|
9746
|
+
try {
|
|
9747
|
+
raw = JSON.parse(readFileSync3(configPath, "utf8"));
|
|
9748
|
+
} catch (error) {
|
|
9749
|
+
throw new Error(`Invalid agent config JSON at ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
9750
|
+
}
|
|
9751
|
+
const parsed = AgentConfigFileSchema.safeParse(raw);
|
|
9752
|
+
if (!parsed.success) {
|
|
9753
|
+
throw new Error(`Invalid agent config at ${configPath}: ${parsed.error.message}`);
|
|
9754
|
+
}
|
|
9755
|
+
return parsed.data;
|
|
9756
|
+
}
|
|
9757
|
+
function writeDefaultAgentConfigFile(stateDir, options = {}) {
|
|
9758
|
+
mkdirSync2(stateDir, { recursive: true });
|
|
9759
|
+
const path = resolveAgentConfigPath(stateDir);
|
|
9760
|
+
if (existsSync2(path) && !options.force) {
|
|
9761
|
+
let initialized_at2 = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
9762
|
+
let schema_version = AGENT_CONFIG_SCHEMA_VERSION;
|
|
9763
|
+
try {
|
|
9764
|
+
const existing = readAgentConfigFile(path);
|
|
9765
|
+
initialized_at2 = existing?.initialized_at ?? initialized_at2;
|
|
9766
|
+
schema_version = existing?.schema_version ?? schema_version;
|
|
9767
|
+
} catch {
|
|
9768
|
+
}
|
|
9769
|
+
return {
|
|
9770
|
+
path,
|
|
9771
|
+
written: false,
|
|
9772
|
+
initialized_at: initialized_at2,
|
|
9773
|
+
schema_version
|
|
9774
|
+
};
|
|
9775
|
+
}
|
|
9776
|
+
const initialized_at = options.initializedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
9777
|
+
const body = `${JSON.stringify(defaultAgentConfigFile({ initializedAt: initialized_at }), null, 2)}
|
|
9778
|
+
`;
|
|
9779
|
+
writeFileSync2(path, body, "utf8");
|
|
9780
|
+
return {
|
|
9781
|
+
path,
|
|
9782
|
+
written: true,
|
|
9783
|
+
initialized_at,
|
|
9784
|
+
schema_version: AGENT_CONFIG_SCHEMA_VERSION
|
|
9785
|
+
};
|
|
9786
|
+
}
|
|
9498
9787
|
function loadAgentConfig(overrides = {}) {
|
|
9499
|
-
const
|
|
9500
|
-
const
|
|
9788
|
+
const { configPath: configPathOption, ...fieldOverrides } = overrides;
|
|
9789
|
+
const stateDir = fieldOverrides.stateDir ?? resolveAgentStateDir();
|
|
9790
|
+
const configPath = configPathOption === void 0 ? resolveAgentConfigPath(stateDir) : configPathOption;
|
|
9791
|
+
const file = configPath === null ? void 0 : readAgentConfigFile(configPath);
|
|
9792
|
+
const controllerUrl = fieldOverrides.controllerUrl ?? (envSet("RBO_CONTROLLER_URL") ? process.env.RBO_CONTROLLER_URL : void 0) ?? (file?.controller_url?.trim() ? file.controller_url.trim() : void 0);
|
|
9793
|
+
const controllerFingerprint = fieldOverrides.controllerFingerprint ?? (envSet("RBO_CONTROLLER_FINGERPRINT") ? process.env.RBO_CONTROLLER_FINGERPRINT : void 0) ?? (file?.controller_fingerprint?.trim() ? file.controller_fingerprint.trim() : void 0);
|
|
9501
9794
|
if (!controllerUrl) {
|
|
9502
|
-
throw new Error("
|
|
9795
|
+
throw new Error("controller_url is required \u2014 set it in agent.json (or RBO_CONTROLLER_URL=wss://<host>:7411/agent)");
|
|
9503
9796
|
}
|
|
9504
9797
|
if (!controllerFingerprint) {
|
|
9505
|
-
throw new Error("
|
|
9798
|
+
throw new Error("controller_fingerprint is required \u2014 set it in agent.json (or RBO_CONTROLLER_FINGERPRINT from `rbo controller fingerprint`)");
|
|
9799
|
+
}
|
|
9800
|
+
const repoCache = mergeRepoCache(file?.repo_cache, fieldOverrides.repoCache);
|
|
9801
|
+
let secretMap = fieldOverrides.secretMap;
|
|
9802
|
+
if (secretMap === void 0) {
|
|
9803
|
+
if (envSet("RBO_SECRET_MAP")) {
|
|
9804
|
+
secretMap = parseSecretMap(process.env.RBO_SECRET_MAP);
|
|
9805
|
+
} else if (file?.secret_map !== void 0) {
|
|
9806
|
+
secretMap = file.secret_map ?? void 0;
|
|
9807
|
+
}
|
|
9808
|
+
}
|
|
9809
|
+
let repoCacheDir = fieldOverrides.repoCacheDir;
|
|
9810
|
+
if (repoCacheDir === void 0) {
|
|
9811
|
+
if (envSet("RBO_REPO_CACHE_DIR") && process.env.RBO_REPO_CACHE_DIR?.trim()) {
|
|
9812
|
+
repoCacheDir = process.env.RBO_REPO_CACHE_DIR.trim();
|
|
9813
|
+
} else if (file?.repo_cache_dir) {
|
|
9814
|
+
repoCacheDir = file.repo_cache_dir;
|
|
9815
|
+
}
|
|
9816
|
+
}
|
|
9817
|
+
let configuredPriority = fieldOverrides.configuredPriority;
|
|
9818
|
+
if (configuredPriority === void 0) {
|
|
9819
|
+
if (envSet("RBO_CONFIGURED_PRIORITY")) {
|
|
9820
|
+
configuredPriority = parseEnvNumber("RBO_CONFIGURED_PRIORITY", process.env.RBO_CONFIGURED_PRIORITY);
|
|
9821
|
+
} else if (file?.configured_priority !== void 0 && file.configured_priority !== null) {
|
|
9822
|
+
configuredPriority = file.configured_priority;
|
|
9823
|
+
}
|
|
9824
|
+
}
|
|
9825
|
+
let diskMinFreeBytes = fieldOverrides.diskMinFreeBytes;
|
|
9826
|
+
if (diskMinFreeBytes === void 0) {
|
|
9827
|
+
if (envSet("RBO_DISK_MIN_FREE_BYTES")) {
|
|
9828
|
+
diskMinFreeBytes = parseEnvInt("RBO_DISK_MIN_FREE_BYTES", process.env.RBO_DISK_MIN_FREE_BYTES);
|
|
9829
|
+
} else if (file?.disk_min_free_bytes !== void 0 && file.disk_min_free_bytes !== null) {
|
|
9830
|
+
diskMinFreeBytes = file.disk_min_free_bytes;
|
|
9831
|
+
} else {
|
|
9832
|
+
diskMinFreeBytes = Math.max(DEFAULT_DISK_MIN_FREE_BYTES, Math.floor(repoCache.min_free_disk_gb * 1024 ** 3));
|
|
9833
|
+
}
|
|
9506
9834
|
}
|
|
9507
9835
|
return {
|
|
9508
9836
|
controllerUrl,
|
|
9509
9837
|
controllerFingerprint,
|
|
9510
|
-
displayName:
|
|
9511
|
-
maxJobs:
|
|
9512
|
-
stateDir
|
|
9513
|
-
repoCacheDir
|
|
9514
|
-
secretMap
|
|
9515
|
-
gitAllowlist:
|
|
9516
|
-
repoCache
|
|
9517
|
-
buildCache:
|
|
9518
|
-
logSpoolMaxBytes:
|
|
9519
|
-
logSendQueueMax:
|
|
9520
|
-
disconnectGraceSeconds:
|
|
9521
|
-
orphanTimeoutSeconds:
|
|
9522
|
-
reconcileDeadlineSeconds:
|
|
9523
|
-
diskMinFreeBytes
|
|
9524
|
-
|
|
9525
|
-
return overrides.diskMinFreeBytes;
|
|
9526
|
-
}
|
|
9527
|
-
if (process.env.RBO_DISK_MIN_FREE_BYTES) {
|
|
9528
|
-
return Number(process.env.RBO_DISK_MIN_FREE_BYTES);
|
|
9529
|
-
}
|
|
9530
|
-
const repoCache = parseRepoCache(overrides.repoCache);
|
|
9531
|
-
return Math.max(DEFAULT_DISK_MIN_FREE_BYTES, Math.floor(repoCache.min_free_disk_gb * 1024 ** 3));
|
|
9532
|
-
})(),
|
|
9533
|
-
configuredPriority: (() => {
|
|
9534
|
-
if (overrides.configuredPriority !== void 0) {
|
|
9535
|
-
return overrides.configuredPriority;
|
|
9536
|
-
}
|
|
9537
|
-
if (process.env.RBO_CONFIGURED_PRIORITY) {
|
|
9538
|
-
return Number(process.env.RBO_CONFIGURED_PRIORITY);
|
|
9539
|
-
}
|
|
9540
|
-
return void 0;
|
|
9541
|
-
})()
|
|
9838
|
+
displayName: fieldOverrides.displayName ?? (envSet("RBO_AGENT_NAME") ? process.env.RBO_AGENT_NAME : void 0) ?? file?.display_name ?? "rbo-agent",
|
|
9839
|
+
maxJobs: fieldOverrides.maxJobs ?? (envSet("RBO_MAX_JOBS") ? parseEnvInt("RBO_MAX_JOBS", process.env.RBO_MAX_JOBS) : void 0) ?? file?.max_jobs ?? 1,
|
|
9840
|
+
stateDir,
|
|
9841
|
+
repoCacheDir,
|
|
9842
|
+
secretMap,
|
|
9843
|
+
gitAllowlist: mergeGitAllowlist(file?.git_allowlist, parseGitAllowlistFromEnv(), fieldOverrides.gitAllowlist),
|
|
9844
|
+
repoCache,
|
|
9845
|
+
buildCache: mergeBuildCache(file?.build_cache, fieldOverrides.buildCache),
|
|
9846
|
+
logSpoolMaxBytes: fieldOverrides.logSpoolMaxBytes ?? (envSet("RBO_LOG_SPOOL_MAX_BYTES") ? parseEnvInt("RBO_LOG_SPOOL_MAX_BYTES", process.env.RBO_LOG_SPOOL_MAX_BYTES) : void 0) ?? file?.log_spool_max_bytes ?? DEFAULT_LOG_SPOOL_MAX_BYTES,
|
|
9847
|
+
logSendQueueMax: fieldOverrides.logSendQueueMax ?? (envSet("RBO_LOG_SEND_QUEUE_MAX") ? parseEnvInt("RBO_LOG_SEND_QUEUE_MAX", process.env.RBO_LOG_SEND_QUEUE_MAX) : void 0) ?? file?.log_send_queue_max ?? DEFAULT_LOG_SEND_QUEUE_MAX,
|
|
9848
|
+
disconnectGraceSeconds: fieldOverrides.disconnectGraceSeconds ?? (envSet("RBO_DISCONNECT_GRACE_SECONDS") ? parseEnvInt("RBO_DISCONNECT_GRACE_SECONDS", process.env.RBO_DISCONNECT_GRACE_SECONDS) : void 0) ?? file?.disconnect_grace_seconds ?? 60,
|
|
9849
|
+
orphanTimeoutSeconds: fieldOverrides.orphanTimeoutSeconds ?? (envSet("RBO_ORPHAN_TIMEOUT_SECONDS") ? parseEnvInt("RBO_ORPHAN_TIMEOUT_SECONDS", process.env.RBO_ORPHAN_TIMEOUT_SECONDS) : void 0) ?? file?.orphan_timeout_seconds ?? 300,
|
|
9850
|
+
reconcileDeadlineSeconds: fieldOverrides.reconcileDeadlineSeconds ?? (envSet("RBO_RECONCILE_DEADLINE_SECONDS") ? parseEnvInt("RBO_RECONCILE_DEADLINE_SECONDS", process.env.RBO_RECONCILE_DEADLINE_SECONDS) : void 0) ?? file?.reconcile_deadline_seconds ?? 120,
|
|
9851
|
+
diskMinFreeBytes,
|
|
9852
|
+
configuredPriority
|
|
9542
9853
|
};
|
|
9543
9854
|
}
|
|
9544
9855
|
function resolveRepoCacheRoot(config) {
|
|
@@ -9554,7 +9865,15 @@ function ensureStateDir(config) {
|
|
|
9554
9865
|
mkdirSync2(config.stateDir, { recursive: true });
|
|
9555
9866
|
}
|
|
9556
9867
|
|
|
9868
|
+
// ../agent/dist/run.js
|
|
9869
|
+
init_dist();
|
|
9870
|
+
|
|
9557
9871
|
// ../agent/dist/capabilities/probe.js
|
|
9872
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
9873
|
+
import { readFile as readFile5, readdir as readdir5, statfs } from "node:fs/promises";
|
|
9874
|
+
import { arch, cpus as cpus2, freemem, hostname, loadavg, platform as platform2, release, totalmem } from "node:os";
|
|
9875
|
+
import { join as join8 } from "node:path";
|
|
9876
|
+
import { promisify as promisify2 } from "node:util";
|
|
9558
9877
|
var execFileAsync2 = promisify2(execFile2);
|
|
9559
9878
|
function osFamily() {
|
|
9560
9879
|
const p = platform2();
|
|
@@ -9704,7 +10023,7 @@ async function probeCapabilities(input) {
|
|
|
9704
10023
|
// ../agent/dist/connection/client.js
|
|
9705
10024
|
init_dist2();
|
|
9706
10025
|
init_dist();
|
|
9707
|
-
import { existsSync as
|
|
10026
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
9708
10027
|
import { hostname as hostname2 } from "node:os";
|
|
9709
10028
|
import { join as join22 } from "node:path";
|
|
9710
10029
|
import WebSocket from "ws";
|
|
@@ -9808,7 +10127,7 @@ init_dist4();
|
|
|
9808
10127
|
init_dist();
|
|
9809
10128
|
init_dist3();
|
|
9810
10129
|
import { createHash as createHash6 } from "node:crypto";
|
|
9811
|
-
import { createReadStream, existsSync as
|
|
10130
|
+
import { createReadStream, existsSync as existsSync4 } from "node:fs";
|
|
9812
10131
|
import { copyFile, mkdir as mkdir9, readFile as readFile13, rename as rename4, rm as rm9, stat as stat7 } from "node:fs/promises";
|
|
9813
10132
|
import { request as httpsRequest } from "node:https";
|
|
9814
10133
|
import { join as join21 } from "node:path";
|
|
@@ -9911,7 +10230,7 @@ var SpoolSender = class {
|
|
|
9911
10230
|
|
|
9912
10231
|
// ../agent/dist/recovery/attempt-metadata.js
|
|
9913
10232
|
init_dist();
|
|
9914
|
-
import { mkdirSync as mkdirSync3, readFileSync as
|
|
10233
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
9915
10234
|
import { readdir as readdir10, rm as rm6 } from "node:fs/promises";
|
|
9916
10235
|
import { dirname as dirname5, join as join18 } from "node:path";
|
|
9917
10236
|
function attemptMetadataDir(stateDir, attemptId) {
|
|
@@ -9929,14 +10248,14 @@ function writeAttemptMetadata(stateDir, meta) {
|
|
|
9929
10248
|
...meta,
|
|
9930
10249
|
updated_at: meta.updated_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
9931
10250
|
};
|
|
9932
|
-
|
|
10251
|
+
writeFileSync3(tmp, `${JSON.stringify(payload, null, 2)}
|
|
9933
10252
|
`, { encoding: "utf8", mode: 384 });
|
|
9934
10253
|
renameSync(tmp, target);
|
|
9935
10254
|
}
|
|
9936
10255
|
function readAttemptMetadata(stateDir, attemptId) {
|
|
9937
10256
|
const path = attemptMetadataPath(stateDir, attemptId);
|
|
9938
10257
|
try {
|
|
9939
|
-
const raw =
|
|
10258
|
+
const raw = readFileSync4(path, "utf8");
|
|
9940
10259
|
return JSON.parse(raw);
|
|
9941
10260
|
} catch {
|
|
9942
10261
|
return null;
|
|
@@ -10084,8 +10403,8 @@ async function applyDiskPressureCleanup(options) {
|
|
|
10084
10403
|
for (const name of entries) {
|
|
10085
10404
|
const metaPath = join19(reposDir, name, "metadata.json");
|
|
10086
10405
|
try {
|
|
10087
|
-
const { readFile:
|
|
10088
|
-
const raw = await
|
|
10406
|
+
const { readFile: readFile19 } = await import("node:fs/promises");
|
|
10407
|
+
const raw = await readFile19(metaPath, "utf8");
|
|
10089
10408
|
const meta = JSON.parse(raw);
|
|
10090
10409
|
if ((meta.active_worktree_count ?? 0) > 0) {
|
|
10091
10410
|
continue;
|
|
@@ -11067,7 +11386,7 @@ var AgentJobExecutor = class {
|
|
|
11067
11386
|
const currentProfiles = this.config.toolchainProfiles ?? [];
|
|
11068
11387
|
for (const profile of offer.selected_toolchain_profiles ?? []) {
|
|
11069
11388
|
const activationPath = profile.activation.path;
|
|
11070
|
-
if (activationPath && !
|
|
11389
|
+
if (activationPath && !existsSync4(activationPath)) {
|
|
11071
11390
|
this.failTerminal(run.attempt_id, run.lease_id, run.lease_epoch, "toolchain_changed", `Selected toolchain path missing: ${activationPath}`);
|
|
11072
11391
|
await rm9(attemptDir, { recursive: true, force: true }).catch(() => void 0);
|
|
11073
11392
|
this.clearAttempt();
|
|
@@ -11839,8 +12158,8 @@ var AgentConnection = class {
|
|
|
11839
12158
|
return join22(this.options.stateDir, "agent-state.json");
|
|
11840
12159
|
}
|
|
11841
12160
|
loadState() {
|
|
11842
|
-
if (
|
|
11843
|
-
return JSON.parse(
|
|
12161
|
+
if (existsSync5(this.statePath())) {
|
|
12162
|
+
return JSON.parse(readFileSync5(this.statePath(), "utf8"));
|
|
11844
12163
|
}
|
|
11845
12164
|
const pair = generateDeviceKeyPair();
|
|
11846
12165
|
const state = {
|
|
@@ -11852,7 +12171,7 @@ var AgentConnection = class {
|
|
|
11852
12171
|
}
|
|
11853
12172
|
saveState(state) {
|
|
11854
12173
|
mkdirSync4(this.options.stateDir, { recursive: true });
|
|
11855
|
-
|
|
12174
|
+
writeFileSync4(this.statePath(), JSON.stringify(state), { mode: 384 });
|
|
11856
12175
|
}
|
|
11857
12176
|
hasStoredCredential() {
|
|
11858
12177
|
const state = this.loadState();
|
|
@@ -12329,7 +12648,7 @@ init_dist();
|
|
|
12329
12648
|
|
|
12330
12649
|
// src/commands/daemon.ts
|
|
12331
12650
|
import { spawn as nodeSpawn } from "node:child_process";
|
|
12332
|
-
import { existsSync as
|
|
12651
|
+
import { existsSync as existsSync6, openSync, readFileSync as readFileSync6 } from "node:fs";
|
|
12333
12652
|
import { mkdir as mkdir10, writeFile as writeFile10 } from "node:fs/promises";
|
|
12334
12653
|
import { dirname as dirname6, join as join23 } from "node:path";
|
|
12335
12654
|
function stripDaemonFlag(args) {
|
|
@@ -12360,10 +12679,10 @@ function isProcessAlive(pid) {
|
|
|
12360
12679
|
}
|
|
12361
12680
|
}
|
|
12362
12681
|
function assertNoLivePid(pidFile, label) {
|
|
12363
|
-
if (!
|
|
12682
|
+
if (!existsSync6(pidFile)) {
|
|
12364
12683
|
return;
|
|
12365
12684
|
}
|
|
12366
|
-
const raw =
|
|
12685
|
+
const raw = readFileSync6(pidFile, "utf8").trim();
|
|
12367
12686
|
const pid = Number.parseInt(raw, 10);
|
|
12368
12687
|
if (isProcessAlive(pid)) {
|
|
12369
12688
|
throw new Error(
|
|
@@ -12407,36 +12726,21 @@ async function spawnDetachedDaemon(options) {
|
|
|
12407
12726
|
}
|
|
12408
12727
|
|
|
12409
12728
|
// src/commands/agent.ts
|
|
12410
|
-
var AGENT_MARKER = "agent.json";
|
|
12411
|
-
var AGENT_SCHEMA_VERSION = 1;
|
|
12412
12729
|
function isAgentInitialized(stateDir) {
|
|
12413
|
-
return
|
|
12730
|
+
return existsSync7(join24(stateDir, AGENT_CONFIG_FILENAME));
|
|
12414
12731
|
}
|
|
12415
|
-
async function
|
|
12416
|
-
const
|
|
12417
|
-
|
|
12418
|
-
return void 0;
|
|
12419
|
-
}
|
|
12420
|
-
const marker = JSON.parse(await readFile14(markerPath, "utf8"));
|
|
12732
|
+
async function runAgentInit(options = {}) {
|
|
12733
|
+
const stateDir = options.stateDir ?? resolveAgentStateDir();
|
|
12734
|
+
const result = writeDefaultAgentConfigFile(stateDir, { force: options.force });
|
|
12421
12735
|
return {
|
|
12422
12736
|
stateDir,
|
|
12423
|
-
initialized_at:
|
|
12424
|
-
schema_version:
|
|
12737
|
+
initialized_at: result.initialized_at,
|
|
12738
|
+
schema_version: result.schema_version,
|
|
12739
|
+
configPath: result.path,
|
|
12740
|
+
configWritten: result.written,
|
|
12741
|
+
...result.written ? {} : { hint: "agent.json already exists; pass --force to rewrite defaults" }
|
|
12425
12742
|
};
|
|
12426
12743
|
}
|
|
12427
|
-
async function runAgentInit(options = {}) {
|
|
12428
|
-
const stateDir = options.stateDir ?? resolveAgentStateDir();
|
|
12429
|
-
await mkdir11(stateDir, { recursive: true });
|
|
12430
|
-
const existing = await readAgentMarker(stateDir);
|
|
12431
|
-
if (existing) {
|
|
12432
|
-
return existing;
|
|
12433
|
-
}
|
|
12434
|
-
const initialized_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
12435
|
-
const marker = { initialized_at, schema_version: AGENT_SCHEMA_VERSION };
|
|
12436
|
-
await writeFile11(join24(stateDir, AGENT_MARKER), `${JSON.stringify(marker, null, 2)}
|
|
12437
|
-
`, "utf8");
|
|
12438
|
-
return { stateDir, ...marker };
|
|
12439
|
-
}
|
|
12440
12744
|
function assertAgentInitialized(stateDir) {
|
|
12441
12745
|
if (!isAgentInitialized(stateDir)) {
|
|
12442
12746
|
throw new Error("Agent is not initialized. Run `rbo agent init` first.");
|
|
@@ -12476,8 +12780,24 @@ async function postAdmin(baseUrl, action, body) {
|
|
|
12476
12780
|
}
|
|
12477
12781
|
return json;
|
|
12478
12782
|
}
|
|
12479
|
-
function listAgentsRemote(baseUrl) {
|
|
12480
|
-
|
|
12783
|
+
async function listAgentsRemote(baseUrl) {
|
|
12784
|
+
const [agentsResult, pairingResult] = await Promise.all([
|
|
12785
|
+
postAdmin(baseUrl, "agents/list", {}),
|
|
12786
|
+
postAdmin(baseUrl, "pairing/list", {
|
|
12787
|
+
state: "pending"
|
|
12788
|
+
})
|
|
12789
|
+
]);
|
|
12790
|
+
return {
|
|
12791
|
+
agents: agentsResult.agents,
|
|
12792
|
+
pending_pairings: pairingResult.requests.map((row) => ({
|
|
12793
|
+
id: row.id,
|
|
12794
|
+
display_name: row.display_name,
|
|
12795
|
+
hostname: row.hostname,
|
|
12796
|
+
state: row.state,
|
|
12797
|
+
one_time_code: row.one_time_code,
|
|
12798
|
+
expires_at: row.expires_at
|
|
12799
|
+
}))
|
|
12800
|
+
};
|
|
12481
12801
|
}
|
|
12482
12802
|
function approveAgentRemote(baseUrl, pairingRequestId) {
|
|
12483
12803
|
return postAdmin(baseUrl, "pairing/approve", { pairing_request_id: pairingRequestId });
|
|
@@ -12490,8 +12810,9 @@ function probeAgentRemote(baseUrl, agentId) {
|
|
|
12490
12810
|
}
|
|
12491
12811
|
|
|
12492
12812
|
// src/commands/controller.ts
|
|
12493
|
-
|
|
12494
|
-
import {
|
|
12813
|
+
init_config();
|
|
12814
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
12815
|
+
import { readFile as readFile17 } from "node:fs/promises";
|
|
12495
12816
|
import { join as join32 } from "node:path";
|
|
12496
12817
|
|
|
12497
12818
|
// ../controller/dist/run.js
|
|
@@ -12509,7 +12830,7 @@ init_dist4();
|
|
|
12509
12830
|
init_dist2();
|
|
12510
12831
|
init_dist();
|
|
12511
12832
|
import { join as join31 } from "node:path";
|
|
12512
|
-
import { z as
|
|
12833
|
+
import { z as z9 } from "zod";
|
|
12513
12834
|
|
|
12514
12835
|
// ../controller/dist/agents/probe.js
|
|
12515
12836
|
function requestAgentProbe(connectedAgents, agentId) {
|
|
@@ -12597,7 +12918,7 @@ function validateToolInput(name, args) {
|
|
|
12597
12918
|
if (!def) {
|
|
12598
12919
|
throw RboError.validation(`Unknown tool '${name}'`);
|
|
12599
12920
|
}
|
|
12600
|
-
const parsed =
|
|
12921
|
+
const parsed = z9.object(def.inputShape).safeParse(args ?? {});
|
|
12601
12922
|
if (!parsed.success) {
|
|
12602
12923
|
throw RboError.validation(`Invalid input for tool '${name}'`, {
|
|
12603
12924
|
issues: parsed.error.issues.map((issue) => ({
|
|
@@ -12745,7 +13066,7 @@ function revokeAgent(db, agentId) {
|
|
|
12745
13066
|
}
|
|
12746
13067
|
}
|
|
12747
13068
|
function updateAgentCapabilities(db, agentId, report) {
|
|
12748
|
-
db.prepare("UPDATE agents SET capabilities_json = ?, hostname = ?, last_seen_at = ? WHERE id = ?").run(JSON.stringify(report), report.hostname, nowIso(), agentId);
|
|
13069
|
+
db.prepare("UPDATE agents SET capabilities_json = ?, hostname = ?, max_jobs = ?, last_seen_at = ? WHERE id = ?").run(JSON.stringify(report), report.hostname, report.execution.max_jobs, nowIso(), agentId);
|
|
12749
13070
|
}
|
|
12750
13071
|
function patchAgentCpuLoad(db, agentId, cpuLoad) {
|
|
12751
13072
|
const row = db.prepare("SELECT capabilities_json FROM agents WHERE id = ?").get(agentId);
|
|
@@ -12829,11 +13150,20 @@ function getPairingRequest(db, id) {
|
|
|
12829
13150
|
const row = db.prepare("SELECT * FROM pairing_requests WHERE id = ?").get(id);
|
|
12830
13151
|
return row ?? null;
|
|
12831
13152
|
}
|
|
13153
|
+
function expireStalePairingRequests(db) {
|
|
13154
|
+
const now = nowIso();
|
|
13155
|
+
const result = db.prepare(`UPDATE pairing_requests
|
|
13156
|
+
SET state = 'expired', resolved_at = COALESCE(resolved_at, ?)
|
|
13157
|
+
WHERE state IN ('pending', 'approved') AND expires_at <= ?`).run(now, now);
|
|
13158
|
+
return result.changes;
|
|
13159
|
+
}
|
|
12832
13160
|
function listPairingRequests(db, state) {
|
|
13161
|
+
expireStalePairingRequests(db);
|
|
12833
13162
|
const rows = state ? db.prepare("SELECT * FROM pairing_requests WHERE state = ? ORDER BY created_at").all(state) : db.prepare("SELECT * FROM pairing_requests ORDER BY created_at").all();
|
|
12834
13163
|
return rows;
|
|
12835
13164
|
}
|
|
12836
13165
|
function createPairingRequest(db, input) {
|
|
13166
|
+
expireStalePairingRequests(db);
|
|
12837
13167
|
const thumbprint = publicKeyThumbprint(input.devicePublicKeyPem);
|
|
12838
13168
|
const existing = db.prepare("SELECT * FROM pairing_requests WHERE device_thumbprint = ? AND state IN ('pending', 'approved') AND expires_at > ?").get(thumbprint, nowIso());
|
|
12839
13169
|
if (existing) {
|
|
@@ -13609,7 +13939,13 @@ async function runController(overrides = {}) {
|
|
|
13609
13939
|
init_dist();
|
|
13610
13940
|
async function runControllerInit(options) {
|
|
13611
13941
|
const identity = await ensureControllerIdentity(options.dataDir);
|
|
13612
|
-
|
|
13942
|
+
const config = writeDefaultControllerConfigFile(options.dataDir, { force: options.force });
|
|
13943
|
+
return {
|
|
13944
|
+
controllerId: identity.controllerId,
|
|
13945
|
+
fingerprint: identity.fingerprint,
|
|
13946
|
+
configPath: config.path,
|
|
13947
|
+
configWritten: config.written
|
|
13948
|
+
};
|
|
13613
13949
|
}
|
|
13614
13950
|
async function runControllerFingerprint(options) {
|
|
13615
13951
|
const identity = await ensureControllerIdentity(options.dataDir);
|
|
@@ -13618,10 +13954,10 @@ async function runControllerFingerprint(options) {
|
|
|
13618
13954
|
async function readExistingControllerId(dataDir) {
|
|
13619
13955
|
const certPath = join32(dataDir, "security", "controller-cert.pem");
|
|
13620
13956
|
const metaPath = join32(dataDir, "security", "controller.json");
|
|
13621
|
-
if (!
|
|
13957
|
+
if (!existsSync9(certPath) || !existsSync9(metaPath)) {
|
|
13622
13958
|
return void 0;
|
|
13623
13959
|
}
|
|
13624
|
-
const meta = JSON.parse(await
|
|
13960
|
+
const meta = JSON.parse(await readFile17(metaPath, "utf8"));
|
|
13625
13961
|
return meta.controller_id;
|
|
13626
13962
|
}
|
|
13627
13963
|
async function isControllerInitialized(dataDir) {
|
|
@@ -13665,11 +14001,25 @@ async function runControllerRestore(options) {
|
|
|
13665
14001
|
// src/commands/doctor.ts
|
|
13666
14002
|
init_dist4();
|
|
13667
14003
|
import { execFile as execFile9 } from "node:child_process";
|
|
13668
|
-
import { mkdirSync as mkdirSync6, writeFileSync as
|
|
14004
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "node:fs";
|
|
13669
14005
|
import { join as join33 } from "node:path";
|
|
13670
|
-
import { promisify as promisify9 } from "node:util";
|
|
14006
|
+
import { promisify as promisify9, styleText } from "node:util";
|
|
13671
14007
|
var execFileAsync9 = promisify9(execFile9);
|
|
13672
14008
|
var REQUIRED_NODE_ENGINES = { major: 22, minor: 14 };
|
|
14009
|
+
function doctorStatusTag(check) {
|
|
14010
|
+
if (!check.ok) return "FAIL";
|
|
14011
|
+
if (check.warn) return "WARN";
|
|
14012
|
+
return "OK ";
|
|
14013
|
+
}
|
|
14014
|
+
function formatDoctorCheckLine(check, options = {}) {
|
|
14015
|
+
const tag = doctorStatusTag(check);
|
|
14016
|
+
const colorName = !check.ok ? "red" : check.warn ? "yellow" : "green";
|
|
14017
|
+
const styledTag = options.color === false ? tag : styleText(colorName, tag, {
|
|
14018
|
+
stream: options.stream ?? process.stdout,
|
|
14019
|
+
validateStream: options.color !== true
|
|
14020
|
+
});
|
|
14021
|
+
return `${styledTag} ${check.name}: ${check.detail}`;
|
|
14022
|
+
}
|
|
13673
14023
|
async function checkGit() {
|
|
13674
14024
|
try {
|
|
13675
14025
|
const { stdout } = await execFileAsync9("git", ["--version"]);
|
|
@@ -13682,7 +14032,7 @@ function checkDataDirWritable(dataDir) {
|
|
|
13682
14032
|
try {
|
|
13683
14033
|
mkdirSync6(dataDir, { recursive: true });
|
|
13684
14034
|
const probe = join33(dataDir, ".rbo-doctor-probe");
|
|
13685
|
-
|
|
14035
|
+
writeFileSync6(probe, "ok");
|
|
13686
14036
|
return { name: "data_dir_writable", ok: true, detail: dataDir };
|
|
13687
14037
|
} catch (error) {
|
|
13688
14038
|
return { name: "data_dir_writable", ok: false, detail: String(error) };
|
|
@@ -13777,6 +14127,18 @@ async function runDoctor(options) {
|
|
|
13777
14127
|
}
|
|
13778
14128
|
|
|
13779
14129
|
// src/commands/flags.ts
|
|
14130
|
+
function parseForceFlag(args) {
|
|
14131
|
+
const rest = [];
|
|
14132
|
+
let force = false;
|
|
14133
|
+
for (const arg of args) {
|
|
14134
|
+
if (arg === "--force") {
|
|
14135
|
+
force = true;
|
|
14136
|
+
continue;
|
|
14137
|
+
}
|
|
14138
|
+
rest.push(arg);
|
|
14139
|
+
}
|
|
14140
|
+
return { force, rest };
|
|
14141
|
+
}
|
|
13780
14142
|
function parseDataDirFlag(args) {
|
|
13781
14143
|
const rest = [];
|
|
13782
14144
|
let dataDir;
|
|
@@ -13849,6 +14211,7 @@ function cancelJobRemote(baseUrl, jobId, reason) {
|
|
|
13849
14211
|
}
|
|
13850
14212
|
|
|
13851
14213
|
// src/commands/service.ts
|
|
14214
|
+
init_dist();
|
|
13852
14215
|
import { execFile as execFile10 } from "node:child_process";
|
|
13853
14216
|
import { promisify as promisify10 } from "node:util";
|
|
13854
14217
|
var execFileAsync10 = promisify10(execFile10);
|
|
@@ -13881,14 +14244,21 @@ var defaultCommandRunner = {
|
|
|
13881
14244
|
var SERVICE_NAME = "RBOAgent";
|
|
13882
14245
|
var LAUNCHD_LABEL = "com.rbo.agent";
|
|
13883
14246
|
var SYSTEMD_UNIT = "rbo-agent";
|
|
13884
|
-
function
|
|
14247
|
+
function renderAgentServiceCommand(options = {}) {
|
|
14248
|
+
const nodePath = options.nodePath ?? process.execPath;
|
|
14249
|
+
const rboScriptPath = options.rboScriptPath ?? process.argv[1] ?? "rbo.js";
|
|
14250
|
+
const stateDir = options.stateDir ?? resolveAgentStateDir();
|
|
14251
|
+
return `"${nodePath}" "${rboScriptPath}" agent start --state-dir "${stateDir}"`;
|
|
14252
|
+
}
|
|
14253
|
+
function renderServiceInstallPlan(platform3, options = {}) {
|
|
14254
|
+
const binPath = renderAgentServiceCommand(options);
|
|
13885
14255
|
switch (platform3) {
|
|
13886
14256
|
case "win32":
|
|
13887
14257
|
return {
|
|
13888
14258
|
kind: "windows_service",
|
|
13889
14259
|
serviceName: SERVICE_NAME,
|
|
13890
14260
|
commands: [
|
|
13891
|
-
`sc.exe create ${SERVICE_NAME} binPath=
|
|
14261
|
+
`sc.exe create ${SERVICE_NAME} binPath= ${JSON.stringify(binPath)} start= auto`,
|
|
13892
14262
|
`sc.exe description ${SERVICE_NAME} "Remote Build Orchestrator Agent"`,
|
|
13893
14263
|
`sc.exe start ${SERVICE_NAME}`
|
|
13894
14264
|
]
|
|
@@ -13898,6 +14268,7 @@ function renderServiceInstallPlan(platform3, executablePath = "C:/Program Files/
|
|
|
13898
14268
|
kind: "launchd",
|
|
13899
14269
|
serviceName: LAUNCHD_LABEL,
|
|
13900
14270
|
commands: [
|
|
14271
|
+
`# Write /Library/LaunchDaemons/${LAUNCHD_LABEL}.plist with ProgramArguments: ${binPath}`,
|
|
13901
14272
|
`launchctl load -w /Library/LaunchDaemons/${LAUNCHD_LABEL}.plist`,
|
|
13902
14273
|
`launchctl start ${LAUNCHD_LABEL}`
|
|
13903
14274
|
]
|
|
@@ -13907,6 +14278,7 @@ function renderServiceInstallPlan(platform3, executablePath = "C:/Program Files/
|
|
|
13907
14278
|
kind: "systemd",
|
|
13908
14279
|
serviceName: SYSTEMD_UNIT,
|
|
13909
14280
|
commands: [
|
|
14281
|
+
`# Write /etc/systemd/system/${SYSTEMD_UNIT}.service with ExecStart=${binPath}`,
|
|
13910
14282
|
"systemctl daemon-reload",
|
|
13911
14283
|
`systemctl enable ${SYSTEMD_UNIT}`,
|
|
13912
14284
|
`systemctl start ${SYSTEMD_UNIT}`
|
|
@@ -14009,10 +14381,10 @@ function renderServiceStopPlan(platform3) {
|
|
|
14009
14381
|
};
|
|
14010
14382
|
}
|
|
14011
14383
|
}
|
|
14012
|
-
function renderServiceActionPlan(platform3, action,
|
|
14384
|
+
function renderServiceActionPlan(platform3, action, options) {
|
|
14013
14385
|
switch (action) {
|
|
14014
14386
|
case "install":
|
|
14015
|
-
return renderServiceInstallPlan(platform3,
|
|
14387
|
+
return renderServiceInstallPlan(platform3, options);
|
|
14016
14388
|
case "uninstall":
|
|
14017
14389
|
return renderServiceUninstallPlan(platform3);
|
|
14018
14390
|
case "status":
|
|
@@ -14027,7 +14399,11 @@ function hasExecuteFlag(args) {
|
|
|
14027
14399
|
return args.includes("--execute");
|
|
14028
14400
|
}
|
|
14029
14401
|
function formatDryRunPlan(action, plan) {
|
|
14030
|
-
const lines = [
|
|
14402
|
+
const lines = [
|
|
14403
|
+
`# ${action} (dry run \u2014 pass --execute to run these commands)`,
|
|
14404
|
+
"# Prefer `rbo agent start --daemon` for day-to-day use; OS service install is best-effort",
|
|
14405
|
+
"# and expects node + bundled rbo.js (not a Program Files rbo-agent.exe)."
|
|
14406
|
+
];
|
|
14031
14407
|
for (const command of plan.commands) {
|
|
14032
14408
|
lines.push(command);
|
|
14033
14409
|
}
|
|
@@ -14036,6 +14412,10 @@ function formatDryRunPlan(action, plan) {
|
|
|
14036
14412
|
async function executeServicePlan(plan, runner = defaultCommandRunner) {
|
|
14037
14413
|
const results = [];
|
|
14038
14414
|
for (const command of plan.commands) {
|
|
14415
|
+
if (command.trimStart().startsWith("#")) {
|
|
14416
|
+
results.push({ command, stdout: "", stderr: "skipped comment", code: 0 });
|
|
14417
|
+
continue;
|
|
14418
|
+
}
|
|
14039
14419
|
const { stdout, stderr, code } = await runner.run(command);
|
|
14040
14420
|
results.push({ command, stdout, stderr, code });
|
|
14041
14421
|
if (code !== 0) {
|
|
@@ -14067,7 +14447,8 @@ async function main() {
|
|
|
14067
14447
|
const dataDir = flagDataDir ?? resolveControllerDataDir();
|
|
14068
14448
|
const sub = controllerArgs[0];
|
|
14069
14449
|
if (sub === "init") {
|
|
14070
|
-
const
|
|
14450
|
+
const { force } = parseForceFlag(controllerArgs.slice(1));
|
|
14451
|
+
const result = await runControllerInit({ dataDir, force });
|
|
14071
14452
|
console.log(JSON.stringify(result, null, 2));
|
|
14072
14453
|
return;
|
|
14073
14454
|
}
|
|
@@ -14106,7 +14487,7 @@ async function main() {
|
|
|
14106
14487
|
}
|
|
14107
14488
|
case "agents": {
|
|
14108
14489
|
const result = await listAgentsRemote(controllerUrl);
|
|
14109
|
-
console.log(JSON.stringify(result
|
|
14490
|
+
console.log(JSON.stringify(result, null, 2));
|
|
14110
14491
|
return;
|
|
14111
14492
|
}
|
|
14112
14493
|
case "agent": {
|
|
@@ -14135,7 +14516,8 @@ async function main() {
|
|
|
14135
14516
|
return;
|
|
14136
14517
|
}
|
|
14137
14518
|
if (sub === "init") {
|
|
14138
|
-
const
|
|
14519
|
+
const { force } = parseForceFlag(agentArgs.slice(1));
|
|
14520
|
+
const result = await runAgentInit({ stateDir, force });
|
|
14139
14521
|
console.log(JSON.stringify(result, null, 2));
|
|
14140
14522
|
return;
|
|
14141
14523
|
}
|
|
@@ -14178,8 +14560,7 @@ async function main() {
|
|
|
14178
14560
|
const dataDir = flagDataDir ?? resolveControllerDataDir();
|
|
14179
14561
|
const report = await runDoctor({ dataDir, controllerUrl });
|
|
14180
14562
|
for (const check of report.checks) {
|
|
14181
|
-
|
|
14182
|
-
console.log(`${tag} ${check.name}: ${check.detail}`);
|
|
14563
|
+
console.log(formatDoctorCheckLine(check));
|
|
14183
14564
|
}
|
|
14184
14565
|
process.exitCode = report.ok ? 0 : 1;
|
|
14185
14566
|
return;
|
|
@@ -14189,7 +14570,7 @@ async function main() {
|
|
|
14189
14570
|
if (!requestPath) {
|
|
14190
14571
|
throw new Error("Usage: rbo submit <job-request.json>");
|
|
14191
14572
|
}
|
|
14192
|
-
const request = JSON.parse(await
|
|
14573
|
+
const request = JSON.parse(await readFile18(requestPath, "utf8"));
|
|
14193
14574
|
const result = await submitJobRemote(controllerUrl, request);
|
|
14194
14575
|
console.log(JSON.stringify(result, null, 2));
|
|
14195
14576
|
return;
|