crewpay-goat-plugin 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CrewPay / fxams
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,39 @@
1
- # Temporary Holding Version
1
+ # crewpay-goat-plugin
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ GOAT SDK plugin for [CrewPay](https://crewpay.dev).
4
+
5
+ ```bash
6
+ npm i crewpay-goat-plugin
7
+ ```
8
+
9
+ ```ts
10
+ import { crewpay } from 'crewpay-goat-plugin'
11
+
12
+ const plugin = crewpay()
13
+ const dry = await plugin.service.crewpay_launch_dry_run({
14
+ name: 'Desk Cat',
15
+ ticker: 'DCAT',
16
+ autoHire: { seats: 3 },
17
+ })
18
+ // human confirms…
19
+ await plugin.service.crewpay_launch({
20
+ name: 'Desk Cat',
21
+ ticker: 'DCAT',
22
+ autoHire: { seats: 3 },
23
+ dryRunToken: dry.dryRunToken,
24
+ humanConfirmed: true,
25
+ confirmPhrase: 'APPROVE_SOL_SPEND',
26
+ })
27
+ ```
28
+
29
+ Prefer `npx -y crewpay-mcp@1.2.1` for local signing; hosted MCP `https://mcp.crewpay.dev/mcp` for discover/autohire without a launcher secret.
30
+
31
+ ## Safety
32
+
33
+ - Fee map **60/15/25**; buyback cron not live yet
34
+ - Launch/wire require dry-run token + exact phrase `APPROVE_SOL_SPEND`
35
+
36
+ ## Links
37
+
38
+ - Source: https://github.com/fxams/goat-plugin-crewpay
39
+ - Upstream: https://github.com/goat-sdk/goat
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Minimal CrewPay REST client for framework plugins.
3
+ * Keys from env only — never accept private keys as function arguments.
4
+ *
5
+ * Spend gates: dry-run issues a short-lived dryRunToken; launch/wire require that
6
+ * token (launch) plus the exact confirmPhrase APPROVE_SOL_SPEND (not a free-form
7
+ * boolean the model can invent from chat alone).
8
+ */
9
+ export declare const DEFAULT_API_URL = "https://api.crewpay.dev";
10
+ /** Exact phrase a human must supply for SOL-spending calls. */
11
+ export declare const SOL_SPEND_CONFIRM_PHRASE = "APPROVE_SOL_SPEND";
12
+ export type CrewPayEnv = {
13
+ apiUrl?: string;
14
+ apiKey?: string;
15
+ launcherKey?: string;
16
+ };
17
+ export declare function loadCrewPayEnv(overrides?: CrewPayEnv): Required<Pick<CrewPayEnv, 'apiUrl'>> & CrewPayEnv;
18
+ /** Canonical hash of launch fields so dry-run and launch bodies must match. */
19
+ export declare function hashLaunchBody(body: Record<string, unknown>): string;
20
+ export declare class CrewPayClient {
21
+ private readonly env;
22
+ constructor(env?: ReturnType<typeof loadCrewPayEnv>);
23
+ private headers;
24
+ fetch(path: string, init?: RequestInit & {
25
+ auth?: boolean;
26
+ launcher?: boolean;
27
+ }): Promise<unknown>;
28
+ discover(): Promise<unknown>;
29
+ claimKey(body?: {
30
+ label?: string;
31
+ agentName?: string;
32
+ model?: string;
33
+ }): Promise<unknown>;
34
+ autohire(body: Record<string, unknown>): Promise<unknown>;
35
+ dryRun(body: Record<string, unknown>): Promise<{
36
+ dryRunToken: string;
37
+ confirmPhraseRequired: string;
38
+ tip: string;
39
+ }>;
40
+ /**
41
+ * Real launch — spends SOL. Requires a prior dryRunToken for this body plus
42
+ * humanConfirmed and confirmPhrase APPROVE_SOL_SPEND.
43
+ */
44
+ launch(body: Record<string, unknown>, opts: {
45
+ humanConfirmed: boolean;
46
+ confirmPhrase: string;
47
+ dryRunToken: string;
48
+ }): Promise<unknown>;
49
+ wireFees(body: {
50
+ mint: string;
51
+ mode?: string;
52
+ crew?: unknown[];
53
+ }, opts: {
54
+ humanConfirmed: boolean;
55
+ confirmPhrase: string;
56
+ }): Promise<unknown>;
57
+ crank(body: {
58
+ mint: string;
59
+ }): Promise<unknown>;
60
+ proof(): Promise<unknown>;
61
+ }
package/dist/client.js ADDED
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Minimal CrewPay REST client for framework plugins.
3
+ * Keys from env only — never accept private keys as function arguments.
4
+ *
5
+ * Spend gates: dry-run issues a short-lived dryRunToken; launch/wire require that
6
+ * token (launch) plus the exact confirmPhrase APPROVE_SOL_SPEND (not a free-form
7
+ * boolean the model can invent from chat alone).
8
+ */
9
+ import { createHash, randomBytes } from 'node:crypto';
10
+ export const DEFAULT_API_URL = 'https://api.crewpay.dev';
11
+ /** Exact phrase a human must supply for SOL-spending calls. */
12
+ export const SOL_SPEND_CONFIRM_PHRASE = 'APPROVE_SOL_SPEND';
13
+ const pendingDryRuns = new Map();
14
+ const DRY_RUN_TTL_MS = 30 * 60 * 1000;
15
+ export function loadCrewPayEnv(overrides = {}) {
16
+ return {
17
+ apiUrl: (overrides.apiUrl || process.env.CREW_API_URL || DEFAULT_API_URL).replace(/\/$/, ''),
18
+ apiKey: overrides.apiKey ||
19
+ process.env.CREWPAY_API_KEY?.trim() ||
20
+ process.env.CREW_AGENT_API_KEY?.trim() ||
21
+ '',
22
+ launcherKey: overrides.launcherKey ||
23
+ process.env.CREW_LAUNCHER_KEY?.trim() ||
24
+ '',
25
+ };
26
+ }
27
+ /** Canonical hash of launch fields so dry-run and launch bodies must match. */
28
+ export function hashLaunchBody(body) {
29
+ const pick = {
30
+ name: body.name,
31
+ ticker: body.ticker,
32
+ description: body.description ?? null,
33
+ mode: body.mode ?? 'agent',
34
+ initialBuySol: body.initialBuySol ?? 0,
35
+ imageUrl: body.imageUrl ?? null,
36
+ autoHire: body.autoHire ?? null,
37
+ crew: body.crew ?? null,
38
+ holderKol: body.holderKol ?? false,
39
+ };
40
+ return createHash('sha256').update(JSON.stringify(pick)).digest('hex');
41
+ }
42
+ function assertSpendConfirm(opts, action) {
43
+ if (opts.humanConfirmed !== true) {
44
+ throw new Error(`${action} blocked: humanConfirmed must be true after an explicit human approval`);
45
+ }
46
+ if (opts.confirmPhrase !== SOL_SPEND_CONFIRM_PHRASE) {
47
+ throw new Error(`${action} blocked: confirmPhrase must be exactly "${SOL_SPEND_CONFIRM_PHRASE}" (human-supplied; do not invent)`);
48
+ }
49
+ }
50
+ export class CrewPayClient {
51
+ env;
52
+ constructor(env = loadCrewPayEnv()) {
53
+ this.env = env;
54
+ }
55
+ headers(opts) {
56
+ const h = new Headers({ accept: 'application/json', 'content-type': 'application/json' });
57
+ if (this.env.apiKey)
58
+ h.set('x-crew-api-key', this.env.apiKey);
59
+ if (opts?.launcher) {
60
+ if (!this.env.launcherKey) {
61
+ throw new Error('Missing CREW_LAUNCHER_KEY in env — use a dedicated low-SOL burner. Never pass secrets as arguments.');
62
+ }
63
+ h.set('x-launcher-key', this.env.launcherKey);
64
+ }
65
+ return h;
66
+ }
67
+ async fetch(path, init) {
68
+ const headers = this.headers({ launcher: init?.launcher });
69
+ if (init?.auth && !this.env.apiKey) {
70
+ throw new Error('Missing CREWPAY_API_KEY / CREW_AGENT_API_KEY — claim via POST /api/agent/keys/claim');
71
+ }
72
+ const res = await fetch(`${this.env.apiUrl}${path}`, { ...init, headers });
73
+ const text = await res.text();
74
+ let json = null;
75
+ try {
76
+ json = text ? JSON.parse(text) : null;
77
+ }
78
+ catch {
79
+ json = { raw: text.slice(0, 500) };
80
+ }
81
+ if (!res.ok) {
82
+ const err = typeof json === 'object' && json && 'error' in json
83
+ ? String(json.error)
84
+ : text.slice(0, 300);
85
+ throw new Error(`CREW API ${res.status}: ${err}`);
86
+ }
87
+ return json;
88
+ }
89
+ discover() {
90
+ return this.fetch('/api/agent');
91
+ }
92
+ claimKey(body = {}) {
93
+ return this.fetch('/api/agent/keys/claim', {
94
+ method: 'POST',
95
+ body: JSON.stringify({ label: 'agent', ...body }),
96
+ });
97
+ }
98
+ autohire(body) {
99
+ return this.fetch('/api/agent/autohire', {
100
+ method: 'POST',
101
+ auth: true,
102
+ body: JSON.stringify(body),
103
+ });
104
+ }
105
+ async dryRun(body) {
106
+ const data = await this.fetch('/api/agent/launch/dry-run', {
107
+ method: 'POST',
108
+ auth: true,
109
+ body: JSON.stringify(body),
110
+ });
111
+ const dryRunToken = randomBytes(16).toString('hex');
112
+ pendingDryRuns.set(dryRunToken, {
113
+ bodyHash: hashLaunchBody(body),
114
+ expiresAt: Date.now() + DRY_RUN_TTL_MS,
115
+ });
116
+ return {
117
+ ...(typeof data === 'object' && data ? data : { result: data }),
118
+ dryRunToken,
119
+ confirmPhraseRequired: SOL_SPEND_CONFIRM_PHRASE,
120
+ tip: `To launch: pass the same body + dryRunToken + humanConfirmed:true + confirmPhrase:"${SOL_SPEND_CONFIRM_PHRASE}" (human must supply the phrase).`,
121
+ };
122
+ }
123
+ /**
124
+ * Real launch — spends SOL. Requires a prior dryRunToken for this body plus
125
+ * humanConfirmed and confirmPhrase APPROVE_SOL_SPEND.
126
+ */
127
+ launch(body, opts) {
128
+ assertSpendConfirm(opts, 'Launch');
129
+ const pending = pendingDryRuns.get(opts.dryRunToken);
130
+ if (!pending || pending.expiresAt < Date.now()) {
131
+ throw new Error('Launch blocked: missing/expired dryRunToken — call dryRun first and reuse its dryRunToken');
132
+ }
133
+ if (pending.bodyHash !== hashLaunchBody(body)) {
134
+ throw new Error('Launch blocked: body does not match the dry-run that issued dryRunToken');
135
+ }
136
+ pendingDryRuns.delete(opts.dryRunToken);
137
+ return this.fetch('/api/agent/launch', {
138
+ method: 'POST',
139
+ auth: true,
140
+ launcher: true,
141
+ body: JSON.stringify(body),
142
+ });
143
+ }
144
+ wireFees(body, opts) {
145
+ assertSpendConfirm(opts, 'Wire-fees');
146
+ return this.fetch('/api/agent/wire-fees', {
147
+ method: 'POST',
148
+ auth: true,
149
+ launcher: true,
150
+ body: JSON.stringify(body),
151
+ });
152
+ }
153
+ crank(body) {
154
+ return this.fetch('/api/agent/crank', {
155
+ method: 'POST',
156
+ auth: true,
157
+ body: JSON.stringify(body),
158
+ });
159
+ }
160
+ proof() {
161
+ return this.fetch('/api/proof');
162
+ }
163
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * GOAT SDK plugin scaffold for CrewPay.
3
+ * Prefer MCP: npx -y crewpay-mcp@1.2.1 or https://mcp.crewpay.dev/mcp
4
+ */
5
+ export type CrewPayGoatOptions = {
6
+ apiUrl?: string;
7
+ apiKey?: string;
8
+ };
9
+ export declare class CrewPayGoatService {
10
+ private readonly opts;
11
+ constructor(opts?: CrewPayGoatOptions);
12
+ crewpay_discover(): Promise<unknown>;
13
+ crewpay_autohire(params: Record<string, unknown>): Promise<unknown>;
14
+ crewpay_launch_dry_run(params: Record<string, unknown>): Promise<{
15
+ dryRunToken: string;
16
+ confirmPhraseRequired: string;
17
+ tip: string;
18
+ }>;
19
+ /**
20
+ * Spends SOL. Requires dryRunToken from dry-run + confirmPhrase APPROVE_SOL_SPEND + humanConfirmed.
21
+ */
22
+ crewpay_launch(params: Record<string, unknown> & {
23
+ humanConfirmed?: boolean;
24
+ confirmPhrase?: string;
25
+ dryRunToken?: string;
26
+ }): Promise<unknown>;
27
+ crewpay_wire_fees(params: {
28
+ mint: string;
29
+ mode?: string;
30
+ humanConfirmed?: boolean;
31
+ confirmPhrase?: string;
32
+ }): Promise<unknown>;
33
+ crewpay_crank(params: {
34
+ mint: string;
35
+ }): Promise<unknown>;
36
+ crewpay_proof(): Promise<unknown>;
37
+ }
38
+ export declare function crewpay(options?: CrewPayGoatOptions): {
39
+ name: string;
40
+ description: string;
41
+ service: CrewPayGoatService;
42
+ };
43
+ export default crewpay;
package/dist/index.js ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * GOAT SDK plugin scaffold for CrewPay.
3
+ * Prefer MCP: npx -y crewpay-mcp@1.2.1 or https://mcp.crewpay.dev/mcp
4
+ */
5
+ import { CrewPayClient, loadCrewPayEnv, SOL_SPEND_CONFIRM_PHRASE, } from './client.js';
6
+ function api(opts = {}) {
7
+ return new CrewPayClient(loadCrewPayEnv(opts));
8
+ }
9
+ export class CrewPayGoatService {
10
+ opts;
11
+ constructor(opts = {}) {
12
+ this.opts = opts;
13
+ }
14
+ async crewpay_discover() {
15
+ return api(this.opts).discover();
16
+ }
17
+ async crewpay_autohire(params) {
18
+ return api(this.opts).autohire(params);
19
+ }
20
+ async crewpay_launch_dry_run(params) {
21
+ return api(this.opts).dryRun(params);
22
+ }
23
+ /**
24
+ * Spends SOL. Requires dryRunToken from dry-run + confirmPhrase APPROVE_SOL_SPEND + humanConfirmed.
25
+ */
26
+ async crewpay_launch(params) {
27
+ const { humanConfirmed, confirmPhrase, dryRunToken, ...rest } = params;
28
+ return api(this.opts).launch(rest, {
29
+ humanConfirmed: humanConfirmed === true,
30
+ confirmPhrase: confirmPhrase || '',
31
+ dryRunToken: dryRunToken || '',
32
+ });
33
+ }
34
+ async crewpay_wire_fees(params) {
35
+ const { humanConfirmed, confirmPhrase, mint, mode } = params;
36
+ return api(this.opts).wireFees({ mint, mode }, {
37
+ humanConfirmed: humanConfirmed === true,
38
+ confirmPhrase: confirmPhrase || '',
39
+ });
40
+ }
41
+ async crewpay_crank(params) {
42
+ return api(this.opts).crank(params);
43
+ }
44
+ async crewpay_proof() {
45
+ return api(this.opts).proof();
46
+ }
47
+ }
48
+ export function crewpay(options = {}) {
49
+ return {
50
+ name: 'crewpay',
51
+ description: `CrewPay — Pump.fun KOL Autohire + fee-shares (60/15/25). Buyback not live. Dry-run token + ${SOL_SPEND_CONFIRM_PHRASE} before launch.`,
52
+ service: new CrewPayGoatService(options),
53
+ };
54
+ }
55
+ export default crewpay;
package/package.json CHANGED
@@ -1,6 +1,49 @@
1
1
  {
2
2
  "name": "crewpay-goat-plugin",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "GOAT SDK plugin for CrewPay REST tools. Fee map 60/15/25; buyback not live; dryRunToken + APPROVE_SOL_SPEND before launch.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc -p tsconfig.json",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "goat-sdk",
25
+ "crewpay",
26
+ "solana",
27
+ "pump.fun",
28
+ "ai-agent"
29
+ ],
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/fxams/goat-plugin-crewpay.git"
34
+ },
35
+ "homepage": "https://crewpay.dev/agents",
36
+ "peerDependencies": {
37
+ "@goat-sdk/core": "*"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.10.0",
41
+ "typescript": "^5.7.0"
42
+ },
43
+ "engines": {
44
+ "node": ">=20"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ }
49
+ }