add magenta log prefixes for delegated subagents (#387)
When delegate() runs multiple subagents in parallel, their logs interleave without visual distinction. Use AsyncLocalStorage to automatically prefix every log line inside runSubagent() with the task label in magenta (e.g. [frontend-review]). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
pullfrog[bot]
co-authored by
Cursor
parent
d5ab3706db
commit
1ed3da8273
@@ -135589,6 +135589,7 @@ var Effort = type.enumerated("mini", "auto", "max");
|
|||||||
// utils/log.ts
|
// utils/log.ts
|
||||||
var core = __toESM(require_core(), 1);
|
var core = __toESM(require_core(), 1);
|
||||||
var import_table = __toESM(require_src(), 1);
|
var import_table = __toESM(require_src(), 1);
|
||||||
|
import { AsyncLocalStorage } from "node:async_hooks";
|
||||||
|
|
||||||
// utils/globals.ts
|
// utils/globals.ts
|
||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
@@ -135597,6 +135598,23 @@ var isGitHubActions = !!process.env.GITHUB_ACTIONS;
|
|||||||
var isInsideDocker = existsSync("/.dockerenv");
|
var isInsideDocker = existsSync("/.dockerenv");
|
||||||
|
|
||||||
// utils/log.ts
|
// utils/log.ts
|
||||||
|
var logContext = new AsyncLocalStorage();
|
||||||
|
var MAGENTA = "\x1B[35m";
|
||||||
|
var RESET = "\x1B[0m";
|
||||||
|
function withLogPrefix(prefix, fn2) {
|
||||||
|
return logContext.run({ prefix }, fn2);
|
||||||
|
}
|
||||||
|
function prefixLines(message) {
|
||||||
|
const ctx = logContext.getStore();
|
||||||
|
if (!ctx) return message;
|
||||||
|
const colored = `${MAGENTA}${ctx.prefix}${RESET} `;
|
||||||
|
return message.split("\n").map((line) => `${colored}${line}`).join("\n");
|
||||||
|
}
|
||||||
|
function prefixPlain(name) {
|
||||||
|
const ctx = logContext.getStore();
|
||||||
|
if (!ctx) return name;
|
||||||
|
return `${ctx.prefix} ${name}`;
|
||||||
|
}
|
||||||
var isRunnerDebugEnabled = () => core.isDebug();
|
var isRunnerDebugEnabled = () => core.isDebug();
|
||||||
var isLocalDebugEnabled = () => process.env.LOG_LEVEL === "debug" || process.env.ACTIONS_STEP_DEBUG === "true";
|
var isLocalDebugEnabled = () => process.env.LOG_LEVEL === "debug" || process.env.ACTIONS_STEP_DEBUG === "true";
|
||||||
var isDebugEnabled = () => isLocalDebugEnabled() || isRunnerDebugEnabled();
|
var isDebugEnabled = () => isLocalDebugEnabled() || isRunnerDebugEnabled();
|
||||||
@@ -135612,10 +135630,11 @@ ${arg.stack}`;
|
|||||||
}).join(" ");
|
}).join(" ");
|
||||||
}
|
}
|
||||||
function startGroup2(name) {
|
function startGroup2(name) {
|
||||||
|
const prefixed = prefixPlain(name);
|
||||||
if (isGitHubActions) {
|
if (isGitHubActions) {
|
||||||
core.startGroup(name);
|
core.startGroup(prefixed);
|
||||||
} else {
|
} else {
|
||||||
console.group(name);
|
console.group(prefixed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function endGroup2() {
|
function endGroup2() {
|
||||||
@@ -135688,7 +135707,7 @@ function boxString(text, options) {
|
|||||||
}
|
}
|
||||||
function box(text, options) {
|
function box(text, options) {
|
||||||
const boxContent = boxString(text, options);
|
const boxContent = boxString(text, options);
|
||||||
core.info(boxContent);
|
core.info(prefixLines(boxContent));
|
||||||
}
|
}
|
||||||
async function writeSummary(text) {
|
async function writeSummary(text) {
|
||||||
if (!isGitHubActions) return;
|
if (!isGitHubActions) return;
|
||||||
@@ -135708,42 +135727,42 @@ function printTable(rows, options) {
|
|||||||
);
|
);
|
||||||
const formatted = (0, import_table.table)(tableData);
|
const formatted = (0, import_table.table)(tableData);
|
||||||
if (title) {
|
if (title) {
|
||||||
core.info(`
|
core.info(prefixLines(`
|
||||||
${title}`);
|
${title}`));
|
||||||
}
|
}
|
||||||
core.info(`
|
core.info(prefixLines(`
|
||||||
${formatted}
|
${formatted}
|
||||||
`);
|
`));
|
||||||
}
|
}
|
||||||
function separator(length = 50) {
|
function separator(length = 50) {
|
||||||
const separatorText = "\u2500".repeat(length);
|
const separatorText = "\u2500".repeat(length);
|
||||||
core.info(separatorText);
|
core.info(prefixLines(separatorText));
|
||||||
}
|
}
|
||||||
var log = {
|
var log = {
|
||||||
/** Print info message */
|
/** Print info message */
|
||||||
info: (...args2) => {
|
info: (...args2) => {
|
||||||
core.info(`${ts()}${formatArgs(args2)}`);
|
core.info(prefixLines(`${ts()}${formatArgs(args2)}`));
|
||||||
},
|
},
|
||||||
/** Print a warning message. Use only for warnings that should be displayed in the job summary. */
|
/** Print a warning message. Use only for warnings that should be displayed in the job summary. */
|
||||||
warning: (...args2) => {
|
warning: (...args2) => {
|
||||||
core.warning(`${ts()}${formatArgs(args2)}`);
|
core.warning(prefixLines(`${ts()}${formatArgs(args2)}`));
|
||||||
},
|
},
|
||||||
/** Print an error message. Use only for errors that should be displayed in the job summary. */
|
/** Print an error message. Use only for errors that should be displayed in the job summary. */
|
||||||
error: (...args2) => {
|
error: (...args2) => {
|
||||||
core.error(`${ts()}${formatArgs(args2)}`);
|
core.error(prefixLines(`${ts()}${formatArgs(args2)}`));
|
||||||
},
|
},
|
||||||
/** Print success message */
|
/** Print success message */
|
||||||
success: (...args2) => {
|
success: (...args2) => {
|
||||||
core.info(`${ts()}\xBB ${formatArgs(args2)}`);
|
core.info(prefixLines(`${ts()}\xBB ${formatArgs(args2)}`));
|
||||||
},
|
},
|
||||||
/** Print debug message (only when debug mode is enabled) */
|
/** Print debug message (only when debug mode is enabled) */
|
||||||
debug: (...args2) => {
|
debug: (...args2) => {
|
||||||
if (isRunnerDebugEnabled()) {
|
if (isRunnerDebugEnabled()) {
|
||||||
core.debug(formatArgs(args2));
|
core.debug(prefixLines(formatArgs(args2)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isLocalDebugEnabled()) {
|
if (isLocalDebugEnabled()) {
|
||||||
core.info(`${ts()}[DEBUG] ${formatArgs(args2)}`);
|
core.info(prefixLines(`${ts()}[DEBUG] ${formatArgs(args2)}`));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/** Print a formatted box with text */
|
/** Print a formatted box with text */
|
||||||
@@ -136443,41 +136462,43 @@ ${params.instructions}`;
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
async function runSubagent(params) {
|
async function runSubagent(params) {
|
||||||
params.subagent.keepAliveInterval = setInterval(markActivity, 3e4);
|
return withLogPrefix(`[${params.subagent.label}]`, async () => {
|
||||||
const mcpServer = await startSubagentMcpServer({
|
params.subagent.keepAliveInterval = setInterval(markActivity, 3e4);
|
||||||
ctx: params.ctx,
|
const mcpServer = await startSubagentMcpServer({
|
||||||
subagentId: params.subagent.id
|
|
||||||
});
|
|
||||||
const subagentTmpdir = join(params.ctx.tmpdir, params.subagent.id);
|
|
||||||
mkdirSync(subagentTmpdir, { recursive: true });
|
|
||||||
try {
|
|
||||||
const subagentPayload = { ...params.ctx.payload, effort: params.effort };
|
|
||||||
const subagentInstructions = buildSubagentInstructions({
|
|
||||||
ctx: params.ctx,
|
ctx: params.ctx,
|
||||||
label: params.subagent.label,
|
subagentId: params.subagent.id
|
||||||
instructions: params.instructions
|
|
||||||
});
|
});
|
||||||
const result = await params.ctx.agent.run({
|
const subagentTmpdir = join(params.ctx.tmpdir, params.subagent.id);
|
||||||
payload: subagentPayload,
|
mkdirSync(subagentTmpdir, { recursive: true });
|
||||||
mcpServerUrl: mcpServer.url,
|
|
||||||
tmpdir: subagentTmpdir,
|
|
||||||
instructions: subagentInstructions
|
|
||||||
});
|
|
||||||
params.subagent.usage = result.usage;
|
|
||||||
writeFileSync(params.subagent.stdoutFilePath, result.output ?? "", "utf-8");
|
|
||||||
completeSubagent({ ctx: params.ctx, subagent: params.subagent, success: result.success });
|
|
||||||
return { success: result.success, error: result.error };
|
|
||||||
} catch (err) {
|
|
||||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
||||||
try {
|
try {
|
||||||
writeFileSync(params.subagent.stdoutFilePath, "", "utf-8");
|
const subagentPayload = { ...params.ctx.payload, effort: params.effort };
|
||||||
} catch {
|
const subagentInstructions = buildSubagentInstructions({
|
||||||
|
ctx: params.ctx,
|
||||||
|
label: params.subagent.label,
|
||||||
|
instructions: params.instructions
|
||||||
|
});
|
||||||
|
const result = await params.ctx.agent.run({
|
||||||
|
payload: subagentPayload,
|
||||||
|
mcpServerUrl: mcpServer.url,
|
||||||
|
tmpdir: subagentTmpdir,
|
||||||
|
instructions: subagentInstructions
|
||||||
|
});
|
||||||
|
params.subagent.usage = result.usage;
|
||||||
|
writeFileSync(params.subagent.stdoutFilePath, result.output ?? "", "utf-8");
|
||||||
|
completeSubagent({ ctx: params.ctx, subagent: params.subagent, success: result.success });
|
||||||
|
return { success: result.success, error: result.error };
|
||||||
|
} catch (err) {
|
||||||
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||||
|
try {
|
||||||
|
writeFileSync(params.subagent.stdoutFilePath, "", "utf-8");
|
||||||
|
} catch {
|
||||||
|
}
|
||||||
|
completeSubagent({ ctx: params.ctx, subagent: params.subagent, success: false });
|
||||||
|
return { success: false, error: errorMessage };
|
||||||
|
} finally {
|
||||||
|
await mcpServer.stop();
|
||||||
}
|
}
|
||||||
completeSubagent({ ctx: params.ctx, subagent: params.subagent, success: false });
|
});
|
||||||
return { success: false, error: errorMessage };
|
|
||||||
} finally {
|
|
||||||
await mcpServer.stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// mcp/askQuestion.ts
|
// mcp/askQuestion.ts
|
||||||
|
|||||||
@@ -25507,6 +25507,7 @@ var core3 = __toESM(require_core(), 1);
|
|||||||
// utils/log.ts
|
// utils/log.ts
|
||||||
var core = __toESM(require_core(), 1);
|
var core = __toESM(require_core(), 1);
|
||||||
var import_table = __toESM(require_src(), 1);
|
var import_table = __toESM(require_src(), 1);
|
||||||
|
import { AsyncLocalStorage } from "node:async_hooks";
|
||||||
|
|
||||||
// utils/globals.ts
|
// utils/globals.ts
|
||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
@@ -25515,6 +25516,20 @@ var isGitHubActions = !!process.env.GITHUB_ACTIONS;
|
|||||||
var isInsideDocker = existsSync("/.dockerenv");
|
var isInsideDocker = existsSync("/.dockerenv");
|
||||||
|
|
||||||
// utils/log.ts
|
// utils/log.ts
|
||||||
|
var logContext = new AsyncLocalStorage();
|
||||||
|
var MAGENTA = "\x1B[35m";
|
||||||
|
var RESET = "\x1B[0m";
|
||||||
|
function prefixLines(message) {
|
||||||
|
const ctx = logContext.getStore();
|
||||||
|
if (!ctx) return message;
|
||||||
|
const colored = `${MAGENTA}${ctx.prefix}${RESET} `;
|
||||||
|
return message.split("\n").map((line) => `${colored}${line}`).join("\n");
|
||||||
|
}
|
||||||
|
function prefixPlain(name) {
|
||||||
|
const ctx = logContext.getStore();
|
||||||
|
if (!ctx) return name;
|
||||||
|
return `${ctx.prefix} ${name}`;
|
||||||
|
}
|
||||||
var isRunnerDebugEnabled = () => core.isDebug();
|
var isRunnerDebugEnabled = () => core.isDebug();
|
||||||
var isLocalDebugEnabled = () => process.env.LOG_LEVEL === "debug" || process.env.ACTIONS_STEP_DEBUG === "true";
|
var isLocalDebugEnabled = () => process.env.LOG_LEVEL === "debug" || process.env.ACTIONS_STEP_DEBUG === "true";
|
||||||
var isDebugEnabled = () => isLocalDebugEnabled() || isRunnerDebugEnabled();
|
var isDebugEnabled = () => isLocalDebugEnabled() || isRunnerDebugEnabled();
|
||||||
@@ -25530,10 +25545,11 @@ ${arg.stack}`;
|
|||||||
}).join(" ");
|
}).join(" ");
|
||||||
}
|
}
|
||||||
function startGroup2(name) {
|
function startGroup2(name) {
|
||||||
|
const prefixed = prefixPlain(name);
|
||||||
if (isGitHubActions) {
|
if (isGitHubActions) {
|
||||||
core.startGroup(name);
|
core.startGroup(prefixed);
|
||||||
} else {
|
} else {
|
||||||
console.group(name);
|
console.group(prefixed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function endGroup2() {
|
function endGroup2() {
|
||||||
@@ -25606,7 +25622,7 @@ function boxString(text, options) {
|
|||||||
}
|
}
|
||||||
function box(text, options) {
|
function box(text, options) {
|
||||||
const boxContent = boxString(text, options);
|
const boxContent = boxString(text, options);
|
||||||
core.info(boxContent);
|
core.info(prefixLines(boxContent));
|
||||||
}
|
}
|
||||||
function printTable(rows, options) {
|
function printTable(rows, options) {
|
||||||
const { title } = options || {};
|
const { title } = options || {};
|
||||||
@@ -25620,42 +25636,42 @@ function printTable(rows, options) {
|
|||||||
);
|
);
|
||||||
const formatted = (0, import_table.table)(tableData);
|
const formatted = (0, import_table.table)(tableData);
|
||||||
if (title) {
|
if (title) {
|
||||||
core.info(`
|
core.info(prefixLines(`
|
||||||
${title}`);
|
${title}`));
|
||||||
}
|
}
|
||||||
core.info(`
|
core.info(prefixLines(`
|
||||||
${formatted}
|
${formatted}
|
||||||
`);
|
`));
|
||||||
}
|
}
|
||||||
function separator(length = 50) {
|
function separator(length = 50) {
|
||||||
const separatorText = "\u2500".repeat(length);
|
const separatorText = "\u2500".repeat(length);
|
||||||
core.info(separatorText);
|
core.info(prefixLines(separatorText));
|
||||||
}
|
}
|
||||||
var log = {
|
var log = {
|
||||||
/** Print info message */
|
/** Print info message */
|
||||||
info: (...args) => {
|
info: (...args) => {
|
||||||
core.info(`${ts()}${formatArgs(args)}`);
|
core.info(prefixLines(`${ts()}${formatArgs(args)}`));
|
||||||
},
|
},
|
||||||
/** Print a warning message. Use only for warnings that should be displayed in the job summary. */
|
/** Print a warning message. Use only for warnings that should be displayed in the job summary. */
|
||||||
warning: (...args) => {
|
warning: (...args) => {
|
||||||
core.warning(`${ts()}${formatArgs(args)}`);
|
core.warning(prefixLines(`${ts()}${formatArgs(args)}`));
|
||||||
},
|
},
|
||||||
/** Print an error message. Use only for errors that should be displayed in the job summary. */
|
/** Print an error message. Use only for errors that should be displayed in the job summary. */
|
||||||
error: (...args) => {
|
error: (...args) => {
|
||||||
core.error(`${ts()}${formatArgs(args)}`);
|
core.error(prefixLines(`${ts()}${formatArgs(args)}`));
|
||||||
},
|
},
|
||||||
/** Print success message */
|
/** Print success message */
|
||||||
success: (...args) => {
|
success: (...args) => {
|
||||||
core.info(`${ts()}\xBB ${formatArgs(args)}`);
|
core.info(prefixLines(`${ts()}\xBB ${formatArgs(args)}`));
|
||||||
},
|
},
|
||||||
/** Print debug message (only when debug mode is enabled) */
|
/** Print debug message (only when debug mode is enabled) */
|
||||||
debug: (...args) => {
|
debug: (...args) => {
|
||||||
if (isRunnerDebugEnabled()) {
|
if (isRunnerDebugEnabled()) {
|
||||||
core.debug(formatArgs(args));
|
core.debug(prefixLines(formatArgs(args)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isLocalDebugEnabled()) {
|
if (isLocalDebugEnabled()) {
|
||||||
core.info(`${ts()}[DEBUG] ${formatArgs(args)}`);
|
core.info(prefixLines(`${ts()}[DEBUG] ${formatArgs(args)}`));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/** Print a formatted box with text */
|
/** Print a formatted box with text */
|
||||||
|
|||||||
+36
-33
@@ -6,6 +6,7 @@ import type { Effort } from "../external.ts";
|
|||||||
import { ghPullfrogMcpName } from "../external.ts";
|
import { ghPullfrogMcpName } from "../external.ts";
|
||||||
import { markActivity } from "../utils/activity.ts";
|
import { markActivity } from "../utils/activity.ts";
|
||||||
import type { ResolvedInstructions } from "../utils/instructions.ts";
|
import type { ResolvedInstructions } from "../utils/instructions.ts";
|
||||||
|
import { withLogPrefix } from "../utils/log.ts";
|
||||||
import { type SubagentState, startSubagentMcpServer, type ToolContext } from "./server.ts";
|
import { type SubagentState, startSubagentMcpServer, type ToolContext } from "./server.ts";
|
||||||
|
|
||||||
type CreateSubagentParams = {
|
type CreateSubagentParams = {
|
||||||
@@ -132,41 +133,43 @@ type RunSubagentResult = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function runSubagent(params: RunSubagentParams): Promise<RunSubagentResult> {
|
export async function runSubagent(params: RunSubagentParams): Promise<RunSubagentResult> {
|
||||||
params.subagent.keepAliveInterval = setInterval(markActivity, 30_000);
|
return withLogPrefix(`[${params.subagent.label}]`, async () => {
|
||||||
const mcpServer = await startSubagentMcpServer({
|
params.subagent.keepAliveInterval = setInterval(markActivity, 30_000);
|
||||||
ctx: params.ctx,
|
const mcpServer = await startSubagentMcpServer({
|
||||||
subagentId: params.subagent.id,
|
|
||||||
});
|
|
||||||
// each subagent gets its own tmpdir so parallel agents don't clobber config files
|
|
||||||
const subagentTmpdir = join(params.ctx.tmpdir, params.subagent.id);
|
|
||||||
mkdirSync(subagentTmpdir, { recursive: true });
|
|
||||||
try {
|
|
||||||
const subagentPayload = { ...params.ctx.payload, effort: params.effort };
|
|
||||||
const subagentInstructions = buildSubagentInstructions({
|
|
||||||
ctx: params.ctx,
|
ctx: params.ctx,
|
||||||
label: params.subagent.label,
|
subagentId: params.subagent.id,
|
||||||
instructions: params.instructions,
|
|
||||||
});
|
});
|
||||||
const result = await params.ctx.agent.run({
|
// each subagent gets its own tmpdir so parallel agents don't clobber config files
|
||||||
payload: subagentPayload,
|
const subagentTmpdir = join(params.ctx.tmpdir, params.subagent.id);
|
||||||
mcpServerUrl: mcpServer.url,
|
mkdirSync(subagentTmpdir, { recursive: true });
|
||||||
tmpdir: subagentTmpdir,
|
|
||||||
instructions: subagentInstructions,
|
|
||||||
});
|
|
||||||
params.subagent.usage = result.usage;
|
|
||||||
writeFileSync(params.subagent.stdoutFilePath, result.output ?? "", "utf-8");
|
|
||||||
completeSubagent({ ctx: params.ctx, subagent: params.subagent, success: result.success });
|
|
||||||
return { success: result.success, error: result.error };
|
|
||||||
} catch (err) {
|
|
||||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
||||||
try {
|
try {
|
||||||
writeFileSync(params.subagent.stdoutFilePath, "", "utf-8");
|
const subagentPayload = { ...params.ctx.payload, effort: params.effort };
|
||||||
} catch {
|
const subagentInstructions = buildSubagentInstructions({
|
||||||
// best-effort
|
ctx: params.ctx,
|
||||||
|
label: params.subagent.label,
|
||||||
|
instructions: params.instructions,
|
||||||
|
});
|
||||||
|
const result = await params.ctx.agent.run({
|
||||||
|
payload: subagentPayload,
|
||||||
|
mcpServerUrl: mcpServer.url,
|
||||||
|
tmpdir: subagentTmpdir,
|
||||||
|
instructions: subagentInstructions,
|
||||||
|
});
|
||||||
|
params.subagent.usage = result.usage;
|
||||||
|
writeFileSync(params.subagent.stdoutFilePath, result.output ?? "", "utf-8");
|
||||||
|
completeSubagent({ ctx: params.ctx, subagent: params.subagent, success: result.success });
|
||||||
|
return { success: result.success, error: result.error };
|
||||||
|
} catch (err) {
|
||||||
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||||
|
try {
|
||||||
|
writeFileSync(params.subagent.stdoutFilePath, "", "utf-8");
|
||||||
|
} catch {
|
||||||
|
// best-effort
|
||||||
|
}
|
||||||
|
completeSubagent({ ctx: params.ctx, subagent: params.subagent, success: false });
|
||||||
|
return { success: false, error: errorMessage };
|
||||||
|
} finally {
|
||||||
|
await mcpServer.stop();
|
||||||
}
|
}
|
||||||
completeSubagent({ ctx: params.ctx, subagent: params.subagent, success: false });
|
});
|
||||||
return { success: false, error: errorMessage };
|
|
||||||
} finally {
|
|
||||||
await mcpServer.stop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28846,6 +28846,7 @@ var require_semver2 = __commonJS({
|
|||||||
// utils/log.ts
|
// utils/log.ts
|
||||||
var core = __toESM(require_core(), 1);
|
var core = __toESM(require_core(), 1);
|
||||||
var import_table = __toESM(require_src(), 1);
|
var import_table = __toESM(require_src(), 1);
|
||||||
|
import { AsyncLocalStorage } from "node:async_hooks";
|
||||||
|
|
||||||
// utils/globals.ts
|
// utils/globals.ts
|
||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
@@ -28854,6 +28855,20 @@ var isGitHubActions = !!process.env.GITHUB_ACTIONS;
|
|||||||
var isInsideDocker = existsSync("/.dockerenv");
|
var isInsideDocker = existsSync("/.dockerenv");
|
||||||
|
|
||||||
// utils/log.ts
|
// utils/log.ts
|
||||||
|
var logContext = new AsyncLocalStorage();
|
||||||
|
var MAGENTA = "\x1B[35m";
|
||||||
|
var RESET = "\x1B[0m";
|
||||||
|
function prefixLines(message) {
|
||||||
|
const ctx = logContext.getStore();
|
||||||
|
if (!ctx) return message;
|
||||||
|
const colored = `${MAGENTA}${ctx.prefix}${RESET} `;
|
||||||
|
return message.split("\n").map((line) => `${colored}${line}`).join("\n");
|
||||||
|
}
|
||||||
|
function prefixPlain(name) {
|
||||||
|
const ctx = logContext.getStore();
|
||||||
|
if (!ctx) return name;
|
||||||
|
return `${ctx.prefix} ${name}`;
|
||||||
|
}
|
||||||
var isRunnerDebugEnabled = () => core.isDebug();
|
var isRunnerDebugEnabled = () => core.isDebug();
|
||||||
var isLocalDebugEnabled = () => process.env.LOG_LEVEL === "debug" || process.env.ACTIONS_STEP_DEBUG === "true";
|
var isLocalDebugEnabled = () => process.env.LOG_LEVEL === "debug" || process.env.ACTIONS_STEP_DEBUG === "true";
|
||||||
var isDebugEnabled = () => isLocalDebugEnabled() || isRunnerDebugEnabled();
|
var isDebugEnabled = () => isLocalDebugEnabled() || isRunnerDebugEnabled();
|
||||||
@@ -28869,10 +28884,11 @@ ${arg.stack}`;
|
|||||||
}).join(" ");
|
}).join(" ");
|
||||||
}
|
}
|
||||||
function startGroup2(name) {
|
function startGroup2(name) {
|
||||||
|
const prefixed = prefixPlain(name);
|
||||||
if (isGitHubActions) {
|
if (isGitHubActions) {
|
||||||
core.startGroup(name);
|
core.startGroup(prefixed);
|
||||||
} else {
|
} else {
|
||||||
console.group(name);
|
console.group(prefixed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function endGroup2() {
|
function endGroup2() {
|
||||||
@@ -28945,7 +28961,7 @@ function boxString(text, options) {
|
|||||||
}
|
}
|
||||||
function box(text, options) {
|
function box(text, options) {
|
||||||
const boxContent = boxString(text, options);
|
const boxContent = boxString(text, options);
|
||||||
core.info(boxContent);
|
core.info(prefixLines(boxContent));
|
||||||
}
|
}
|
||||||
function printTable(rows, options) {
|
function printTable(rows, options) {
|
||||||
const { title } = options || {};
|
const { title } = options || {};
|
||||||
@@ -28959,42 +28975,42 @@ function printTable(rows, options) {
|
|||||||
);
|
);
|
||||||
const formatted = (0, import_table.table)(tableData);
|
const formatted = (0, import_table.table)(tableData);
|
||||||
if (title) {
|
if (title) {
|
||||||
core.info(`
|
core.info(prefixLines(`
|
||||||
${title}`);
|
${title}`));
|
||||||
}
|
}
|
||||||
core.info(`
|
core.info(prefixLines(`
|
||||||
${formatted}
|
${formatted}
|
||||||
`);
|
`));
|
||||||
}
|
}
|
||||||
function separator(length = 50) {
|
function separator(length = 50) {
|
||||||
const separatorText = "\u2500".repeat(length);
|
const separatorText = "\u2500".repeat(length);
|
||||||
core.info(separatorText);
|
core.info(prefixLines(separatorText));
|
||||||
}
|
}
|
||||||
var log = {
|
var log = {
|
||||||
/** Print info message */
|
/** Print info message */
|
||||||
info: (...args2) => {
|
info: (...args2) => {
|
||||||
core.info(`${ts()}${formatArgs(args2)}`);
|
core.info(prefixLines(`${ts()}${formatArgs(args2)}`));
|
||||||
},
|
},
|
||||||
/** Print a warning message. Use only for warnings that should be displayed in the job summary. */
|
/** Print a warning message. Use only for warnings that should be displayed in the job summary. */
|
||||||
warning: (...args2) => {
|
warning: (...args2) => {
|
||||||
core.warning(`${ts()}${formatArgs(args2)}`);
|
core.warning(prefixLines(`${ts()}${formatArgs(args2)}`));
|
||||||
},
|
},
|
||||||
/** Print an error message. Use only for errors that should be displayed in the job summary. */
|
/** Print an error message. Use only for errors that should be displayed in the job summary. */
|
||||||
error: (...args2) => {
|
error: (...args2) => {
|
||||||
core.error(`${ts()}${formatArgs(args2)}`);
|
core.error(prefixLines(`${ts()}${formatArgs(args2)}`));
|
||||||
},
|
},
|
||||||
/** Print success message */
|
/** Print success message */
|
||||||
success: (...args2) => {
|
success: (...args2) => {
|
||||||
core.info(`${ts()}\xBB ${formatArgs(args2)}`);
|
core.info(prefixLines(`${ts()}\xBB ${formatArgs(args2)}`));
|
||||||
},
|
},
|
||||||
/** Print debug message (only when debug mode is enabled) */
|
/** Print debug message (only when debug mode is enabled) */
|
||||||
debug: (...args2) => {
|
debug: (...args2) => {
|
||||||
if (isRunnerDebugEnabled()) {
|
if (isRunnerDebugEnabled()) {
|
||||||
core.debug(formatArgs(args2));
|
core.debug(prefixLines(formatArgs(args2)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isLocalDebugEnabled()) {
|
if (isLocalDebugEnabled()) {
|
||||||
core.info(`${ts()}[DEBUG] ${formatArgs(args2)}`);
|
core.info(prefixLines(`${ts()}[DEBUG] ${formatArgs(args2)}`));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/** Print a formatted box with text */
|
/** Print a formatted box with text */
|
||||||
|
|||||||
+45
-12
@@ -2,11 +2,43 @@
|
|||||||
* Logging utilities that work well in both local and GitHub Actions environments
|
* Logging utilities that work well in both local and GitHub Actions environments
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { AsyncLocalStorage } from "node:async_hooks";
|
||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import { table } from "table";
|
import { table } from "table";
|
||||||
import type { AgentUsage } from "../agents/shared.ts";
|
import type { AgentUsage } from "../agents/shared.ts";
|
||||||
import { isGitHubActions, isInsideDocker } from "./globals.ts";
|
import { isGitHubActions, isInsideDocker } from "./globals.ts";
|
||||||
|
|
||||||
|
// --- subagent log prefix via AsyncLocalStorage ---
|
||||||
|
|
||||||
|
type LogContext = { prefix: string };
|
||||||
|
|
||||||
|
const logContext = new AsyncLocalStorage<LogContext>();
|
||||||
|
|
||||||
|
const MAGENTA = "\x1b[35m";
|
||||||
|
const RESET = "\x1b[0m";
|
||||||
|
|
||||||
|
/** run `fn` with every log line prefixed by `prefix` (e.g. "[task-label]") in magenta */
|
||||||
|
export function withLogPrefix<T>(prefix: string, fn: () => Promise<T>): Promise<T> {
|
||||||
|
return logContext.run({ prefix }, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function prefixLines(message: string): string {
|
||||||
|
const ctx = logContext.getStore();
|
||||||
|
if (!ctx) return message;
|
||||||
|
const colored = `${MAGENTA}${ctx.prefix}${RESET} `;
|
||||||
|
return message
|
||||||
|
.split("\n")
|
||||||
|
.map((line) => `${colored}${line}`)
|
||||||
|
.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** plain-text prefix (no ANSI) for GitHub Actions group names */
|
||||||
|
function prefixPlain(name: string): string {
|
||||||
|
const ctx = logContext.getStore();
|
||||||
|
if (!ctx) return name;
|
||||||
|
return `${ctx.prefix} ${name}`;
|
||||||
|
}
|
||||||
|
|
||||||
const isRunnerDebugEnabled = () => core.isDebug();
|
const isRunnerDebugEnabled = () => core.isDebug();
|
||||||
|
|
||||||
const isLocalDebugEnabled = () =>
|
const isLocalDebugEnabled = () =>
|
||||||
@@ -36,10 +68,11 @@ function formatArgs(args: unknown[]): string {
|
|||||||
* Start a collapsed group (GitHub Actions) or regular group (local)
|
* Start a collapsed group (GitHub Actions) or regular group (local)
|
||||||
*/
|
*/
|
||||||
function startGroup(name: string): void {
|
function startGroup(name: string): void {
|
||||||
|
const prefixed = prefixPlain(name);
|
||||||
if (isGitHubActions) {
|
if (isGitHubActions) {
|
||||||
core.startGroup(name);
|
core.startGroup(prefixed);
|
||||||
} else {
|
} else {
|
||||||
console.group(name);
|
console.group(prefixed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +186,7 @@ function box(
|
|||||||
}
|
}
|
||||||
): void {
|
): void {
|
||||||
const boxContent = boxString(text, options);
|
const boxContent = boxString(text, options);
|
||||||
core.info(boxContent);
|
core.info(prefixLines(boxContent));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -200,9 +233,9 @@ function printTable(
|
|||||||
const formatted = table(tableData);
|
const formatted = table(tableData);
|
||||||
|
|
||||||
if (title) {
|
if (title) {
|
||||||
core.info(`\n${title}`);
|
core.info(prefixLines(`\n${title}`));
|
||||||
}
|
}
|
||||||
core.info(`\n${formatted}\n`);
|
core.info(prefixLines(`\n${formatted}\n`));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -210,7 +243,7 @@ function printTable(
|
|||||||
*/
|
*/
|
||||||
function separator(length: number = 50): void {
|
function separator(length: number = 50): void {
|
||||||
const separatorText = "─".repeat(length);
|
const separatorText = "─".repeat(length);
|
||||||
core.info(separatorText);
|
core.info(prefixLines(separatorText));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -219,32 +252,32 @@ function separator(length: number = 50): void {
|
|||||||
export const log = {
|
export const log = {
|
||||||
/** Print info message */
|
/** Print info message */
|
||||||
info: (...args: unknown[]): void => {
|
info: (...args: unknown[]): void => {
|
||||||
core.info(`${ts()}${formatArgs(args)}`);
|
core.info(prefixLines(`${ts()}${formatArgs(args)}`));
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Print a warning message. Use only for warnings that should be displayed in the job summary. */
|
/** Print a warning message. Use only for warnings that should be displayed in the job summary. */
|
||||||
warning: (...args: unknown[]): void => {
|
warning: (...args: unknown[]): void => {
|
||||||
core.warning(`${ts()}${formatArgs(args)}`);
|
core.warning(prefixLines(`${ts()}${formatArgs(args)}`));
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Print an error message. Use only for errors that should be displayed in the job summary. */
|
/** Print an error message. Use only for errors that should be displayed in the job summary. */
|
||||||
error: (...args: unknown[]): void => {
|
error: (...args: unknown[]): void => {
|
||||||
core.error(`${ts()}${formatArgs(args)}`);
|
core.error(prefixLines(`${ts()}${formatArgs(args)}`));
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Print success message */
|
/** Print success message */
|
||||||
success: (...args: unknown[]): void => {
|
success: (...args: unknown[]): void => {
|
||||||
core.info(`${ts()}» ${formatArgs(args)}`);
|
core.info(prefixLines(`${ts()}» ${formatArgs(args)}`));
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Print debug message (only when debug mode is enabled) */
|
/** Print debug message (only when debug mode is enabled) */
|
||||||
debug: (...args: unknown[]): void => {
|
debug: (...args: unknown[]): void => {
|
||||||
if (isRunnerDebugEnabled()) {
|
if (isRunnerDebugEnabled()) {
|
||||||
core.debug(formatArgs(args));
|
core.debug(prefixLines(formatArgs(args)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isLocalDebugEnabled()) {
|
if (isLocalDebugEnabled()) {
|
||||||
core.info(`${ts()}[DEBUG] ${formatArgs(args)}`);
|
core.info(prefixLines(`${ts()}[DEBUG] ${formatArgs(args)}`));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user