Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,3 @@ jobs:
if: always()
- run: bun pm pack
if: always()
- run: bun run compile
if: always()
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,9 @@ jobs:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
compile-binaries:
uses: ./.github/workflows/compile.yml
permissions:
contents: read
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
44 changes: 44 additions & 0 deletions .github/workflows/compile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Compiles native binaries for each platform and verifies they run.
name: compile-binaries
on:
workflow_call:
inputs:
ref:
required: true
type: string

jobs:
smoke-test:
name: Compile (${{ matrix.name }})
runs-on: ${{ fromJSON(matrix.runner) }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- name: Linux
runner: '["codebuild-agentcore-e2e-${{ github.run_id }}-${{ github.run_attempt }}"]'
target: linux-x64
binary: ./dist/bin/agentcore-linux-x64
- name: Windows
runner: '["codebuild-agentcore-e2e-${{ github.run_id }}-${{ github.run_attempt }}", "image:windows-1.0"]'
target: windows-x64
binary: ./dist/bin/agentcore-windows-x64.exe
# CodeBuild does not support macOS.
- name: macOS
runner: '["macos-latest"]'
target: darwin-arm64
binary: ./dist/bin/agentcore-darwin-arm64

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would a simpler way of doing all this just be to call make? Bun can cross-compile.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to do it individually since the matrix is parallelizing across runners so each runner needs its own binary.

steps:
- uses: actions/checkout@v7
with:
ref: ${{ inputs.ref }}
persist-credentials: false
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- name: Compile binary
run: bun run compile:${{ matrix.target }}
- name: Verify binary runs
run: |
${{ matrix.binary }} --help
3 changes: 1 addition & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ process.exit(
// TODO: wire this id into telemetry as well
const cliSessionId = crypto.randomUUID();

const rootLogger = createFileLogger({
const rootLogger = await createFileLogger({
filePath: join(homedir(), ".agentcore", "logs", "output"),
// TODO: allow overriding via global settings
logLevel: LOG_LEVEL.DEBUG,
Expand Down Expand Up @@ -50,7 +50,6 @@ process.exit(
await rootHandler.route(argv);
} catch (e) {
const error = e instanceof Error ? e : new Error(String(e));
io.stderr.write(`${error.name}: ${error.message}\n`);
rootLogger
.child({ errorName: error.name, errorMessage: error.message, stack: error.stack ?? "" })
.error();
Expand Down
45 changes: 25 additions & 20 deletions src/logging/fileLogger.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pino from "pino";
import pinoRoll from "pino-roll";
import { type AsyncLogger, type LoggerBindings, type LogLevel } from "./types";

export interface FileLoggerConfig {
Expand Down Expand Up @@ -32,31 +33,35 @@ function wrapPinoLogger(pinoLogger: pino.Logger): AsyncLogger {
* @param config - Logger configuration (file path, rotation limits, level).
* @returns A {@link AsyncLogger} that writes to a rotating file via pino.
*/
export function createFileLogger(config: FileLoggerConfig): AsyncLogger {
export async function createFileLogger(config: FileLoggerConfig): Promise<AsyncLogger> {
const maxSizeInMB = config.maxSizeInMB ?? 10;
const maxFileCount = config.maxFileCount ?? 5;
const bindings = config.bindings ?? {};

// setup logging stream in the same thread as main execution to avoid separate worker thread.
// separate worker thread attempts to import pino-roll at runtime, which fails when run as an executable.
const stream = await pinoRoll({
extension: ".log",
dateFormat: "yyyy-MM-dd'T'HH-mm-ss",
// Rotate when file reaches {maxSizeInMB} MB, and start deleting once we have {maxFileCount} files
size: `${maxSizeInMB}m`,
limit: { count: maxFileCount },
file: config.filePath,
mkdir: true,
});

return wrapPinoLogger(
pino({
level: config.logLevel,
base: undefined, // omit pid and hostname
formatters: {
level(label) {
return { level: label };
},
},
transport: {
target: "pino-roll",
options: {
extension: ".log",
dateFormat: "yyyy-MM-dd'T'HH-mm-ss",
// Rotate when file reaches {maxSizeInMB} MB, and start deleting once we have {maxFileCount} files
size: `${maxSizeInMB}m`,
limit: { count: maxFileCount },
file: config.filePath,
mkdir: true,
pino(
{
level: config.logLevel,
base: undefined, // omit pid and hostname
formatters: {
level(label) {
return { level: label };
},
},
},
}),
stream,
),
).child(bindings);
}
34 changes: 34 additions & 0 deletions src/logging/pinoRoll.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Type declarations for pino-roll, which does not ship its own types.
// Source of truth: https://github.com/mcollina/pino-roll/blob/master/pino-roll.js
declare module "pino-roll" {
import type { SonicBoom, SonicBoomOpts } from "sonic-boom";

interface LimitOptions {
count?: number;
}

interface PinoRollOptions extends Omit<SonicBoomOpts, "dest"> {
/** Absolute or relative path to the log file. */
file: string;
/** Maximum size before rotation (e.g. "10m", "1g"). */
size?: string | number;
/** Rotation frequency ("daily", "hourly", or milliseconds). */
frequency?: string | number;
/** File extension appended after the number (e.g. ".log"). */
extension?: string;
/** Whether to create a symlink to the current log file. */
symlink?: boolean;
/** Date format string appended to the file name. */
dateFormat?: string;
/** Strategy for removing old log files. */
limit?: LimitOptions;
/** Create parent directories if they don't exist. */
mkdir?: boolean;
}

/**
* Creates a Pino transport (a SonicBoom stream) that writes to files
* and automatically rotates based on size, frequency, or both.
*/
export default function pinoRoll(options: PinoRollOptions): Promise<SonicBoom>;
}
2 changes: 1 addition & 1 deletion src/middleware/withLogging.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe("withLogging", () => {

beforeEach(async () => {
tempDir = await mkdtemp(join(tmpdir(), "logging-test-"));
logger = createFileLogger({
logger = await createFileLogger({
filePath: join(tempDir, "output"),
logLevel: LOG_LEVEL.DEBUG,
});
Expand Down
4 changes: 3 additions & 1 deletion src/runnable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export async function runWithExitCode(
try {
await fn(argv);
return ExitCode.SUCCESS;
} catch {
} catch (e) {
const error = e instanceof Error ? e : new Error(String(e));
console.error(`${error.name}: ${error.message}`);
return ExitCode.FAILURE;
}
}
3 changes: 1 addition & 2 deletions src/testing/logging.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ export async function assertLogsMatch(
): Promise<void> {
let lastResults: ReturnType<typeof evaluateQueries> = [];

// pino-roll writes via async worker threads, so logs may not be flushed to
// disk immediately. Poll until all query conditions are satisfied.
// Poll until all query conditions are satisfied.
try {
await waitFor(async () => {
const content = await readLogFile(dir);
Expand Down
Loading