Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d633da1be | |||
| ede6cfdfbe | |||
| 7745a0befb | |||
| 1abbd7ff41 | |||
| 6a0d9cc244 |
@@ -0,0 +1,64 @@
|
|||||||
|
name: Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- "package.json"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "20"
|
||||||
|
cache: "pnpm"
|
||||||
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build for npm with zshy
|
||||||
|
run: |
|
||||||
|
cd action
|
||||||
|
pnpm build:npm
|
||||||
|
|
||||||
|
- name: Get version from package.json
|
||||||
|
id: get_version
|
||||||
|
run: |
|
||||||
|
cd action
|
||||||
|
VERSION=$(node -p "require('./package.json').version")
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "Publishing version: $VERSION"
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: v${{ steps.get_version.outputs.version }}
|
||||||
|
release_name: "@pullfrog/action v${{ steps.get_version.outputs.version }}"
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
- name: Publish to npm
|
||||||
|
run: |
|
||||||
|
cd action
|
||||||
|
npm publish --access public
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
@@ -79,7 +79,7 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ steps.version.outputs.tag }}
|
tag_name: ${{ steps.version.outputs.tag }}
|
||||||
release_name: Action Release ${{ steps.version.outputs.tag }}
|
release_name: ${{ steps.version.outputs.tag }}
|
||||||
body: |
|
body: |
|
||||||
Automated release for action version ${{ steps.version.outputs.version }}
|
Automated release for action version ${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
+12
-2
@@ -1,4 +1,4 @@
|
|||||||
# macOS settings file
|
# macOS settings file
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# Contains all your dependencies
|
# Contains all your dependencies
|
||||||
@@ -34,4 +34,14 @@ yarn-error.log*
|
|||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
vite.config.ts.timestamp-*
|
vite.config.ts.timestamp-*
|
||||||
|
|
||||||
examples
|
examples
|
||||||
|
|
||||||
|
# Act temporary distribution directory
|
||||||
|
.act-dist/
|
||||||
|
|
||||||
|
# Temporary backup of node_modules
|
||||||
|
.node_modules_backup/
|
||||||
|
|
||||||
|
# Temporary directory for cloned repos
|
||||||
|
.temp/
|
||||||
|
dist
|
||||||
|
|||||||
+1
-1
@@ -3,4 +3,4 @@ echo "🔨 Building action..."
|
|||||||
npm run build
|
npm run build
|
||||||
|
|
||||||
# Add the built files to the commit
|
# Add the built files to the commit
|
||||||
git add index.cjs dist/
|
git add index.cjs entry.cjs
|
||||||
|
|||||||
@@ -1,4 +1,70 @@
|
|||||||
# Pullfrog
|
# Pullfrog Action
|
||||||
|
|
||||||
A simple GitHub Action that prints a customizable message to the console.
|
GitHub Action for running Claude Code and other agents via Pullfrog.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# Test with default prompt
|
||||||
|
npm run play # Run locally on your machine
|
||||||
|
npm run play -- --act # Run in Docker (simulates GitHub Actions)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing with play.ts
|
||||||
|
|
||||||
|
The `play.ts` script provides two ways to test the action:
|
||||||
|
|
||||||
|
### Local Mode (Default)
|
||||||
|
```bash
|
||||||
|
npm run play # Uses fixtures/play.txt
|
||||||
|
npm run play fixtures/complex.txt # Custom prompt file
|
||||||
|
```
|
||||||
|
- Clones the scratch repository to `.temp`
|
||||||
|
- Runs Claude Code directly on your machine
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
## 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
|
||||||
+13
-10
@@ -1,17 +1,20 @@
|
|||||||
name: 'Simple Message Action'
|
name: "Pullfrog Claude Code Action"
|
||||||
description: 'A simple GitHub Action that prints a message to the console'
|
description: "Execute Claude Code with a prompt using Anthropic API"
|
||||||
author: 'Pullfrog'
|
author: "Pullfrog"
|
||||||
|
|
||||||
inputs:
|
inputs:
|
||||||
message:
|
prompt:
|
||||||
description: 'Message to print to console'
|
description: "Prompt to send to Claude Code"
|
||||||
required: true
|
required: true
|
||||||
default: 'Hello from Pullfrog Action!'
|
default: "Hello from Claude Code!"
|
||||||
|
anthropic_api_key:
|
||||||
|
description: "Anthropic API key for Claude Code authentication"
|
||||||
|
required: false
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: 'node20'
|
using: "node20"
|
||||||
main: 'index.cjs'
|
main: "entry.cjs"
|
||||||
|
|
||||||
branding:
|
branding:
|
||||||
icon: 'message-circle'
|
icon: "code"
|
||||||
color: 'blue'
|
color: "orange"
|
||||||
|
|||||||
@@ -0,0 +1,402 @@
|
|||||||
|
import { access, constants } from "node:fs/promises";
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
import { boxString, tableString } from "../utils";
|
||||||
|
import { spawn } from "../utils/subprocess";
|
||||||
|
import type { Agent, AgentConfig, AgentResult } from "./types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Claude Code agent implementation
|
||||||
|
*/
|
||||||
|
export class ClaudeAgent implements Agent {
|
||||||
|
private apiKey: string;
|
||||||
|
public runStats = {
|
||||||
|
toolsUsed: 0,
|
||||||
|
turns: 0,
|
||||||
|
startTime: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// $: ExecaMethod;
|
||||||
|
|
||||||
|
constructor(config: AgentConfig) {
|
||||||
|
if (!config.apiKey) {
|
||||||
|
throw new Error("Claude agent requires an API key");
|
||||||
|
}
|
||||||
|
this.apiKey = config.apiKey;
|
||||||
|
// Removed execa dependency - using spawn utility instead
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if Claude Code CLI is already installed
|
||||||
|
*/
|
||||||
|
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> {
|
||||||
|
// Check if Claude Code is already installed
|
||||||
|
if (await this.isClaudeInstalled()) {
|
||||||
|
core.info("Claude Code is already installed, skipping installation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info("Installing Claude Code...");
|
||||||
|
try {
|
||||||
|
// Use shell execution to properly handle the pipe
|
||||||
|
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: (chunk) => process.stdout.write(chunk),
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
async execute(prompt: string): Promise<AgentResult> {
|
||||||
|
core.info("Running Claude Code...");
|
||||||
|
// printTable([[prompt]]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Execute Claude Code with the prompt directly using proper headless mode
|
||||||
|
// core.info(`Executing Claude Code with prompt: ${prompt.substring(0, 100)}...`);
|
||||||
|
|
||||||
|
const claudePath = `${process.env.HOME}/.local/bin/claude`;
|
||||||
|
// console.log("Using Claude Code from:", claudePath);
|
||||||
|
console.log(boxString(prompt, { title: "Prompt" }));
|
||||||
|
const args = [
|
||||||
|
"--print",
|
||||||
|
"--output-format",
|
||||||
|
"stream-json",
|
||||||
|
"--verbose",
|
||||||
|
"--permission-mode",
|
||||||
|
"acceptEdits",
|
||||||
|
];
|
||||||
|
const env = {
|
||||||
|
ANTHROPIC_API_KEY: this.apiKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start a collapsible log group for streaming output
|
||||||
|
core.startGroup("🔄 Run details");
|
||||||
|
|
||||||
|
// Initialize run statistics
|
||||||
|
this.runStats = {
|
||||||
|
toolsUsed: 0,
|
||||||
|
turns: 0,
|
||||||
|
startTime: Date.now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const finalResult = "";
|
||||||
|
const totalCost = 0;
|
||||||
|
|
||||||
|
// run Claude Code with the prompt
|
||||||
|
const result = await spawn({
|
||||||
|
cmd: claudePath,
|
||||||
|
args,
|
||||||
|
env,
|
||||||
|
input: prompt,
|
||||||
|
timeout: 10 * 60 * 1000, // 10 minutes
|
||||||
|
onStdout: (_chunk) => {
|
||||||
|
// console.log(chunk);
|
||||||
|
processJSONChunk(_chunk, this);
|
||||||
|
},
|
||||||
|
onStderr: (_chunk) => {
|
||||||
|
if (_chunk.trim()) {
|
||||||
|
// core.warning(`[warn] ${chunk}`);
|
||||||
|
processJSONChunk(_chunk, this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// throw on non-zero exit code
|
||||||
|
if (result.exitCode !== 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Command failed with exit code ${result.exitCode}\n\nStdout: ${result.stdout}\n\nStderr: ${result.stderr}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the complete buffered stdout to extract final results
|
||||||
|
// if (result.stdout.trim()) {
|
||||||
|
// const lines = result.stdout.trim().split("\n");
|
||||||
|
// for (const line of lines) {
|
||||||
|
// if (line.trim()) {
|
||||||
|
// const chunkResult = processJsonChunk(line);
|
||||||
|
// if (chunkResult.finalResult) finalResult = chunkResult.finalResult;
|
||||||
|
// if (chunkResult.totalCost) totalCost = chunkResult.totalCost;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Log run summary
|
||||||
|
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) {
|
||||||
|
// Ensure group is closed even if error occurs before group is started
|
||||||
|
try {
|
||||||
|
core.endGroup();
|
||||||
|
} catch {
|
||||||
|
// Group might not have been started, ignore
|
||||||
|
}
|
||||||
|
const errorMessage =
|
||||||
|
error instanceof Error ? error.message : "Unknown error";
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: `Failed to execute Claude Code: ${errorMessage}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a JSON chunk line and extract result data
|
||||||
|
*/
|
||||||
|
// function processJsonChunk(line: string): { finalResult?: string; totalCost?: number } {
|
||||||
|
// try {
|
||||||
|
// const chunk = JSON.parse(line.trim());
|
||||||
|
// processJSONChunk(chunk);
|
||||||
|
|
||||||
|
// // Collect final result and cost data
|
||||||
|
// if (chunk.type === "result" && chunk.result) {
|
||||||
|
// return {
|
||||||
|
// finalResult: chunk.result,
|
||||||
|
// totalCost: chunk.total_cost_usd || 0,
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// return {};
|
||||||
|
// } catch {
|
||||||
|
// core.debug(`Failed to parse JSON line: ${line}`);
|
||||||
|
// return {};
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pretty print a JSON chunk based on its type
|
||||||
|
*/
|
||||||
|
function processJSONChunk(chunk: string, agent?: ClaudeAgent): void {
|
||||||
|
try {
|
||||||
|
// Parse the JSON string first
|
||||||
|
console.log(chunk);
|
||||||
|
const parsedChunk = JSON.parse(chunk.trim());
|
||||||
|
|
||||||
|
switch (parsedChunk.type) {
|
||||||
|
case "system":
|
||||||
|
if (parsedChunk.subtype === "init") {
|
||||||
|
core.info(`🚀 Starting Claude Code session...`);
|
||||||
|
// core.info(`📁 Working directory: ${parsedChunk.cwd}`);
|
||||||
|
// core.info(`🔑 Permission mode: ${parsedChunk.permissionMode}`);
|
||||||
|
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":
|
||||||
|
if (parsedChunk.message?.content) {
|
||||||
|
// Track turns
|
||||||
|
if (agent) {
|
||||||
|
agent.runStats.turns++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const content of parsedChunk.message.content) {
|
||||||
|
if (content.type === "text") {
|
||||||
|
// Skip empty text content
|
||||||
|
if (content.text.trim()) {
|
||||||
|
core.info(
|
||||||
|
boxString(content.text.trim(), { title: "Claude Code" }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (content.type === "tool_use") {
|
||||||
|
// Track tools used
|
||||||
|
if (agent) {
|
||||||
|
agent.runStats.toolsUsed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enhanced tool usage logging
|
||||||
|
const toolName = content.name;
|
||||||
|
// const toolId = content.id;
|
||||||
|
|
||||||
|
core.info(`→ ${toolName}`);
|
||||||
|
|
||||||
|
// Log tool-specific details based on tool type
|
||||||
|
if (content.input) {
|
||||||
|
const input = content.input;
|
||||||
|
|
||||||
|
// Common tool input fields
|
||||||
|
if (input.description) {
|
||||||
|
core.info(` └─ ${input.description}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tool-specific input fields
|
||||||
|
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}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For multi-edit or complex operations
|
||||||
|
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}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// For task operations
|
||||||
|
if (input.task) {
|
||||||
|
core.info(` └─ task: ${input.task}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For bash operations with specific details
|
||||||
|
if (input.bash_command) {
|
||||||
|
core.info(` └─ bash_command: ${input.bash_command}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log tool ID for debugging
|
||||||
|
// core.debug(` 🔗 Tool ID: ${toolId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "user":
|
||||||
|
if (parsedChunk.message?.content) {
|
||||||
|
for (const content of parsedChunk.message.content) {
|
||||||
|
if (content.type === "tool_result") {
|
||||||
|
if (content.is_error) {
|
||||||
|
core.warning(`❌ Tool error: ${content.content}`);
|
||||||
|
} else {
|
||||||
|
// Enhanced tool result logging
|
||||||
|
const _resultContent = content.content.trim();
|
||||||
|
// do nothing for now. usually useless in headless more.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "result":
|
||||||
|
if (parsedChunk.subtype === "success") {
|
||||||
|
if (parsedChunk.result) {
|
||||||
|
core.info(
|
||||||
|
boxString(parsedChunk.result.trim(), {
|
||||||
|
title: "🤖 Claude Code",
|
||||||
|
maxWidth: 70,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
|
// Log unknown chunk types for debugging
|
||||||
|
core.debug(`📦 Unknown chunk type: ${parsedChunk.type}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
core.debug(`Failed to parse chunk: ${error}`);
|
||||||
|
core.debug(`Raw chunk: ${chunk.substring(0, 200)}...`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./claude";
|
||||||
|
export * from "./types";
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Standard interface for all Pullfrog agents
|
||||||
|
*/
|
||||||
|
export interface Agent {
|
||||||
|
/**
|
||||||
|
* Install the agent and any required dependencies
|
||||||
|
*/
|
||||||
|
install(): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the agent with the given prompt
|
||||||
|
* @param prompt The prompt to send to the agent
|
||||||
|
* @param options Additional options specific to the agent
|
||||||
|
*/
|
||||||
|
execute(prompt: string, options?: Record<string, any>): Promise<AgentResult>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result returned by agent execution
|
||||||
|
*/
|
||||||
|
export interface AgentResult {
|
||||||
|
success: boolean;
|
||||||
|
output?: string;
|
||||||
|
error?: string;
|
||||||
|
metadata?: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for agent creation
|
||||||
|
*/
|
||||||
|
export interface AgentConfig {
|
||||||
|
apiKey?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
Vendored
-17731
File diff suppressed because one or more lines are too long
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry point for GitHub Action
|
||||||
|
* This file is bundled to entry.cjs and called directly by GitHub Actions
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
import { main } from "./main";
|
||||||
|
|
||||||
|
async function run(): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Get inputs from GitHub Actions
|
||||||
|
const prompt = core.getInput("prompt", { required: true });
|
||||||
|
const anthropicApiKey = core.getInput("anthropic_api_key", {
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!anthropicApiKey) {
|
||||||
|
throw new Error("anthropic_api_key is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!prompt) {
|
||||||
|
throw new Error("prompt is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create params object
|
||||||
|
const params = {
|
||||||
|
prompt,
|
||||||
|
anthropicApiKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run the main function
|
||||||
|
const result = await main(params);
|
||||||
|
|
||||||
|
// Set outputs
|
||||||
|
core.setOutput("status", result.success ? "success" : "failed");
|
||||||
|
core.setOutput("prompt", prompt);
|
||||||
|
core.setOutput("output", result.output || "");
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
throw new Error(result.error || "Agent execution failed");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage =
|
||||||
|
error instanceof Error ? error.message : "Unknown error occurred";
|
||||||
|
core.setFailed(`Action failed: ${errorMessage}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the action
|
||||||
|
run().catch((error) => {
|
||||||
|
console.error("Action failed:", error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
+9
-7
@@ -1,14 +1,16 @@
|
|||||||
import { build } from 'esbuild';
|
import { build } from "esbuild";
|
||||||
|
|
||||||
|
// Build the GitHub Action bundle only
|
||||||
|
// For npm package builds, use zshy (pnpm build:npm)
|
||||||
await build({
|
await build({
|
||||||
entryPoints: ['./index.ts'],
|
entryPoints: ["./entry.ts"],
|
||||||
bundle: true,
|
bundle: true,
|
||||||
outfile: './index.cjs',
|
outfile: "./entry.cjs",
|
||||||
format: 'cjs',
|
format: "cjs",
|
||||||
platform: 'node',
|
platform: "node",
|
||||||
target: 'node20',
|
target: "node20",
|
||||||
minify: false,
|
minify: false,
|
||||||
sourcemap: false,
|
sourcemap: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('✅ Build completed successfully!');
|
console.log("✅ Build completed successfully!");
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import type { MainParams } from "../main";
|
||||||
|
|
||||||
|
const testParams = {
|
||||||
|
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."
|
||||||
|
} satisfies MainParams;
|
||||||
|
|
||||||
|
export default testParams;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Print the list of tools available. Then create a new file called test.txt with the content "Hello from Pullfrog!".
|
||||||
@@ -1,17 +1,8 @@
|
|||||||
import * as core from "@actions/core";
|
/**
|
||||||
|
* Library entry point for npm package
|
||||||
|
* This exports the main function for programmatic usage
|
||||||
|
*/
|
||||||
|
|
||||||
try {
|
export { main } from "./main";
|
||||||
// Get the message input parameter, with a default fallback
|
export { ClaudeAgent } from "./agents";
|
||||||
const message = core.getInput("message") || "Hello from Pullfrog Action!";
|
export type { Agent, AgentConfig, AgentResult } from "./agents/types";
|
||||||
|
|
||||||
// Print the message to console and GitHub Actions logs
|
|
||||||
console.log(`🐸 Pullfrog says: ${message}`);
|
|
||||||
core.info(`Action executed successfully with message: ${message}`);
|
|
||||||
|
|
||||||
// Set an output for potential use by other actions
|
|
||||||
core.setOutput("message", message);
|
|
||||||
} catch (error) {
|
|
||||||
// Handle any errors and fail the action
|
|
||||||
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
||||||
core.setFailed(`Action failed: ${errorMessage}`);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import * as core from "@actions/core";
|
||||||
|
import { ClaudeAgent } from "./agents";
|
||||||
|
|
||||||
|
export interface MainParams {
|
||||||
|
prompt: string;
|
||||||
|
anthropicApiKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MainResult {
|
||||||
|
success: boolean;
|
||||||
|
output?: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function main(params: MainParams): Promise<MainResult> {
|
||||||
|
try {
|
||||||
|
// Use provided API key or fall back to environment variable
|
||||||
|
const anthropicApiKey =
|
||||||
|
params.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
|
||||||
|
|
||||||
|
if (!anthropicApiKey) {
|
||||||
|
throw new Error("anthropic_api_key is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info(`→ Starting agent run with Claude Code`);
|
||||||
|
|
||||||
|
// Create and install the Claude agent
|
||||||
|
const agent = new ClaudeAgent({ apiKey: anthropicApiKey });
|
||||||
|
await agent.install();
|
||||||
|
|
||||||
|
// Execute the agent with the prompt
|
||||||
|
const result = await agent.execute(params.prompt);
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: result.error || "Agent execution failed",
|
||||||
|
output: result.output,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
output: result.output || "",
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage =
|
||||||
|
error instanceof Error ? error.message : "Unknown error occurred";
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: errorMessage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+865
-6
@@ -1,20 +1,26 @@
|
|||||||
{
|
{
|
||||||
"name": "action",
|
"name": "@pullfrog/action",
|
||||||
"version": "0.0.2",
|
"version": "0.0.6",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "action",
|
"name": "@pullfrog/action",
|
||||||
"version": "0.0.2",
|
"version": "0.0.6",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.1"
|
"@actions/core": "^1.10.1",
|
||||||
|
"execa": "^9.6.0",
|
||||||
|
"table": "^6.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.10.0",
|
"@types/node": "^20.10.0",
|
||||||
|
"commander": "^14.0.0",
|
||||||
|
"dotenv": "^17.2.2",
|
||||||
"esbuild": "^0.25.9",
|
"esbuild": "^0.25.9",
|
||||||
"typescript": "^5.3.0"
|
"husky": "^9.0.0",
|
||||||
|
"typescript": "^5.3.0",
|
||||||
|
"zshy": "^0.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@actions/core": {
|
"node_modules/@actions/core": {
|
||||||
@@ -503,6 +509,62 @@
|
|||||||
"node": ">=14"
|
"node": ">=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
|
"version": "2.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.stat": "2.0.5",
|
||||||
|
"run-parallel": "^1.1.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nodelib/fs.stat": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nodelib/fs.walk": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.scandir": "2.1.5",
|
||||||
|
"fastq": "^1.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sec-ant/readable-stream": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@sindresorhus/merge-streams": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.19.11",
|
"version": "20.19.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz",
|
||||||
@@ -513,6 +575,136 @@
|
|||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ajv": {
|
||||||
|
"version": "8.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"fast-uri": "^3.0.1",
|
||||||
|
"json-schema-traverse": "^1.0.0",
|
||||||
|
"require-from-string": "^2.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/arg": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/astral-regex": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/braces": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fill-range": "^7.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/commander": {
|
||||||
|
"version": "14.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz",
|
||||||
|
"integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cross-spawn": {
|
||||||
|
"version": "7.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"path-key": "^3.1.0",
|
||||||
|
"shebang-command": "^2.0.0",
|
||||||
|
"which": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "17.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz",
|
||||||
|
"integrity": "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://dotenvx.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.25.9",
|
"version": "0.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
|
||||||
@@ -555,6 +747,612 @@
|
|||||||
"@esbuild/win32-x64": "0.25.9"
|
"@esbuild/win32-x64": "0.25.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/execa": {
|
||||||
|
"version": "9.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz",
|
||||||
|
"integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@sindresorhus/merge-streams": "^4.0.0",
|
||||||
|
"cross-spawn": "^7.0.6",
|
||||||
|
"figures": "^6.1.0",
|
||||||
|
"get-stream": "^9.0.0",
|
||||||
|
"human-signals": "^8.0.1",
|
||||||
|
"is-plain-obj": "^4.1.0",
|
||||||
|
"is-stream": "^4.0.1",
|
||||||
|
"npm-run-path": "^6.0.0",
|
||||||
|
"pretty-ms": "^9.2.0",
|
||||||
|
"signal-exit": "^4.1.0",
|
||||||
|
"strip-final-newline": "^4.0.0",
|
||||||
|
"yoctocolors": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.19.0 || >=20.5.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sindresorhus/execa?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-deep-equal": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/fast-glob": {
|
||||||
|
"version": "3.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||||
|
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.stat": "^2.0.2",
|
||||||
|
"@nodelib/fs.walk": "^1.2.3",
|
||||||
|
"glob-parent": "^5.1.2",
|
||||||
|
"merge2": "^1.3.0",
|
||||||
|
"micromatch": "^4.0.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-uri": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fastify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fastify"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/fastq": {
|
||||||
|
"version": "1.19.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
||||||
|
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"reusify": "^1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/figures": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-unicode-supported": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fill-range": {
|
||||||
|
"version": "7.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"to-regex-range": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/get-stream": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@sec-ant/readable-stream": "^0.4.1",
|
||||||
|
"is-stream": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/glob-parent": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"is-glob": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/globby": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@sindresorhus/merge-streams": "^2.1.0",
|
||||||
|
"fast-glob": "^3.3.3",
|
||||||
|
"ignore": "^7.0.3",
|
||||||
|
"path-type": "^6.0.0",
|
||||||
|
"slash": "^5.1.0",
|
||||||
|
"unicorn-magic": "^0.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/globby/node_modules/@sindresorhus/merge-streams": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/human-signals": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/husky": {
|
||||||
|
"version": "9.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
|
||||||
|
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"husky": "bin.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/typicode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ignore": {
|
||||||
|
"version": "7.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||||
|
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-extglob": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-glob": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-extglob": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-number": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-plain-obj": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-stream": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-unicode-supported": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/isexe": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/json-schema-traverse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/lodash.truncate": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
|
||||||
|
"integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/merge2": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/micromatch": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
|
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"braces": "^3.0.3",
|
||||||
|
"picomatch": "^2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm-run-path": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"path-key": "^4.0.0",
|
||||||
|
"unicorn-magic": "^0.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npm-run-path/node_modules/path-key": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/parse-ms": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-key": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-type": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/picomatch": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pretty-ms": {
|
||||||
|
"version": "9.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz",
|
||||||
|
"integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"parse-ms": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/queue-microtask": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/require-from-string": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/reusify": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"iojs": ">=1.0.0",
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/run-parallel": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"queue-microtask": "^1.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/shebang-command": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"shebang-regex": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/shebang-regex": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/signal-exit": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/slash": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/slice-ansi": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"astral-regex": "^2.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-final-newline": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/table": {
|
||||||
|
"version": "6.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz",
|
||||||
|
"integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"ajv": "^8.0.1",
|
||||||
|
"lodash.truncate": "^4.4.2",
|
||||||
|
"slice-ansi": "^4.0.0",
|
||||||
|
"string-width": "^4.2.3",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/to-regex-range": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-number": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tunnel": {
|
"node_modules/tunnel": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||||
@@ -596,6 +1394,67 @@
|
|||||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/unicorn-magic": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/which": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"isexe": "^2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-which": "bin/node-which"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yoctocolors": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zshy": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/zshy/-/zshy-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-9BP1xv9BK4xNf8N5K85q+n81uFRT4p92JrT2BShXwgn8iw63QeSbdd7Ytw5RzqFG3AeIGtqpOEJPk4iiM77dsw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"arg": "^5.0.2",
|
||||||
|
"globby": "^14.1.0",
|
||||||
|
"table": "^6.9.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"zshy": "dist/index.cjs"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">5.5.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-11
@@ -1,25 +1,41 @@
|
|||||||
{
|
{
|
||||||
"name": "action",
|
"name": "@pullfrog/action",
|
||||||
"version": "0.0.3",
|
"version": "0.0.6",
|
||||||
"main": "index.js",
|
"type": "module",
|
||||||
|
"files": [
|
||||||
|
"index.js",
|
||||||
|
"index.cjs",
|
||||||
|
"index.d.ts",
|
||||||
|
"index.d.cts",
|
||||||
|
"agents",
|
||||||
|
"utils",
|
||||||
|
"main.js",
|
||||||
|
"main.d.ts"
|
||||||
|
],
|
||||||
"directories": {
|
"directories": {
|
||||||
"example": "examples"
|
"example": "examples"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "pnpm install && node esbuild.config.js && rm -rf node_modules",
|
||||||
"build": "node esbuild.config.js",
|
"build:npm": "zshy",
|
||||||
"dev": "node esbuild.config.js --watch",
|
"build:dev": "node esbuild.config.js",
|
||||||
"prepare": "husky"
|
"prepare": "husky",
|
||||||
|
"play": "tsx --env-file=../.env play.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.1"
|
"@actions/core": "^1.10.1",
|
||||||
|
"execa": "^9.6.0",
|
||||||
|
"table": "^6.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.10.0",
|
"@types/node": "^20.10.0",
|
||||||
|
"commander": "^14.0.0",
|
||||||
|
"dotenv": "^17.2.2",
|
||||||
"esbuild": "^0.25.9",
|
"esbuild": "^0.25.9",
|
||||||
"husky": "^9.0.0",
|
"husky": "^9.0.0",
|
||||||
"typescript": "^5.3.0"
|
"typescript": "^5.3.0",
|
||||||
|
"zshy": "^0.4.1"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -28,9 +44,11 @@
|
|||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"type": "module",
|
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/pullfrog/pullfrog/issues"
|
"url": "https://github.com/pullfrog/pullfrog/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/pullfrog/pullfrog#readme"
|
"homepage": "https://github.com/pullfrog/pullfrog#readme",
|
||||||
|
"zshy": {
|
||||||
|
"exports": "./index.ts"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
#!/usr/bin/env tsx
|
||||||
|
|
||||||
|
import { execSync } from "node:child_process";
|
||||||
|
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||||
|
import { dirname, join, resolve, extname } from "node:path";
|
||||||
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||||
|
import { Command } from "commander";
|
||||||
|
import { main } from "./main";
|
||||||
|
import { runAct } from "./utils/act";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
async function loadPrompt(filePath: string): Promise<string> {
|
||||||
|
const ext = extname(filePath).toLowerCase();
|
||||||
|
|
||||||
|
// Try to resolve the file path
|
||||||
|
let resolvedPath: string;
|
||||||
|
|
||||||
|
// First try as fixtures path
|
||||||
|
const fixturesPath = join(__dirname, "fixtures", filePath);
|
||||||
|
if (existsSync(fixturesPath)) {
|
||||||
|
resolvedPath = fixturesPath;
|
||||||
|
} else if (existsSync(filePath)) {
|
||||||
|
resolvedPath = resolve(filePath);
|
||||||
|
} else {
|
||||||
|
throw new Error(`File not found: ${filePath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ext) {
|
||||||
|
case ".txt": {
|
||||||
|
// Plain text - pass directly as prompt
|
||||||
|
return readFileSync(resolvedPath, "utf8").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
case ".json": {
|
||||||
|
// JSON - stringify and pass as prompt
|
||||||
|
const content = readFileSync(resolvedPath, "utf8");
|
||||||
|
const parsed = JSON.parse(content);
|
||||||
|
return JSON.stringify(parsed, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
case ".ts": {
|
||||||
|
// TypeScript - dynamic import and stringify default export
|
||||||
|
const fileUrl = pathToFileURL(resolvedPath).href;
|
||||||
|
const module = await import(fileUrl);
|
||||||
|
|
||||||
|
if (!module.default) {
|
||||||
|
throw new Error(
|
||||||
|
`TypeScript file ${filePath} must have a default export`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a string, use it directly
|
||||||
|
if (typeof module.default === "string") {
|
||||||
|
return module.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a MainParams object with a prompt field, extract the prompt
|
||||||
|
if (typeof module.default === "object" && module.default.prompt) {
|
||||||
|
return module.default.prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise stringify it
|
||||||
|
return JSON.stringify(module.default, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(
|
||||||
|
`Unsupported file type: ${ext}. Supported types: .txt, .json, .ts`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runPlay(
|
||||||
|
filePath: string,
|
||||||
|
options: { act?: boolean },
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Load the prompt from the specified file
|
||||||
|
const prompt = await loadPrompt(filePath);
|
||||||
|
|
||||||
|
if (options.act) {
|
||||||
|
// Use Docker/act to run the action
|
||||||
|
console.log("🐳 Running with Docker/act...");
|
||||||
|
runAct(prompt);
|
||||||
|
} else {
|
||||||
|
// Clone the test repository and run directly
|
||||||
|
const tempDir = join(process.cwd(), ".temp");
|
||||||
|
const repoUrl = "git@github.com:pullfrogai/scratch.git";
|
||||||
|
|
||||||
|
// Remove existing temp directory if it exists
|
||||||
|
if (existsSync(tempDir)) {
|
||||||
|
console.log("🗑️ Removing existing .temp directory...");
|
||||||
|
rmSync(tempDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone the repository
|
||||||
|
console.log("📦 Cloning pullfrogai/scratch into .temp...");
|
||||||
|
execSync(`git clone ${repoUrl} ${tempDir}`, { stdio: "inherit" });
|
||||||
|
|
||||||
|
// List of environment variables to copy to .temp
|
||||||
|
const envVarsToCopy = [
|
||||||
|
"ANTHROPIC_API_KEY",
|
||||||
|
"GITHUB_TOKEN",
|
||||||
|
// Add more environment variables here as needed
|
||||||
|
];
|
||||||
|
|
||||||
|
// Build .env content from the list
|
||||||
|
const envLines = envVarsToCopy
|
||||||
|
.map((varName) => `${varName}=${process.env[varName] || ""}`)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
const envPath = join(tempDir, ".env");
|
||||||
|
writeFileSync(envPath, envLines + "\n");
|
||||||
|
console.log("📝 Created .env file in .temp directory with:");
|
||||||
|
|
||||||
|
let hasRequiredVars = true;
|
||||||
|
envVarsToCopy.forEach((varName) => {
|
||||||
|
const hasValue = !!process.env[varName];
|
||||||
|
console.log(` - ${varName}: ${hasValue ? "✓" : "✗ (missing)"}`);
|
||||||
|
|
||||||
|
// Check for required variables
|
||||||
|
if (varName === "ANTHROPIC_API_KEY" && !hasValue) {
|
||||||
|
hasRequiredVars = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasRequiredVars) {
|
||||||
|
console.warn("\n⚠️ Warning: ANTHROPIC_API_KEY is not set or empty.");
|
||||||
|
console.warn(
|
||||||
|
" Please ensure you have a valid API key in your .env file.",
|
||||||
|
);
|
||||||
|
console.warn(
|
||||||
|
" Get your API key from: https://console.anthropic.com/api-keys\n",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change to the temp directory
|
||||||
|
process.chdir(tempDir);
|
||||||
|
|
||||||
|
console.log("🚀 Running test in .temp directory...");
|
||||||
|
console.log("─".repeat(50));
|
||||||
|
console.log(`Prompt from ${filePath}:`);
|
||||||
|
console.log(prompt);
|
||||||
|
console.log("─".repeat(50));
|
||||||
|
|
||||||
|
// Run main with the params object
|
||||||
|
const result = await main({ prompt });
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
console.log("✅ Test completed successfully");
|
||||||
|
if (result.output) {
|
||||||
|
console.log("Output:", result.output);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error("❌ Test failed:", result.error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Error:", (error as Error).message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up CLI
|
||||||
|
const program = new Command();
|
||||||
|
|
||||||
|
program
|
||||||
|
.name("play")
|
||||||
|
.description("Test the Pullfrog action with various prompts")
|
||||||
|
.version("1.0.0")
|
||||||
|
.argument(
|
||||||
|
"[file]",
|
||||||
|
"Prompt file to use (.txt, .json, or .ts)",
|
||||||
|
"fixtures/play.txt",
|
||||||
|
)
|
||||||
|
.option(
|
||||||
|
"--act",
|
||||||
|
"Use Docker/act to run the action instead of running directly",
|
||||||
|
)
|
||||||
|
.action(async (file: string, options: { act?: boolean }) => {
|
||||||
|
await runPlay(file, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Parse arguments and run
|
||||||
|
program.parseAsync(process.argv).catch((error) => {
|
||||||
|
console.error("Fatal error:", error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
+40
-12
@@ -1,17 +1,45 @@
|
|||||||
{
|
{
|
||||||
|
// Visit https://aka.ms/tsconfig to read more about this file
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2020",
|
// File Layout
|
||||||
"module": "commonjs",
|
// "rootDir": "./src",
|
||||||
"lib": ["ES2020"],
|
// "outDir": "./dist",
|
||||||
"outDir": "./",
|
|
||||||
"rootDir": "./",
|
// Environment Settings
|
||||||
|
// See also https://aka.ms/tsconfig/module
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"target": "esnext",
|
||||||
|
"types": [],
|
||||||
|
// For nodejs:
|
||||||
|
// "lib": ["esnext"],
|
||||||
|
// "types": ["node"],
|
||||||
|
// and npm install -D @types/node
|
||||||
|
|
||||||
|
// Other Outputs
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
|
||||||
|
// Stricter Typechecking Options
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
|
||||||
|
// Style Options
|
||||||
|
// "noImplicitReturns": true,
|
||||||
|
// "noImplicitOverride": true,
|
||||||
|
// "noUnusedLocals": true,
|
||||||
|
// "noUnusedParameters": true,
|
||||||
|
// "noFallthroughCasesInSwitch": true,
|
||||||
|
// "noPropertyAccessFromIndexSignature": true,
|
||||||
|
|
||||||
|
// Recommended Options
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"esModuleInterop": true,
|
"jsx": "react-jsx",
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noUncheckedSideEffectImports": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
}
|
||||||
"declaration": false,
|
|
||||||
"sourceMap": false
|
|
||||||
},
|
|
||||||
"include": ["*.ts"],
|
|
||||||
"exclude": ["node_modules", "**/*.test.ts"]
|
|
||||||
}
|
}
|
||||||
|
|||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
import { execSync } from "node:child_process";
|
||||||
|
import { existsSync, readFileSync } from "node:fs";
|
||||||
|
import { dirname, join } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { config, parse } from "dotenv";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
export function runAct(prompt: string): void {
|
||||||
|
// First, ensure the scratch repo is cloned
|
||||||
|
const tempDir = join(__dirname, "..", ".temp");
|
||||||
|
|
||||||
|
// Check if .temp exists and either reset it or clone it
|
||||||
|
if (existsSync(tempDir)) {
|
||||||
|
console.log("📦 Resetting existing .temp repository...");
|
||||||
|
execSync("git reset --hard HEAD && git clean -fd", {
|
||||||
|
cwd: tempDir,
|
||||||
|
stdio: "inherit",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("📦 Cloning pullfrogai/scratch into .temp...");
|
||||||
|
const repoUrl = "git@github.com:pullfrogai/scratch.git";
|
||||||
|
execSync(`git clone ${repoUrl} ${tempDir}`, { stdio: "inherit" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowPath = join(tempDir, ".github", "workflows", "pullfrog.yml");
|
||||||
|
const envPath = join(__dirname, "..", "..", ".env");
|
||||||
|
|
||||||
|
// Load environment variables into process
|
||||||
|
config({ path: envPath });
|
||||||
|
|
||||||
|
// Parse environment variables from .env file to get keys
|
||||||
|
let envVars: string[] = [];
|
||||||
|
try {
|
||||||
|
const content = readFileSync(envPath, "utf8");
|
||||||
|
const parsed = parse(content);
|
||||||
|
envVars = Object.keys(parsed);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
`Warning: Could not read .env file: ${(error as Error).message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build fresh bundles with esbuild
|
||||||
|
const actionPath = join(__dirname, "..");
|
||||||
|
console.log("🔨 Building fresh bundles with esbuild...");
|
||||||
|
execSync("node esbuild.config.js", {
|
||||||
|
cwd: actionPath,
|
||||||
|
stdio: "inherit",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create minimal dist for act (avoids pnpm symlink issues)
|
||||||
|
const distPath = join(actionPath, ".act-dist");
|
||||||
|
console.log("📦 Creating minimal distribution for act...");
|
||||||
|
execSync(`rm -rf "${distPath}" && mkdir -p "${distPath}"`, { shell: true });
|
||||||
|
|
||||||
|
// Copy only necessary files (bundled, no node_modules needed)
|
||||||
|
["action.yml", "entry.cjs", "index.cjs", "package.json"].forEach((file) => {
|
||||||
|
const src = join(actionPath, file);
|
||||||
|
if (existsSync(src)) {
|
||||||
|
execSync(`cp "${src}" "${distPath}"`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Build the act command with input directly
|
||||||
|
// Properly escape the prompt for shell
|
||||||
|
const escapedPrompt = prompt.replace(/'/g, "'\\''");
|
||||||
|
|
||||||
|
const actCommandParts = [
|
||||||
|
"act",
|
||||||
|
"workflow_dispatch",
|
||||||
|
"-W",
|
||||||
|
workflowPath,
|
||||||
|
"--input",
|
||||||
|
`prompt='${escapedPrompt}'`,
|
||||||
|
"--local-repository",
|
||||||
|
`pullfrog/pullfrog@v0=${distPath}`, // Use minimal dist without symlinks
|
||||||
|
];
|
||||||
|
|
||||||
|
// Add all environment variables as secrets (without values)
|
||||||
|
envVars.forEach((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("");
|
||||||
|
|
||||||
|
// Execute act
|
||||||
|
execSync(actCommand, {
|
||||||
|
stdio: "inherit",
|
||||||
|
cwd: join(__dirname, "..", ".."),
|
||||||
|
});
|
||||||
|
// Clean up
|
||||||
|
execSync(`rm -rf "${distPath}"`);
|
||||||
|
} catch (error) {
|
||||||
|
// Clean up on error
|
||||||
|
execSync(`rm -rf "${distPath}"`);
|
||||||
|
console.error("❌ Act execution failed:", (error as Error).message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { mkdtempSync, writeFileSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a temporary file with the given content
|
||||||
|
*/
|
||||||
|
export function createTempFile(content: string, filename = "temp.txt"): string {
|
||||||
|
const tempDir = mkdtempSync(join(tmpdir(), "pullfrog-"));
|
||||||
|
const filePath = join(tempDir, filename);
|
||||||
|
writeFileSync(filePath, content);
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export * from "./files";
|
||||||
|
export * from "./subprocess";
|
||||||
|
export * from "./table";
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import { spawn as nodeSpawn } from "node:child_process";
|
||||||
|
|
||||||
|
export interface SpawnOptions {
|
||||||
|
cmd: string;
|
||||||
|
args: string[];
|
||||||
|
env?: Record<string, string>;
|
||||||
|
input?: string;
|
||||||
|
timeout?: number;
|
||||||
|
onStdout?: (chunk: string) => void;
|
||||||
|
onStderr?: (chunk: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpawnResult {
|
||||||
|
stdout: string;
|
||||||
|
stderr: string;
|
||||||
|
exitCode: number;
|
||||||
|
durationMs: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn a subprocess with streaming callbacks and buffered results
|
||||||
|
*/
|
||||||
|
export async function spawn(options: SpawnOptions): Promise<SpawnResult> {
|
||||||
|
const { cmd, args, env, input, timeout, onStdout, onStderr } = options;
|
||||||
|
|
||||||
|
const startTime = Date.now();
|
||||||
|
let stdoutBuffer = "";
|
||||||
|
let stderrBuffer = "";
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Spawn the child process
|
||||||
|
const child = nodeSpawn(cmd, args, {
|
||||||
|
env: env ? { ...process.env, ...env } : process.env,
|
||||||
|
stdio: ["pipe", "pipe", "pipe"],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set up timeout if specified
|
||||||
|
let timeoutId: NodeJS.Timeout | undefined;
|
||||||
|
let isTimedOut = false;
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
isTimedOut = true;
|
||||||
|
child.kill("SIGTERM");
|
||||||
|
|
||||||
|
// If SIGTERM doesn't work, use SIGKILL after 5 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!child.killed) {
|
||||||
|
child.kill("SIGKILL");
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle stdout streaming
|
||||||
|
if (child.stdout) {
|
||||||
|
child.stdout.on("data", (data: Buffer) => {
|
||||||
|
const chunk = data.toString();
|
||||||
|
stdoutBuffer += chunk;
|
||||||
|
onStdout?.(chunk);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle stderr streaming
|
||||||
|
if (child.stderr) {
|
||||||
|
child.stderr.on("data", (data: Buffer) => {
|
||||||
|
const chunk = data.toString();
|
||||||
|
stderrBuffer += chunk;
|
||||||
|
onStderr?.(chunk);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle process completion
|
||||||
|
child.on("close", (exitCode) => {
|
||||||
|
const durationMs = Date.now() - startTime;
|
||||||
|
|
||||||
|
if (timeoutId) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTimedOut) {
|
||||||
|
reject(new Error(`Process timed out after ${timeout}ms`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
stdout: stdoutBuffer,
|
||||||
|
stderr: stderrBuffer,
|
||||||
|
exitCode: exitCode || 0,
|
||||||
|
durationMs,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle process errors
|
||||||
|
child.on("error", (error) => {
|
||||||
|
const durationMs = Date.now() - startTime;
|
||||||
|
|
||||||
|
if (timeoutId) {
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Still return buffered output even on error
|
||||||
|
resolve({
|
||||||
|
stdout: stdoutBuffer,
|
||||||
|
stderr: stderrBuffer,
|
||||||
|
exitCode: 1,
|
||||||
|
durationMs,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send input if provided
|
||||||
|
if (input && child.stdin) {
|
||||||
|
child.stdin.write(input);
|
||||||
|
child.stdin.end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
+172
@@ -0,0 +1,172 @@
|
|||||||
|
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 (options?.title) {
|
||||||
|
rows.unshift([options.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Create a simple two-column table for displaying key-value pairs
|
||||||
|
// * @param data - Array of [key, value] pairs
|
||||||
|
// * @param title - Optional table title
|
||||||
|
// * @param indent - Optional indentation string
|
||||||
|
// */
|
||||||
|
// export function printKeyValueTable(
|
||||||
|
// data: [string, string][],
|
||||||
|
// title?: string,
|
||||||
|
// indent?: string
|
||||||
|
// ): void {
|
||||||
|
// const rows: string[][] = [["Key", "Value"], ...data];
|
||||||
|
// const options: Parameters<typeof printTable>[1] = {};
|
||||||
|
// if (title !== undefined) options.title = title;
|
||||||
|
// if (indent !== undefined) options.indent = indent;
|
||||||
|
// printTable(rows, options);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Create a path resolution table (specific use case)
|
||||||
|
// * @param pathData - Array of [location, resolvedPath] pairs
|
||||||
|
// * @param indent - Optional indentation string
|
||||||
|
// */
|
||||||
|
// export function printPathTable(pathData: [string, string][], indent?: string): void {
|
||||||
|
// const rows: string[][] = [["Location", "Resolved path"], ...pathData];
|
||||||
|
// const options: Parameters<typeof printTable>[1] = {};
|
||||||
|
// if (indent !== undefined) options.indent = indent;
|
||||||
|
// printTable(rows, options);
|
||||||
|
// }
|
||||||
Reference in New Issue
Block a user