Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 434458a068 | |||
| 193954fdd7 | |||
| ab2d762658 | |||
| 876663cd1a | |||
| b2badf6d16 | |||
| 05fb2065b2 | |||
| 2042a5bf98 | |||
| 12da2b770c | |||
| a26ada9839 | |||
| 1328894afd | |||
| 85731f8360 | |||
| 1922352d86 | |||
| c0f31415a3 | |||
| 706ce04895 | |||
| 09be8e3068 | |||
| c6c1210fa0 | |||
| 0368512b9e | |||
| 9fb6135fd2 | |||
| bb78e5f94b | |||
| c668578c6f | |||
| 7f1566d9c2 | |||
| dd482566c2 | |||
| 57029c32a3 | |||
| 757d336475 | |||
| d03debab4b | |||
| a05829f781 | |||
| c8ba7940e3 |
@@ -12,69 +12,13 @@ pnpm install
|
|||||||
|
|
||||||
# Test with default prompt
|
# Test with default prompt
|
||||||
npm run play # Run locally on your machine
|
npm run play # Run locally on your machine
|
||||||
npm run play -- --act # Run in Docker (simulates GitHub Actions)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Testing with play.ts
|
## Testing with play.ts
|
||||||
|
|
||||||
The `play.ts` script provides two ways to test the action:
|
|
||||||
|
|
||||||
### Local Mode (Default)
|
|
||||||
```bash
|
```bash
|
||||||
npm run play # Uses fixtures/play.txt
|
pnpm play # Uses fixtures/play.txt
|
||||||
npm run play fixtures/complex.txt # Custom prompt file
|
|
||||||
```
|
```
|
||||||
- Clones the scratch repository to `.temp`
|
- Clones the scratch repository to `.temp`
|
||||||
- Runs Claude Code directly on your machine
|
- Runs Claude Code directly on your machine
|
||||||
- Fast iteration for development
|
- Fast iteration for development
|
||||||
|
|
||||||
### Docker Mode (--act flag)
|
|
||||||
```bash
|
|
||||||
npm run play -- --act # Uses fixtures/play.txt
|
|
||||||
npm run play fixtures/simple.txt -- --act # Custom prompt file
|
|
||||||
```
|
|
||||||
- Builds fresh bundles with esbuild
|
|
||||||
- Creates minimal distribution without node_modules
|
|
||||||
- Runs in Docker container via `act`
|
|
||||||
- Simulates GitHub Actions environment
|
|
||||||
|
|
||||||
### Prompt Files
|
|
||||||
|
|
||||||
Supports `.txt`, `.json`, and `.ts` files:
|
|
||||||
```bash
|
|
||||||
npm run play prompt.txt # Plain text prompt
|
|
||||||
npm run play config.json # JSON configuration
|
|
||||||
npm run play dynamic.ts # TypeScript with default export
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm build # Production build (bundles & removes node_modules)
|
|
||||||
pnpm build:dev # Development build (keeps node_modules)
|
|
||||||
pnpm dev # Watch mode
|
|
||||||
```
|
|
||||||
|
|
||||||
The action is bundled into `entry.cjs` with all dependencies included, eliminating runtime dependency on node_modules.
|
|
||||||
|
|
||||||
## Environment Variables
|
|
||||||
|
|
||||||
Create `.env` in `/action`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ANTHROPIC_API_KEY=sk-ant-api03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # Claude API key
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
- **entry.cjs**: Bundled action entry point (self-contained)
|
|
||||||
- **agents/**: Agent implementations (Claude, etc.)
|
|
||||||
- **utils/**: Utilities for subprocess, act, and formatting
|
|
||||||
- **fixtures/**: Test prompt files
|
|
||||||
|
|
||||||
## Why No node_modules?
|
|
||||||
|
|
||||||
pnpm uses symlinks that cause "invalid symlink" errors when `act` copies the action to Docker. Our solution:
|
|
||||||
1. Bundle everything into `entry.cjs`
|
|
||||||
2. Remove node_modules after building
|
|
||||||
3. Create minimal `.act-dist` for Docker testing
|
|
||||||
+3
-5
@@ -10,9 +10,6 @@ inputs:
|
|||||||
anthropic_api_key:
|
anthropic_api_key:
|
||||||
description: "Anthropic API key for Claude Code authentication"
|
description: "Anthropic API key for Claude Code authentication"
|
||||||
required: false
|
required: false
|
||||||
github_installation_token:
|
|
||||||
description: "GitHub App installation token"
|
|
||||||
required: false
|
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
@@ -35,9 +32,10 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{ github.action_path }}
|
working-directory: ${{ github.action_path }}
|
||||||
- name: Run agent
|
- name: Run agent
|
||||||
run: node entry.ts
|
run: |
|
||||||
|
cd $GITHUB_WORKSPACE
|
||||||
|
node ${{ github.action_path }}/entry.ts
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ${{ github.action_path }}
|
|
||||||
env:
|
env:
|
||||||
INPUTS_JSON: ${{ toJSON(inputs) }}
|
INPUTS_JSON: ${{ toJSON(inputs) }}
|
||||||
|
|
||||||
|
|||||||
+114
-293
@@ -1,8 +1,9 @@
|
|||||||
import { access, constants } from "node:fs/promises";
|
|
||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
|
import { query, type SDKMessage } from "@anthropic-ai/claude-agent-sdk";
|
||||||
import { createMcpConfig } from "../mcp/config.ts";
|
import { createMcpConfig } from "../mcp/config.ts";
|
||||||
import { spawn } from "../utils/subprocess.ts";
|
import { log } from "../utils/cli.ts";
|
||||||
import { boxString, tableString } from "../utils/table.ts";
|
import { debugLog, isDebug } from "../utils/logging.ts";
|
||||||
|
import { instructions } from "./shared.ts";
|
||||||
import type { Agent, AgentConfig, AgentResult } from "./types.ts";
|
import type { Agent, AgentConfig, AgentResult } from "./types.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10,322 +11,142 @@ import type { Agent, AgentConfig, AgentResult } from "./types.ts";
|
|||||||
*/
|
*/
|
||||||
export class ClaudeAgent implements Agent {
|
export class ClaudeAgent implements Agent {
|
||||||
private apiKey: string;
|
private apiKey: string;
|
||||||
public runStats = {
|
private githubInstallationToken: string;
|
||||||
toolsUsed: 0,
|
|
||||||
turns: 0,
|
|
||||||
startTime: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(config: AgentConfig) {
|
constructor(config: AgentConfig) {
|
||||||
if (!config.apiKey) {
|
|
||||||
throw new Error("Claude agent requires an API key");
|
|
||||||
}
|
|
||||||
this.apiKey = config.apiKey;
|
this.apiKey = config.apiKey;
|
||||||
|
this.githubInstallationToken = config.githubInstallationToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if Claude Code CLI is already installed
|
* Install is a no-op since Claude CLI is bundled with the SDK
|
||||||
*/
|
|
||||||
private async isClaudeInstalled(): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
const claudePath = `${process.env.HOME}/.local/bin/claude`;
|
|
||||||
await access(claudePath, constants.F_OK | constants.X_OK);
|
|
||||||
return true;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Install Claude Code CLI
|
|
||||||
*/
|
*/
|
||||||
async install(): Promise<void> {
|
async install(): Promise<void> {
|
||||||
if (await this.isClaudeInstalled()) {
|
// No installation needed - CLI is bundled with @anthropic-ai/claude-agent-sdk
|
||||||
core.info("Claude Code is already installed, skipping installation");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
core.info("Installing Claude Code...");
|
|
||||||
try {
|
|
||||||
const result = await spawn({
|
|
||||||
cmd: "bash",
|
|
||||||
args: [
|
|
||||||
"-c",
|
|
||||||
"curl -fsSL https://claude.ai/install.sh | bash -s 1.0.93",
|
|
||||||
],
|
|
||||||
env: { ANTHROPIC_API_KEY: this.apiKey },
|
|
||||||
timeout: 120000, // 2 minute timeout
|
|
||||||
onStdout: () => {},
|
|
||||||
onStderr: (chunk) => process.stderr.write(chunk),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.exitCode !== 0) {
|
|
||||||
throw new Error(
|
|
||||||
`Installation failed with exit code ${result.exitCode}: ${result.stderr}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
core.info("Claude Code installed successfully");
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(`Failed to install Claude Code: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute Claude Code with the given prompt
|
* Execute Claude Code with the given prompt using the SDK
|
||||||
*/
|
*/
|
||||||
async execute(prompt: string): Promise<AgentResult> {
|
async execute(prompt: string): Promise<AgentResult> {
|
||||||
core.info("Running Claude Code...");
|
log.info("Running Claude Agent SDK...");
|
||||||
|
|
||||||
try {
|
log.box(prompt, { title: "Prompt" });
|
||||||
const claudePath = `${process.env.HOME}/.local/bin/claude`;
|
|
||||||
console.log(boxString(prompt, { title: "Prompt" }));
|
|
||||||
const args = [
|
|
||||||
"--print",
|
|
||||||
"--output-format",
|
|
||||||
"stream-json",
|
|
||||||
"--verbose",
|
|
||||||
"--debug",
|
|
||||||
"--permission-mode",
|
|
||||||
"bypassPermissions",
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!process.env.GITHUB_INSTALLATION_TOKEN) {
|
const mcpConfig = JSON.parse(createMcpConfig(this.githubInstallationToken));
|
||||||
throw new Error(
|
|
||||||
"GITHUB_INSTALLATION_TOKEN is required for GitHub integration"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const mcpConfig = createMcpConfig(process.env.GITHUB_INSTALLATION_TOKEN);
|
if (isDebug()) {
|
||||||
console.log("📋 MCP Config:", mcpConfig);
|
debugLog(`📋 MCP Config: ${JSON.stringify(mcpConfig, null, 2)}`);
|
||||||
args.push("--mcp-config", mcpConfig);
|
|
||||||
|
|
||||||
const env = {
|
|
||||||
ANTHROPIC_API_KEY: this.apiKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
core.startGroup("🔄 Run details");
|
|
||||||
|
|
||||||
this.runStats = {
|
|
||||||
toolsUsed: 0,
|
|
||||||
turns: 0,
|
|
||||||
startTime: Date.now(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const finalResult = "";
|
|
||||||
const totalCost = 0;
|
|
||||||
|
|
||||||
const result = await spawn({
|
|
||||||
cmd: claudePath,
|
|
||||||
args,
|
|
||||||
env,
|
|
||||||
input: prompt,
|
|
||||||
timeout: 10 * 60 * 1000, // 10 minutes
|
|
||||||
onStdout: (_chunk) => {
|
|
||||||
processJSONChunk(_chunk, this);
|
|
||||||
},
|
|
||||||
onStderr: (_chunk) => {
|
|
||||||
if (_chunk.trim()) {
|
|
||||||
processJSONChunk(_chunk, this);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.exitCode !== 0) {
|
|
||||||
throw new Error(
|
|
||||||
`Command failed with exit code ${result.exitCode}\n\nStdout: ${result.stdout}\n\nStderr: ${result.stderr}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const duration = Date.now() - this.runStats.startTime;
|
|
||||||
core.info(
|
|
||||||
`📊 Run Summary: ${this.runStats.toolsUsed} tools used, ${this.runStats.turns} turns, ${duration}ms duration`
|
|
||||||
);
|
|
||||||
|
|
||||||
core.info("✅ Task complete.");
|
|
||||||
core.endGroup(); // End the collapsible log group
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
output: finalResult,
|
|
||||||
metadata: {
|
|
||||||
promptLength: prompt.length,
|
|
||||||
exitCode: result.exitCode,
|
|
||||||
durationMs: result.durationMs,
|
|
||||||
totalCost,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} catch (error: any) {
|
|
||||||
try {
|
|
||||||
core.endGroup();
|
|
||||||
} catch {}
|
|
||||||
const errorMessage =
|
|
||||||
error instanceof Error ? error.message : "Unknown error";
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: `Failed to execute Claude Code: ${errorMessage}`,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize session
|
||||||
|
core.info(`🚀 Starting Claude Agent SDK session...`);
|
||||||
|
|
||||||
|
// Set API key environment variable for SDK
|
||||||
|
process.env.ANTHROPIC_API_KEY = this.apiKey;
|
||||||
|
|
||||||
|
// Create the query with SDK options
|
||||||
|
const queryInstance = query({
|
||||||
|
prompt: `${instructions}\n\n${prompt}`,
|
||||||
|
options: {
|
||||||
|
permissionMode: "bypassPermissions",
|
||||||
|
mcpServers: mcpConfig.mcpServers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stream the results
|
||||||
|
for await (const message of queryInstance) {
|
||||||
|
const handler = messageHandlers[message.type];
|
||||||
|
await handler(message as never);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.success("Task complete.");
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
output: "",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
type SDKMessageType = SDKMessage["type"];
|
||||||
* Pretty print a JSON chunk based on its type
|
|
||||||
*/
|
|
||||||
function processJSONChunk(chunk: string, agent?: ClaudeAgent): void {
|
|
||||||
try {
|
|
||||||
console.log(chunk);
|
|
||||||
const parsedChunk = JSON.parse(chunk.trim());
|
|
||||||
|
|
||||||
switch (parsedChunk.type) {
|
type SDKMessageHandler<type extends SDKMessageType = SDKMessageType> = (
|
||||||
case "system":
|
data: Extract<SDKMessage, { type: type }>
|
||||||
if (parsedChunk.subtype === "init") {
|
) => void | Promise<void>;
|
||||||
core.info(`🚀 Starting Claude Code session...`);
|
|
||||||
core.info(
|
|
||||||
tableString([
|
|
||||||
["model", parsedChunk.model],
|
|
||||||
["cwd", parsedChunk.cwd],
|
|
||||||
["permission_mode", parsedChunk.permissionMode],
|
|
||||||
[
|
|
||||||
"tools",
|
|
||||||
parsedChunk.tools?.length
|
|
||||||
? `${parsedChunk.tools.length} tools`
|
|
||||||
: "none",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"mcp_servers",
|
|
||||||
parsedChunk.mcp_servers?.length
|
|
||||||
? `${parsedChunk.mcp_servers.length} servers`
|
|
||||||
: "none",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"slash_commands",
|
|
||||||
parsedChunk.slash_commands?.length
|
|
||||||
? `${parsedChunk.slash_commands.length} commands`
|
|
||||||
: "none",
|
|
||||||
],
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "assistant":
|
type SDKMessageHandlers = {
|
||||||
if (parsedChunk.message?.content) {
|
[type in SDKMessageType]: SDKMessageHandler<type>;
|
||||||
if (agent) {
|
};
|
||||||
agent.runStats.turns++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const content of parsedChunk.message.content) {
|
const messageHandlers: SDKMessageHandlers = {
|
||||||
if (content.type === "text") {
|
assistant: (data) => {
|
||||||
if (content.text.trim()) {
|
if (data.message?.content) {
|
||||||
core.info(
|
for (const content of data.message.content) {
|
||||||
boxString(content.text.trim(), { title: "Claude Code" })
|
if (content.type === "text" && content.text?.trim()) {
|
||||||
);
|
log.box(content.text.trim(), { title: "Claude" });
|
||||||
}
|
} else if (content.type === "tool_use") {
|
||||||
} else if (content.type === "tool_use") {
|
log.info(`→ ${content.name}`);
|
||||||
if (agent) {
|
|
||||||
agent.runStats.toolsUsed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
const toolName = content.name;
|
if (content.input) {
|
||||||
|
const input = content.input as any;
|
||||||
core.info(`→ ${toolName}`);
|
if (input.description) log.info(` └─ ${input.description}`);
|
||||||
|
if (input.command) log.info(` └─ command: ${input.command}`);
|
||||||
if (content.input) {
|
if (input.file_path) log.info(` └─ file: ${input.file_path}`);
|
||||||
const input = content.input;
|
if (input.content) {
|
||||||
|
const preview =
|
||||||
if (input.description) {
|
input.content.length > 100
|
||||||
core.info(` └─ ${input.description}`);
|
? `${input.content.substring(0, 100)}...`
|
||||||
}
|
: input.content;
|
||||||
|
log.info(` └─ content: ${preview}`);
|
||||||
if (input.command) {
|
|
||||||
core.info(` └─ command: ${input.command}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.file_path) {
|
|
||||||
core.info(` └─ file: ${input.file_path}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.content) {
|
|
||||||
const contentPreview =
|
|
||||||
input.content.length > 100
|
|
||||||
? `${input.content.substring(0, 100)}...`
|
|
||||||
: input.content;
|
|
||||||
core.info(` └─ content: ${contentPreview}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.query) {
|
|
||||||
core.info(` └─ query: ${input.query}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.pattern) {
|
|
||||||
core.info(` └─ pattern: ${input.pattern}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.url) {
|
|
||||||
core.info(` └─ url: ${input.url}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.edits && Array.isArray(input.edits)) {
|
|
||||||
core.info(` └─ edits: ${input.edits.length} changes`);
|
|
||||||
input.edits.forEach((edit: any, index: number) => {
|
|
||||||
if (edit.file_path) {
|
|
||||||
core.info(` ${index + 1}. ${edit.file_path}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.task) {
|
|
||||||
core.info(` └─ task: ${input.task}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.bash_command) {
|
|
||||||
core.info(` └─ bash_command: ${input.bash_command}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
if (input.query) log.info(` └─ query: ${input.query}`);
|
||||||
}
|
if (input.pattern) log.info(` └─ pattern: ${input.pattern}`);
|
||||||
break;
|
if (input.url) log.info(` └─ url: ${input.url}`);
|
||||||
|
if (input.edits && Array.isArray(input.edits)) {
|
||||||
case "user":
|
log.info(` └─ edits: ${input.edits.length} changes`);
|
||||||
if (parsedChunk.message?.content) {
|
input.edits.forEach((edit: any, index: number) => {
|
||||||
for (const content of parsedChunk.message.content) {
|
if (edit.file_path) log.info(` ${index + 1}. ${edit.file_path}`);
|
||||||
if (content.type === "tool_result") {
|
});
|
||||||
if (content.is_error) {
|
|
||||||
core.warning(`❌ Tool error: ${content.content}`);
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (input.task) log.info(` └─ task: ${input.task}`);
|
||||||
|
if (input.bash_command) log.info(` └─ bash_command: ${input.bash_command}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
|
|
||||||
case "result":
|
|
||||||
if (parsedChunk.subtype === "success") {
|
|
||||||
core.info(
|
|
||||||
tableString([
|
|
||||||
[
|
|
||||||
"Cost",
|
|
||||||
`$${parsedChunk.total_cost_usd?.toFixed(4) || "0.0000"}`,
|
|
||||||
],
|
|
||||||
["Input Tokens", parsedChunk.usage?.input_tokens || 0],
|
|
||||||
["Output Tokens", parsedChunk.usage?.output_tokens || 0],
|
|
||||||
["Duration", `${parsedChunk.duration_ms}ms`],
|
|
||||||
["Turns", parsedChunk.num_turns || 1],
|
|
||||||
])
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
core.error(`❌ Failed: ${parsedChunk.error || "Unknown error"}`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
core.debug(`📦 Unknown chunk type: ${parsedChunk.type}`);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
},
|
||||||
core.debug(`Failed to parse chunk: ${error}`);
|
user: (data) => {
|
||||||
core.debug(`Raw chunk: ${chunk.substring(0, 200)}...`);
|
if (data.message?.content) {
|
||||||
}
|
for (const content of data.message.content) {
|
||||||
}
|
if (content.type === "tool_result" && content.is_error) {
|
||||||
|
log.warning(`Tool error: ${content.content}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
result: async (data) => {
|
||||||
|
if (data.subtype === "success") {
|
||||||
|
await log.summaryTable([
|
||||||
|
[
|
||||||
|
{ data: "Cost", header: true },
|
||||||
|
{ data: "Input Tokens", header: true },
|
||||||
|
{ data: "Output Tokens", header: true },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
`$${data.total_cost_usd?.toFixed(4) || "0.0000"}`,
|
||||||
|
String(data.usage?.input_tokens || 0),
|
||||||
|
String(data.usage?.output_tokens || 0),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
} else if (data.subtype === "error_max_turns") {
|
||||||
|
log.error(`Max turns reached: ${JSON.stringify(data)}`);
|
||||||
|
} else if (data.subtype === "error_during_execution") {
|
||||||
|
log.error(`Execution error: ${JSON.stringify(data)}`);
|
||||||
|
} else {
|
||||||
|
log.error(`Failed: ${JSON.stringify(data)}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
system: () => {},
|
||||||
|
stream_event: () => {},
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { mcpServerName } from "../mcp/config.ts";
|
||||||
|
|
||||||
|
export const instructions = `- use the ${mcpServerName} MCP server to interact with github
|
||||||
|
- if ${mcpServerName} is not available or doesn't include the functionality you need, describe why and bail
|
||||||
|
- do not under any circumstances use the gh cli
|
||||||
|
- if prompted by a comment to respond to create a new issue, pr or anything else, after succeeding,
|
||||||
|
also respond to the original comment with a very brief message containing a link to it
|
||||||
|
- if prompted to review a PR:
|
||||||
|
(1) get PR info with mcp__${mcpServerName}__get_pull_request
|
||||||
|
(2) fetch both branches: git fetch origin <base> --depth=20 && git fetch origin <head>
|
||||||
|
(3) checkout the PR branch: git checkout origin/<head> (you MUST do this before reading any files)
|
||||||
|
(4) view diff: git diff origin/<base>...origin/<head> (this shows what changed)
|
||||||
|
(5) read files from the checked-out PR branch to understand the implementation
|
||||||
|
replace <base> and <head> with 'base' and 'head' from the PR info
|
||||||
|
`;
|
||||||
+2
-2
@@ -29,6 +29,6 @@ export interface AgentResult {
|
|||||||
* Configuration for agent creation
|
* Configuration for agent creation
|
||||||
*/
|
*/
|
||||||
export interface AgentConfig {
|
export interface AgentConfig {
|
||||||
apiKey?: string;
|
apiKey: string;
|
||||||
[key: string]: any;
|
githubInstallationToken: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import * as core from "@actions/core";
|
|||||||
import { type } from "arktype";
|
import { type } from "arktype";
|
||||||
import { Inputs, main } from "./main.ts";
|
import { Inputs, main } from "./main.ts";
|
||||||
import packageJson from "./package.json" with { type: "json" };
|
import packageJson from "./package.json" with { type: "json" };
|
||||||
|
import { log } from "./utils/cli.ts";
|
||||||
|
|
||||||
async function run(): Promise<void> {
|
async function run(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
console.log(`🐸 Running pullfrog/action@${packageJson.version}...`);
|
log.info(`🐸 Running pullfrog/action@${packageJson.version}...`);
|
||||||
|
|
||||||
const inputsJson = process.env.INPUTS_JSON;
|
const inputsJson = process.env.INPUTS_JSON;
|
||||||
if (!inputsJson) {
|
if (!inputsJson) {
|
||||||
|
|||||||
+5
-9
@@ -1,13 +1,9 @@
|
|||||||
import type { MainParams } from "../main.ts";
|
import type { Inputs } from "../main.ts";
|
||||||
|
|
||||||
const testParams = {
|
const testParams = {
|
||||||
inputs: {
|
prompt:
|
||||||
prompt:
|
"List all files in the current directory, then create a file called dynamic-test.txt with the content 'This was loaded from a TypeScript file!', then delete it.",
|
||||||
"List all files in the current directory, then create a file called dynamic-test.txt with the content 'This was loaded from a TypeScript file!', then delete it.",
|
anthropic_api_key: "sk-test-key",
|
||||||
anthropic_api_key: "sk-test-key",
|
} satisfies Inputs;
|
||||||
},
|
|
||||||
env: {},
|
|
||||||
cwd: process.cwd(),
|
|
||||||
} satisfies MainParams;
|
|
||||||
|
|
||||||
export default testParams;
|
export default testParams;
|
||||||
|
|||||||
+1
-3
@@ -1,3 +1 @@
|
|||||||
Use the MCP GitHub comment tool to add a comment containing your best frog joke to GitHub issue https://github.com/pullfrogai/scratch/issues/2.
|
add a github review for the following PR: https://github.com/pullfrogai/scratch/pull/16
|
||||||
|
|
||||||
Do not use the gh cli. If the mcp tool does not work, bail.
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
export { ClaudeAgent } from "./agents/claude.ts";
|
export { ClaudeAgent } from "./agents/claude.ts";
|
||||||
export type { Agent, AgentConfig, AgentResult } from "./agents/types.ts";
|
export type { Agent, AgentConfig, AgentResult } from "./agents/types.ts";
|
||||||
export {
|
export {
|
||||||
type ActionInputs as ExecutionInputs,
|
type Inputs as ExecutionInputs,
|
||||||
type MainResult,
|
type MainResult,
|
||||||
main,
|
main,
|
||||||
} from "./main.ts";
|
} from "./main.ts";
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import { type } from "arktype";
|
import { type } from "arktype";
|
||||||
import { ClaudeAgent } from "./agents/claude.ts";
|
import { ClaudeAgent } from "./agents/claude.ts";
|
||||||
|
import { parseRepoContext, setupGitHubInstallationToken } from "./utils/github.ts";
|
||||||
|
import { setupGitAuth, setupGitConfig } from "./utils/setup.ts";
|
||||||
|
|
||||||
export const Inputs = type({
|
export const Inputs = type({
|
||||||
prompt: "string",
|
prompt: "string",
|
||||||
"anthropic_api_key?": "string | undefined",
|
"anthropic_api_key?": "string | undefined",
|
||||||
"github_installation_token?": "string | undefined",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type ActionInputs = typeof Inputs.infer;
|
export type Inputs = typeof Inputs.infer;
|
||||||
|
|
||||||
export interface MainResult {
|
export interface MainResult {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
@@ -16,11 +17,22 @@ export interface MainResult {
|
|||||||
error?: string | undefined;
|
error?: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function main(inputs: ActionInputs): Promise<MainResult> {
|
export async function main(inputs: Inputs): Promise<MainResult> {
|
||||||
try {
|
try {
|
||||||
core.info(`→ Starting agent run with Claude Code`);
|
core.info(`→ Starting agent run with Claude Code`);
|
||||||
|
|
||||||
const agent = new ClaudeAgent({ apiKey: inputs.anthropic_api_key! });
|
setupGitConfig();
|
||||||
|
|
||||||
|
const githubInstallationToken = await setupGitHubInstallationToken();
|
||||||
|
const repoContext = parseRepoContext();
|
||||||
|
|
||||||
|
setupGitAuth(githubInstallationToken, repoContext);
|
||||||
|
|
||||||
|
const agent = new ClaudeAgent({
|
||||||
|
apiKey: inputs.anthropic_api_key!,
|
||||||
|
githubInstallationToken,
|
||||||
|
});
|
||||||
|
|
||||||
await agent.install();
|
await agent.install();
|
||||||
|
|
||||||
const result = await agent.execute(inputs.prompt);
|
const result = await agent.execute(inputs.prompt);
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { type } from "arktype";
|
||||||
|
import { contextualize, tool } from "./shared.ts";
|
||||||
|
|
||||||
|
export const Comment = type({
|
||||||
|
issueNumber: type.number.describe("the issue number to comment on"),
|
||||||
|
body: type.string.describe("the comment body content"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CommentTool = tool({
|
||||||
|
name: "create_issue_comment",
|
||||||
|
description: "Create a comment on a GitHub issue",
|
||||||
|
parameters: Comment,
|
||||||
|
execute: contextualize(async ({ issueNumber, body }, ctx) => {
|
||||||
|
const result = await ctx.octokit.rest.issues.createComment({
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
issue_number: issueNumber,
|
||||||
|
body: body,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
commentId: result.data.id,
|
||||||
|
url: result.data.html_url,
|
||||||
|
body: result.data.body,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
+7
-10
@@ -1,30 +1,27 @@
|
|||||||
/**
|
/**
|
||||||
* Simple MCP configuration helper for adding our minimal GitHub comment server
|
* Simple MCP configuration helper for adding our minimal GitHub comment server
|
||||||
*/
|
*/
|
||||||
// const actionPath = process.env.GITHUB_ACTION_PATH || process.cwd();
|
|
||||||
|
|
||||||
import { fromHere } from "@ark/fs";
|
import { fromHere } from "@ark/fs";
|
||||||
|
import { parseRepoContext } from "../utils/github.ts";
|
||||||
|
|
||||||
const actionPath = fromHere("..");
|
const actionPath = fromHere("..");
|
||||||
|
|
||||||
|
export const mcpServerName = "gh-pullfrog";
|
||||||
|
|
||||||
export function createMcpConfig(githubInstallationToken: string) {
|
export function createMcpConfig(githubInstallationToken: string) {
|
||||||
const githubRepository = process.env.GITHUB_REPOSITORY;
|
const repoContext = parseRepoContext();
|
||||||
if (!githubRepository) {
|
const githubRepository = `${repoContext.owner}/${repoContext.name}`;
|
||||||
throw new Error(
|
|
||||||
"GITHUB_REPOSITORY environment variable is required for MCP GitHub integration"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return JSON.stringify(
|
return JSON.stringify(
|
||||||
{
|
{
|
||||||
mcpServers: {
|
mcpServers: {
|
||||||
minimal_github_comment: {
|
[mcpServerName]: {
|
||||||
command: "node",
|
command: "node",
|
||||||
args: [`${actionPath}/mcp/server.ts`],
|
args: [`${actionPath}/mcp/server.ts`],
|
||||||
env: {
|
env: {
|
||||||
GITHUB_INSTALLATION_TOKEN: githubInstallationToken,
|
GITHUB_INSTALLATION_TOKEN: githubInstallationToken,
|
||||||
GITHUB_REPOSITORY: githubRepository,
|
GITHUB_REPOSITORY: githubRepository,
|
||||||
LOG_LEVEL: "debug",
|
LOG_LEVEL: process.env.LOG_LEVEL,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { type } from "arktype";
|
||||||
|
import { contextualize, tool } from "./shared.ts";
|
||||||
|
|
||||||
|
export const Issue = type({
|
||||||
|
title: type.string.describe("the title of the issue"),
|
||||||
|
body: type.string.describe("the body content of the issue"),
|
||||||
|
labels: type.string
|
||||||
|
.array()
|
||||||
|
.describe("optional array of label names to apply to the issue")
|
||||||
|
.optional(),
|
||||||
|
assignees: type.string
|
||||||
|
.array()
|
||||||
|
.describe("optional array of usernames to assign to the issue")
|
||||||
|
.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const IssueTool = tool({
|
||||||
|
name: "create_issue",
|
||||||
|
description: "Create a new GitHub issue",
|
||||||
|
parameters: Issue,
|
||||||
|
execute: contextualize(async ({ title, body, labels, assignees }, ctx) => {
|
||||||
|
const result = await ctx.octokit.rest.issues.create({
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
title: title,
|
||||||
|
body: body,
|
||||||
|
labels: labels ?? [],
|
||||||
|
assignees: assignees ?? [],
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
issueId: result.data.id,
|
||||||
|
number: result.data.number,
|
||||||
|
url: result.data.html_url,
|
||||||
|
title: result.data.title,
|
||||||
|
state: result.data.state,
|
||||||
|
labels: result.data.labels?.map((label) => (typeof label === "string" ? label : label.name)),
|
||||||
|
assignees: result.data.assignees?.map((assignee) => assignee.login),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { execSync } from "node:child_process";
|
||||||
|
import { type } from "arktype";
|
||||||
|
import { contextualize, tool } from "./shared.ts";
|
||||||
|
|
||||||
|
export const PullRequest = type({
|
||||||
|
title: type.string.describe("the title of the pull request"),
|
||||||
|
body: type.string.describe("the body content of the pull request"),
|
||||||
|
base: type.string.describe("the base branch to merge into (e.g., 'main')"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PullRequestTool = tool({
|
||||||
|
name: "create_pull_request",
|
||||||
|
description: "Create a pull request from the current branch",
|
||||||
|
parameters: PullRequest,
|
||||||
|
execute: contextualize(async ({ title, body, base }, ctx) => {
|
||||||
|
// Get the current branch name
|
||||||
|
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", {
|
||||||
|
encoding: "utf8",
|
||||||
|
}).trim();
|
||||||
|
|
||||||
|
console.log(`Current branch: ${currentBranch}`);
|
||||||
|
|
||||||
|
const result = await ctx.octokit.rest.pulls.create({
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
title: title,
|
||||||
|
body: body,
|
||||||
|
head: currentBranch,
|
||||||
|
base: base,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
pullRequestId: result.data.id,
|
||||||
|
number: result.data.number,
|
||||||
|
url: result.data.html_url,
|
||||||
|
title: result.data.title,
|
||||||
|
head: result.data.head.ref,
|
||||||
|
base: result.data.base.ref,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { type } from "arktype";
|
||||||
|
import { contextualize, tool } from "./shared.ts";
|
||||||
|
|
||||||
|
export const PullRequestInfo = type({
|
||||||
|
pull_number: type.number.describe("The pull request number to fetch"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PullRequestInfoTool = tool({
|
||||||
|
name: "get_pull_request",
|
||||||
|
description: "Retrieve minimal information for a specific pull request by number.",
|
||||||
|
parameters: PullRequestInfo,
|
||||||
|
execute: contextualize(async ({ pull_number }, ctx) => {
|
||||||
|
const pr = await ctx.octokit.rest.pulls.get({
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
pull_number,
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = pr.data;
|
||||||
|
|
||||||
|
const baseBranch = data.base.ref;
|
||||||
|
const headBranch = data.head.ref;
|
||||||
|
|
||||||
|
return {
|
||||||
|
number: data.number,
|
||||||
|
url: data.html_url,
|
||||||
|
title: data.title,
|
||||||
|
state: data.state,
|
||||||
|
draft: data.draft,
|
||||||
|
merged: data.merged,
|
||||||
|
base: baseBranch,
|
||||||
|
head: headBranch,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { type } from "arktype";
|
||||||
|
import { contextualize, tool } from "./shared.ts";
|
||||||
|
import type { RestEndpointMethodTypes } from "@octokit/rest";
|
||||||
|
|
||||||
|
export const Review = type({
|
||||||
|
pull_number: type.number.describe("The pull request number to review"),
|
||||||
|
event: type.enumerated("APPROVE", "REQUEST_CHANGES", "COMMENT").describe("'APPROVE', 'REQUEST_CHANGES', or 'COMMENT' (the review action)"),
|
||||||
|
body: type.string.describe("The body content for the review (required for REQUEST_CHANGES or COMMENT)").optional(),
|
||||||
|
commit_id: type.string.describe("Optional SHA of the commit being reviewed. Defaults to latest.").optional(),
|
||||||
|
comments: type
|
||||||
|
({
|
||||||
|
path: type.string.describe("The file path to comment on"),
|
||||||
|
position: type.number.describe("The diff position in the file"),
|
||||||
|
body: type.string.describe("The comment text"),
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.describe("Array of draft review comments for specific lines, optional.")
|
||||||
|
.optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ReviewTool = tool({
|
||||||
|
name: "submit_pull_request_review",
|
||||||
|
description: "Submit a review (approve, request changes, or comment) for an existing pull request.",
|
||||||
|
parameters: Review,
|
||||||
|
execute: contextualize(async ({ pull_number, event, body, commit_id, comments = [] }, ctx) => {
|
||||||
|
// Compose the request
|
||||||
|
const params: RestEndpointMethodTypes["pulls"]["createReview"]["parameters"] = {
|
||||||
|
owner: ctx.owner,
|
||||||
|
repo: ctx.name,
|
||||||
|
pull_number,
|
||||||
|
event,
|
||||||
|
};
|
||||||
|
if (body) params.body = body;
|
||||||
|
if (commit_id) params.commit_id = commit_id;
|
||||||
|
if (comments.length > 0) params.comments = comments;
|
||||||
|
const result = await ctx.octokit.rest.pulls.createReview(params);
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
reviewId: result.data.id,
|
||||||
|
html_url: result.data.html_url,
|
||||||
|
state: result.data.state,
|
||||||
|
user: result.data.user?.login,
|
||||||
|
submitted_at: result.data.submitted_at,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
+11
-85
@@ -1,92 +1,18 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
// Minimal GitHub Issue Comment MCP Server
|
// Minimal GitHub Issue Comment MCP Server
|
||||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
import { FastMCP } from "fastmcp";
|
||||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
import { CommentTool } from "./comment.ts";
|
||||||
import { Octokit } from "@octokit/rest";
|
import { IssueTool } from "./issue.ts";
|
||||||
import { type } from "arktype";
|
import { PullRequestTool } from "./pr.ts";
|
||||||
import { z } from "zod";
|
import { ReviewTool } from "./review.ts";
|
||||||
import { resolveRepoContext } from "../utils/repo-context.ts";
|
import { PullRequestInfoTool } from "./prInfo.ts";
|
||||||
|
import { addTools } from "./shared.ts";
|
||||||
|
|
||||||
const server = new McpServer({
|
const server = new FastMCP({
|
||||||
name: "Minimal GitHub Issue Comment Server",
|
name: "gh-pullfrog",
|
||||||
version: "0.0.1",
|
version: "0.0.1",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Define the schema for creating issue comments
|
addTools(server, [CommentTool, IssueTool, PullRequestTool, ReviewTool, PullRequestInfoTool]);
|
||||||
const Comment = type({
|
|
||||||
issueNumber: type.number.describe("the issue number to comment on"),
|
|
||||||
body: type.string.describe("the comment body content"),
|
|
||||||
});
|
|
||||||
|
|
||||||
server.tool(
|
server.start();
|
||||||
"create_issue_comment",
|
|
||||||
"Create a comment on a GitHub issue",
|
|
||||||
{
|
|
||||||
issueNumber: z.number().describe("the issue number to comment on"),
|
|
||||||
body: z.string().describe("the comment body content"),
|
|
||||||
},
|
|
||||||
async ({ issueNumber, body }) => {
|
|
||||||
try {
|
|
||||||
Comment.assert({ issueNumber, body });
|
|
||||||
|
|
||||||
const githubInstallationToken = process.env.GITHUB_INSTALLATION_TOKEN;
|
|
||||||
if (!githubInstallationToken) {
|
|
||||||
throw new Error("GITHUB_INSTALLATION_TOKEN environment variable is required");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve repository context from environment
|
|
||||||
const repoContext = resolveRepoContext();
|
|
||||||
|
|
||||||
const octokit = new Octokit({
|
|
||||||
auth: githubInstallationToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await octokit.rest.issues.createComment({
|
|
||||||
owner: repoContext.owner,
|
|
||||||
repo: repoContext.name,
|
|
||||||
issue_number: issueNumber,
|
|
||||||
body: body,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text: JSON.stringify(
|
|
||||||
{
|
|
||||||
success: true,
|
|
||||||
commentId: result.data.id,
|
|
||||||
url: result.data.html_url,
|
|
||||||
body: result.data.body,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text: `Error creating comment: ${errorMessage}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
error: errorMessage,
|
|
||||||
isError: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
async function runServer() {
|
|
||||||
const transport = new StdioServerTransport();
|
|
||||||
await server.connect(transport);
|
|
||||||
process.on("exit", () => {
|
|
||||||
server.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await runServer();
|
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { cached } from "@ark/util";
|
||||||
|
import { Octokit } from "@octokit/rest";
|
||||||
|
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
||||||
|
import type { FastMCP, Tool } from "fastmcp";
|
||||||
|
import { parseRepoContext, type RepoContext } from "../utils/github.ts";
|
||||||
|
|
||||||
|
export interface ToolResult {
|
||||||
|
content: {
|
||||||
|
type: "text";
|
||||||
|
text: string;
|
||||||
|
}[];
|
||||||
|
isError?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMcpContext = cached((): McpContext => {
|
||||||
|
const githubInstallationToken = process.env.GITHUB_INSTALLATION_TOKEN;
|
||||||
|
if (!githubInstallationToken) {
|
||||||
|
throw new Error("GITHUB_INSTALLATION_TOKEN environment variable is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...parseRepoContext(),
|
||||||
|
octokit: new Octokit({
|
||||||
|
auth: githubInstallationToken,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface McpContext extends RepoContext {
|
||||||
|
octokit: Octokit;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tool = <const params>(tool: Tool<any, StandardSchemaV1<params>>) => tool;
|
||||||
|
|
||||||
|
export const addTools = (server: FastMCP, tools: Tool<any, any>[]) => {
|
||||||
|
for (const tool of tools) {
|
||||||
|
server.addTool(tool);
|
||||||
|
}
|
||||||
|
return server;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const contextualize =
|
||||||
|
<T>(executor: (params: T, ctx: McpContext) => Promise<Record<string, any>>) =>
|
||||||
|
async (params: T): Promise<ToolResult> => {
|
||||||
|
try {
|
||||||
|
const ctx = getMcpContext();
|
||||||
|
const result = await executor(params, ctx);
|
||||||
|
return handleToolSuccess(result);
|
||||||
|
} catch (error) {
|
||||||
|
return handleToolError(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleToolSuccess = (data: Record<string, any>): ToolResult => {
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: JSON.stringify(data, null, 2),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleToolError = (error: unknown): ToolResult => {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `Error: ${errorMessage}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isError: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
+8
-6
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@pullfrog/action",
|
"name": "@pullfrog/action",
|
||||||
"version": "0.0.45",
|
"version": "0.0.65",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"index.js",
|
"index.js",
|
||||||
@@ -21,15 +21,17 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.11.1",
|
"@actions/core": "^1.11.1",
|
||||||
"@ark/fs": "0.49.0",
|
"@anthropic-ai/claude-agent-sdk": "^0.1.26",
|
||||||
"@modelcontextprotocol/sdk": "^1.20.0",
|
"@ark/fs": "0.50.0",
|
||||||
|
"@ark/util": "0.50.0",
|
||||||
"@octokit/rest": "^22.0.0",
|
"@octokit/rest": "^22.0.0",
|
||||||
"@octokit/webhooks-types": "^7.6.1",
|
"@octokit/webhooks-types": "^7.6.1",
|
||||||
"arktype": "^2.1.22",
|
"@standard-schema/spec": "1.0.0",
|
||||||
|
"arktype": "^2.1.25",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"execa": "^9.6.0",
|
"execa": "^9.6.0",
|
||||||
"table": "^6.9.0",
|
"fastmcp": "^3.20.0",
|
||||||
"zod": "^3.24.4"
|
"table": "^6.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.7.2",
|
"@types/node": "^24.7.2",
|
||||||
|
|||||||
@@ -4,25 +4,18 @@ import { pathToFileURL } from "node:url";
|
|||||||
import { fromHere } from "@ark/fs";
|
import { fromHere } from "@ark/fs";
|
||||||
import arg from "arg";
|
import arg from "arg";
|
||||||
import { config } from "dotenv";
|
import { config } from "dotenv";
|
||||||
import { type ActionInputs, main } from "./main.ts";
|
import { type Inputs, main } from "./main.ts";
|
||||||
import packageJson from "./package.json" with { type: "json" };
|
import packageJson from "./package.json" with { type: "json" };
|
||||||
import { runAct } from "./utils/act.ts";
|
import { log } from "./utils/cli.ts";
|
||||||
import { setupGitHubInstallationToken } from "./utils/github.ts";
|
|
||||||
import { setupTestRepo } from "./utils/setup.ts";
|
import { setupTestRepo } from "./utils/setup.ts";
|
||||||
|
|
||||||
config();
|
config();
|
||||||
|
|
||||||
export async function run(
|
export async function run(
|
||||||
prompt: string,
|
prompt: string
|
||||||
options: { act?: boolean } = {}
|
|
||||||
): Promise<{ success: boolean; output?: string | undefined; error?: string | undefined }> {
|
): Promise<{ success: boolean; output?: string | undefined; error?: string | undefined }> {
|
||||||
try {
|
try {
|
||||||
console.log(`🐸 Running pullfrog/action@${packageJson.version}...`);
|
log.info(`🐸 Running pullfrog/action@${packageJson.version}...`);
|
||||||
if (options.act) {
|
|
||||||
console.log("🐳 Running with Docker/act...");
|
|
||||||
runAct(prompt);
|
|
||||||
return { success: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
const tempDir = join(process.cwd(), ".temp");
|
const tempDir = join(process.cwd(), ".temp");
|
||||||
setupTestRepo({ tempDir, forceClean: true });
|
setupTestRepo({ tempDir, forceClean: true });
|
||||||
@@ -30,22 +23,14 @@ export async function run(
|
|||||||
const originalCwd = process.cwd();
|
const originalCwd = process.cwd();
|
||||||
process.chdir(tempDir);
|
process.chdir(tempDir);
|
||||||
|
|
||||||
console.log("🚀 Running action with prompt...");
|
log.info("🚀 Running action with prompt...");
|
||||||
console.log("─".repeat(50));
|
log.separator();
|
||||||
console.log("Prompt:");
|
log.box(prompt, { title: "Prompt" });
|
||||||
console.log(prompt);
|
log.separator();
|
||||||
console.log("─".repeat(50));
|
|
||||||
|
|
||||||
console.log("🔑 Setting up GitHub installation token...");
|
const inputs: Inputs = {
|
||||||
const installationToken = await setupGitHubInstallationToken();
|
|
||||||
process.env.GITHUB_INSTALLATION_TOKEN = installationToken;
|
|
||||||
|
|
||||||
console.log("✅ GitHub installation token setup successfully");
|
|
||||||
|
|
||||||
const inputs: ActionInputs = {
|
|
||||||
prompt,
|
prompt,
|
||||||
anthropic_api_key: process.env.ANTHROPIC_API_KEY,
|
anthropic_api_key: process.env.ANTHROPIC_API_KEY,
|
||||||
github_installation_token: installationToken,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await main(inputs);
|
const result = await main(inputs);
|
||||||
@@ -53,18 +38,15 @@ export async function run(
|
|||||||
process.chdir(originalCwd);
|
process.chdir(originalCwd);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log("✅ Action completed successfully");
|
log.success("Action completed successfully");
|
||||||
if (result.output) {
|
|
||||||
console.log("Output:", result.output);
|
|
||||||
}
|
|
||||||
return { success: true, output: result.output || undefined, error: undefined };
|
return { success: true, output: result.output || undefined, error: undefined };
|
||||||
} else {
|
} else {
|
||||||
console.error("❌ Action failed:", result.error);
|
log.error(`Action failed: ${result.error || "Unknown error"}`);
|
||||||
return { success: false, error: result.error || undefined, output: undefined };
|
return { success: false, error: result.error || undefined, output: undefined };
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
const errorMessage = (error as Error).message;
|
const errorMessage = (err as Error).message;
|
||||||
console.error("❌ Error:", errorMessage);
|
log.error(`Error: ${errorMessage}`);
|
||||||
return { success: false, error: errorMessage, output: undefined };
|
return { success: false, error: errorMessage, output: undefined };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,7 +54,6 @@ export async function run(
|
|||||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
const args = arg({
|
const args = arg({
|
||||||
"--help": Boolean,
|
"--help": Boolean,
|
||||||
"--act": Boolean,
|
|
||||||
"--raw": String,
|
"--raw": String,
|
||||||
"-h": "--help",
|
"-h": "--help",
|
||||||
});
|
});
|
||||||
@@ -87,7 +68,6 @@ Arguments:
|
|||||||
file Prompt file to use (.txt, .json, or .ts) [default: fixtures/basic.txt]
|
file Prompt file to use (.txt, .json, or .ts) [default: fixtures/basic.txt]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--act Use Docker/act to run the action instead of running directly
|
|
||||||
--raw [prompt] Use raw string as prompt instead of loading from file
|
--raw [prompt] Use raw string as prompt instead of loading from file
|
||||||
-h, --help Show this help message
|
-h, --help Show this help message
|
||||||
|
|
||||||
@@ -95,7 +75,7 @@ Examples:
|
|||||||
tsx play.ts # Use default fixture
|
tsx play.ts # Use default fixture
|
||||||
tsx play.ts fixtures/basic.txt # Use specific text file
|
tsx play.ts fixtures/basic.txt # Use specific text file
|
||||||
tsx play.ts custom.json # Use JSON file
|
tsx play.ts custom.json # Use JSON file
|
||||||
tsx play.ts --act fixtures/test.ts # Use TypeScript file with Docker/act
|
tsx play.ts fixtures/test.ts # Use TypeScript file
|
||||||
tsx play.ts --raw "Hello world" # Use raw string as prompt
|
tsx play.ts --raw "Hello world" # Use raw string as prompt
|
||||||
`);
|
`);
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
@@ -156,13 +136,13 @@ Examples:
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await run(prompt, { act: args["--act"] || false });
|
const result = await run(prompt);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error("❌ Error:", (error as Error).message);
|
log.error((err as Error).message);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+421
-26
@@ -11,33 +11,39 @@ importers:
|
|||||||
'@actions/core':
|
'@actions/core':
|
||||||
specifier: ^1.11.1
|
specifier: ^1.11.1
|
||||||
version: 1.11.1
|
version: 1.11.1
|
||||||
|
'@anthropic-ai/claude-agent-sdk':
|
||||||
|
specifier: ^0.1.26
|
||||||
|
version: 0.1.30(zod@3.25.76)
|
||||||
'@ark/fs':
|
'@ark/fs':
|
||||||
specifier: 0.49.0
|
specifier: 0.50.0
|
||||||
version: 0.49.0
|
version: 0.50.0
|
||||||
'@modelcontextprotocol/sdk':
|
'@ark/util':
|
||||||
specifier: ^1.20.0
|
specifier: 0.50.0
|
||||||
version: 1.20.0
|
version: 0.50.0
|
||||||
'@octokit/rest':
|
'@octokit/rest':
|
||||||
specifier: ^22.0.0
|
specifier: ^22.0.0
|
||||||
version: 22.0.0
|
version: 22.0.0
|
||||||
'@octokit/webhooks-types':
|
'@octokit/webhooks-types':
|
||||||
specifier: ^7.6.1
|
specifier: ^7.6.1
|
||||||
version: 7.6.1
|
version: 7.6.1
|
||||||
|
'@standard-schema/spec':
|
||||||
|
specifier: 1.0.0
|
||||||
|
version: 1.0.0
|
||||||
arktype:
|
arktype:
|
||||||
specifier: ^2.1.22
|
specifier: ^2.1.25
|
||||||
version: 2.1.22
|
version: 2.1.25
|
||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^17.2.3
|
specifier: ^17.2.3
|
||||||
version: 17.2.3
|
version: 17.2.3
|
||||||
execa:
|
execa:
|
||||||
specifier: ^9.6.0
|
specifier: ^9.6.0
|
||||||
version: 9.6.0
|
version: 9.6.0
|
||||||
|
fastmcp:
|
||||||
|
specifier: ^3.20.0
|
||||||
|
version: 3.20.0(arktype@2.1.25)
|
||||||
table:
|
table:
|
||||||
specifier: ^6.9.0
|
specifier: ^6.9.0
|
||||||
version: 6.9.0
|
version: 6.9.0
|
||||||
zod:
|
|
||||||
specifier: ^3.24.4
|
|
||||||
version: 3.25.76
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^24.7.2
|
specifier: ^24.7.2
|
||||||
@@ -63,19 +69,92 @@ packages:
|
|||||||
'@actions/io@1.1.3':
|
'@actions/io@1.1.3':
|
||||||
resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==}
|
resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==}
|
||||||
|
|
||||||
'@ark/fs@0.49.0':
|
'@anthropic-ai/claude-agent-sdk@0.1.30':
|
||||||
resolution: {integrity: sha512-AEjAQS/bu1CGIRiKK/XLaQ73cSJHixfexq28wNt+kBpQ0h1RwVIVzaGsn/+5IWw6DEbR7LB+3hil5gzrzEeyZQ==}
|
resolution: {integrity: sha512-lo1tqxCr2vygagFp6kUMHKSN6AAWlULCskwGKtLB/JcIXy/8H8GsLSKX54anTsvc9mBbCR8wWASdFmiiL9NSKA==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
zod: ^3.24.1
|
||||||
|
|
||||||
'@ark/schema@0.49.0':
|
'@ark/fs@0.50.0':
|
||||||
resolution: {integrity: sha512-GphZBLpW72iS0v4YkeUtV3YIno35Gimd7+ezbPO9GwEi9kzdUrPVjvf6aXSBAfHikaFc/9pqZOpv3pOXnC71tw==}
|
resolution: {integrity: sha512-6OrxNt2T+/pL4RUMZK/aiVRLIS3acNs5uSpHDyZAP6+OXwXchFSQ1lJTH+uuGBlDWeQxPD7VmymYXLF5s1Eyhw==}
|
||||||
|
|
||||||
'@ark/util@0.49.0':
|
'@ark/schema@0.53.0':
|
||||||
resolution: {integrity: sha512-/BtnX7oCjNkxi2vi6y1399b+9xd1jnCrDYhZ61f0a+3X8x8DxlK52VgEEzyuC2UQMPACIfYrmHkhD3lGt2GaMA==}
|
resolution: {integrity: sha512-1PB7RThUiTlmIu8jbSurPrhHpVixPd4C+xNBUF/HrjIENCeDcAMg36n5mpMzED7OQGDVIzpfXXiMnaTiutjHJw==}
|
||||||
|
|
||||||
|
'@ark/util@0.50.0':
|
||||||
|
resolution: {integrity: sha512-tIkgIMVRpkfXRQIEf0G2CJryZVtHVrqcWHMDa5QKo0OEEBu0tHkRSIMm4Ln8cd8Bn9TPZtvc/kE2Gma8RESPSg==}
|
||||||
|
|
||||||
|
'@ark/util@0.53.0':
|
||||||
|
resolution: {integrity: sha512-TGn4gLlA6dJcQiqrtCtd88JhGb2XBHo6qIejsDre+nxpGuUVW4G3YZGVrwjNBTO0EyR+ykzIo4joHJzOj+/cpA==}
|
||||||
|
|
||||||
|
'@borewit/text-codec@0.1.1':
|
||||||
|
resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==}
|
||||||
|
|
||||||
'@fastify/busboy@2.1.1':
|
'@fastify/busboy@2.1.1':
|
||||||
resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
|
resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
'@img/sharp-darwin-arm64@0.33.5':
|
||||||
|
resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-darwin-x64@0.33.5':
|
||||||
|
resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-arm64@1.0.4':
|
||||||
|
resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-x64@1.0.4':
|
||||||
|
resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm64@1.0.4':
|
||||||
|
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm@1.0.5':
|
||||||
|
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-x64@1.0.4':
|
||||||
|
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm64@0.33.5':
|
||||||
|
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm@0.33.5':
|
||||||
|
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-linux-x64@0.33.5':
|
||||||
|
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@img/sharp-win32-x64@0.33.5':
|
||||||
|
resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
|
||||||
|
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
'@modelcontextprotocol/sdk@1.20.0':
|
'@modelcontextprotocol/sdk@1.20.0':
|
||||||
resolution: {integrity: sha512-kOQ4+fHuT4KbR2iq2IjeV32HiihueuOf1vJkq18z08CLZ1UQrTc8BXJpVfxZkq45+inLLD+D4xx4nBjUelJa4Q==}
|
resolution: {integrity: sha512-kOQ4+fHuT4KbR2iq2IjeV32HiihueuOf1vJkq18z08CLZ1UQrTc8BXJpVfxZkq45+inLLD+D4xx4nBjUelJa4Q==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -142,6 +221,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
|
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@standard-schema/spec@1.0.0':
|
||||||
|
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
|
||||||
|
|
||||||
|
'@tokenizer/inflate@0.2.7':
|
||||||
|
resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
'@tokenizer/token@0.3.0':
|
||||||
|
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
|
||||||
|
|
||||||
'@types/node@24.7.2':
|
'@types/node@24.7.2':
|
||||||
resolution: {integrity: sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==}
|
resolution: {integrity: sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==}
|
||||||
|
|
||||||
@@ -159,15 +248,26 @@ packages:
|
|||||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
ansi-regex@6.2.2:
|
||||||
|
resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
ansi-styles@4.3.0:
|
ansi-styles@4.3.0:
|
||||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
ansi-styles@6.2.3:
|
||||||
|
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
arg@5.0.2:
|
arg@5.0.2:
|
||||||
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
|
||||||
|
|
||||||
arktype@2.1.22:
|
arkregex@0.0.2:
|
||||||
resolution: {integrity: sha512-xdzl6WcAhrdahvRRnXaNwsipCgHuNoLobRqhiP8RjnfL9Gp947abGlo68GAIyLtxbD+MLzNyH2YR4kEqioMmYQ==}
|
resolution: {integrity: sha512-ttjDUICBVoXD/m8bf7eOjx8XMR6yIT2FmmW9vsN0FCcFOygEZvvIX8zK98tTdXkzi0LkRi5CmadB44jFEIyDNA==}
|
||||||
|
|
||||||
|
arktype@2.1.25:
|
||||||
|
resolution: {integrity: sha512-fdj10sNlUPeDRg1QUqMbzJ4Q7gutTOWOpLUNdcC4vxeVrN0G+cbDOvLbuxQOFj/NDAode1G7kwFv4yKwQvupJg==}
|
||||||
|
|
||||||
astral-regex@2.0.0:
|
astral-regex@2.0.0:
|
||||||
resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
|
resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
|
||||||
@@ -192,6 +292,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
|
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
cliui@9.0.1:
|
||||||
|
resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
engines: {node: '>=7.0.0'}
|
engines: {node: '>=7.0.0'}
|
||||||
@@ -247,6 +351,9 @@ packages:
|
|||||||
ee-first@1.1.1:
|
ee-first@1.1.1:
|
||||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||||
|
|
||||||
|
emoji-regex@10.6.0:
|
||||||
|
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
|
||||||
|
|
||||||
emoji-regex@8.0.0:
|
emoji-regex@8.0.0:
|
||||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
|
|
||||||
@@ -266,6 +373,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
escalade@3.2.0:
|
||||||
|
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
escape-html@1.0.3:
|
escape-html@1.0.3:
|
||||||
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
||||||
|
|
||||||
@@ -307,10 +418,21 @@ packages:
|
|||||||
fast-uri@3.1.0:
|
fast-uri@3.1.0:
|
||||||
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
|
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
|
||||||
|
|
||||||
|
fastmcp@3.20.0:
|
||||||
|
resolution: {integrity: sha512-W4Mx0Ft0mqb+BvZ2rHijQQSIKCX8FlRhLOCpK22rMJMxFR33rRyoDRa/WZ4+sDTQc099G7JvkrMBpkTJv1O12g==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
fflate@0.8.2:
|
||||||
|
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
|
||||||
|
|
||||||
figures@6.1.0:
|
figures@6.1.0:
|
||||||
resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
|
resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
file-type@21.0.0:
|
||||||
|
resolution: {integrity: sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
finalhandler@2.1.0:
|
finalhandler@2.1.0:
|
||||||
resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==}
|
resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
@@ -326,6 +448,18 @@ packages:
|
|||||||
function-bind@1.1.2:
|
function-bind@1.1.2:
|
||||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||||
|
|
||||||
|
fuse.js@7.1.0:
|
||||||
|
resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
get-caller-file@2.0.5:
|
||||||
|
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||||
|
engines: {node: 6.* || 8.* || >= 10.*}
|
||||||
|
|
||||||
|
get-east-asian-width@1.4.0:
|
||||||
|
resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
get-intrinsic@1.3.0:
|
get-intrinsic@1.3.0:
|
||||||
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -366,6 +500,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==}
|
resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
ieee754@1.2.1:
|
||||||
|
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||||
|
|
||||||
inherits@2.0.4:
|
inherits@2.0.4:
|
||||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||||
|
|
||||||
@@ -408,6 +545,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
mcp-proxy@5.9.0:
|
||||||
|
resolution: {integrity: sha512-xonJSkuy4wmwXeykBFl0mjRMt4D0pAGCtFIfBFUz4O80VrO92ruJseIdASJO3wN6MdYHi3vbZLOQol3NsUpg4g==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
media-typer@1.1.0:
|
media-typer@1.1.0:
|
||||||
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
|
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
@@ -562,18 +703,33 @@ packages:
|
|||||||
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
|
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
strict-event-emitter-types@2.0.0:
|
||||||
|
resolution: {integrity: sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==}
|
||||||
|
|
||||||
string-width@4.2.3:
|
string-width@4.2.3:
|
||||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
string-width@7.2.0:
|
||||||
|
resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
strip-ansi@6.0.1:
|
strip-ansi@6.0.1:
|
||||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
strip-ansi@7.1.2:
|
||||||
|
resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
strip-final-newline@4.0.0:
|
strip-final-newline@4.0.0:
|
||||||
resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
|
resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
strtok3@10.3.4:
|
||||||
|
resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
table@6.9.0:
|
table@6.9.0:
|
||||||
resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==}
|
resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
@@ -582,6 +738,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||||
engines: {node: '>=0.6'}
|
engines: {node: '>=0.6'}
|
||||||
|
|
||||||
|
token-types@6.1.1:
|
||||||
|
resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==}
|
||||||
|
engines: {node: '>=14.16'}
|
||||||
|
|
||||||
tunnel@0.0.6:
|
tunnel@0.0.6:
|
||||||
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
|
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
|
||||||
engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
|
engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
|
||||||
@@ -595,6 +755,10 @@ packages:
|
|||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
uint8array-extras@1.5.0:
|
||||||
|
resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
undici-types@7.14.0:
|
undici-types@7.14.0:
|
||||||
resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==}
|
resolution: {integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==}
|
||||||
|
|
||||||
@@ -602,6 +766,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==}
|
resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==}
|
||||||
engines: {node: '>=14.0'}
|
engines: {node: '>=14.0'}
|
||||||
|
|
||||||
|
undici@7.16.0:
|
||||||
|
resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==}
|
||||||
|
engines: {node: '>=20.18.1'}
|
||||||
|
|
||||||
unicorn-magic@0.3.0:
|
unicorn-magic@0.3.0:
|
||||||
resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
|
resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -616,6 +784,9 @@ packages:
|
|||||||
uri-js@4.4.1:
|
uri-js@4.4.1:
|
||||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||||
|
|
||||||
|
uri-templates@0.2.0:
|
||||||
|
resolution: {integrity: sha512-EWkjYEN0L6KOfEoOH6Wj4ghQqU7eBZMJqRHQnxQAq+dSEzRPClkWjf8557HkWQXF6BrAUoLSAyy9i3RVTliaNg==}
|
||||||
|
|
||||||
vary@1.1.2:
|
vary@1.1.2:
|
||||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
@@ -625,9 +796,48 @@ packages:
|
|||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
wrap-ansi@9.0.2:
|
||||||
|
resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
wrappy@1.0.2:
|
wrappy@1.0.2:
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
|
|
||||||
|
xsschema@0.3.5:
|
||||||
|
resolution: {integrity: sha512-f+dcy11dTv7aBEEfTbXWnrm/1b/Ds2k4taN0TpN6KCtPAD58kyE/R1znUdrHdBnhIFexj9k+pS1fm6FgtSISrQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@valibot/to-json-schema': ^1.0.0
|
||||||
|
arktype: ^2.1.20
|
||||||
|
effect: ^3.16.0
|
||||||
|
sury: ^10.0.0
|
||||||
|
zod: ^3.25.0 || ^4.0.0
|
||||||
|
zod-to-json-schema: ^3.24.5
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@valibot/to-json-schema':
|
||||||
|
optional: true
|
||||||
|
arktype:
|
||||||
|
optional: true
|
||||||
|
effect:
|
||||||
|
optional: true
|
||||||
|
sury:
|
||||||
|
optional: true
|
||||||
|
zod:
|
||||||
|
optional: true
|
||||||
|
zod-to-json-schema:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
y18n@5.0.8:
|
||||||
|
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
yargs-parser@22.0.0:
|
||||||
|
resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==}
|
||||||
|
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
|
||||||
|
|
||||||
|
yargs@18.0.0:
|
||||||
|
resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==}
|
||||||
|
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
|
||||||
|
|
||||||
yoctocolors@2.1.2:
|
yoctocolors@2.1.2:
|
||||||
resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==}
|
resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -658,16 +868,74 @@ snapshots:
|
|||||||
|
|
||||||
'@actions/io@1.1.3': {}
|
'@actions/io@1.1.3': {}
|
||||||
|
|
||||||
'@ark/fs@0.49.0': {}
|
'@anthropic-ai/claude-agent-sdk@0.1.30(zod@3.25.76)':
|
||||||
|
|
||||||
'@ark/schema@0.49.0':
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ark/util': 0.49.0
|
zod: 3.25.76
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-darwin-arm64': 0.33.5
|
||||||
|
'@img/sharp-darwin-x64': 0.33.5
|
||||||
|
'@img/sharp-linux-arm': 0.33.5
|
||||||
|
'@img/sharp-linux-arm64': 0.33.5
|
||||||
|
'@img/sharp-linux-x64': 0.33.5
|
||||||
|
'@img/sharp-win32-x64': 0.33.5
|
||||||
|
|
||||||
'@ark/util@0.49.0': {}
|
'@ark/fs@0.50.0': {}
|
||||||
|
|
||||||
|
'@ark/schema@0.53.0':
|
||||||
|
dependencies:
|
||||||
|
'@ark/util': 0.53.0
|
||||||
|
|
||||||
|
'@ark/util@0.50.0': {}
|
||||||
|
|
||||||
|
'@ark/util@0.53.0': {}
|
||||||
|
|
||||||
|
'@borewit/text-codec@0.1.1': {}
|
||||||
|
|
||||||
'@fastify/busboy@2.1.1': {}
|
'@fastify/busboy@2.1.1': {}
|
||||||
|
|
||||||
|
'@img/sharp-darwin-arm64@0.33.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-darwin-arm64': 1.0.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-darwin-x64@0.33.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-darwin-x64': 1.0.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-arm64@1.0.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-darwin-x64@1.0.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm64@1.0.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-arm@1.0.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-libvips-linux-x64@1.0.4':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm64@0.33.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-arm64': 1.0.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-arm@0.33.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-arm': 1.0.5
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-linux-x64@0.33.5':
|
||||||
|
optionalDependencies:
|
||||||
|
'@img/sharp-libvips-linux-x64': 1.0.4
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@img/sharp-win32-x64@0.33.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@modelcontextprotocol/sdk@1.20.0':
|
'@modelcontextprotocol/sdk@1.20.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv: 6.12.6
|
ajv: 6.12.6
|
||||||
@@ -753,6 +1021,18 @@ snapshots:
|
|||||||
|
|
||||||
'@sindresorhus/merge-streams@4.0.0': {}
|
'@sindresorhus/merge-streams@4.0.0': {}
|
||||||
|
|
||||||
|
'@standard-schema/spec@1.0.0': {}
|
||||||
|
|
||||||
|
'@tokenizer/inflate@0.2.7':
|
||||||
|
dependencies:
|
||||||
|
debug: 4.4.3
|
||||||
|
fflate: 0.8.2
|
||||||
|
token-types: 6.1.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
|
'@tokenizer/token@0.3.0': {}
|
||||||
|
|
||||||
'@types/node@24.7.2':
|
'@types/node@24.7.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 7.14.0
|
undici-types: 7.14.0
|
||||||
@@ -778,16 +1058,25 @@ snapshots:
|
|||||||
|
|
||||||
ansi-regex@5.0.1: {}
|
ansi-regex@5.0.1: {}
|
||||||
|
|
||||||
|
ansi-regex@6.2.2: {}
|
||||||
|
|
||||||
ansi-styles@4.3.0:
|
ansi-styles@4.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-convert: 2.0.1
|
color-convert: 2.0.1
|
||||||
|
|
||||||
|
ansi-styles@6.2.3: {}
|
||||||
|
|
||||||
arg@5.0.2: {}
|
arg@5.0.2: {}
|
||||||
|
|
||||||
arktype@2.1.22:
|
arkregex@0.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ark/schema': 0.49.0
|
'@ark/util': 0.53.0
|
||||||
'@ark/util': 0.49.0
|
|
||||||
|
arktype@2.1.25:
|
||||||
|
dependencies:
|
||||||
|
'@ark/schema': 0.53.0
|
||||||
|
'@ark/util': 0.53.0
|
||||||
|
arkregex: 0.0.2
|
||||||
|
|
||||||
astral-regex@2.0.0: {}
|
astral-regex@2.0.0: {}
|
||||||
|
|
||||||
@@ -819,6 +1108,12 @@ snapshots:
|
|||||||
call-bind-apply-helpers: 1.0.2
|
call-bind-apply-helpers: 1.0.2
|
||||||
get-intrinsic: 1.3.0
|
get-intrinsic: 1.3.0
|
||||||
|
|
||||||
|
cliui@9.0.1:
|
||||||
|
dependencies:
|
||||||
|
string-width: 7.2.0
|
||||||
|
strip-ansi: 7.1.2
|
||||||
|
wrap-ansi: 9.0.2
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-name: 1.1.4
|
color-name: 1.1.4
|
||||||
@@ -862,6 +1157,8 @@ snapshots:
|
|||||||
|
|
||||||
ee-first@1.1.1: {}
|
ee-first@1.1.1: {}
|
||||||
|
|
||||||
|
emoji-regex@10.6.0: {}
|
||||||
|
|
||||||
emoji-regex@8.0.0: {}
|
emoji-regex@8.0.0: {}
|
||||||
|
|
||||||
encodeurl@2.0.0: {}
|
encodeurl@2.0.0: {}
|
||||||
@@ -874,6 +1171,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
|
|
||||||
|
escalade@3.2.0: {}
|
||||||
|
|
||||||
escape-html@1.0.3: {}
|
escape-html@1.0.3: {}
|
||||||
|
|
||||||
etag@1.8.1: {}
|
etag@1.8.1: {}
|
||||||
@@ -943,10 +1242,43 @@ snapshots:
|
|||||||
|
|
||||||
fast-uri@3.1.0: {}
|
fast-uri@3.1.0: {}
|
||||||
|
|
||||||
|
fastmcp@3.20.0(arktype@2.1.25):
|
||||||
|
dependencies:
|
||||||
|
'@modelcontextprotocol/sdk': 1.20.0
|
||||||
|
'@standard-schema/spec': 1.0.0
|
||||||
|
execa: 9.6.0
|
||||||
|
file-type: 21.0.0
|
||||||
|
fuse.js: 7.1.0
|
||||||
|
mcp-proxy: 5.9.0
|
||||||
|
strict-event-emitter-types: 2.0.0
|
||||||
|
undici: 7.16.0
|
||||||
|
uri-templates: 0.2.0
|
||||||
|
xsschema: 0.3.5(arktype@2.1.25)(zod-to-json-schema@3.24.6(zod@3.25.76))(zod@3.25.76)
|
||||||
|
yargs: 18.0.0
|
||||||
|
zod: 3.25.76
|
||||||
|
zod-to-json-schema: 3.24.6(zod@3.25.76)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@valibot/to-json-schema'
|
||||||
|
- arktype
|
||||||
|
- effect
|
||||||
|
- supports-color
|
||||||
|
- sury
|
||||||
|
|
||||||
|
fflate@0.8.2: {}
|
||||||
|
|
||||||
figures@6.1.0:
|
figures@6.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-unicode-supported: 2.1.0
|
is-unicode-supported: 2.1.0
|
||||||
|
|
||||||
|
file-type@21.0.0:
|
||||||
|
dependencies:
|
||||||
|
'@tokenizer/inflate': 0.2.7
|
||||||
|
strtok3: 10.3.4
|
||||||
|
token-types: 6.1.1
|
||||||
|
uint8array-extras: 1.5.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
finalhandler@2.1.0:
|
finalhandler@2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
@@ -964,6 +1296,12 @@ snapshots:
|
|||||||
|
|
||||||
function-bind@1.1.2: {}
|
function-bind@1.1.2: {}
|
||||||
|
|
||||||
|
fuse.js@7.1.0: {}
|
||||||
|
|
||||||
|
get-caller-file@2.0.5: {}
|
||||||
|
|
||||||
|
get-east-asian-width@1.4.0: {}
|
||||||
|
|
||||||
get-intrinsic@1.3.0:
|
get-intrinsic@1.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind-apply-helpers: 1.0.2
|
call-bind-apply-helpers: 1.0.2
|
||||||
@@ -1013,6 +1351,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safer-buffer: 2.1.2
|
safer-buffer: 2.1.2
|
||||||
|
|
||||||
|
ieee754@1.2.1: {}
|
||||||
|
|
||||||
inherits@2.0.4: {}
|
inherits@2.0.4: {}
|
||||||
|
|
||||||
ipaddr.js@1.9.1: {}
|
ipaddr.js@1.9.1: {}
|
||||||
@@ -1037,6 +1377,8 @@ snapshots:
|
|||||||
|
|
||||||
math-intrinsics@1.1.0: {}
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
|
mcp-proxy@5.9.0: {}
|
||||||
|
|
||||||
media-typer@1.1.0: {}
|
media-typer@1.1.0: {}
|
||||||
|
|
||||||
merge-descriptors@2.0.0: {}
|
merge-descriptors@2.0.0: {}
|
||||||
@@ -1193,18 +1535,34 @@ snapshots:
|
|||||||
|
|
||||||
statuses@2.0.2: {}
|
statuses@2.0.2: {}
|
||||||
|
|
||||||
|
strict-event-emitter-types@2.0.0: {}
|
||||||
|
|
||||||
string-width@4.2.3:
|
string-width@4.2.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
emoji-regex: 8.0.0
|
emoji-regex: 8.0.0
|
||||||
is-fullwidth-code-point: 3.0.0
|
is-fullwidth-code-point: 3.0.0
|
||||||
strip-ansi: 6.0.1
|
strip-ansi: 6.0.1
|
||||||
|
|
||||||
|
string-width@7.2.0:
|
||||||
|
dependencies:
|
||||||
|
emoji-regex: 10.6.0
|
||||||
|
get-east-asian-width: 1.4.0
|
||||||
|
strip-ansi: 7.1.2
|
||||||
|
|
||||||
strip-ansi@6.0.1:
|
strip-ansi@6.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-regex: 5.0.1
|
ansi-regex: 5.0.1
|
||||||
|
|
||||||
|
strip-ansi@7.1.2:
|
||||||
|
dependencies:
|
||||||
|
ansi-regex: 6.2.2
|
||||||
|
|
||||||
strip-final-newline@4.0.0: {}
|
strip-final-newline@4.0.0: {}
|
||||||
|
|
||||||
|
strtok3@10.3.4:
|
||||||
|
dependencies:
|
||||||
|
'@tokenizer/token': 0.3.0
|
||||||
|
|
||||||
table@6.9.0:
|
table@6.9.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv: 8.17.1
|
ajv: 8.17.1
|
||||||
@@ -1215,6 +1573,12 @@ snapshots:
|
|||||||
|
|
||||||
toidentifier@1.0.1: {}
|
toidentifier@1.0.1: {}
|
||||||
|
|
||||||
|
token-types@6.1.1:
|
||||||
|
dependencies:
|
||||||
|
'@borewit/text-codec': 0.1.1
|
||||||
|
'@tokenizer/token': 0.3.0
|
||||||
|
ieee754: 1.2.1
|
||||||
|
|
||||||
tunnel@0.0.6: {}
|
tunnel@0.0.6: {}
|
||||||
|
|
||||||
type-is@2.0.1:
|
type-is@2.0.1:
|
||||||
@@ -1225,12 +1589,16 @@ snapshots:
|
|||||||
|
|
||||||
typescript@5.9.3: {}
|
typescript@5.9.3: {}
|
||||||
|
|
||||||
|
uint8array-extras@1.5.0: {}
|
||||||
|
|
||||||
undici-types@7.14.0: {}
|
undici-types@7.14.0: {}
|
||||||
|
|
||||||
undici@5.29.0:
|
undici@5.29.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@fastify/busboy': 2.1.1
|
'@fastify/busboy': 2.1.1
|
||||||
|
|
||||||
|
undici@7.16.0: {}
|
||||||
|
|
||||||
unicorn-magic@0.3.0: {}
|
unicorn-magic@0.3.0: {}
|
||||||
|
|
||||||
universal-user-agent@7.0.3: {}
|
universal-user-agent@7.0.3: {}
|
||||||
@@ -1241,14 +1609,41 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.3.1
|
punycode: 2.3.1
|
||||||
|
|
||||||
|
uri-templates@0.2.0: {}
|
||||||
|
|
||||||
vary@1.1.2: {}
|
vary@1.1.2: {}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
|
|
||||||
|
wrap-ansi@9.0.2:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 6.2.3
|
||||||
|
string-width: 7.2.0
|
||||||
|
strip-ansi: 7.1.2
|
||||||
|
|
||||||
wrappy@1.0.2: {}
|
wrappy@1.0.2: {}
|
||||||
|
|
||||||
|
xsschema@0.3.5(arktype@2.1.25)(zod-to-json-schema@3.24.6(zod@3.25.76))(zod@3.25.76):
|
||||||
|
optionalDependencies:
|
||||||
|
arktype: 2.1.25
|
||||||
|
zod: 3.25.76
|
||||||
|
zod-to-json-schema: 3.24.6(zod@3.25.76)
|
||||||
|
|
||||||
|
y18n@5.0.8: {}
|
||||||
|
|
||||||
|
yargs-parser@22.0.0: {}
|
||||||
|
|
||||||
|
yargs@18.0.0:
|
||||||
|
dependencies:
|
||||||
|
cliui: 9.0.1
|
||||||
|
escalade: 3.2.0
|
||||||
|
get-caller-file: 2.0.5
|
||||||
|
string-width: 7.2.0
|
||||||
|
y18n: 5.0.8
|
||||||
|
yargs-parser: 22.0.0
|
||||||
|
|
||||||
yoctocolors@2.1.2: {}
|
yoctocolors@2.1.2: {}
|
||||||
|
|
||||||
zod-to-json-schema@3.24.6(zod@3.25.76):
|
zod-to-json-schema@3.24.6(zod@3.25.76):
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import { execSync } from "node:child_process";
|
|
||||||
import { existsSync } from "node:fs";
|
|
||||||
import { dirname, join } from "node:path";
|
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
import { config } from "dotenv";
|
|
||||||
import { buildAction, setupTestRepo } from "./setup.ts";
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = dirname(__filename);
|
|
||||||
|
|
||||||
const tempDir = join(__dirname, "..", ".temp");
|
|
||||||
const actionPath = join(__dirname, "..");
|
|
||||||
const envPath = join(__dirname, "..", "..", ".env");
|
|
||||||
|
|
||||||
const ENV_VARS = ["ANTHROPIC_API_KEY", "GITHUB_INSTALLATION_TOKEN"];
|
|
||||||
|
|
||||||
export function runAct(prompt: string): void {
|
|
||||||
setupTestRepo({ tempDir });
|
|
||||||
|
|
||||||
config({ path: envPath });
|
|
||||||
|
|
||||||
buildAction(actionPath);
|
|
||||||
|
|
||||||
const workflowPath = join(tempDir, ".github", "workflows", "pullfrog.yml");
|
|
||||||
|
|
||||||
const distPath = join(actionPath, ".act-dist");
|
|
||||||
console.log("📦 Creating minimal distribution for act...");
|
|
||||||
execSync(`rm -rf "${distPath}" && mkdir -p "${distPath}"`, { shell: "/bin/bash" });
|
|
||||||
|
|
||||||
["action.yml", "entry.cjs", "index.cjs", "package.json"].forEach((file) => {
|
|
||||||
const src = join(actionPath, file);
|
|
||||||
if (existsSync(src)) {
|
|
||||||
execSync(`cp "${src}" "${distPath}"`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const escapedPrompt = prompt.replace(/'/g, "'\\''");
|
|
||||||
|
|
||||||
const actCommandParts = [
|
|
||||||
"act",
|
|
||||||
"workflow_dispatch",
|
|
||||||
"-W",
|
|
||||||
workflowPath,
|
|
||||||
"--input",
|
|
||||||
`prompt='${escapedPrompt}'`,
|
|
||||||
"--local-repository",
|
|
||||||
`pullfrog/action@v0=${distPath}`, // Use minimal dist without symlinks
|
|
||||||
];
|
|
||||||
|
|
||||||
ENV_VARS.forEach((key) => {
|
|
||||||
if (process.env[key]) {
|
|
||||||
actCommandParts.push("-s", key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const actCommand = actCommandParts.join(" ");
|
|
||||||
|
|
||||||
console.log("🚀 Running act with prompt:");
|
|
||||||
console.log("─".repeat(50));
|
|
||||||
console.log(prompt);
|
|
||||||
console.log("─".repeat(50));
|
|
||||||
console.log("");
|
|
||||||
|
|
||||||
execSync(actCommand, {
|
|
||||||
stdio: "inherit",
|
|
||||||
cwd: join(__dirname, "..", ".."),
|
|
||||||
});
|
|
||||||
execSync(`rm -rf "${distPath}"`);
|
|
||||||
} catch (error) {
|
|
||||||
execSync(`rm -rf "${distPath}"`);
|
|
||||||
console.error("❌ Act execution failed:", (error as Error).message);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+186
@@ -0,0 +1,186 @@
|
|||||||
|
/**
|
||||||
|
* CLI output utilities that work well in both local and GitHub Actions environments
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
|
||||||
|
const isGitHubActions = !!process.env.GITHUB_ACTIONS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a formatted box with text (for console output)
|
||||||
|
*/
|
||||||
|
function boxString(
|
||||||
|
text: string,
|
||||||
|
options?: {
|
||||||
|
title?: string;
|
||||||
|
maxWidth?: number;
|
||||||
|
indent?: string;
|
||||||
|
padding?: number;
|
||||||
|
}
|
||||||
|
): string {
|
||||||
|
const { title, maxWidth = 80, indent = "", padding = 1 } = options || {};
|
||||||
|
|
||||||
|
const lines = text.trim().split("\n");
|
||||||
|
const wrappedLines: string[] = [];
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.length <= maxWidth - padding * 2) {
|
||||||
|
wrappedLines.push(line);
|
||||||
|
} else {
|
||||||
|
const words = line.split(" ");
|
||||||
|
let currentLine = "";
|
||||||
|
|
||||||
|
for (const word of words) {
|
||||||
|
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
||||||
|
if (testLine.length <= maxWidth - padding * 2) {
|
||||||
|
currentLine = testLine;
|
||||||
|
} else {
|
||||||
|
if (currentLine) {
|
||||||
|
wrappedLines.push(currentLine);
|
||||||
|
currentLine = word;
|
||||||
|
} else {
|
||||||
|
wrappedLines.push(word.substring(0, maxWidth - padding * 2));
|
||||||
|
currentLine = word.substring(maxWidth - padding * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentLine) {
|
||||||
|
wrappedLines.push(currentLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxLineLength = Math.max(...wrappedLines.map((line) => line.length));
|
||||||
|
const boxWidth = maxLineLength + padding * 2;
|
||||||
|
|
||||||
|
let result = "";
|
||||||
|
|
||||||
|
if (title) {
|
||||||
|
const titleLine = ` ${title} `;
|
||||||
|
const titlePadding = Math.max(0, boxWidth - titleLine.length);
|
||||||
|
result += `${indent}┌${titleLine}${"─".repeat(titlePadding)}┐\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!title) {
|
||||||
|
result += `${indent}┌${"─".repeat(boxWidth)}┐\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const line of wrappedLines) {
|
||||||
|
const paddedLine = line.padEnd(maxLineLength);
|
||||||
|
result += `${indent}│${" ".repeat(padding)}${paddedLine}${" ".repeat(padding)}│\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += `${indent}└${"─".repeat(boxWidth)}┘`;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a formatted box with text
|
||||||
|
* Works well in both local and GitHub Actions environments
|
||||||
|
*/
|
||||||
|
function box(
|
||||||
|
text: string,
|
||||||
|
options?: {
|
||||||
|
title?: string;
|
||||||
|
maxWidth?: number;
|
||||||
|
}
|
||||||
|
): void {
|
||||||
|
core.info(boxString(text, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a table to GitHub Actions job summary (rich formatting)
|
||||||
|
* Also logs to console. Only use this once at the end of execution.
|
||||||
|
*/
|
||||||
|
async function summaryTable(
|
||||||
|
rows: Array<Array<{ data: string; header?: boolean } | string>>,
|
||||||
|
options?: {
|
||||||
|
title?: string;
|
||||||
|
}
|
||||||
|
): Promise<void> {
|
||||||
|
const { title } = options || {};
|
||||||
|
|
||||||
|
// Convert rows to format expected by Job Summaries API
|
||||||
|
const formattedRows = rows.map((row) =>
|
||||||
|
row.map((cell) => {
|
||||||
|
if (typeof cell === "string") {
|
||||||
|
return { data: cell };
|
||||||
|
}
|
||||||
|
return cell;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isGitHubActions) {
|
||||||
|
const summary = core.summary;
|
||||||
|
if (title) {
|
||||||
|
summary.addHeading(title);
|
||||||
|
}
|
||||||
|
summary.addTable(formattedRows);
|
||||||
|
await summary.write();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also log to console for visibility
|
||||||
|
if (title) {
|
||||||
|
core.info(`\n${title}`);
|
||||||
|
}
|
||||||
|
const tableText = formattedRows.map((row) => row.map((cell) => cell.data).join(" | ")).join("\n");
|
||||||
|
core.info(`\n${tableText}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a separator line
|
||||||
|
*/
|
||||||
|
function separator(length: number = 50): void {
|
||||||
|
core.info("─".repeat(length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main logging utility object - import this once and access all utilities
|
||||||
|
*/
|
||||||
|
export const log = {
|
||||||
|
/**
|
||||||
|
* Print info message
|
||||||
|
*/
|
||||||
|
info: (message: string): void => {
|
||||||
|
core.info(message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print warning message
|
||||||
|
*/
|
||||||
|
warning: (message: string): void => {
|
||||||
|
core.warning(message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print error message
|
||||||
|
*/
|
||||||
|
error: (message: string): void => {
|
||||||
|
core.error(message);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print success message
|
||||||
|
*/
|
||||||
|
success: (message: string): void => {
|
||||||
|
core.info(`✅ ${message}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a formatted box with text
|
||||||
|
*/
|
||||||
|
box,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a table to GitHub Actions job summary (rich formatting)
|
||||||
|
* Only use this once at the end of execution
|
||||||
|
*/
|
||||||
|
summaryTable,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a separator line
|
||||||
|
*/
|
||||||
|
separator,
|
||||||
|
};
|
||||||
+26
-5
@@ -1,6 +1,5 @@
|
|||||||
import { createSign } from "node:crypto";
|
import { createSign } from "node:crypto";
|
||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import { resolveRepoContext } from "./repo-context.ts";
|
|
||||||
|
|
||||||
export interface InstallationToken {
|
export interface InstallationToken {
|
||||||
token: string;
|
token: string;
|
||||||
@@ -55,7 +54,7 @@ function isGitHubActionsEnvironment(): boolean {
|
|||||||
|
|
||||||
async function acquireTokenViaOIDC(): Promise<string> {
|
async function acquireTokenViaOIDC(): Promise<string> {
|
||||||
core.info("Generating OIDC token...");
|
core.info("Generating OIDC token...");
|
||||||
|
|
||||||
const oidcToken = await core.getIDToken("pullfrog-api");
|
const oidcToken = await core.getIDToken("pullfrog-api");
|
||||||
core.info("OIDC token generated successfully");
|
core.info("OIDC token generated successfully");
|
||||||
|
|
||||||
@@ -208,7 +207,7 @@ const findInstallationId = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function acquireTokenViaGitHubApp(): Promise<string> {
|
async function acquireTokenViaGitHubApp(): Promise<string> {
|
||||||
const repoContext = resolveRepoContext();
|
const repoContext = parseRepoContext();
|
||||||
|
|
||||||
const config: GitHubAppConfig = {
|
const config: GitHubAppConfig = {
|
||||||
appId: process.env.GITHUB_APP_ID!,
|
appId: process.env.GITHUB_APP_ID!,
|
||||||
@@ -244,9 +243,31 @@ export async function setupGitHubInstallationToken(): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const token = await acquireNewToken();
|
const token = await acquireNewToken();
|
||||||
|
|
||||||
core.setSecret(token);
|
core.setSecret(token);
|
||||||
process.env.GITHUB_INSTALLATION_TOKEN = token;
|
process.env.GITHUB_INSTALLATION_TOKEN = token;
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RepoContext {
|
||||||
|
owner: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse repository context from GITHUB_REPOSITORY environment variable.
|
||||||
|
*/
|
||||||
|
export function parseRepoContext(): RepoContext {
|
||||||
|
const githubRepo = process.env.GITHUB_REPOSITORY;
|
||||||
|
if (!githubRepo) {
|
||||||
|
throw new Error("GITHUB_REPOSITORY environment variable is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
const [owner, name] = githubRepo.split("/");
|
||||||
|
if (!owner || !name) {
|
||||||
|
throw new Error(`Invalid GITHUB_REPOSITORY format: ${githubRepo}. Expected 'owner/repo'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { owner, name };
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Centralized debug logging utility
|
||||||
|
* Controls debug behavior based on LOG_LEVEL environment variable
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
|
||||||
|
const isDebugEnabled = process.env.LOG_LEVEL === "debug";
|
||||||
|
const isGitHubActions = !!process.env.GITHUB_ACTIONS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if debug logging is enabled
|
||||||
|
*/
|
||||||
|
export function isDebug(): boolean {
|
||||||
|
return isDebugEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log debug message if debug is enabled
|
||||||
|
* Uses core.debug() in GitHub Actions, console.log() locally
|
||||||
|
*/
|
||||||
|
export function debugLog(message: string): void {
|
||||||
|
if (isDebugEnabled) {
|
||||||
|
if (isGitHubActions) {
|
||||||
|
core.debug(message);
|
||||||
|
} else {
|
||||||
|
console.log(`[DEBUG] ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
export interface RepoContext {
|
|
||||||
owner: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve repository context from GITHUB_REPOSITORY environment variable.
|
|
||||||
* Throws if not available.
|
|
||||||
*/
|
|
||||||
export function resolveRepoContext(): RepoContext {
|
|
||||||
const githubRepo = process.env.GITHUB_REPOSITORY;
|
|
||||||
if (!githubRepo) {
|
|
||||||
throw new Error('GITHUB_REPOSITORY environment variable is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
const [owner, name] = githubRepo.split('/');
|
|
||||||
if (!owner || !name) {
|
|
||||||
throw new Error(`Invalid GITHUB_REPOSITORY format: ${githubRepo}. Expected 'owner/repo'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { owner, name };
|
|
||||||
}
|
|
||||||
+26
-7
@@ -1,5 +1,6 @@
|
|||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import { existsSync, rmSync } from "node:fs";
|
import { existsSync, rmSync } from "node:fs";
|
||||||
|
import type { RepoContext } from "./github.ts";
|
||||||
|
|
||||||
export interface SetupOptions {
|
export interface SetupOptions {
|
||||||
tempDir: string;
|
tempDir: string;
|
||||||
@@ -38,12 +39,30 @@ export function setupTestRepo(options: SetupOptions): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the action bundles
|
* Setup git configuration to avoid identity errors
|
||||||
*/
|
*/
|
||||||
export function buildAction(actionPath: string): void {
|
export function setupGitConfig(): void {
|
||||||
console.log("🔨 Building fresh bundles with esbuild...");
|
console.log("🔧 Setting up git configuration...");
|
||||||
execSync("node esbuild.config.js", {
|
execSync('git config user.email "action@pullfrog.ai"', { stdio: "inherit" });
|
||||||
cwd: actionPath,
|
execSync('git config user.name "Pullfrog Action"', { stdio: "inherit" });
|
||||||
stdio: "inherit",
|
}
|
||||||
});
|
|
||||||
|
/**
|
||||||
|
* Setup git authentication using GitHub token
|
||||||
|
*/
|
||||||
|
export function setupGitAuth(githubToken: string, repoContext: RepoContext): void {
|
||||||
|
console.log("🔐 Setting up git authentication...");
|
||||||
|
|
||||||
|
// Remove existing git auth headers that actions/checkout might have set
|
||||||
|
try {
|
||||||
|
execSync("git config --unset-all http.https://github.com/.extraheader", { stdio: "inherit" });
|
||||||
|
console.log("✓ Removed existing authentication headers");
|
||||||
|
} catch {
|
||||||
|
console.log("No existing authentication headers to remove");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update remote URL to embed the token
|
||||||
|
const remoteUrl = `https://x-access-token:${githubToken}@github.com/${repoContext.owner}/${repoContext.name}.git`;
|
||||||
|
execSync(`git remote set-url origin "${remoteUrl}"`, { stdio: "inherit" });
|
||||||
|
console.log("✓ Updated remote URL with authentication token");
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -6,6 +6,7 @@ export interface SpawnOptions {
|
|||||||
env?: Record<string, string>;
|
env?: Record<string, string>;
|
||||||
input?: string;
|
input?: string;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
|
cwd?: string;
|
||||||
onStdout?: (chunk: string) => void;
|
onStdout?: (chunk: string) => void;
|
||||||
onStderr?: (chunk: string) => void;
|
onStderr?: (chunk: string) => void;
|
||||||
}
|
}
|
||||||
@@ -21,7 +22,7 @@ export interface SpawnResult {
|
|||||||
* Spawn a subprocess with streaming callbacks and buffered results
|
* Spawn a subprocess with streaming callbacks and buffered results
|
||||||
*/
|
*/
|
||||||
export async function spawn(options: SpawnOptions): Promise<SpawnResult> {
|
export async function spawn(options: SpawnOptions): Promise<SpawnResult> {
|
||||||
const { cmd, args, env, input, timeout, onStdout, onStderr } = options;
|
const { cmd, args, env, input, timeout, cwd, onStdout, onStderr } = options;
|
||||||
|
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
let stdoutBuffer = "";
|
let stdoutBuffer = "";
|
||||||
@@ -31,6 +32,7 @@ export async function spawn(options: SpawnOptions): Promise<SpawnResult> {
|
|||||||
const child = nodeSpawn(cmd, args, {
|
const child = nodeSpawn(cmd, args, {
|
||||||
env: env ? { ...process.env, ...env } : process.env,
|
env: env ? { ...process.env, ...env } : process.env,
|
||||||
stdio: ["pipe", "pipe", "pipe"],
|
stdio: ["pipe", "pipe", "pipe"],
|
||||||
|
cwd: cwd || process.cwd(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let timeoutId: NodeJS.Timeout | undefined;
|
let timeoutId: NodeJS.Timeout | undefined;
|
||||||
|
|||||||
-142
@@ -1,142 +0,0 @@
|
|||||||
import { table } from "table";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print a formatted table with consistent styling
|
|
||||||
* @param rows - Array of string arrays representing table rows
|
|
||||||
* @param options - Optional table configuration
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function tableString(
|
|
||||||
rows: string[][],
|
|
||||||
options?: {
|
|
||||||
title?: string;
|
|
||||||
indent?: string;
|
|
||||||
drawHorizontalLine?: (lineIndex: number, rowCount: number) => boolean;
|
|
||||||
}
|
|
||||||
): string {
|
|
||||||
const {
|
|
||||||
title,
|
|
||||||
indent = "",
|
|
||||||
drawHorizontalLine = (lineIndex: number, rowCount: number) => {
|
|
||||||
return lineIndex === 0 || (options?.title && lineIndex === 1) || lineIndex === rowCount;
|
|
||||||
},
|
|
||||||
} = options || {};
|
|
||||||
|
|
||||||
if (title) {
|
|
||||||
rows.unshift([title]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tableOutput = table(rows, {
|
|
||||||
drawHorizontalLine,
|
|
||||||
border: {
|
|
||||||
topBody: `─`,
|
|
||||||
topJoin: `┬`,
|
|
||||||
topLeft: `┌`,
|
|
||||||
topRight: `┐`,
|
|
||||||
|
|
||||||
bottomBody: `─`,
|
|
||||||
bottomJoin: `┴`,
|
|
||||||
bottomLeft: `└`,
|
|
||||||
bottomRight: `┘`,
|
|
||||||
|
|
||||||
bodyLeft: `│`,
|
|
||||||
bodyRight: `│`,
|
|
||||||
bodyJoin: `│`,
|
|
||||||
|
|
||||||
joinBody: `─`,
|
|
||||||
joinLeft: `├`,
|
|
||||||
joinRight: `┤`,
|
|
||||||
joinJoin: `┼`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const indentedOutput = tableOutput.split("\n").join(`\n${indent}`).trim();
|
|
||||||
|
|
||||||
return `${indent}${indentedOutput}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print a multi-line string in a formatted box with text wrapping
|
|
||||||
* @param text - The text to display
|
|
||||||
* @param options - Optional configuration for the box
|
|
||||||
*/
|
|
||||||
export function boxString(
|
|
||||||
text: string,
|
|
||||||
options?: {
|
|
||||||
title?: string;
|
|
||||||
maxWidth?: number;
|
|
||||||
indent?: string;
|
|
||||||
padding?: number;
|
|
||||||
}
|
|
||||||
): string {
|
|
||||||
const { title, maxWidth = 80, indent = "", padding = 1 } = options || {};
|
|
||||||
|
|
||||||
// Clean up the text and split into lines
|
|
||||||
const lines = text.trim().split("\n");
|
|
||||||
|
|
||||||
// Word wrap each line to fit within maxWidth
|
|
||||||
const wrappedLines: string[] = [];
|
|
||||||
for (const line of lines) {
|
|
||||||
if (line.length <= maxWidth - padding * 2) {
|
|
||||||
wrappedLines.push(line);
|
|
||||||
} else {
|
|
||||||
// Word wrap the line
|
|
||||||
const words = line.split(" ");
|
|
||||||
let currentLine = "";
|
|
||||||
|
|
||||||
for (const word of words) {
|
|
||||||
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
||||||
if (testLine.length <= maxWidth - padding * 2) {
|
|
||||||
currentLine = testLine;
|
|
||||||
} else {
|
|
||||||
if (currentLine) {
|
|
||||||
wrappedLines.push(currentLine);
|
|
||||||
currentLine = word;
|
|
||||||
} else {
|
|
||||||
// Word is too long, break it
|
|
||||||
wrappedLines.push(word.substring(0, maxWidth - padding * 2));
|
|
||||||
currentLine = word.substring(maxWidth - padding * 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentLine) {
|
|
||||||
wrappedLines.push(currentLine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the maximum line length for box sizing
|
|
||||||
const maxLineLength = Math.max(...wrappedLines.map((line) => line.length));
|
|
||||||
const boxWidth = maxLineLength + padding * 2;
|
|
||||||
|
|
||||||
// Create the box
|
|
||||||
const topBorder = "┌" + "─".repeat(boxWidth) + "┐";
|
|
||||||
const bottomBorder = "└" + "─".repeat(boxWidth) + "┘";
|
|
||||||
const sideBorder = "│";
|
|
||||||
|
|
||||||
let result = "";
|
|
||||||
|
|
||||||
// Add title if provided
|
|
||||||
if (title) {
|
|
||||||
const titleLine = ` ${title} `;
|
|
||||||
const titlePadding = Math.max(0, boxWidth - titleLine.length);
|
|
||||||
result += `${indent}┌${titleLine}${"─".repeat(titlePadding)}┐\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add top border (or title border)
|
|
||||||
if (!title) {
|
|
||||||
result += `${indent}${topBorder}\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add content lines
|
|
||||||
for (const line of wrappedLines) {
|
|
||||||
const paddedLine = line.padEnd(maxLineLength);
|
|
||||||
result += `${indent}${sideBorder}${" ".repeat(padding)}${paddedLine}${" ".repeat(padding)}${sideBorder}\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add bottom border
|
|
||||||
result += `${indent}${bottomBorder}`;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user