@prover-coder-ai/docker-git 1.0.41 → 1.0.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/docker-git/main.js +484 -133
- package/dist/src/docker-git/main.js.map +1 -1
- package/package.json +1 -1
|
@@ -269,6 +269,8 @@ var defaultTemplateConfig = {
|
|
|
269
269
|
codexAuthPath: "./.docker-git/.orch/auth/codex",
|
|
270
270
|
codexSharedAuthPath: "./.docker-git/.orch/auth/codex",
|
|
271
271
|
codexHome: "/home/dev/.codex",
|
|
272
|
+
cpuLimit: "30%",
|
|
273
|
+
ramLimit: "30%",
|
|
272
274
|
dockerNetworkMode: defaultDockerNetworkMode,
|
|
273
275
|
dockerSharedNetworkName: defaultDockerSharedNetworkName,
|
|
274
276
|
enableMcpPlaywright: false,
|
|
@@ -1209,6 +1211,38 @@ var ensureGhAuthImage = (fs, path, cwd, buildLabel) => ensureDockerImage(fs, pat
|
|
|
1209
1211
|
buildLabel
|
|
1210
1212
|
});
|
|
1211
1213
|
//#endregion
|
|
1214
|
+
//#region ../lib/src/usecases/github-api-helpers.ts
|
|
1215
|
+
/**
|
|
1216
|
+
* Run `gh api <args>` inside the auth Docker container and return trimmed stdout.
|
|
1217
|
+
*
|
|
1218
|
+
* @pure false
|
|
1219
|
+
* @effect CommandExecutor (Docker)
|
|
1220
|
+
* @invariant exits with CommandFailedError on non-zero exit code
|
|
1221
|
+
* @complexity O(1)
|
|
1222
|
+
*/
|
|
1223
|
+
var runGhApiCapture = (cwd, hostPath, token, args) => runDockerAuthCapture(buildDockerAuthSpec({
|
|
1224
|
+
cwd,
|
|
1225
|
+
image: ghImageName,
|
|
1226
|
+
hostPath,
|
|
1227
|
+
containerPath: ghAuthDir,
|
|
1228
|
+
env: `GH_TOKEN=${token}`,
|
|
1229
|
+
args: ["api", ...args],
|
|
1230
|
+
interactive: false
|
|
1231
|
+
}), [0], (exitCode) => new CommandFailedError({
|
|
1232
|
+
command: `gh api ${args.join(" ")}`,
|
|
1233
|
+
exitCode
|
|
1234
|
+
})).pipe(Effect.map((raw) => raw.trim()));
|
|
1235
|
+
/**
|
|
1236
|
+
* Like `runGhApiCapture` but returns `null` instead of failing on API errors
|
|
1237
|
+
* (e.g. HTTP 404 / non-zero exit code).
|
|
1238
|
+
*
|
|
1239
|
+
* @pure false
|
|
1240
|
+
* @effect CommandExecutor (Docker)
|
|
1241
|
+
* @invariant never fails — errors become null
|
|
1242
|
+
* @complexity O(1)
|
|
1243
|
+
*/
|
|
1244
|
+
var runGhApiNullable = (cwd, hostPath, token, args) => runGhApiCapture(cwd, hostPath, token, args).pipe(Effect.catchTag("CommandFailedError", () => Effect.succeed("")), Effect.map((raw) => raw.length === 0 ? null : raw));
|
|
1245
|
+
//#endregion
|
|
1212
1246
|
//#region ../lib/src/usecases/runtime.ts
|
|
1213
1247
|
var withFsPathContext = (run) => Effect.gen(function* (_) {
|
|
1214
1248
|
return yield* _(run({
|
|
@@ -1226,20 +1260,7 @@ var resolveGithubToken$1 = (envText) => {
|
|
|
1226
1260
|
const labeled = entries.find((entry) => entry.key.startsWith("GITHUB_TOKEN__"));
|
|
1227
1261
|
return labeled && labeled.value.trim().length > 0 ? labeled.value.trim() : null;
|
|
1228
1262
|
};
|
|
1229
|
-
var
|
|
1230
|
-
cwd,
|
|
1231
|
-
image: ghImageName,
|
|
1232
|
-
hostPath,
|
|
1233
|
-
containerPath: ghAuthDir,
|
|
1234
|
-
env: `GH_TOKEN=${token}`,
|
|
1235
|
-
args: ["api", ...args],
|
|
1236
|
-
interactive: false
|
|
1237
|
-
}), [0], (exitCode) => new CommandFailedError({
|
|
1238
|
-
command: `gh api ${args.join(" ")}`,
|
|
1239
|
-
exitCode
|
|
1240
|
-
})).pipe(Effect.map((raw) => raw.trim()));
|
|
1241
|
-
var runGhApiCloneUrl = (cwd, hostPath, token, args) => runGhApiCapture(cwd, hostPath, token, args).pipe(Effect.catchTag("CommandFailedError", () => Effect.succeed("")), Effect.map((raw) => raw.length === 0 ? null : raw));
|
|
1242
|
-
var resolveViewerLogin = (cwd, hostPath, token) => Effect.gen(function* (_) {
|
|
1263
|
+
var resolveViewerLogin$1 = (cwd, hostPath, token) => Effect.gen(function* (_) {
|
|
1243
1264
|
const command = "gh api /user --jq .login";
|
|
1244
1265
|
const raw = yield* _(runGhApiCapture(cwd, hostPath, token, [
|
|
1245
1266
|
"/user",
|
|
@@ -1252,12 +1273,12 @@ var resolveViewerLogin = (cwd, hostPath, token) => Effect.gen(function* (_) {
|
|
|
1252
1273
|
})));
|
|
1253
1274
|
return raw;
|
|
1254
1275
|
});
|
|
1255
|
-
var resolveRepoCloneUrl = (cwd, hostPath, token, fullName) =>
|
|
1276
|
+
var resolveRepoCloneUrl = (cwd, hostPath, token, fullName) => runGhApiNullable(cwd, hostPath, token, [
|
|
1256
1277
|
`/repos/${fullName}`,
|
|
1257
1278
|
"--jq",
|
|
1258
1279
|
".clone_url"
|
|
1259
1280
|
]);
|
|
1260
|
-
var createFork = (cwd, hostPath, token, owner, repo) =>
|
|
1281
|
+
var createFork = (cwd, hostPath, token, owner, repo) => runGhApiNullable(cwd, hostPath, token, [
|
|
1261
1282
|
"-X",
|
|
1262
1283
|
"POST",
|
|
1263
1284
|
`/repos/${owner}/${repo}/forks`,
|
|
@@ -1275,7 +1296,7 @@ var resolveGithubForkUrl = (repoUrl, envGlobalPath) => withFsPathContext(({ cwd,
|
|
|
1275
1296
|
const ghRoot = resolvePathFromCwd(path, cwd, ghAuthRoot);
|
|
1276
1297
|
yield* _(fs.makeDirectory(ghRoot, { recursive: true }));
|
|
1277
1298
|
yield* _(ensureGhAuthImage(fs, path, cwd, "gh api"));
|
|
1278
|
-
const viewer = yield* _(resolveViewerLogin(cwd, ghRoot, token));
|
|
1299
|
+
const viewer = yield* _(resolveViewerLogin$1(cwd, ghRoot, token));
|
|
1279
1300
|
if (viewer.toLowerCase() === repo.owner.toLowerCase()) return null;
|
|
1280
1301
|
const existingFork = yield* _(resolveRepoCloneUrl(cwd, ghRoot, token, `${viewer}/${repo.repo}`));
|
|
1281
1302
|
if (existingFork !== null) return existingFork;
|
|
@@ -1336,6 +1357,8 @@ var TemplateConfigSchema = Schema.Struct({
|
|
|
1336
1357
|
codexAuthPath: Schema.String,
|
|
1337
1358
|
codexSharedAuthPath: Schema.optionalWith(Schema.String, { default: () => defaultTemplateConfig.codexSharedAuthPath }),
|
|
1338
1359
|
codexHome: Schema.String,
|
|
1360
|
+
cpuLimit: Schema.optionalWith(Schema.String, { default: () => defaultTemplateConfig.cpuLimit }),
|
|
1361
|
+
ramLimit: Schema.optionalWith(Schema.String, { default: () => defaultTemplateConfig.ramLimit }),
|
|
1339
1362
|
dockerNetworkMode: Schema.optionalWith(Schema.Literal("shared", "project"), { default: () => defaultTemplateConfig.dockerNetworkMode }),
|
|
1340
1363
|
dockerSharedNetworkName: Schema.optionalWith(Schema.String, { default: () => defaultTemplateConfig.dockerSharedNetworkName }),
|
|
1341
1364
|
enableMcpPlaywright: Schema.optionalWith(Schema.Boolean, { default: () => defaultTemplateConfig.enableMcpPlaywright }),
|
|
@@ -1506,28 +1529,93 @@ var withProjectIndexAndSsh = (run) => pipe(loadProjectIndex(), Effect.flatMap((i
|
|
|
1506
1529
|
return yield* _(run(index, yield* _(findSshPrivateKey(yield* _(FileSystem.FileSystem), yield* _(Path.Path), process.cwd()))));
|
|
1507
1530
|
})));
|
|
1508
1531
|
//#endregion
|
|
1509
|
-
//#region ../lib/src/
|
|
1510
|
-
var
|
|
1511
|
-
|
|
1512
|
-
|
|
1532
|
+
//#region ../lib/src/core/resource-limits.ts
|
|
1533
|
+
var mebibyte = 1024 ** 2;
|
|
1534
|
+
var minimumResolvedCpuLimit = .25;
|
|
1535
|
+
var minimumResolvedRamLimitMib = 512;
|
|
1536
|
+
var precisionScale = 100;
|
|
1537
|
+
var cpuAbsolutePattern = /^\d+(?:\.\d+)?$/u;
|
|
1538
|
+
var ramAbsolutePattern = /^\d+(?:\.\d+)?(?:b|k|kb|m|mb|g|gb|t|tb)$/iu;
|
|
1539
|
+
var percentPattern = /^\d+(?:\.\d+)?%$/u;
|
|
1540
|
+
var normalizePrecision = (value) => Math.round(value * precisionScale) / precisionScale;
|
|
1541
|
+
var missingLimit = () => void 0;
|
|
1542
|
+
var parsePercent = (candidate) => {
|
|
1543
|
+
if (!percentPattern.test(candidate)) return null;
|
|
1544
|
+
const parsed = Number(candidate.slice(0, -1));
|
|
1545
|
+
if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 100) return null;
|
|
1546
|
+
return normalizePrecision(parsed);
|
|
1547
|
+
};
|
|
1548
|
+
var percentReason = (kind) => kind === "cpu" ? "expected CPU like 30% or 1.5" : "expected RAM like 30%, 512m or 4g";
|
|
1549
|
+
var normalizePercent = (candidate, kind) => {
|
|
1550
|
+
const parsed = parsePercent(candidate);
|
|
1551
|
+
if (parsed === null) return Either.left({
|
|
1552
|
+
_tag: "InvalidOption",
|
|
1553
|
+
option: kind === "cpu" ? "--cpu" : "--ram",
|
|
1554
|
+
reason: percentReason(kind)
|
|
1555
|
+
});
|
|
1556
|
+
return Either.right(`${parsed}%`);
|
|
1513
1557
|
};
|
|
1514
|
-
var
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1558
|
+
var normalizeCpuLimit = (value, option) => {
|
|
1559
|
+
const candidate = value?.trim().toLowerCase() ?? "";
|
|
1560
|
+
if (candidate.length === 0) return Either.right(missingLimit());
|
|
1561
|
+
if (candidate.endsWith("%")) return normalizePercent(candidate, "cpu");
|
|
1562
|
+
if (!cpuAbsolutePattern.test(candidate)) return Either.left({
|
|
1563
|
+
_tag: "InvalidOption",
|
|
1564
|
+
option,
|
|
1565
|
+
reason: "expected CPU like 30% or 1.5"
|
|
1566
|
+
});
|
|
1567
|
+
const parsed = Number(candidate);
|
|
1568
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return Either.left({
|
|
1569
|
+
_tag: "InvalidOption",
|
|
1570
|
+
option,
|
|
1571
|
+
reason: "must be greater than 0"
|
|
1572
|
+
});
|
|
1573
|
+
return Either.right(String(normalizePrecision(parsed)));
|
|
1517
1574
|
};
|
|
1518
|
-
var
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
if (
|
|
1522
|
-
if (
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1575
|
+
var normalizeRamLimit = (value, option) => {
|
|
1576
|
+
const candidate = value?.trim().toLowerCase() ?? "";
|
|
1577
|
+
if (candidate.length === 0) return Either.right(missingLimit());
|
|
1578
|
+
if (candidate.endsWith("%")) return normalizePercent(candidate, "ram");
|
|
1579
|
+
if (!ramAbsolutePattern.test(candidate)) return Either.left({
|
|
1580
|
+
_tag: "InvalidOption",
|
|
1581
|
+
option,
|
|
1582
|
+
reason: "expected RAM like 30%, 512m or 4g"
|
|
1583
|
+
});
|
|
1584
|
+
return Either.right(candidate);
|
|
1585
|
+
};
|
|
1586
|
+
var withDefaultResourceLimitIntent = (template) => ({
|
|
1587
|
+
...template,
|
|
1588
|
+
cpuLimit: template.cpuLimit ?? "30%",
|
|
1589
|
+
ramLimit: template.ramLimit ?? "30%"
|
|
1590
|
+
});
|
|
1591
|
+
var resolvePercentCpuLimit = (percent, cpuCount) => Math.max(minimumResolvedCpuLimit, normalizePrecision(Math.max(1, cpuCount) * percent / 100));
|
|
1592
|
+
var resolvePercentRamLimit = (percent, totalMemoryBytes) => {
|
|
1593
|
+
const totalMib = Math.max(minimumResolvedRamLimitMib, Math.floor(totalMemoryBytes / mebibyte));
|
|
1594
|
+
return `${Math.max(minimumResolvedRamLimitMib, Math.floor(totalMib * percent / 100))}m`;
|
|
1595
|
+
};
|
|
1596
|
+
var resolveComposeResourceLimits = (template, hostResources) => {
|
|
1597
|
+
const cpuLimitIntent = template.cpuLimit ?? "30%";
|
|
1598
|
+
const ramLimitIntent = template.ramLimit ?? "30%";
|
|
1599
|
+
const cpuPercent = parsePercent(cpuLimitIntent);
|
|
1600
|
+
const ramPercent = parsePercent(ramLimitIntent);
|
|
1601
|
+
return {
|
|
1602
|
+
cpuLimit: cpuPercent === null ? Number(cpuLimitIntent) : resolvePercentCpuLimit(cpuPercent, hostResources.cpuCount),
|
|
1603
|
+
ramLimit: ramPercent === null ? ramLimitIntent : resolvePercentRamLimit(ramPercent, hostResources.totalMemoryBytes)
|
|
1604
|
+
};
|
|
1526
1605
|
};
|
|
1527
1606
|
//#endregion
|
|
1607
|
+
//#region ../lib/src/usecases/resource-limits.ts
|
|
1608
|
+
var resolveTemplateResourceLimits = (template) => Effect.succeed(withDefaultResourceLimitIntent(template));
|
|
1609
|
+
//#endregion
|
|
1528
1610
|
//#region ../lib/src/usecases/state-repo/git-commands.ts
|
|
1529
1611
|
var successExitCode = Number(ExitCode(0));
|
|
1530
|
-
var gitBaseEnv$1 = {
|
|
1612
|
+
var gitBaseEnv$1 = {
|
|
1613
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
1614
|
+
GIT_AUTHOR_NAME: "docker-git",
|
|
1615
|
+
GIT_AUTHOR_EMAIL: "docker-git@users.noreply.github.com",
|
|
1616
|
+
GIT_COMMITTER_NAME: "docker-git",
|
|
1617
|
+
GIT_COMMITTER_EMAIL: "docker-git@users.noreply.github.com"
|
|
1618
|
+
};
|
|
1531
1619
|
var git = (cwd, args, env = gitBaseEnv$1) => runCommandWithExitCodes({
|
|
1532
1620
|
cwd,
|
|
1533
1621
|
command: "git",
|
|
@@ -1543,7 +1631,7 @@ var gitExitCode = (cwd, args, env = gitBaseEnv$1) => runCommandExitCode({
|
|
|
1543
1631
|
args,
|
|
1544
1632
|
env
|
|
1545
1633
|
});
|
|
1546
|
-
var gitCapture = (cwd, args, env = gitBaseEnv$1) => runCommandCapture({
|
|
1634
|
+
var gitCapture$1 = (cwd, args, env = gitBaseEnv$1) => runCommandCapture({
|
|
1547
1635
|
cwd,
|
|
1548
1636
|
command: "git",
|
|
1549
1637
|
args,
|
|
@@ -1559,6 +1647,68 @@ var hasOriginRemote = (root) => Effect.map(gitExitCode(root, [
|
|
|
1559
1647
|
"origin"
|
|
1560
1648
|
]), (exit) => exit === successExitCode);
|
|
1561
1649
|
//#endregion
|
|
1650
|
+
//#region ../lib/src/usecases/state-repo/adopt-remote.ts
|
|
1651
|
+
var adoptRemoteHistoryIfOrphan = (root, repoRef, env) => Effect.gen(function* (_) {
|
|
1652
|
+
const fetchExit = yield* _(gitExitCode(root, [
|
|
1653
|
+
"fetch",
|
|
1654
|
+
"origin",
|
|
1655
|
+
repoRef
|
|
1656
|
+
], env));
|
|
1657
|
+
if (fetchExit !== successExitCode) {
|
|
1658
|
+
yield* _(Effect.logWarning(`git fetch origin ${repoRef} failed (exit ${fetchExit}); starting fresh history`));
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
const remoteRef = `origin/${repoRef}`;
|
|
1662
|
+
if ((yield* _(gitExitCode(root, [
|
|
1663
|
+
"show-ref",
|
|
1664
|
+
"--verify",
|
|
1665
|
+
"--quiet",
|
|
1666
|
+
`refs/remotes/${remoteRef}`
|
|
1667
|
+
], env))) !== successExitCode) return;
|
|
1668
|
+
if ((yield* _(gitExitCode(root, ["rev-parse", "HEAD"], env))) !== successExitCode) {
|
|
1669
|
+
yield* _(git(root, ["reset", remoteRef], env));
|
|
1670
|
+
yield* _(gitExitCode(root, ["checkout-index", "--all"], env));
|
|
1671
|
+
yield* _(Effect.log(`Adopted remote history from ${remoteRef}`));
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
if ((yield* _(gitExitCode(root, [
|
|
1675
|
+
"merge-base",
|
|
1676
|
+
"HEAD",
|
|
1677
|
+
remoteRef
|
|
1678
|
+
], env))) === successExitCode) return;
|
|
1679
|
+
yield* _(Effect.logWarning(`Local history has no common ancestor with ${remoteRef}; merging unrelated histories`));
|
|
1680
|
+
if ((yield* _(gitExitCode(root, [
|
|
1681
|
+
"merge",
|
|
1682
|
+
"--allow-unrelated-histories",
|
|
1683
|
+
"--no-edit",
|
|
1684
|
+
remoteRef
|
|
1685
|
+
], env))) === successExitCode) {
|
|
1686
|
+
yield* _(Effect.log(`Merged unrelated histories from ${remoteRef}`));
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
yield* _(gitExitCode(root, ["merge", "--abort"], env));
|
|
1690
|
+
yield* _(Effect.logWarning(`Merge conflict with ${remoteRef}; sync will open a PR for manual resolution`));
|
|
1691
|
+
});
|
|
1692
|
+
//#endregion
|
|
1693
|
+
//#region ../lib/src/usecases/state-repo/env.ts
|
|
1694
|
+
var isTruthyEnv = (value) => {
|
|
1695
|
+
const normalized = value.trim().toLowerCase();
|
|
1696
|
+
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
|
|
1697
|
+
};
|
|
1698
|
+
var isFalsyEnv = (value) => {
|
|
1699
|
+
const normalized = value.trim().toLowerCase();
|
|
1700
|
+
return normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off";
|
|
1701
|
+
};
|
|
1702
|
+
var autoSyncStrictEnvKey = "DOCKER_GIT_STATE_AUTO_SYNC_STRICT";
|
|
1703
|
+
var defaultSyncMessage = "chore(state): sync";
|
|
1704
|
+
var isAutoSyncEnabled = (envValue, hasRemote) => {
|
|
1705
|
+
if (envValue === void 0) return hasRemote;
|
|
1706
|
+
if (envValue.trim().length === 0) return hasRemote;
|
|
1707
|
+
if (isFalsyEnv(envValue)) return false;
|
|
1708
|
+
if (isTruthyEnv(envValue)) return true;
|
|
1709
|
+
return true;
|
|
1710
|
+
};
|
|
1711
|
+
//#endregion
|
|
1562
1712
|
//#region ../lib/src/usecases/state-repo/github-auth.ts
|
|
1563
1713
|
var githubTokenKey = "GITHUB_TOKEN";
|
|
1564
1714
|
var githubHttpsRemoteRe = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/;
|
|
@@ -3736,7 +3886,8 @@ var renderAgentModeEnv = (agentMode) => agentMode !== void 0 && agentMode.length
|
|
|
3736
3886
|
var renderAgentAutoEnv = (agentAuto) => agentAuto === true ? ` AGENT_AUTO: "1"\n` : "";
|
|
3737
3887
|
var renderProjectsRootHostMount = (projectsRoot) => `\${DOCKER_GIT_PROJECTS_ROOT_HOST:-${projectsRoot}}`;
|
|
3738
3888
|
var renderSharedCodexHostMount = (projectsRoot) => `\${DOCKER_GIT_PROJECTS_ROOT_HOST:-${projectsRoot}}/.orch/auth/codex`;
|
|
3739
|
-
var
|
|
3889
|
+
var renderResourceLimits = (resourceLimits) => resourceLimits === void 0 ? "" : ` cpus: ${resourceLimits.cpuLimit}\n mem_limit: "${resourceLimits.ramLimit}"\n memswap_limit: "${resourceLimits.ramLimit}"\n`;
|
|
3890
|
+
var buildPlaywrightFragments = (config, networkName, resourceLimits) => {
|
|
3740
3891
|
if (!config.enableMcpPlaywright) return {
|
|
3741
3892
|
maybeDependsOn: "",
|
|
3742
3893
|
maybePlaywrightEnv: "",
|
|
@@ -3751,11 +3902,11 @@ var buildPlaywrightFragments = (config, networkName) => {
|
|
|
3751
3902
|
return {
|
|
3752
3903
|
maybeDependsOn: ` depends_on:\n - ${browserServiceName}\n`,
|
|
3753
3904
|
maybePlaywrightEnv: ` MCP_PLAYWRIGHT_ENABLE: "1"\n MCP_PLAYWRIGHT_CDP_ENDPOINT: "${browserCdpEndpoint}"\n`,
|
|
3754
|
-
maybeBrowserService: `\n ${browserServiceName}:\n build:\n context: .\n dockerfile: ${browserDockerfile}\n container_name: ${browserContainerName}\n restart: unless-stopped\n environment:\n VNC_NOPW: "1"\n shm_size: "2gb"\n expose:\n - "9223"\n volumes:\n - ${browserVolumeName}:/data\n networks:\n - ${networkName}\n`,
|
|
3905
|
+
maybeBrowserService: `\n ${browserServiceName}:\n build:\n context: .\n dockerfile: ${browserDockerfile}\n container_name: ${browserContainerName}\n restart: unless-stopped\n${renderResourceLimits(resourceLimits)} environment:\n VNC_NOPW: "1"\n shm_size: "2gb"\n expose:\n - "9223"\n volumes:\n - ${browserVolumeName}:/data\n networks:\n - ${networkName}\n`,
|
|
3755
3906
|
maybeBrowserVolume: ` ${browserVolumeName}:\n`
|
|
3756
3907
|
};
|
|
3757
3908
|
};
|
|
3758
|
-
var buildComposeFragments = (config) => {
|
|
3909
|
+
var buildComposeFragments = (config, resourceLimits) => {
|
|
3759
3910
|
const networkMode = config.dockerNetworkMode;
|
|
3760
3911
|
const networkName = resolveComposeNetworkName(config);
|
|
3761
3912
|
const forkRepoUrl = config.forkRepoUrl ?? "";
|
|
@@ -3767,7 +3918,7 @@ var buildComposeFragments = (config) => {
|
|
|
3767
3918
|
const maybeClaudeAuthLabelEnv = renderClaudeAuthLabelEnv(claudeAuthLabel);
|
|
3768
3919
|
const maybeAgentModeEnv = renderAgentModeEnv(config.agentMode);
|
|
3769
3920
|
const maybeAgentAutoEnv = renderAgentAutoEnv(config.agentAuto);
|
|
3770
|
-
const playwright = buildPlaywrightFragments(config, networkName);
|
|
3921
|
+
const playwright = buildPlaywrightFragments(config, networkName, resourceLimits);
|
|
3771
3922
|
return {
|
|
3772
3923
|
networkMode,
|
|
3773
3924
|
networkName,
|
|
@@ -3783,7 +3934,7 @@ var buildComposeFragments = (config) => {
|
|
|
3783
3934
|
forkRepoUrl
|
|
3784
3935
|
};
|
|
3785
3936
|
};
|
|
3786
|
-
var renderComposeServices = (config, fragments) => `services:
|
|
3937
|
+
var renderComposeServices = (config, fragments, resourceLimits) => `services:
|
|
3787
3938
|
${config.serviceName}:
|
|
3788
3939
|
build: .
|
|
3789
3940
|
container_name: ${config.containerName}
|
|
@@ -3802,7 +3953,7 @@ ${fragments.maybePlaywrightEnv}${fragments.maybeDependsOn} env_file:
|
|
|
3802
3953
|
- ${config.envProjectPath}
|
|
3803
3954
|
ports:
|
|
3804
3955
|
- "127.0.0.1:${config.sshPort}:22"
|
|
3805
|
-
volumes:
|
|
3956
|
+
${renderResourceLimits(resourceLimits)} volumes:
|
|
3806
3957
|
- ${config.volumeName}:/home/${config.sshUser}
|
|
3807
3958
|
- ${renderProjectsRootHostMount(config.dockerGitPath)}:/home/${config.sshUser}/.docker-git
|
|
3808
3959
|
- ${config.authorizedKeysPath}:/authorized_keys:ro
|
|
@@ -3820,10 +3971,10 @@ var renderComposeNetworks = (networkMode, networkName) => networkMode === "share
|
|
|
3820
3971
|
var renderComposeVolumes = (config, maybeBrowserVolume) => `volumes:
|
|
3821
3972
|
${config.volumeName}:
|
|
3822
3973
|
${maybeBrowserVolume}`;
|
|
3823
|
-
var renderDockerCompose = (config) => {
|
|
3824
|
-
const fragments = buildComposeFragments(config);
|
|
3974
|
+
var renderDockerCompose = (config, resourceLimits) => {
|
|
3975
|
+
const fragments = buildComposeFragments(config, resourceLimits);
|
|
3825
3976
|
return [
|
|
3826
|
-
renderComposeServices(config, fragments),
|
|
3977
|
+
renderComposeServices(config, fragments, resourceLimits),
|
|
3827
3978
|
renderComposeNetworks(fragments.networkMode, fragments.networkName),
|
|
3828
3979
|
renderComposeVolumes(config, fragments.maybeBrowserVolume)
|
|
3829
3980
|
].join("\n\n");
|
|
@@ -4097,7 +4248,7 @@ var renderConfigJson = (config) => `${JSON.stringify({
|
|
|
4097
4248
|
template: config
|
|
4098
4249
|
}, null, 2)}
|
|
4099
4250
|
`;
|
|
4100
|
-
var planFiles = (config) => {
|
|
4251
|
+
var planFiles = (config, composeResourceLimits) => {
|
|
4101
4252
|
const maybePlaywrightFiles = config.enableMcpPlaywright ? [{
|
|
4102
4253
|
_tag: "File",
|
|
4103
4254
|
relativePath: "Dockerfile.browser",
|
|
@@ -4123,7 +4274,7 @@ var planFiles = (config) => {
|
|
|
4123
4274
|
{
|
|
4124
4275
|
_tag: "File",
|
|
4125
4276
|
relativePath: "docker-compose.yml",
|
|
4126
|
-
contents: renderDockerCompose(config)
|
|
4277
|
+
contents: renderDockerCompose(config, composeResourceLimits)
|
|
4127
4278
|
},
|
|
4128
4279
|
{
|
|
4129
4280
|
_tag: "File",
|
|
@@ -4154,6 +4305,20 @@ var planFiles = (config) => {
|
|
|
4154
4305
|
//#endregion
|
|
4155
4306
|
//#region ../lib/src/shell/files.ts
|
|
4156
4307
|
var ensureParentDir = (path, fs, filePath) => fs.makeDirectory(path.dirname(filePath), { recursive: true });
|
|
4308
|
+
var fallbackHostResources = {
|
|
4309
|
+
cpuCount: 1,
|
|
4310
|
+
totalMemoryBytes: 1024 ** 3
|
|
4311
|
+
};
|
|
4312
|
+
var loadHostResources = () => Effect.tryPromise({
|
|
4313
|
+
try: () => import("node:os").then((os) => ({
|
|
4314
|
+
cpuCount: os.availableParallelism(),
|
|
4315
|
+
totalMemoryBytes: os.totalmem()
|
|
4316
|
+
})),
|
|
4317
|
+
catch: (error) => new Error(String(error))
|
|
4318
|
+
}).pipe(Effect.match({
|
|
4319
|
+
onFailure: () => fallbackHostResources,
|
|
4320
|
+
onSuccess: (value) => value
|
|
4321
|
+
}));
|
|
4157
4322
|
var isFileSpec = (spec) => spec._tag === "File";
|
|
4158
4323
|
var resolveSpecPath = (path, baseDir, spec) => path.join(baseDir, spec.relativePath);
|
|
4159
4324
|
var writeSpec = (path, fs, baseDir, spec) => {
|
|
@@ -4181,7 +4346,8 @@ var failOnExistingFiles = (existingFilePaths, skipExistingFiles) => {
|
|
|
4181
4346
|
var writeProjectFiles = (outDir, config, force, skipExistingFiles = false) => Effect.gen(function* (_) {
|
|
4182
4347
|
const { fs, path, resolved: baseDir } = yield* _(resolveBaseDir(outDir));
|
|
4183
4348
|
yield* _(fs.makeDirectory(baseDir, { recursive: true }));
|
|
4184
|
-
const
|
|
4349
|
+
const normalizedConfig = withDefaultResourceLimitIntent(config);
|
|
4350
|
+
const specs = planFiles(normalizedConfig, resolveComposeResourceLimits(normalizedConfig, yield* _(loadHostResources())));
|
|
4185
4351
|
const created = [];
|
|
4186
4352
|
const existingFilePaths = force ? [] : yield* _(collectExistingFilePaths(fs, path, baseDir, specs));
|
|
4187
4353
|
const existingSet = new Set(existingFilePaths);
|
|
@@ -4280,7 +4446,7 @@ var commitAllIfNeeded = (root, message, env) => Effect.gen(function* (_) {
|
|
|
4280
4446
|
], env));
|
|
4281
4447
|
});
|
|
4282
4448
|
var sanitizeBranchComponent = (value) => value.trim().replaceAll(" ", "-").replaceAll(":", "-").replaceAll("..", "-").replaceAll("@{", "-").replaceAll("\\", "-").replaceAll("^", "-").replaceAll("~", "-");
|
|
4283
|
-
var
|
|
4449
|
+
var pullRemoteAndRestoreLocal = (root, baseBranch, env) => Effect.gen(function* (_) {
|
|
4284
4450
|
const fetchExit = yield* _(gitExitCode(root, [
|
|
4285
4451
|
"fetch",
|
|
4286
4452
|
"origin",
|
|
@@ -4295,13 +4461,29 @@ var rebaseOntoOriginIfPossible = (root, baseBranch, env) => Effect.gen(function*
|
|
|
4295
4461
|
"--verify",
|
|
4296
4462
|
"--quiet",
|
|
4297
4463
|
`refs/remotes/origin/${baseBranch}`
|
|
4298
|
-
], env))) !== successExitCode) return
|
|
4299
|
-
|
|
4300
|
-
yield* _(gitExitCode(root, ["
|
|
4301
|
-
|
|
4464
|
+
], env))) !== successExitCode) return;
|
|
4465
|
+
yield* _(git(root, ["add", "-A"], env));
|
|
4466
|
+
const stashExit = yield* _(gitExitCode(root, ["stash", "--include-untracked"], env));
|
|
4467
|
+
yield* _(git(root, [
|
|
4468
|
+
"reset",
|
|
4469
|
+
"--hard",
|
|
4470
|
+
`origin/${baseBranch}`
|
|
4471
|
+
], env));
|
|
4472
|
+
if (stashExit === successExitCode) {
|
|
4473
|
+
if ((yield* _(gitExitCode(root, ["stash", "pop"], env))) !== successExitCode) {
|
|
4474
|
+
yield* _(gitExitCode(root, [
|
|
4475
|
+
"checkout",
|
|
4476
|
+
"--theirs",
|
|
4477
|
+
"--",
|
|
4478
|
+
"."
|
|
4479
|
+
], env));
|
|
4480
|
+
yield* _(git(root, ["add", "-A"], env));
|
|
4481
|
+
yield* _(gitExitCode(root, ["stash", "drop"], env));
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4302
4484
|
});
|
|
4303
4485
|
var pushToNewBranch = (root, baseBranch, originPushTarget, env) => Effect.gen(function* (_) {
|
|
4304
|
-
const headShort = yield* _(gitCapture(root, [
|
|
4486
|
+
const headShort = yield* _(gitCapture$1(root, [
|
|
4305
4487
|
"rev-parse",
|
|
4306
4488
|
"--short",
|
|
4307
4489
|
"HEAD"
|
|
@@ -4316,7 +4498,7 @@ var pushToNewBranch = (root, baseBranch, originPushTarget, env) => Effect.gen(fu
|
|
|
4316
4498
|
return branch;
|
|
4317
4499
|
});
|
|
4318
4500
|
var resolveBaseBranch = (value) => value === "HEAD" ? "main" : value;
|
|
4319
|
-
var getCurrentBranch = (root, env) => gitCapture(root, [
|
|
4501
|
+
var getCurrentBranch = (root, env) => gitCapture$1(root, [
|
|
4320
4502
|
"rev-parse",
|
|
4321
4503
|
"--abbrev-ref",
|
|
4322
4504
|
"HEAD"
|
|
@@ -4324,15 +4506,9 @@ var getCurrentBranch = (root, env) => gitCapture(root, [
|
|
|
4324
4506
|
var runStateSyncOps = (root, originUrl, message, env, options) => Effect.gen(function* (_) {
|
|
4325
4507
|
const originPushTarget = resolveOriginPushTarget(options?.originPushUrlOverride ?? null);
|
|
4326
4508
|
yield* _(normalizeLegacyStateProjects(root));
|
|
4327
|
-
yield* _(commitAllIfNeeded(root, resolveSyncMessage(message), env));
|
|
4328
4509
|
const baseBranch = resolveBaseBranch(yield* _(getCurrentBranch(root, env)));
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
const compareUrl = tryBuildGithubCompareUrl(originUrl, baseBranch, prBranch);
|
|
4332
|
-
yield* _(Effect.logWarning(`State sync needs manual merge: pushed changes to branch '${prBranch}'.`));
|
|
4333
|
-
yield* _(logOpenPr(originUrl, baseBranch, prBranch, compareUrl));
|
|
4334
|
-
return;
|
|
4335
|
-
}
|
|
4510
|
+
yield* _(pullRemoteAndRestoreLocal(root, baseBranch, env));
|
|
4511
|
+
yield* _(commitAllIfNeeded(root, resolveSyncMessage(message), env));
|
|
4336
4512
|
const pushExit = yield* _(gitExitCode(root, [
|
|
4337
4513
|
"push",
|
|
4338
4514
|
"--no-verify",
|
|
@@ -4391,7 +4567,7 @@ var stateSync = (message) => Effect.gen(function* (_) {
|
|
|
4391
4567
|
exitCode: originUrlExit
|
|
4392
4568
|
})));
|
|
4393
4569
|
}
|
|
4394
|
-
const originUrl = yield* _(gitCapture(root, [
|
|
4570
|
+
const originUrl = yield* _(gitCapture$1(root, [
|
|
4395
4571
|
"remote",
|
|
4396
4572
|
"get-url",
|
|
4397
4573
|
"origin"
|
|
@@ -4419,7 +4595,7 @@ var autoSyncState = (message) => Effect.gen(function* (_) {
|
|
|
4419
4595
|
onFailure: (error) => Effect.logWarning(`State auto-sync failed: ${String(error)}`),
|
|
4420
4596
|
onSuccess: () => Effect.void
|
|
4421
4597
|
}), Effect.asVoid);
|
|
4422
|
-
var cloneStateRepo = (root, input) => Effect.gen(function* (_) {
|
|
4598
|
+
var cloneStateRepo = (root, input, env) => Effect.gen(function* (_) {
|
|
4423
4599
|
const cloneBranchExit = yield* _(runCommandExitCode({
|
|
4424
4600
|
cwd: root,
|
|
4425
4601
|
command: "git",
|
|
@@ -4430,7 +4606,7 @@ var cloneStateRepo = (root, input) => Effect.gen(function* (_) {
|
|
|
4430
4606
|
input.repoUrl,
|
|
4431
4607
|
root
|
|
4432
4608
|
],
|
|
4433
|
-
env
|
|
4609
|
+
env
|
|
4434
4610
|
}));
|
|
4435
4611
|
if (cloneBranchExit === successExitCode) return;
|
|
4436
4612
|
yield* _(Effect.logWarning(`git clone --branch ${input.repoRef} failed (exit ${cloneBranchExit}); retrying without --branch`));
|
|
@@ -4442,60 +4618,65 @@ var cloneStateRepo = (root, input) => Effect.gen(function* (_) {
|
|
|
4442
4618
|
input.repoUrl,
|
|
4443
4619
|
root
|
|
4444
4620
|
],
|
|
4445
|
-
env
|
|
4621
|
+
env
|
|
4446
4622
|
}));
|
|
4447
4623
|
if (cloneDefaultExit !== successExitCode) return yield* _(Effect.fail(new CommandFailedError({
|
|
4448
4624
|
command: "git clone",
|
|
4449
4625
|
exitCode: cloneDefaultExit
|
|
4450
4626
|
})));
|
|
4451
4627
|
}).pipe(Effect.asVoid);
|
|
4452
|
-
var initRepoIfNeeded = (fs, path, root, input) => Effect.gen(function* (_) {
|
|
4628
|
+
var initRepoIfNeeded = (fs, path, root, input, env) => Effect.gen(function* (_) {
|
|
4453
4629
|
yield* _(fs.makeDirectory(root, { recursive: true }));
|
|
4454
4630
|
const gitDir = path.join(root, ".git");
|
|
4455
4631
|
if (yield* _(fs.exists(gitDir))) return;
|
|
4456
4632
|
if ((yield* _(fs.readDirectory(root))).length === 0) {
|
|
4457
|
-
yield* _(cloneStateRepo(root, input));
|
|
4633
|
+
yield* _(cloneStateRepo(root, input, env));
|
|
4458
4634
|
yield* _(Effect.log(`State dir cloned: ${root}`));
|
|
4459
4635
|
return;
|
|
4460
4636
|
}
|
|
4461
|
-
yield* _(git(root, ["init"],
|
|
4637
|
+
yield* _(git(root, ["init", "--initial-branch=main"], env));
|
|
4462
4638
|
}).pipe(Effect.asVoid);
|
|
4463
|
-
var ensureOriginRemote = (root, repoUrl) => Effect.gen(function* (_) {
|
|
4639
|
+
var ensureOriginRemote = (root, repoUrl, env) => Effect.gen(function* (_) {
|
|
4464
4640
|
if ((yield* _(gitExitCode(root, [
|
|
4465
4641
|
"remote",
|
|
4466
4642
|
"set-url",
|
|
4467
4643
|
"origin",
|
|
4468
4644
|
repoUrl
|
|
4469
|
-
],
|
|
4645
|
+
], env))) === successExitCode) return;
|
|
4470
4646
|
yield* _(git(root, [
|
|
4471
4647
|
"remote",
|
|
4472
4648
|
"add",
|
|
4473
4649
|
"origin",
|
|
4474
4650
|
repoUrl
|
|
4475
|
-
],
|
|
4651
|
+
], env));
|
|
4476
4652
|
});
|
|
4477
|
-
var checkoutBranchBestEffort = (root, repoRef) => Effect.gen(function* (_) {
|
|
4653
|
+
var checkoutBranchBestEffort = (root, repoRef, env) => Effect.gen(function* (_) {
|
|
4478
4654
|
const checkoutExit = yield* _(gitExitCode(root, [
|
|
4479
4655
|
"checkout",
|
|
4480
4656
|
"-B",
|
|
4481
4657
|
repoRef
|
|
4482
|
-
],
|
|
4658
|
+
], env));
|
|
4483
4659
|
if (checkoutExit === successExitCode) return;
|
|
4484
4660
|
yield* _(Effect.logWarning(`git checkout -B ${repoRef} failed (exit ${checkoutExit})`));
|
|
4485
4661
|
});
|
|
4486
|
-
var stateInit = (input) =>
|
|
4487
|
-
const
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4662
|
+
var stateInit = (input) => {
|
|
4663
|
+
const doInit = (env) => Effect.gen(function* (_) {
|
|
4664
|
+
const fs = yield* _(FileSystem.FileSystem);
|
|
4665
|
+
const path = yield* _(Path.Path);
|
|
4666
|
+
const root = resolveStateRoot(path, process.cwd());
|
|
4667
|
+
yield* _(initRepoIfNeeded(fs, path, root, input, env));
|
|
4668
|
+
yield* _(ensureOriginRemote(root, input.repoUrl, env));
|
|
4669
|
+
yield* _(adoptRemoteHistoryIfOrphan(root, input.repoRef, env));
|
|
4670
|
+
yield* _(checkoutBranchBestEffort(root, input.repoRef, env));
|
|
4671
|
+
yield* _(ensureStateGitignore(fs, path, root));
|
|
4672
|
+
yield* _(Effect.log(`State dir ready: ${root}`));
|
|
4673
|
+
yield* _(Effect.log(`Remote: ${input.repoUrl}`));
|
|
4674
|
+
}).pipe(Effect.asVoid);
|
|
4675
|
+
const token = input.token?.trim() ?? "";
|
|
4676
|
+
return token.length > 0 && isGithubHttpsRemote(input.repoUrl) ? withGithubAskpassEnv(token, doInit) : doInit(gitBaseEnv$1);
|
|
4677
|
+
};
|
|
4497
4678
|
var stateStatus = Effect.gen(function* (_) {
|
|
4498
|
-
const output = yield* _(gitCapture(resolveStateRoot(yield* _(Path.Path), process.cwd()), [
|
|
4679
|
+
const output = yield* _(gitCapture$1(resolveStateRoot(yield* _(Path.Path), process.cwd()), [
|
|
4499
4680
|
"status",
|
|
4500
4681
|
"-sb",
|
|
4501
4682
|
"--porcelain=v1"
|
|
@@ -4514,7 +4695,7 @@ var statePull = Effect.gen(function* (_) {
|
|
|
4514
4695
|
yield* _(git(root, ["pull", "--rebase"], gitBaseEnv$1));
|
|
4515
4696
|
return;
|
|
4516
4697
|
}
|
|
4517
|
-
const originUrl = yield* _(gitCapture(root, [
|
|
4698
|
+
const originUrl = yield* _(gitCapture$1(root, [
|
|
4518
4699
|
"remote",
|
|
4519
4700
|
"get-url",
|
|
4520
4701
|
"origin"
|
|
@@ -4539,13 +4720,13 @@ var statePush = Effect.gen(function* (_) {
|
|
|
4539
4720
|
], gitBaseEnv$1));
|
|
4540
4721
|
return;
|
|
4541
4722
|
}
|
|
4542
|
-
const originUrl = yield* _(gitCapture(root, [
|
|
4723
|
+
const originUrl = yield* _(gitCapture$1(root, [
|
|
4543
4724
|
"remote",
|
|
4544
4725
|
"get-url",
|
|
4545
4726
|
"origin"
|
|
4546
4727
|
], gitBaseEnv$1).pipe(Effect.map((value) => value.trim())));
|
|
4547
4728
|
const token = yield* _(resolveGithubToken(fs, path, root));
|
|
4548
|
-
yield* _(token && token.length > 0 && isGithubHttpsRemote(originUrl) ? withGithubAskpassEnv(token, (env) => pipe(gitCapture(root, [
|
|
4729
|
+
yield* _(token && token.length > 0 && isGithubHttpsRemote(originUrl) ? withGithubAskpassEnv(token, (env) => pipe(gitCapture$1(root, [
|
|
4549
4730
|
"rev-parse",
|
|
4550
4731
|
"--abbrev-ref",
|
|
4551
4732
|
"HEAD"
|
|
@@ -5105,18 +5286,18 @@ var ensureAvailableSshPort = (projectDir, config) => Effect.gen(function* (_) {
|
|
|
5105
5286
|
});
|
|
5106
5287
|
var runDockerComposeUpWithPortCheck = (projectDir) => Effect.gen(function* (_) {
|
|
5107
5288
|
const config = yield* _(readProjectConfig(projectDir));
|
|
5108
|
-
const
|
|
5109
|
-
yield* _(syncManagedProjectFiles(projectDir,
|
|
5110
|
-
yield* _(ensureComposeNetworkReady(projectDir,
|
|
5289
|
+
const resolvedTemplate = yield* _(resolveTemplateResourceLimits((yield* _(runDockerComposePsFormatted(projectDir).pipe(Effect.map((raw) => parseComposePsOutput(raw)), Effect.map((rows) => rows.length > 0)))) ? config.template : yield* _(ensureAvailableSshPort(projectDir, config))));
|
|
5290
|
+
yield* _(syncManagedProjectFiles(projectDir, resolvedTemplate));
|
|
5291
|
+
yield* _(ensureComposeNetworkReady(projectDir, resolvedTemplate));
|
|
5111
5292
|
yield* _(runDockerComposeUp(projectDir));
|
|
5112
|
-
yield* _(ensureClaudeCliReady(projectDir,
|
|
5293
|
+
yield* _(ensureClaudeCliReady(projectDir, resolvedTemplate.containerName));
|
|
5113
5294
|
const ensureBridgeAccess = (containerName) => runDockerInspectContainerBridgeIp(projectDir, containerName).pipe(Effect.flatMap((bridgeIp) => bridgeIp.length > 0 ? Effect.void : runDockerNetworkConnectBridge(projectDir, containerName)), Effect.matchEffect({
|
|
5114
5295
|
onFailure: (error) => Effect.logWarning(`Failed to connect ${containerName} to bridge network: ${error instanceof Error ? error.message : String(error)}`),
|
|
5115
5296
|
onSuccess: () => Effect.void
|
|
5116
5297
|
}));
|
|
5117
|
-
yield* _(ensureBridgeAccess(
|
|
5118
|
-
if (
|
|
5119
|
-
return
|
|
5298
|
+
yield* _(ensureBridgeAccess(resolvedTemplate.containerName));
|
|
5299
|
+
if (resolvedTemplate.enableMcpPlaywright) yield* _(ensureBridgeAccess(`${resolvedTemplate.containerName}-browser`));
|
|
5300
|
+
return resolvedTemplate;
|
|
5120
5301
|
});
|
|
5121
5302
|
//#endregion
|
|
5122
5303
|
//#region ../lib/src/usecases/projects-ssh.ts
|
|
@@ -5408,7 +5589,7 @@ var resolveRootedConfig = (command, ctx) => ({
|
|
|
5408
5589
|
codexAuthPath: ctx.resolveRootPath(command.config.codexAuthPath),
|
|
5409
5590
|
codexSharedAuthPath: ctx.resolveRootPath(command.config.codexSharedAuthPath)
|
|
5410
5591
|
});
|
|
5411
|
-
var resolveCreateConfig = (command, ctx, resolvedOutDir) => resolveSshPort(resolveRootedConfig(command, ctx), resolvedOutDir).pipe(Effect.flatMap((config) => applyGithubForkConfig(config)));
|
|
5592
|
+
var resolveCreateConfig = (command, ctx, resolvedOutDir) => resolveSshPort(resolveRootedConfig(command, ctx), resolvedOutDir).pipe(Effect.flatMap((config) => applyGithubForkConfig(config)), Effect.flatMap((config) => resolveTemplateResourceLimits(config)));
|
|
5412
5593
|
var logCreatedProject = (resolvedOutDir, createdFiles) => Effect.gen(function* (_) {
|
|
5413
5594
|
yield* _(Effect.log(`Created docker-git project in ${resolvedOutDir}`));
|
|
5414
5595
|
for (const file of createdFiles) yield* _(Effect.log(` - ${file}`));
|
|
@@ -5524,7 +5705,7 @@ var normalizeAuthLabel = (value) => {
|
|
|
5524
5705
|
};
|
|
5525
5706
|
//#endregion
|
|
5526
5707
|
//#region ../lib/src/usecases/apply-overrides.ts
|
|
5527
|
-
var hasApplyOverrides = (command) => command.gitTokenLabel !== void 0 || command.codexTokenLabel !== void 0 || command.claudeTokenLabel !== void 0 || command.enableMcpPlaywright !== void 0;
|
|
5708
|
+
var hasApplyOverrides = (command) => command.gitTokenLabel !== void 0 || command.codexTokenLabel !== void 0 || command.claudeTokenLabel !== void 0 || command.cpuLimit !== void 0 || command.ramLimit !== void 0 || command.enableMcpPlaywright !== void 0;
|
|
5528
5709
|
var applyTemplateOverrides = (template, command) => {
|
|
5529
5710
|
if (command === void 0) return template;
|
|
5530
5711
|
let nextTemplate = template;
|
|
@@ -5540,6 +5721,14 @@ var applyTemplateOverrides = (template, command) => {
|
|
|
5540
5721
|
...nextTemplate,
|
|
5541
5722
|
claudeAuthLabel: normalizeAuthLabel(command.claudeTokenLabel)
|
|
5542
5723
|
};
|
|
5724
|
+
if (command.cpuLimit !== void 0) nextTemplate = {
|
|
5725
|
+
...nextTemplate,
|
|
5726
|
+
cpuLimit: command.cpuLimit
|
|
5727
|
+
};
|
|
5728
|
+
if (command.ramLimit !== void 0) nextTemplate = {
|
|
5729
|
+
...nextTemplate,
|
|
5730
|
+
ramLimit: command.ramLimit
|
|
5731
|
+
};
|
|
5543
5732
|
if (command.enableMcpPlaywright !== void 0) nextTemplate = {
|
|
5544
5733
|
...nextTemplate,
|
|
5545
5734
|
enableMcpPlaywright: command.enableMcpPlaywright
|
|
@@ -5547,22 +5736,12 @@ var applyTemplateOverrides = (template, command) => {
|
|
|
5547
5736
|
return nextTemplate;
|
|
5548
5737
|
};
|
|
5549
5738
|
//#endregion
|
|
5550
|
-
//#region ../lib/src/usecases/apply.ts
|
|
5551
|
-
var applyProjectFiles = (projectDir, command) => Effect.gen(function* (_) {
|
|
5552
|
-
yield* _(Effect.log(`Applying docker-git config files in ${projectDir}...`));
|
|
5553
|
-
const resolvedTemplate = applyTemplateOverrides((yield* _(readProjectConfig(projectDir))).template, command);
|
|
5554
|
-
yield* _(writeProjectFiles(projectDir, resolvedTemplate, true));
|
|
5555
|
-
yield* _(ensureCodexConfigFile(projectDir, resolvedTemplate.codexAuthPath));
|
|
5556
|
-
yield* _(ensureClaudeAuthSeedFromHome(defaultProjectsRoot(projectDir), ".orch/auth/claude"));
|
|
5557
|
-
return resolvedTemplate;
|
|
5558
|
-
});
|
|
5739
|
+
//#region ../lib/src/usecases/apply-project-discovery.ts
|
|
5559
5740
|
var gitSuccessExitCode = 0;
|
|
5560
|
-
var gitBranchDetached = "HEAD";
|
|
5561
|
-
var maxLocalConfigSearchDepth = 6;
|
|
5562
5741
|
var gitBaseEnv = { GIT_TERMINAL_PROMPT: "0" };
|
|
5563
5742
|
var emptyConfigPaths = () => [];
|
|
5564
5743
|
var nullProjectCandidate = () => null;
|
|
5565
|
-
var nullString = () => null;
|
|
5744
|
+
var nullString$1 = () => null;
|
|
5566
5745
|
var normalizeRepoIdentity = (repoUrl) => {
|
|
5567
5746
|
const github = parseGithubRepoUrl(repoUrl);
|
|
5568
5747
|
if (github !== null) {
|
|
@@ -5630,7 +5809,7 @@ var tryGitCapture = (cwd, args) => {
|
|
|
5630
5809
|
_tag: "ApplyGitCaptureError",
|
|
5631
5810
|
code
|
|
5632
5811
|
})).pipe(Effect.map((value) => value.trim()), Effect.match({
|
|
5633
|
-
onFailure: nullString,
|
|
5812
|
+
onFailure: nullString$1,
|
|
5634
5813
|
onSuccess: (value) => value
|
|
5635
5814
|
})) : Effect.succeed(null)
|
|
5636
5815
|
}));
|
|
@@ -5680,6 +5859,20 @@ var collectRemoteIdentities = (repoRoot) => Effect.gen(function* (_) {
|
|
|
5680
5859
|
}
|
|
5681
5860
|
return [...identityMap.values()];
|
|
5682
5861
|
});
|
|
5862
|
+
var gitCapture = tryGitCapture;
|
|
5863
|
+
//#endregion
|
|
5864
|
+
//#region ../lib/src/usecases/apply.ts
|
|
5865
|
+
var applyProjectFiles = (projectDir, command) => Effect.gen(function* (_) {
|
|
5866
|
+
yield* _(Effect.log(`Applying docker-git config files in ${projectDir}...`));
|
|
5867
|
+
const resolvedTemplate = yield* _(resolveTemplateResourceLimits(applyTemplateOverrides((yield* _(readProjectConfig(projectDir))).template, command)));
|
|
5868
|
+
yield* _(writeProjectFiles(projectDir, resolvedTemplate, true));
|
|
5869
|
+
yield* _(ensureCodexConfigFile(projectDir, resolvedTemplate.codexAuthPath));
|
|
5870
|
+
yield* _(ensureClaudeAuthSeedFromHome(defaultProjectsRoot(projectDir), ".orch/auth/claude"));
|
|
5871
|
+
return resolvedTemplate;
|
|
5872
|
+
});
|
|
5873
|
+
var gitBranchDetached = "HEAD";
|
|
5874
|
+
var maxLocalConfigSearchDepth = 6;
|
|
5875
|
+
var nullString = () => null;
|
|
5683
5876
|
var resolveFromCurrentTree = () => Effect.gen(function* (_) {
|
|
5684
5877
|
const { fs, path, resolved } = yield* _(resolveBaseDir("."));
|
|
5685
5878
|
const configPath = yield* _(findExistingUpwards(fs, path, resolved, "docker-git.json", maxLocalConfigSearchDepth).pipe(Effect.match({
|
|
@@ -5695,11 +5888,11 @@ var normalizeBranch = (branch) => {
|
|
|
5695
5888
|
};
|
|
5696
5889
|
var resolveFromCurrentRepository = () => Effect.gen(function* (_) {
|
|
5697
5890
|
const cwd = process.cwd();
|
|
5698
|
-
const repoRoot = yield* _(
|
|
5891
|
+
const repoRoot = yield* _(gitCapture(cwd, ["rev-parse", "--show-toplevel"]));
|
|
5699
5892
|
if (repoRoot === null) return null;
|
|
5700
5893
|
const remoteIdentities = yield* _(collectRemoteIdentities(repoRoot));
|
|
5701
5894
|
if (remoteIdentities.length === 0) return null;
|
|
5702
|
-
const branch = normalizeBranch(yield* _(
|
|
5895
|
+
const branch = normalizeBranch(yield* _(gitCapture(repoRoot, [
|
|
5703
5896
|
"rev-parse",
|
|
5704
5897
|
"--abbrev-ref",
|
|
5705
5898
|
"HEAD"
|
|
@@ -6156,6 +6349,87 @@ var authCodexStatus = (command) => withCodexAuth(command, ({ accountPath, cwd })
|
|
|
6156
6349
|
}));
|
|
6157
6350
|
var authCodexLogout = (command) => withCodexAuth(command, ({ accountPath, cwd }) => runCodexLogout(cwd, accountPath)).pipe(Effect.zipRight(autoSyncState(`chore(state): auth codex logout ${normalizeAccountLabel(command.label, "default")}`)));
|
|
6158
6351
|
//#endregion
|
|
6352
|
+
//#region ../lib/src/usecases/state-repo-github.ts
|
|
6353
|
+
var dotDockerGitRepoName = ".docker-git";
|
|
6354
|
+
var defaultStateRef = "main";
|
|
6355
|
+
var resolveViewerLogin = (cwd, hostPath, token) => Effect.gen(function* (_) {
|
|
6356
|
+
const raw = yield* _(runGhApiCapture(cwd, hostPath, token, [
|
|
6357
|
+
"/user",
|
|
6358
|
+
"--jq",
|
|
6359
|
+
".login"
|
|
6360
|
+
]));
|
|
6361
|
+
if (raw.length === 0) return yield* _(Effect.fail(new CommandFailedError({
|
|
6362
|
+
command: "gh api /user --jq .login",
|
|
6363
|
+
exitCode: 1
|
|
6364
|
+
})));
|
|
6365
|
+
return raw;
|
|
6366
|
+
});
|
|
6367
|
+
var getRepoCloneUrl = (cwd, hostPath, token, login) => runGhApiNullable(cwd, hostPath, token, [
|
|
6368
|
+
`/repos/${login}/${dotDockerGitRepoName}`,
|
|
6369
|
+
"--jq",
|
|
6370
|
+
".clone_url"
|
|
6371
|
+
]);
|
|
6372
|
+
var createStateRepo = (cwd, hostPath, token) => runGhApiNullable(cwd, hostPath, token, [
|
|
6373
|
+
"-X",
|
|
6374
|
+
"POST",
|
|
6375
|
+
"/user/repos",
|
|
6376
|
+
"-f",
|
|
6377
|
+
`name=${dotDockerGitRepoName}`,
|
|
6378
|
+
"-f",
|
|
6379
|
+
"private=false",
|
|
6380
|
+
"-f",
|
|
6381
|
+
"auto_init=true",
|
|
6382
|
+
"--jq",
|
|
6383
|
+
".clone_url"
|
|
6384
|
+
]);
|
|
6385
|
+
/**
|
|
6386
|
+
* Ensures the .docker-git state repository exists on GitHub and is initialised locally.
|
|
6387
|
+
*
|
|
6388
|
+
* On GitHub auth, immediately:
|
|
6389
|
+
* 1. Resolve the authenticated user's login via the GitHub API
|
|
6390
|
+
* 2. Check whether `<login>/.docker-git` exists on GitHub
|
|
6391
|
+
* 3. If missing, create the repository (public, auto-initialised with a README)
|
|
6392
|
+
* 4. Initialise the local `~/.docker-git` directory as a clone of that repository
|
|
6393
|
+
*
|
|
6394
|
+
* All failures are swallowed and logged as warnings so they never abort the auth
|
|
6395
|
+
* flow itself.
|
|
6396
|
+
*
|
|
6397
|
+
* @param token - A valid GitHub personal-access or OAuth token
|
|
6398
|
+
* @returns Effect<void, never, GithubStateRepoRuntime>
|
|
6399
|
+
*
|
|
6400
|
+
* @pure false
|
|
6401
|
+
* @effect FileSystem, CommandExecutor (Docker gh CLI, git)
|
|
6402
|
+
* @invariant ∀token ∈ ValidTokens: ensureStateDotDockerGitRepo(token) → cloned(~/.docker-git) ∨ warned
|
|
6403
|
+
* @precondition token.length > 0
|
|
6404
|
+
* @postcondition ~/.docker-git is a git repo with origin pointing to github.com/<login>/.docker-git
|
|
6405
|
+
* @complexity O(1) API calls
|
|
6406
|
+
* @throws Never - all errors are caught and logged
|
|
6407
|
+
*/
|
|
6408
|
+
var ensureStateDotDockerGitRepo = (token) => withFsPathContext(({ cwd, fs, path }) => Effect.gen(function* (_) {
|
|
6409
|
+
const ghRoot = resolvePathFromCwd(path, cwd, ghAuthRoot);
|
|
6410
|
+
yield* _(fs.makeDirectory(ghRoot, { recursive: true }));
|
|
6411
|
+
yield* _(ensureGhAuthImage(fs, path, cwd, "gh api"));
|
|
6412
|
+
const login = yield* _(resolveViewerLogin(cwd, ghRoot, token));
|
|
6413
|
+
let cloneUrl = yield* _(getRepoCloneUrl(cwd, ghRoot, token, login));
|
|
6414
|
+
if (cloneUrl === null) {
|
|
6415
|
+
yield* _(Effect.log(`Creating .docker-git repository for ${login}...`));
|
|
6416
|
+
cloneUrl = yield* _(createStateRepo(cwd, ghRoot, token));
|
|
6417
|
+
}
|
|
6418
|
+
if (cloneUrl === null) {
|
|
6419
|
+
yield* _(Effect.logWarning(`Could not resolve or create .docker-git repository for ${login}`));
|
|
6420
|
+
return;
|
|
6421
|
+
}
|
|
6422
|
+
yield* _(Effect.log(`Initializing state repository: ${cloneUrl}`));
|
|
6423
|
+
yield* _(stateInit({
|
|
6424
|
+
repoUrl: cloneUrl,
|
|
6425
|
+
repoRef: defaultStateRef,
|
|
6426
|
+
token
|
|
6427
|
+
}));
|
|
6428
|
+
})).pipe(Effect.matchEffect({
|
|
6429
|
+
onFailure: (error) => Effect.logWarning(`State repo setup failed: ${error instanceof Error ? error.message : String(error)}`),
|
|
6430
|
+
onSuccess: () => Effect.void
|
|
6431
|
+
}));
|
|
6432
|
+
//#endregion
|
|
6159
6433
|
//#region ../lib/src/usecases/auth-github.ts
|
|
6160
6434
|
var ensureGithubOrchLayout = (cwd, envGlobalPath) => migrateLegacyOrchLayout(cwd, {
|
|
6161
6435
|
envGlobalPath,
|
|
@@ -6253,6 +6527,7 @@ var runGithubInteractiveLogin = (cwd, fs, path, envPath, command) => Effect.gen(
|
|
|
6253
6527
|
const resolved = yield* _(resolveGithubTokenFromGh(cwd, accountPath));
|
|
6254
6528
|
yield* _(ensureEnvFile$1(fs, path, envPath));
|
|
6255
6529
|
yield* _(persistGithubToken(fs, envPath, buildGithubTokenKey(command.label), resolved));
|
|
6530
|
+
return resolved;
|
|
6256
6531
|
});
|
|
6257
6532
|
var authGithubLogin = (command) => withFsPathContext(({ cwd, fs, path }) => Effect.gen(function* (_) {
|
|
6258
6533
|
yield* _(ensureGithubOrchLayout(cwd, command.envGlobalPath));
|
|
@@ -6263,10 +6538,11 @@ var authGithubLogin = (command) => withFsPathContext(({ cwd, fs, path }) => Effe
|
|
|
6263
6538
|
if (token.length > 0) {
|
|
6264
6539
|
yield* _(ensureEnvFile$1(fs, path, envPath));
|
|
6265
6540
|
yield* _(persistGithubToken(fs, envPath, key, token));
|
|
6541
|
+
yield* _(ensureStateDotDockerGitRepo(token));
|
|
6266
6542
|
yield* _(autoSyncState(`chore(state): auth gh ${label}`));
|
|
6267
6543
|
return;
|
|
6268
6544
|
}
|
|
6269
|
-
yield* _(runGithubInteractiveLogin(cwd, fs, path, envPath, command));
|
|
6545
|
+
yield* _(ensureStateDotDockerGitRepo(yield* _(runGithubInteractiveLogin(cwd, fs, path, envPath, command))));
|
|
6270
6546
|
yield* _(autoSyncState(`chore(state): auth gh ${label}`));
|
|
6271
6547
|
}));
|
|
6272
6548
|
var authGithubStatus = (command) => withEnvContext(command.envGlobalPath, ({ current, envPath }) => Effect.gen(function* (_) {
|
|
@@ -6951,6 +7227,22 @@ var valueOptionSpecByFlag = new Map([
|
|
|
6951
7227
|
flag: "--codex-home",
|
|
6952
7228
|
key: "codexHome"
|
|
6953
7229
|
},
|
|
7230
|
+
{
|
|
7231
|
+
flag: "--cpu",
|
|
7232
|
+
key: "cpuLimit"
|
|
7233
|
+
},
|
|
7234
|
+
{
|
|
7235
|
+
flag: "--cpus",
|
|
7236
|
+
key: "cpuLimit"
|
|
7237
|
+
},
|
|
7238
|
+
{
|
|
7239
|
+
flag: "--ram",
|
|
7240
|
+
key: "ramLimit"
|
|
7241
|
+
},
|
|
7242
|
+
{
|
|
7243
|
+
flag: "--memory",
|
|
7244
|
+
key: "ramLimit"
|
|
7245
|
+
},
|
|
6954
7246
|
{
|
|
6955
7247
|
flag: "--network-mode",
|
|
6956
7248
|
key: "dockerNetworkMode"
|
|
@@ -7127,6 +7419,14 @@ var valueFlagUpdaters = {
|
|
|
7127
7419
|
...raw,
|
|
7128
7420
|
codexHome: value
|
|
7129
7421
|
}),
|
|
7422
|
+
cpuLimit: (raw, value) => ({
|
|
7423
|
+
...raw,
|
|
7424
|
+
cpuLimit: value
|
|
7425
|
+
}),
|
|
7426
|
+
ramLimit: (raw, value) => ({
|
|
7427
|
+
...raw,
|
|
7428
|
+
ramLimit: value
|
|
7429
|
+
}),
|
|
7130
7430
|
dockerNetworkMode: (raw, value) => ({
|
|
7131
7431
|
...raw,
|
|
7132
7432
|
dockerNetworkMode: value
|
|
@@ -7304,15 +7604,22 @@ var parseProjectDirWithOptions = (args, defaultProjectDir = ".") => Either.gen(f
|
|
|
7304
7604
|
var parseProjectDirArgs = (args, defaultProjectDir = ".") => Either.map(parseProjectDirWithOptions(args, defaultProjectDir), ({ projectDir }) => ({ projectDir }));
|
|
7305
7605
|
//#endregion
|
|
7306
7606
|
//#region src/docker-git/cli/parser-apply.ts
|
|
7307
|
-
var parseApply = (args) => Either.
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7607
|
+
var parseApply = (args) => Either.gen(function* (_) {
|
|
7608
|
+
const { projectDir, raw } = yield* _(parseProjectDirWithOptions(args));
|
|
7609
|
+
const cpuLimit = yield* _(normalizeCpuLimit(raw.cpuLimit, "--cpu"));
|
|
7610
|
+
const ramLimit = yield* _(normalizeRamLimit(raw.ramLimit, "--ram"));
|
|
7611
|
+
return {
|
|
7612
|
+
_tag: "Apply",
|
|
7613
|
+
projectDir,
|
|
7614
|
+
runUp: raw.up ?? true,
|
|
7615
|
+
gitTokenLabel: raw.gitTokenLabel,
|
|
7616
|
+
codexTokenLabel: raw.codexTokenLabel,
|
|
7617
|
+
claudeTokenLabel: raw.claudeTokenLabel,
|
|
7618
|
+
cpuLimit,
|
|
7619
|
+
ramLimit,
|
|
7620
|
+
enableMcpPlaywright: raw.enableMcpPlaywright
|
|
7621
|
+
};
|
|
7622
|
+
});
|
|
7316
7623
|
//#endregion
|
|
7317
7624
|
//#region src/docker-git/cli/parser-attach.ts
|
|
7318
7625
|
var parseAttach = (args) => {
|
|
@@ -7419,7 +7726,7 @@ var resolveAutoAgentFlags = (raw) => {
|
|
|
7419
7726
|
});
|
|
7420
7727
|
};
|
|
7421
7728
|
//#endregion
|
|
7422
|
-
//#region ../lib/src/core/command-builders.ts
|
|
7729
|
+
//#region ../lib/src/core/command-builders-shared.ts
|
|
7423
7730
|
var parsePort = (value) => {
|
|
7424
7731
|
const parsed = Number(value);
|
|
7425
7732
|
if (!Number.isInteger(parsed)) return Either.left({
|
|
@@ -7434,6 +7741,7 @@ var parsePort = (value) => {
|
|
|
7434
7741
|
});
|
|
7435
7742
|
return Either.right(parsed);
|
|
7436
7743
|
};
|
|
7744
|
+
var parseSshPort = (value) => parsePort(value);
|
|
7437
7745
|
var parseDockerNetworkMode = (value) => {
|
|
7438
7746
|
const candidate = value?.trim() ?? defaultTemplateConfig.dockerNetworkMode;
|
|
7439
7747
|
if (isDockerNetworkMode(candidate)) return Either.right(candidate);
|
|
@@ -7451,6 +7759,8 @@ var nonEmpty = (option, value, fallback) => {
|
|
|
7451
7759
|
});
|
|
7452
7760
|
return Either.right(candidate);
|
|
7453
7761
|
};
|
|
7762
|
+
//#endregion
|
|
7763
|
+
//#region ../lib/src/core/command-builders.ts
|
|
7454
7764
|
var normalizeSecretsRoot = (value) => trimRightChar(value, "/");
|
|
7455
7765
|
var resolveRepoBasics = (raw) => Either.gen(function* (_) {
|
|
7456
7766
|
const resolvedRepo = resolveRepoInput(raw.repoUrl?.trim() ?? "");
|
|
@@ -7470,7 +7780,7 @@ var resolveRepoBasics = (raw) => Either.gen(function* (_) {
|
|
|
7470
7780
|
repoRef,
|
|
7471
7781
|
targetDir: expandContainerHome(sshUser, yield* _(nonEmpty("--target-dir", raw.targetDir, defaultTemplateConfig.targetDir))),
|
|
7472
7782
|
sshUser,
|
|
7473
|
-
sshPort: yield* _(
|
|
7783
|
+
sshPort: yield* _(parseSshPort(raw.sshPort ?? String(defaultTemplateConfig.sshPort)))
|
|
7474
7784
|
};
|
|
7475
7785
|
});
|
|
7476
7786
|
var resolveNames = (raw, projectSlug) => Either.gen(function* (_) {
|
|
@@ -7525,7 +7835,7 @@ var resolveCreateBehavior = (raw) => ({
|
|
|
7525
7835
|
forceEnv: raw.forceEnv ?? false,
|
|
7526
7836
|
enableMcpPlaywright: raw.enableMcpPlaywright ?? false
|
|
7527
7837
|
});
|
|
7528
|
-
var buildTemplateConfig = ({ agentAuto, agentMode, claudeAuthLabel, codexAuthLabel, dockerNetworkMode, dockerSharedNetworkName, enableMcpPlaywright, gitTokenLabel, names, paths, repo }) => ({
|
|
7838
|
+
var buildTemplateConfig = ({ agentAuto, agentMode, claudeAuthLabel, codexAuthLabel, cpuLimit, dockerNetworkMode, dockerSharedNetworkName, enableMcpPlaywright, gitTokenLabel, names, paths, ramLimit, repo }) => ({
|
|
7529
7839
|
containerName: names.containerName,
|
|
7530
7840
|
serviceName: names.serviceName,
|
|
7531
7841
|
sshUser: repo.sshUser,
|
|
@@ -7544,6 +7854,8 @@ var buildTemplateConfig = ({ agentAuto, agentMode, claudeAuthLabel, codexAuthLab
|
|
|
7544
7854
|
codexAuthPath: paths.codexAuthPath,
|
|
7545
7855
|
codexSharedAuthPath: paths.codexSharedAuthPath,
|
|
7546
7856
|
codexHome: paths.codexHome,
|
|
7857
|
+
cpuLimit,
|
|
7858
|
+
ramLimit,
|
|
7547
7859
|
dockerNetworkMode,
|
|
7548
7860
|
dockerSharedNetworkName,
|
|
7549
7861
|
enableMcpPlaywright,
|
|
@@ -7559,6 +7871,8 @@ var buildCreateCommand = (raw) => Either.gen(function* (_) {
|
|
|
7559
7871
|
const gitTokenLabel = normalizeGitTokenLabel(raw.gitTokenLabel);
|
|
7560
7872
|
const codexAuthLabel = normalizeAuthLabel(raw.codexTokenLabel);
|
|
7561
7873
|
const claudeAuthLabel = normalizeAuthLabel(raw.claudeTokenLabel);
|
|
7874
|
+
const cpuLimit = yield* _(normalizeCpuLimit(raw.cpuLimit ?? "30%", "--cpu"));
|
|
7875
|
+
const ramLimit = yield* _(normalizeRamLimit(raw.ramLimit ?? "30%", "--ram"));
|
|
7562
7876
|
const dockerNetworkMode = yield* _(parseDockerNetworkMode(raw.dockerNetworkMode));
|
|
7563
7877
|
const dockerSharedNetworkName = yield* _(nonEmpty("--shared-network", raw.dockerSharedNetworkName, defaultTemplateConfig.dockerSharedNetworkName));
|
|
7564
7878
|
const { agentAuto, agentMode } = yield* _(resolveAutoAgentFlags(raw));
|
|
@@ -7574,6 +7888,8 @@ var buildCreateCommand = (raw) => Either.gen(function* (_) {
|
|
|
7574
7888
|
repo,
|
|
7575
7889
|
names,
|
|
7576
7890
|
paths,
|
|
7891
|
+
cpuLimit,
|
|
7892
|
+
ramLimit,
|
|
7577
7893
|
dockerNetworkMode,
|
|
7578
7894
|
dockerSharedNetworkName,
|
|
7579
7895
|
gitTokenLabel,
|
|
@@ -7856,6 +8172,8 @@ Options:
|
|
|
7856
8172
|
--env-project <path> Host path to project env file (default: ./.orch/env/project.env)
|
|
7857
8173
|
--codex-auth <path> Host path for Codex auth cache (default: <projectsRoot>/.orch/auth/codex)
|
|
7858
8174
|
--codex-home <path> Container path for Codex auth (default: /home/dev/.codex)
|
|
8175
|
+
--cpu <value> CPU limit: percent or cores (examples: 30%, 1.5; default: 30%)
|
|
8176
|
+
--ram <value> RAM limit: percent or size (examples: 30%, 512m, 4g; default: 30%)
|
|
7859
8177
|
--network-mode <mode> Compose network mode: shared|project (default: shared)
|
|
7860
8178
|
--shared-network <name> Shared Docker network name when network-mode=shared (default: docker-git-shared)
|
|
7861
8179
|
--out-dir <path> Output directory (default: <projectsRoot>/<org>/<repo>[/issue-<id>|/pr-<id>])
|
|
@@ -8348,6 +8666,8 @@ var createSteps = [
|
|
|
8348
8666
|
"repoUrl",
|
|
8349
8667
|
"repoRef",
|
|
8350
8668
|
"outDir",
|
|
8669
|
+
"cpuLimit",
|
|
8670
|
+
"ramLimit",
|
|
8351
8671
|
"runUp",
|
|
8352
8672
|
"mcpPlaywright",
|
|
8353
8673
|
"force"
|
|
@@ -8400,15 +8720,38 @@ var menuItems = [
|
|
|
8400
8720
|
];
|
|
8401
8721
|
//#endregion
|
|
8402
8722
|
//#region src/docker-git/menu-create.ts
|
|
8723
|
+
var optionalCreateArgs = (input) => [
|
|
8724
|
+
{
|
|
8725
|
+
value: input.repoUrl,
|
|
8726
|
+
args: ["--repo-url", input.repoUrl]
|
|
8727
|
+
},
|
|
8728
|
+
{
|
|
8729
|
+
value: input.repoRef,
|
|
8730
|
+
args: ["--repo-ref", input.repoRef]
|
|
8731
|
+
},
|
|
8732
|
+
{
|
|
8733
|
+
value: input.outDir,
|
|
8734
|
+
args: ["--out-dir", input.outDir]
|
|
8735
|
+
},
|
|
8736
|
+
{
|
|
8737
|
+
value: input.cpuLimit,
|
|
8738
|
+
args: ["--cpu", input.cpuLimit]
|
|
8739
|
+
},
|
|
8740
|
+
{
|
|
8741
|
+
value: input.ramLimit,
|
|
8742
|
+
args: ["--ram", input.ramLimit]
|
|
8743
|
+
}
|
|
8744
|
+
];
|
|
8745
|
+
var booleanCreateFlags = (input) => [
|
|
8746
|
+
input.runUp ? null : "--no-up",
|
|
8747
|
+
input.enableMcpPlaywright ? "--mcp-playwright" : null,
|
|
8748
|
+
input.force ? "--force" : null,
|
|
8749
|
+
input.forceEnv ? "--force-env" : null
|
|
8750
|
+
].filter((value) => value !== null);
|
|
8403
8751
|
var buildCreateArgs = (input) => {
|
|
8404
8752
|
const args = ["create"];
|
|
8405
|
-
if (
|
|
8406
|
-
|
|
8407
|
-
if (input.outDir.length > 0) args.push("--out-dir", input.outDir);
|
|
8408
|
-
if (!input.runUp) args.push("--no-up");
|
|
8409
|
-
if (input.enableMcpPlaywright) args.push("--mcp-playwright");
|
|
8410
|
-
if (input.force) args.push("--force");
|
|
8411
|
-
if (input.forceEnv) args.push("--force-env");
|
|
8753
|
+
for (const spec of optionalCreateArgs(input)) if (spec.value.length > 0) args.push(spec.args[0], spec.args[1]);
|
|
8754
|
+
for (const flag of booleanCreateFlags(input)) args.push(flag);
|
|
8412
8755
|
return args;
|
|
8413
8756
|
};
|
|
8414
8757
|
var trimLeftSlash = (value) => {
|
|
@@ -8441,6 +8784,8 @@ var resolveCreateInputs = (cwd, values) => {
|
|
|
8441
8784
|
repoUrl,
|
|
8442
8785
|
repoRef: values.repoRef ?? resolvedRepoRef ?? "main",
|
|
8443
8786
|
outDir,
|
|
8787
|
+
cpuLimit: values.cpuLimit ?? "",
|
|
8788
|
+
ramLimit: values.ramLimit ?? "",
|
|
8444
8789
|
runUp: values.runUp !== false,
|
|
8445
8790
|
enableMcpPlaywright: values.enableMcpPlaywright === true,
|
|
8446
8791
|
force: values.force === true,
|
|
@@ -8484,6 +8829,12 @@ var applyCreateStep = (input) => Match.value(input.step).pipe(Match.when("repoUr
|
|
|
8484
8829
|
}), Match.when("outDir", () => {
|
|
8485
8830
|
input.nextValues.outDir = input.buffer.length > 0 ? input.buffer : input.currentDefaults.outDir;
|
|
8486
8831
|
return true;
|
|
8832
|
+
}), Match.when("cpuLimit", () => {
|
|
8833
|
+
input.nextValues.cpuLimit = input.buffer.length > 0 ? input.buffer : input.currentDefaults.cpuLimit;
|
|
8834
|
+
return true;
|
|
8835
|
+
}), Match.when("ramLimit", () => {
|
|
8836
|
+
input.nextValues.ramLimit = input.buffer.length > 0 ? input.buffer : input.currentDefaults.ramLimit;
|
|
8837
|
+
return true;
|
|
8487
8838
|
}), Match.when("runUp", () => {
|
|
8488
8839
|
input.nextValues.runUp = parseYesDefault(input.buffer, input.currentDefaults.runUp);
|
|
8489
8840
|
return true;
|
|
@@ -10106,7 +10457,7 @@ var renderProjectAuthPrompt = (view, message) => {
|
|
|
10106
10457
|
};
|
|
10107
10458
|
//#endregion
|
|
10108
10459
|
//#region src/docker-git/menu-render.ts
|
|
10109
|
-
var renderStepLabel = (step, defaults) => Match.value(step).pipe(Match.when("repoUrl", () => "Repo URL (optional for empty workspace)"), Match.when("repoRef", () => `Repo ref [${defaults.repoRef}]`), Match.when("outDir", () => `Output dir [${defaults.outDir}]`), Match.when("runUp", () => `Run docker compose up now? [${defaults.runUp ? "Y" : "n"}]`), Match.when("mcpPlaywright", () => `Enable Playwright MCP (Chromium sidecar)? [${defaults.enableMcpPlaywright ? "y" : "N"}]`), Match.when("force", () => `Force recreate (overwrite files + wipe volumes)? [${defaults.force ? "y" : "N"}]`), Match.exhaustive);
|
|
10460
|
+
var renderStepLabel = (step, defaults) => Match.value(step).pipe(Match.when("repoUrl", () => "Repo URL (optional for empty workspace)"), Match.when("repoRef", () => `Repo ref [${defaults.repoRef}]`), Match.when("outDir", () => `Output dir [${defaults.outDir}]`), Match.when("cpuLimit", () => `CPU limit [${defaults.cpuLimit || "30%"}]`), Match.when("ramLimit", () => `RAM limit [${defaults.ramLimit || "30%"}]`), Match.when("runUp", () => `Run docker compose up now? [${defaults.runUp ? "Y" : "n"}]`), Match.when("mcpPlaywright", () => `Enable Playwright MCP (Chromium sidecar)? [${defaults.enableMcpPlaywright ? "y" : "N"}]`), Match.when("force", () => `Force recreate (overwrite files + wipe volumes)? [${defaults.force ? "y" : "N"}]`), Match.exhaustive);
|
|
10110
10461
|
var compactElements = (items) => items.filter((item) => item !== null);
|
|
10111
10462
|
var renderMenuHints = (el) => el(Box, {
|
|
10112
10463
|
marginTop: 1,
|