Merge master b189f586d into Codex runner default candidate

Preserve master transport, lifecycle, and release verification controls alongside macOS qualification.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-12 07:55:09 -05:00
commit f4abdc2cfc
749 files changed
+124181 -68418

No files matched your search

+41
View File
@@ -1,6 +1,7 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { createServer } from "node:net";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { Agent, Company, InstanceExperimentalSettings } from "@paperclipai/shared";
import {
@@ -12,6 +13,7 @@ import {
redactTestDriveText,
resolveTestDriveBootstrap,
resolveTestDriveDataDir,
resolveTestDriveServerPort,
testDriveCommand,
type TestDriveApi,
type TestDriveHarness,
@@ -86,6 +88,32 @@ afterEach(() => {
});
describe("test-drive data isolation", () => {
it.each([false, true])("reuses the saved port, with occupied=%s", async (occupied) => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "paperclip-test-drive-port-"));
cleanupDirectories.push(root);
const listener = createServer();
await new Promise<void>((resolve) => listener.listen(0, "127.0.0.1", resolve));
const savedPort = (listener.address() as { port: number }).port;
const close = () => new Promise<void>((resolve, reject) => listener.close(error => error ? reject(error) : resolve()));
if (!occupied) await close();
const configPath = path.join(root, "instances", "default", "config.json");
fs.mkdirSync(path.dirname(configPath), { recursive: true });
fs.writeFileSync(configPath, JSON.stringify({
$meta: { version: 1, updatedAt: new Date().toISOString(), source: "onboard" },
database: {}, logging: { mode: "file" }, telemetry: { enabled: false },
server: { port: savedPort },
}));
try {
await prepareTestDriveEnvironment({ dataDir: root }, os.tmpdir());
if (occupied) { expect(Number(process.env.PORT)).not.toBe(savedPort); expect(Number(process.env.PORT)).toBeGreaterThanOrEqual(3100); }
else expect(Number(process.env.PORT)).toBe(savedPort);
expect(JSON.parse(fs.readFileSync(configPath, "utf8")).server.port).toBe(savedPort);
expect(process.env.HOST).toBe("127.0.0.1");
} finally {
if (occupied) await close();
}
});
it("creates unique retained OS temporary directories and reports absolute paths", () => {
const first = resolveTestDriveDataDir();
const second = resolveTestDriveDataDir();
@@ -600,3 +628,16 @@ describe("test-drive foreground lifecycle", () => {
expect(openBrowser).not.toHaveBeenCalled();
});
});
describe("test drive port recovery", () => {
it("falls back from an occupied saved port at the top of the range", async () => {
const available = vi.fn(async (port: number) => port === 3101);
await expect(resolveTestDriveServerPort(65535, available)).resolves.toBe(3101);
expect(available.mock.calls.map(([port]) => port)).toEqual([65535, 3100, 3101]);
});
it("preserves a free saved port before searching the default range", async () => {
const available = vi.fn(async () => true);
await expect(resolveTestDriveServerPort(3110, available)).resolves.toBe(3110);
expect(available).toHaveBeenCalledTimes(1);
});
});
+9 -1
View File
@@ -59,6 +59,7 @@ interface IssueCreateOptions extends BaseClientOptions {
projectId?: string;
goalId?: string;
parentId?: string;
standalone?: boolean;
requestDepth?: string;
billingCode?: string;
}
@@ -286,10 +287,17 @@ export function registerIssueCommands(program: Command): void {
.option("--project-id <id>", "Project ID")
.option("--goal-id <id>", "Goal ID")
.option("--parent-id <id>", "Parent issue ID")
.option(
"--standalone",
"Create a top-level issue. Without --parent-id, an agent run's issue otherwise becomes the parent.",
)
.option("--request-depth <n>", "Request depth integer")
.option("--billing-code <code>", "Billing code")
.action(async (opts: IssueCreateOptions) => {
try {
if (opts.standalone && opts.parentId) {
throw new Error("--standalone cannot be combined with --parent-id");
}
const ctx = resolveCommandContext(opts, { requireCompany: true });
const payload = createIssueSchema.parse({
title: opts.title,
@@ -299,7 +307,7 @@ export function registerIssueCommands(program: Command): void {
assigneeAgentId: opts.assigneeAgentId,
projectId: opts.projectId,
goalId: opts.goalId,
parentId: opts.parentId,
parentId: opts.standalone ? null : opts.parentId,
requestDepth: parseOptionalInt(opts.requestDepth),
billingCode: opts.billingCode,
});
+16 -5
View File
@@ -150,11 +150,14 @@ async function loopbackPortAvailable(port: number): Promise<boolean> {
});
}
export async function resolveTestDriveServerPort(preferredPort = 3100): Promise<number> {
for (let port = preferredPort; port <= 65_535; port += 1) {
if (await loopbackPortAvailable(port)) return port;
export async function resolveTestDriveServerPort(preferredPort = 3100, available = loopbackPortAvailable): Promise<number> {
if (await available(preferredPort)) return preferredPort;
// A saved high port can be occupied. Search the ordinary range instead of
// trapping the restarted test drive at the top of the TCP port space.
for (let port = 3100; port <= 65_535; port += 1) {
if (port !== preferredPort && await available(port)) return port;
}
throw new Error(`No available loopback port found at or above ${preferredPort}.`);
throw new Error("No available loopback port found at or above 3100.");
}
/**
@@ -194,7 +197,15 @@ export async function prepareTestDriveEnvironment(
process.env.PAPERCLIP_DEPLOYMENT_EXPOSURE = "private";
process.env.PAPERCLIP_BIND = "loopback";
process.env.HOST = "127.0.0.1";
process.env.PORT = String(await resolveTestDriveServerPort());
let preferredPort = 3100;
try {
preferredPort = readConfig(process.env.PAPERCLIP_CONFIG)?.server.port ?? preferredPort;
} catch {
// The normal startup doctor owns invalid-config diagnostics and repair.
}
// Reusing a test drive must preserve callback and enrollment origins when
// its saved port is available. Only move forward if that port is occupied.
process.env.PORT = String(await resolveTestDriveServerPort(preferredPort));
return { dataDir, linkedWorktree };
}