@paytaca/opencode-plugin 0.2.0 → 0.2.2
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/README.md +25 -0
- package/dist/bundled/mcp.d.ts +1 -1
- package/dist/bundled/mcp.d.ts.map +1 -1
- package/dist/bundled/mcp.js +201 -37
- package/dist/bundled/mcp.js.map +1 -1
- package/dist/bundled/proxy.d.ts +1 -1
- package/dist/bundled/proxy.d.ts.map +1 -1
- package/dist/bundled/proxy.js +117 -13
- package/dist/bundled/proxy.js.map +1 -1
- package/dist/bundled/wrapper.d.ts +1 -1
- package/dist/bundled/wrapper.d.ts.map +1 -1
- package/dist/bundled/wrapper.js +133 -9
- package/dist/bundled/wrapper.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -2
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/selfheal.d.ts +13 -0
- package/dist/selfheal.d.ts.map +1 -0
- package/dist/selfheal.js +229 -0
- package/dist/selfheal.js.map +1 -0
- package/package.json +2 -2
- package/scripts/postinstall.js +148 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paytaca/opencode-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "OpenCode plugin for Paytaca AI - AI inference provider powered by Bitcoin Cash micropayments",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@opencode-ai/plugin": "^1.17.8",
|
|
36
|
-
"paytaca-cli": "^0.
|
|
36
|
+
"paytaca-cli": "^0.5.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "^20.0.0",
|
package/scripts/postinstall.js
CHANGED
|
@@ -5,12 +5,14 @@ const os = require('os');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
7
|
const log = (msg) => console.log(`[paytaca] ${msg}`);
|
|
8
|
+
const IS_WIN = process.platform === 'win32';
|
|
8
9
|
|
|
9
10
|
function which(cmd) {
|
|
10
11
|
try {
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
12
|
+
// 'where' on Windows may return CRLF-separated matches; take the first.
|
|
13
|
+
const w = IS_WIN ? 'where' : 'which';
|
|
14
|
+
const out = execSync(`${w} ${cmd}`, { encoding: 'utf8' }).trim();
|
|
15
|
+
return out ? out.split(/\r?\n/)[0].trim() : null;
|
|
14
16
|
} catch {
|
|
15
17
|
return null;
|
|
16
18
|
}
|
|
@@ -37,9 +39,9 @@ function resolveLocalCliBin() {
|
|
|
37
39
|
function globalBinDir() {
|
|
38
40
|
try {
|
|
39
41
|
const prefix = execSync('npm prefix -g', { encoding: 'utf8' }).trim();
|
|
40
|
-
if (prefix) return path.join(prefix,
|
|
42
|
+
if (prefix) return IS_WIN ? prefix : path.join(prefix, 'bin');
|
|
41
43
|
} catch {}
|
|
42
|
-
if (
|
|
44
|
+
if (IS_WIN) return null;
|
|
43
45
|
const homeBin = path.join(os.homedir(), '.npm-global', 'bin');
|
|
44
46
|
if (fs.existsSync(homeBin)) return homeBin;
|
|
45
47
|
const nvmBin = process.env.NVM_BIN;
|
|
@@ -56,11 +58,130 @@ function asdfReshim() {
|
|
|
56
58
|
} catch {}
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
const PACKAGE_NAME = '@paytaca/opencode-plugin';
|
|
62
|
+
|
|
63
|
+
const norm = (p) => path.resolve(p);
|
|
64
|
+
// `win` allows tests to exercise Windows path semantics without touching
|
|
65
|
+
// the real process.platform (which child_process.execSync reads to pick its
|
|
66
|
+
// shell). Defaults to the running platform.
|
|
67
|
+
function normKey(p, win) {
|
|
68
|
+
win = win === undefined ? IS_WIN : win;
|
|
69
|
+
const r = norm(p);
|
|
70
|
+
return win ? r.toLowerCase() : r;
|
|
71
|
+
}
|
|
72
|
+
// Is `child` the same as, or located beneath, `parent`?
|
|
73
|
+
function isInside(child, parent, win) {
|
|
74
|
+
const c = normKey(child, win);
|
|
75
|
+
const p = normKey(parent, win);
|
|
76
|
+
return c === p || c.startsWith(p + path.sep);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Clean up version-inconsistency traps after a fresh install:
|
|
80
|
+
// 1. Remove opencode plugin-cache entries for other versions of this plugin.
|
|
81
|
+
// 2. Pin the dependency spec to this exact version in opencode package.jsons
|
|
82
|
+
// (^ ranges on 0.x exclude newer minors, which blocks upgrades).
|
|
83
|
+
// 3. Delete lockfiles that still pin an old version (they regenerate).
|
|
84
|
+
function selfPin() {
|
|
85
|
+
try {
|
|
86
|
+
const pkgRoot = path.resolve(__dirname, '..');
|
|
87
|
+
const own = JSON.parse(fs.readFileSync(path.join(pkgRoot, 'package.json'), 'utf8')).version;
|
|
88
|
+
if (!own) return;
|
|
89
|
+
|
|
90
|
+
// 1. Stale plugin caches (never touch the directory we were installed into)
|
|
91
|
+
const cacheBase = path.join(
|
|
92
|
+
process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache'),
|
|
93
|
+
'opencode', 'packages', '@paytaca'
|
|
94
|
+
);
|
|
95
|
+
try {
|
|
96
|
+
for (const entry of fs.readdirSync(cacheBase)) {
|
|
97
|
+
if (entry.indexOf('opencode-plugin') === -1) continue;
|
|
98
|
+
const dir = path.join(cacheBase, entry);
|
|
99
|
+
// Paths are case-insensitive on Windows; compare normalized keys.
|
|
100
|
+
if (isInside(pkgRoot, dir)) continue;
|
|
101
|
+
let version = null;
|
|
102
|
+
try {
|
|
103
|
+
version = JSON.parse(
|
|
104
|
+
fs.readFileSync(path.join(dir, 'node_modules', PACKAGE_NAME, 'package.json'), 'utf8')
|
|
105
|
+
).version;
|
|
106
|
+
} catch {}
|
|
107
|
+
if (version !== own) {
|
|
108
|
+
try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch {}
|
|
112
|
+
|
|
113
|
+
// 2. Exact-pin the spec in every opencode scope referencing the plugin.
|
|
114
|
+
// npm runs lifecycle scripts with cwd = the package install dir, so
|
|
115
|
+
// consider both `<cwd>/.opencode` and `<cwd>` itself as project scopes.
|
|
116
|
+
const cfgDir = path.join(
|
|
117
|
+
process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'),
|
|
118
|
+
'opencode'
|
|
119
|
+
);
|
|
120
|
+
const cwd = process.cwd();
|
|
121
|
+
const seen = new Set();
|
|
122
|
+
const configDirs = [];
|
|
123
|
+
for (const d of [cfgDir, path.join(cwd, '.opencode'), cwd]) {
|
|
124
|
+
if (isInside(d, pkgRoot)) continue;
|
|
125
|
+
const key = normKey(d);
|
|
126
|
+
if (seen.has(key)) continue;
|
|
127
|
+
seen.add(key);
|
|
128
|
+
try {
|
|
129
|
+
if (fs.existsSync(path.join(d, 'package.json'))) configDirs.push(d);
|
|
130
|
+
} catch {}
|
|
131
|
+
}
|
|
132
|
+
for (const dir of configDirs) {
|
|
133
|
+
try {
|
|
134
|
+
const file = path.join(dir, 'package.json');
|
|
135
|
+
if (!fs.existsSync(file)) continue;
|
|
136
|
+
const json = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
137
|
+
const deps = json.dependencies || {};
|
|
138
|
+
if (typeof deps[PACKAGE_NAME] !== 'string' || deps[PACKAGE_NAME] === own) continue;
|
|
139
|
+
deps[PACKAGE_NAME] = own;
|
|
140
|
+
json.dependencies = deps;
|
|
141
|
+
fs.writeFileSync(file, JSON.stringify(json, null, 2) + '\n');
|
|
142
|
+
log(`Pinned ${PACKAGE_NAME} to ${own} in ${file}`);
|
|
143
|
+
} catch {}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// 3. Lockfiles pinning an old version would fail integrity on next install
|
|
147
|
+
for (const dir of configDirs) {
|
|
148
|
+
for (const lock of ['package-lock.json', 'bun.lock']) {
|
|
149
|
+
try {
|
|
150
|
+
const file = path.join(dir, lock);
|
|
151
|
+
if (!fs.existsSync(file)) continue;
|
|
152
|
+
if (fs.readFileSync(file, 'utf8').indexOf(PACKAGE_NAME) === -1) continue;
|
|
153
|
+
fs.rmSync(file, { force: true });
|
|
154
|
+
log(`Removed stale ${lock} in ${dir} (regenerates on next install)`);
|
|
155
|
+
} catch {}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
} catch {}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// `opts.platform`/`opts.binDir` let tests drive the Windows/unix branches
|
|
162
|
+
// deterministically; in production they default to the running platform and a
|
|
163
|
+
// resolved global bin directory.
|
|
164
|
+
async function linkGlobally(cliBin, opts) {
|
|
165
|
+
opts = opts || {};
|
|
166
|
+
const win = opts.platform ? opts.platform === 'win32' : IS_WIN;
|
|
167
|
+
const binDir = opts.binDir || globalBinDir();
|
|
61
168
|
if (!binDir) throw new Error('Could not determine global bin directory');
|
|
62
169
|
fs.mkdirSync(binDir, { recursive: true });
|
|
63
170
|
|
|
171
|
+
if (win) {
|
|
172
|
+
// Windows can't run shebang'd .js files, and symlinking requires admin /
|
|
173
|
+
// Developer Mode. Ship a .cmd shim (used by cmd.exe and PowerShell) plus a
|
|
174
|
+
// POSIX-style wrapper so Git-Bash / MSYS users get a working `paytaca` too.
|
|
175
|
+
const cmdFile = path.join(binDir, 'paytaca.cmd');
|
|
176
|
+
fs.writeFileSync(cmdFile, '@echo off\r\nnode "' + cliBin + '" %*\r\n', 'utf8');
|
|
177
|
+
try {
|
|
178
|
+
const shFile = path.join(binDir, 'paytaca');
|
|
179
|
+
fs.writeFileSync(shFile, '#!/bin/sh\nexec node "' + cliBin + '" "$@"\n', 'utf8');
|
|
180
|
+
fs.chmodSync(shFile, '755');
|
|
181
|
+
} catch {}
|
|
182
|
+
return cmdFile;
|
|
183
|
+
}
|
|
184
|
+
|
|
64
185
|
const link = path.join(binDir, 'paytaca');
|
|
65
186
|
if (fs.existsSync(link) || fs.lstatSync(link, { throwIfNoEntry: false })) {
|
|
66
187
|
const st = fs.lstatSync(link, { throwIfNoEntry: false });
|
|
@@ -74,6 +195,7 @@ async function linkGlobally(cliBin) {
|
|
|
74
195
|
|
|
75
196
|
async function main() {
|
|
76
197
|
if (process.env.PAYTACA_PLUGIN_SKIP_POSTINSTALL) return;
|
|
198
|
+
selfPin();
|
|
77
199
|
if (which('paytaca') && runPaytacaVersion()) return;
|
|
78
200
|
|
|
79
201
|
const cliBin = resolveLocalCliBin();
|
|
@@ -86,7 +208,7 @@ async function main() {
|
|
|
86
208
|
const link = await linkGlobally(cliBin);
|
|
87
209
|
log(`Linked paytaca -> ${link}`);
|
|
88
210
|
} catch (err) {
|
|
89
|
-
log(`Could not
|
|
211
|
+
log(`Could not link paytaca globally (${err.message}).`);
|
|
90
212
|
log('Falling back to: npm install -g paytaca-cli');
|
|
91
213
|
try {
|
|
92
214
|
execSync('npm install -g paytaca-cli', { stdio: 'inherit' });
|
|
@@ -97,4 +219,21 @@ async function main() {
|
|
|
97
219
|
}
|
|
98
220
|
}
|
|
99
221
|
|
|
100
|
-
|
|
222
|
+
// Export helpers for tests. When run as a script (npm postinstall), main()
|
|
223
|
+
// executes; when required by a test, only the functions are exposed.
|
|
224
|
+
if (require.main === module) {
|
|
225
|
+
main().catch(() => {});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
module.exports = {
|
|
229
|
+
which,
|
|
230
|
+
runPaytacaVersion,
|
|
231
|
+
resolveLocalCliBin,
|
|
232
|
+
globalBinDir,
|
|
233
|
+
asdfReshim,
|
|
234
|
+
selfPin,
|
|
235
|
+
linkGlobally,
|
|
236
|
+
isInside,
|
|
237
|
+
normKey,
|
|
238
|
+
IS_WIN,
|
|
239
|
+
};
|