fix: Update lock file in the action dir due to #118.
This commit is contained in:
committed by
pullfrog[bot]
parent
ce123c9a57
commit
cfd7f45db9
@@ -241,6 +241,8 @@ export type PayloadEvent =
|
|||||||
// writeable payload type for building payloads
|
// writeable payload type for building payloads
|
||||||
export interface WriteablePayload {
|
export interface WriteablePayload {
|
||||||
"~pullfrog": true;
|
"~pullfrog": true;
|
||||||
|
/** semantic version of the payload to ensure compatibility */
|
||||||
|
version: string;
|
||||||
/** agent slug identifier (e.g., "claude", "codex", "gemini") */
|
/** agent slug identifier (e.g., "claude", "codex", "gemini") */
|
||||||
agent?: AgentName | undefined;
|
agent?: AgentName | undefined;
|
||||||
/** the prompt/instructions for the agent to execute (body if @pullfrog tagged + per-trigger instructions) */
|
/** the prompt/instructions for the agent to execute (body if @pullfrog tagged + per-trigger instructions) */
|
||||||
|
|||||||
@@ -42,10 +42,12 @@
|
|||||||
"execa": "^9.6.0",
|
"execa": "^9.6.0",
|
||||||
"fastmcp": "^3.26.8",
|
"fastmcp": "^3.26.8",
|
||||||
"package-manager-detector": "^1.6.0",
|
"package-manager-detector": "^1.6.0",
|
||||||
|
"semver": "^7.7.3",
|
||||||
"table": "^6.9.0"
|
"table": "^6.9.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.7.2",
|
"@types/node": "^24.7.2",
|
||||||
|
"@types/semver": "^7.7.1",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
"esbuild": "^0.25.9",
|
"esbuild": "^0.25.9",
|
||||||
"husky": "^9.0.0",
|
"husky": "^9.0.0",
|
||||||
|
|||||||
Generated
+18
@@ -59,6 +59,9 @@ importers:
|
|||||||
package-manager-detector:
|
package-manager-detector:
|
||||||
specifier: ^1.6.0
|
specifier: ^1.6.0
|
||||||
version: 1.6.0
|
version: 1.6.0
|
||||||
|
semver:
|
||||||
|
specifier: ^7.7.3
|
||||||
|
version: 7.7.3
|
||||||
table:
|
table:
|
||||||
specifier: ^6.9.0
|
specifier: ^6.9.0
|
||||||
version: 6.9.0
|
version: 6.9.0
|
||||||
@@ -66,6 +69,9 @@ importers:
|
|||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^24.7.2
|
specifier: ^24.7.2
|
||||||
version: 24.7.2
|
version: 24.7.2
|
||||||
|
'@types/semver':
|
||||||
|
specifier: ^7.7.1
|
||||||
|
version: 7.7.1
|
||||||
arg:
|
arg:
|
||||||
specifier: ^5.0.2
|
specifier: ^5.0.2
|
||||||
version: 5.0.2
|
version: 5.0.2
|
||||||
@@ -817,6 +823,9 @@ packages:
|
|||||||
'@types/node@24.7.2':
|
'@types/node@24.7.2':
|
||||||
resolution: {integrity: sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==}
|
resolution: {integrity: sha512-/NbVmcGTP+lj5oa4yiYxxeBjRivKQ5Ns1eSZeB99ExsEQ6rX5XYU1Zy/gGxY/ilqtD4Etx9mKyrPxZRetiahhA==}
|
||||||
|
|
||||||
|
'@types/semver@7.7.1':
|
||||||
|
resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==}
|
||||||
|
|
||||||
'@vitest/expect@4.0.17':
|
'@vitest/expect@4.0.17':
|
||||||
resolution: {integrity: sha512-mEoqP3RqhKlbmUmntNDDCJeTDavDR+fVYkSOw8qRwJFaW/0/5zA9zFeTrHqNtcmwh6j26yMmwx2PqUDPzt5ZAQ==}
|
resolution: {integrity: sha512-mEoqP3RqhKlbmUmntNDDCJeTDavDR+fVYkSOw8qRwJFaW/0/5zA9zFeTrHqNtcmwh6j26yMmwx2PqUDPzt5ZAQ==}
|
||||||
|
|
||||||
@@ -1364,6 +1373,11 @@ packages:
|
|||||||
safer-buffer@2.1.2:
|
safer-buffer@2.1.2:
|
||||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||||
|
|
||||||
|
semver@7.7.3:
|
||||||
|
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
send@1.2.0:
|
send@1.2.0:
|
||||||
resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==}
|
resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==}
|
||||||
engines: {node: '>= 18'}
|
engines: {node: '>= 18'}
|
||||||
@@ -2210,6 +2224,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 7.14.0
|
undici-types: 7.14.0
|
||||||
|
|
||||||
|
'@types/semver@7.7.1': {}
|
||||||
|
|
||||||
'@vitest/expect@4.0.17':
|
'@vitest/expect@4.0.17':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@standard-schema/spec': 1.0.0
|
'@standard-schema/spec': 1.0.0
|
||||||
@@ -2824,6 +2840,8 @@ snapshots:
|
|||||||
|
|
||||||
safer-buffer@2.1.2: {}
|
safer-buffer@2.1.2: {}
|
||||||
|
|
||||||
|
semver@7.7.3: {}
|
||||||
|
|
||||||
send@1.2.0:
|
send@1.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
|
|||||||
Vendored
+2
@@ -1,4 +1,5 @@
|
|||||||
import type { Payload } from "../../external.ts";
|
import type { Payload } from "../../external.ts";
|
||||||
|
import packageJson from "../../package.json" with { type: "json" };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* test fixture: verifies agents use MCP bash tool for shell commands.
|
* test fixture: verifies agents use MCP bash tool for shell commands.
|
||||||
@@ -14,6 +15,7 @@ import type { Payload } from "../../external.ts";
|
|||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
"~pullfrog": true,
|
"~pullfrog": true,
|
||||||
|
version: packageJson.version,
|
||||||
prompt: `Create a file called test-runner.js with the following content:
|
prompt: `Create a file called test-runner.js with the following content:
|
||||||
|
|
||||||
\`\`\`javascript
|
\`\`\`javascript
|
||||||
|
|||||||
Vendored
+2
@@ -1,7 +1,9 @@
|
|||||||
import type { Payload } from "../../external.ts";
|
import type { Payload } from "../../external.ts";
|
||||||
|
import packageJson from "../../package.json" with { type: "json" };
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
"~pullfrog": true,
|
"~pullfrog": true,
|
||||||
|
version: packageJson.version,
|
||||||
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.",
|
||||||
event: {
|
event: {
|
||||||
|
|||||||
Vendored
+2
@@ -1,4 +1,5 @@
|
|||||||
import type { Effort, Payload } from "../../external.ts";
|
import type { Effort, Payload } from "../../external.ts";
|
||||||
|
import packageJson from "../../package.json" with { type: "json" };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test fixture for Claude effort levels.
|
* Test fixture for Claude effort levels.
|
||||||
@@ -17,6 +18,7 @@ const efforts: Effort[] = ["mini", "auto", "max"];
|
|||||||
|
|
||||||
export default efforts.map((effort) => ({
|
export default efforts.map((effort) => ({
|
||||||
"~pullfrog": true,
|
"~pullfrog": true,
|
||||||
|
version: packageJson.version,
|
||||||
agent: "claude",
|
agent: "claude",
|
||||||
prompt: "What is 2 + 2? Reply with just the number.",
|
prompt: "What is 2 + 2? Reply with just the number.",
|
||||||
event: {
|
event: {
|
||||||
|
|||||||
Vendored
+2
@@ -1,4 +1,5 @@
|
|||||||
import type { Effort, Payload } from "../../external.ts";
|
import type { Effort, Payload } from "../../external.ts";
|
||||||
|
import packageJson from "../../package.json" with { type: "json" };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test fixture for Codex effort levels.
|
* Test fixture for Codex effort levels.
|
||||||
@@ -17,6 +18,7 @@ const efforts: Effort[] = ["mini", "auto", "max"];
|
|||||||
|
|
||||||
export default efforts.map((effort) => ({
|
export default efforts.map((effort) => ({
|
||||||
"~pullfrog": true,
|
"~pullfrog": true,
|
||||||
|
version: packageJson.version,
|
||||||
agent: "codex",
|
agent: "codex",
|
||||||
prompt: "What is 2 + 2? Reply with just the number.",
|
prompt: "What is 2 + 2? Reply with just the number.",
|
||||||
event: {
|
event: {
|
||||||
|
|||||||
Vendored
+2
@@ -1,4 +1,5 @@
|
|||||||
import type { Effort, Payload } from "../../external.ts";
|
import type { Effort, Payload } from "../../external.ts";
|
||||||
|
import packageJson from "../../package.json" with { type: "json" };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test fixture for Cursor effort levels.
|
* Test fixture for Cursor effort levels.
|
||||||
@@ -20,6 +21,7 @@ const efforts: Effort[] = ["mini", "auto", "max"];
|
|||||||
|
|
||||||
export default efforts.map((effort) => ({
|
export default efforts.map((effort) => ({
|
||||||
"~pullfrog": true,
|
"~pullfrog": true,
|
||||||
|
version: packageJson.version,
|
||||||
agent: "cursor",
|
agent: "cursor",
|
||||||
prompt: "What is 2 + 2? Reply with just the number.",
|
prompt: "What is 2 + 2? Reply with just the number.",
|
||||||
event: {
|
event: {
|
||||||
|
|||||||
Vendored
+2
@@ -1,4 +1,5 @@
|
|||||||
import type { Effort, Payload } from "../../external.ts";
|
import type { Effort, Payload } from "../../external.ts";
|
||||||
|
import packageJson from "../../package.json" with { type: "json" };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test fixture for Gemini effort levels.
|
* Test fixture for Gemini effort levels.
|
||||||
@@ -17,6 +18,7 @@ const efforts: Effort[] = ["mini", "auto", "max"];
|
|||||||
|
|
||||||
export default efforts.map((effort) => ({
|
export default efforts.map((effort) => ({
|
||||||
"~pullfrog": true,
|
"~pullfrog": true,
|
||||||
|
version: packageJson.version,
|
||||||
agent: "gemini",
|
agent: "gemini",
|
||||||
prompt: "What is 2 + 2? Reply with just the number.",
|
prompt: "What is 2 + 2? Reply with just the number.",
|
||||||
event: {
|
event: {
|
||||||
|
|||||||
+32
-1
@@ -1,4 +1,4 @@
|
|||||||
import { Inputs } from "./payload.ts";
|
import { Inputs, JsonPayload } from "./payload.ts";
|
||||||
|
|
||||||
describe("Inputs schema", () => {
|
describe("Inputs schema", () => {
|
||||||
it("only prompt is required", () => {
|
it("only prompt is required", () => {
|
||||||
@@ -43,3 +43,34 @@ describe("Inputs schema", () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("JsonPayload schema", () => {
|
||||||
|
it("requires ~pullfrog and version", () => {
|
||||||
|
const result = JsonPayload.assert({ "~pullfrog": true, version: "1.2.3" });
|
||||||
|
expect(result).toMatchObject({ "~pullfrog": true, version: "1.2.3" });
|
||||||
|
expect(() => JsonPayload.assert({})).toThrow();
|
||||||
|
expect(() => JsonPayload.assert({ "~pullfrog": true })).toThrow();
|
||||||
|
expect(() => JsonPayload.assert({ version: "1.2.3" })).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
["prompt", "test prompt"],
|
||||||
|
["agent", "claude"],
|
||||||
|
["agent", "codex"],
|
||||||
|
["agent", "cursor"],
|
||||||
|
["agent", "gemini"],
|
||||||
|
["agent", "opencode"],
|
||||||
|
["effort", "mini"],
|
||||||
|
["effort", "auto"],
|
||||||
|
["effort", "max"],
|
||||||
|
["event", { trigger: "unknown" }],
|
||||||
|
] as const)("should accept optional %s with value %s", (prop, value) => {
|
||||||
|
const input = { "~pullfrog": true, version: "1.2.3", [prop]: value };
|
||||||
|
expect(() => JsonPayload.assert(input)).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([["agent"], ["effort"]] as const)("should reject invalid %s values", (prop) => {
|
||||||
|
const input = { "~pullfrog": true, version: "1.2.3", [prop]: "invalid" as any };
|
||||||
|
expect(() => JsonPayload.assert(input)).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
+8
-1
@@ -2,7 +2,9 @@ import { isAbsolute, resolve } from "node:path";
|
|||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import { type } from "arktype";
|
import { type } from "arktype";
|
||||||
import { AgentName, type AuthorPermission, Effort, type PayloadEvent } from "../external.ts";
|
import { AgentName, type AuthorPermission, Effort, type PayloadEvent } from "../external.ts";
|
||||||
|
import packageJson from "../package.json" with { type: "json" };
|
||||||
import type { RepoSettings } from "./repoSettings.ts";
|
import type { RepoSettings } from "./repoSettings.ts";
|
||||||
|
import { validateCompatibility } from "./versioning.ts";
|
||||||
|
|
||||||
// tool permission enum types for inputs
|
// tool permission enum types for inputs
|
||||||
const ToolPermissionInput = type.enumerated("disabled", "enabled");
|
const ToolPermissionInput = type.enumerated("disabled", "enabled");
|
||||||
@@ -11,8 +13,9 @@ const BashPermissionInput = type.enumerated("disabled", "restricted", "enabled")
|
|||||||
// schema for JSON payload passed via prompt (internal dispatch invocation)
|
// schema for JSON payload passed via prompt (internal dispatch invocation)
|
||||||
// note: permissions are intentionally NOT included here to prevent injection attacks
|
// note: permissions are intentionally NOT included here to prevent injection attacks
|
||||||
// permissions are derived from event.authorPermission instead
|
// permissions are derived from event.authorPermission instead
|
||||||
const JsonPayload = type({
|
export const JsonPayload = type({
|
||||||
"~pullfrog": "true",
|
"~pullfrog": "true",
|
||||||
|
"version": "string",
|
||||||
"agent?": AgentName.or("undefined"),
|
"agent?": AgentName.or("undefined"),
|
||||||
"prompt?": "string",
|
"prompt?": "string",
|
||||||
"repoInstructions?": "string",
|
"repoInstructions?": "string",
|
||||||
@@ -94,6 +97,9 @@ export function resolvePayload(repoSettings: RepoSettings) {
|
|||||||
// not JSON, treat as plain string prompt
|
// not JSON, treat as plain string prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate version compatibility from jsonPayload
|
||||||
|
if (jsonPayload) validateCompatibility(jsonPayload.version, packageJson.version);
|
||||||
|
|
||||||
// resolve event - use type guard for jsonPayload.event, fallback to unknown trigger
|
// resolve event - use type guard for jsonPayload.event, fallback to unknown trigger
|
||||||
const rawEvent = jsonPayload?.event;
|
const rawEvent = jsonPayload?.event;
|
||||||
const event: PayloadEvent = isPayloadEvent(rawEvent) ? rawEvent : { trigger: "unknown" };
|
const event: PayloadEvent = isPayloadEvent(rawEvent) ? rawEvent : { trigger: "unknown" };
|
||||||
@@ -111,6 +117,7 @@ export function resolvePayload(repoSettings: RepoSettings) {
|
|||||||
// note: modes are NOT in payload - they come from repoSettings in main()
|
// note: modes are NOT in payload - they come from repoSettings in main()
|
||||||
return {
|
return {
|
||||||
"~pullfrog": true as const,
|
"~pullfrog": true as const,
|
||||||
|
version: jsonPayload?.version ?? packageJson.version,
|
||||||
agent: resolvedAgent,
|
agent: resolvedAgent,
|
||||||
// inverted: jsonPayload.prompt extracts the text from the JSON payload,
|
// inverted: jsonPayload.prompt extracts the text from the JSON payload,
|
||||||
// whereas inputs.prompt IS the raw JSON string when internally dispatched
|
// whereas inputs.prompt IS the raw JSON string when internally dispatched
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { validateCompatibility } from './versioning.ts';
|
||||||
|
|
||||||
|
describe('validateCompatibility', () => {
|
||||||
|
it('should throw if payload version is invalid', () => {
|
||||||
|
expect(() => validateCompatibility('invalid', '1.0.0')).toThrow(/not a valid semantic version/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
["1.0.0", "1.0.0"], // same
|
||||||
|
["1.0.0-alpha.1", "1.0.0"], // action is newer than pre-release
|
||||||
|
["0.1.0", "0.1.1"], // action is newer during active development
|
||||||
|
["1.0.0", "1.0.1"], // action patched
|
||||||
|
["1.0.0", "1.1.0"], // action has a new feature (backward compatible)
|
||||||
|
["1.0.1", "1.0.0"], // payload is newer (patch)
|
||||||
|
["1.1.0", "1.0.0"], // payload is newer (feature is backward compatible)
|
||||||
|
])('should accept compatible payload %#', (payloadVersion, actionVersion) => {
|
||||||
|
expect(() => validateCompatibility(payloadVersion, actionVersion)).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
["0.1.0", "0.2.0"], // action had breaking changes during active development
|
||||||
|
["0.2.0", "0.1.0"], // payload had breaking changes during active development
|
||||||
|
["2.0.0", "1.0.0"], // payload is majorly newer
|
||||||
|
["1.0.0", "2.0.0"], // action had breaking changes
|
||||||
|
])('should reject incompatible payload %#', (payloadVersion, actionVersion) => {
|
||||||
|
expect(() => validateCompatibility(payloadVersion, actionVersion)).toThrow(/is incompatible with action version/);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import semver from 'semver';
|
||||||
|
|
||||||
|
type CompatibilityPolicy =
|
||||||
|
/**
|
||||||
|
* Strict policy: the action must support the same features as the payload version declares
|
||||||
|
* @example Payload version 1.2.3 => ^1.2.0 range of action versions supported
|
||||||
|
* @example Payload version 0.1.55 => ^0.1.55 range of action versions supported
|
||||||
|
*/
|
||||||
|
| 'same-features'
|
||||||
|
/**
|
||||||
|
* Loose policy: the action must have no breaking changes compared to the payload version
|
||||||
|
* @example Payload version 1.2.3 => ^1.0.0 range of action versions supported
|
||||||
|
* @example Payload version 0.1.55 => ^0.1.0 range of action versions supported
|
||||||
|
*/
|
||||||
|
| 'non-breaking';
|
||||||
|
|
||||||
|
const COMPATIBILITY_POLICY: CompatibilityPolicy = "non-breaking";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Error if the action can't process payload
|
||||||
|
* The compatibility is determined according to the COMPATIBILITY_POLICY above.
|
||||||
|
* @param payloadVersion the version of the payload
|
||||||
|
* @param actionVersion the version of the action (recipient)
|
||||||
|
*/
|
||||||
|
export function validateCompatibility(payloadVersion: string, actionVersion: string): void {
|
||||||
|
const payloadSemVer = semver.parse(payloadVersion);
|
||||||
|
if (!payloadSemVer) throw new Error(`Payload version ${payloadVersion} is not a valid semantic version.`);
|
||||||
|
const major = payloadSemVer.major;
|
||||||
|
const minor = payloadSemVer.minor;
|
||||||
|
const patch = payloadSemVer.patch;
|
||||||
|
|
||||||
|
const compatibilityRange = COMPATIBILITY_POLICY === 'same-features'
|
||||||
|
? `^${major}.${minor}.${major === 0 ? patch : 0}`
|
||||||
|
: `^${major}.${major === 0 ? minor : 0}.0`; // non-breaking
|
||||||
|
|
||||||
|
if (!semver.satisfies(actionVersion, compatibilityRange)) {
|
||||||
|
throw new Error(
|
||||||
|
`Payload version ${payloadVersion} is incompatible with action version ${actionVersion}. ` +
|
||||||
|
`Please update your workflow to use at least ${semver.minVersion(compatibilityRange)} version of the action.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user