supipowers 1.2.4 → 1.2.5
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/bin/install.mjs +5 -1
- package/bin/install.ts +110 -14
- package/bin/local-install.sh +17 -13
- package/package.json +1 -1
- package/src/deps/registry.ts +4 -4
package/bin/install.mjs
CHANGED
|
@@ -5,7 +5,11 @@ import { dirname, join } from "node:path";
|
|
|
5
5
|
|
|
6
6
|
const isWindows = process.platform === "win32";
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
|
|
8
|
+
// When invoked via bunx/npx, always force a full install to guarantee
|
|
9
|
+
// node_modules/ and a consistent extension directory. The --force flag
|
|
10
|
+
// bypasses the "already up to date" version check.
|
|
11
|
+
const args = [join(__dirname, "install.ts"), "--force", ...process.argv.slice(2)];
|
|
12
|
+
const result = spawnSync("bun", args, {
|
|
9
13
|
stdio: "inherit",
|
|
10
14
|
env: process.env,
|
|
11
15
|
shell: isWindows,
|
package/bin/install.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { spawnSync } from "node:child_process";
|
|
|
14
14
|
import {
|
|
15
15
|
readFileSync,
|
|
16
16
|
writeFileSync,
|
|
17
|
+
appendFileSync,
|
|
17
18
|
existsSync,
|
|
18
19
|
mkdirSync,
|
|
19
20
|
cpSync,
|
|
@@ -123,10 +124,37 @@ async function exec(cmd: string, args: string[]): Promise<ExecResult> {
|
|
|
123
124
|
return { stdout: r.stdout ?? "", stderr: r.stderr ?? "", code: r.status ?? 1 };
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
// ── CLI Flags
|
|
127
|
+
// ── CLI Flags ────────────────────────────────────────────────────
|
|
127
128
|
|
|
128
129
|
const cliArgs = process.argv.slice(2);
|
|
129
130
|
const skipDeps = cliArgs.includes("--skip-deps");
|
|
131
|
+
const FORCE = cliArgs.includes("--force");
|
|
132
|
+
const DEBUG = cliArgs.includes("--debug");
|
|
133
|
+
|
|
134
|
+
// ── Debug logging ────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
const LOG_FILE = resolve(process.cwd(), "supipowers-install.log");
|
|
137
|
+
|
|
138
|
+
function log(msg: string): void {
|
|
139
|
+
if (!DEBUG) return;
|
|
140
|
+
const line = `[${new Date().toISOString()}] ${msg}\n`;
|
|
141
|
+
appendFileSync(LOG_FILE, line);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (DEBUG) {
|
|
145
|
+
// Start fresh log
|
|
146
|
+
writeFileSync(LOG_FILE, `supipowers installer debug log\n`);
|
|
147
|
+
log(`platform: ${process.platform}`);
|
|
148
|
+
log(`arch: ${process.arch}`);
|
|
149
|
+
log(`bun: ${process.versions?.bun ?? "N/A"}`);
|
|
150
|
+
log(`node: ${process.version}`);
|
|
151
|
+
log(`cwd: ${process.cwd()}`);
|
|
152
|
+
log(`homedir: ${homedir()}`);
|
|
153
|
+
log(`argv: ${JSON.stringify(process.argv)}`);
|
|
154
|
+
log(`__dirname: ${__dirname}`);
|
|
155
|
+
log(`packageRoot will be: ${resolve(__dirname, "..")}`);
|
|
156
|
+
log(`isWindows: ${isWindows}`);
|
|
157
|
+
}
|
|
130
158
|
|
|
131
159
|
// ── Install to platform ──────────────────────────────────────
|
|
132
160
|
|
|
@@ -143,18 +171,29 @@ function installToPlatform(platformDir: string, packageRoot: string): string {
|
|
|
143
171
|
const extDir = join(agentDir, "extensions", "supipowers");
|
|
144
172
|
const installedPkgPath = join(extDir, "package.json");
|
|
145
173
|
|
|
174
|
+
log(`installToPlatform(platformDir=${platformDir}, packageRoot=${packageRoot})`);
|
|
175
|
+
log(` agentDir: ${agentDir}`);
|
|
176
|
+
log(` extDir: ${extDir}`);
|
|
177
|
+
|
|
146
178
|
// Check for existing installation
|
|
147
179
|
let installedVersion: string | null = null;
|
|
148
180
|
if (existsSync(installedPkgPath)) {
|
|
149
181
|
try {
|
|
150
182
|
const installed = JSON.parse(readFileSync(installedPkgPath, "utf8"));
|
|
151
183
|
installedVersion = installed.version;
|
|
184
|
+
log(` existing version: ${installedVersion}`);
|
|
152
185
|
} catch {
|
|
153
|
-
|
|
186
|
+
log(` existing package.json corrupted, treating as fresh install`);
|
|
154
187
|
}
|
|
188
|
+
} else {
|
|
189
|
+
log(` no existing installation found`);
|
|
155
190
|
}
|
|
156
191
|
|
|
157
|
-
|
|
192
|
+
const hasNodeModules = existsSync(join(extDir, "node_modules"));
|
|
193
|
+
log(` node_modules present: ${hasNodeModules}`);
|
|
194
|
+
|
|
195
|
+
if (installedVersion === VERSION && hasNodeModules && !FORCE) {
|
|
196
|
+
log(` already up to date with deps, skipping`);
|
|
158
197
|
note(
|
|
159
198
|
`supipowers v${VERSION} is already installed and up to date.`,
|
|
160
199
|
`Up to date (${platformDir})`,
|
|
@@ -162,9 +201,16 @@ function installToPlatform(platformDir: string, packageRoot: string): string {
|
|
|
162
201
|
return extDir;
|
|
163
202
|
}
|
|
164
203
|
|
|
204
|
+
if (installedVersion === VERSION && !hasNodeModules) {
|
|
205
|
+
log(` same version but node_modules missing — reinstalling deps`);
|
|
206
|
+
}
|
|
207
|
+
if (FORCE) {
|
|
208
|
+
log(` --force flag set, reinstalling`);
|
|
209
|
+
}
|
|
210
|
+
|
|
165
211
|
const action = installedVersion ? "Updating" : "Installing";
|
|
166
212
|
if (installedVersion) {
|
|
167
|
-
note(`v${installedVersion}
|
|
213
|
+
note(`v${installedVersion} \u2192 v${VERSION}`, `Updating supipowers (${platformDir})`);
|
|
168
214
|
}
|
|
169
215
|
|
|
170
216
|
const s = spinner();
|
|
@@ -173,14 +219,40 @@ function installToPlatform(platformDir: string, packageRoot: string): string {
|
|
|
173
219
|
try {
|
|
174
220
|
// Clean previous installation to remove stale files
|
|
175
221
|
if (existsSync(extDir)) {
|
|
222
|
+
log(` removing old extDir`);
|
|
176
223
|
rmSync(extDir, { recursive: true });
|
|
177
224
|
}
|
|
178
225
|
|
|
179
|
-
// Copy extension (src/ + bin/ + package.json)
|
|
226
|
+
// Copy extension (src/ + bin/ + package.json) \u2192 ~/<platform>/agent/extensions/supipowers/
|
|
227
|
+
log(` creating extDir and copying files`);
|
|
180
228
|
mkdirSync(extDir, { recursive: true });
|
|
181
229
|
cpSync(join(packageRoot, "src"), join(extDir, "src"), { recursive: true });
|
|
182
230
|
cpSync(join(packageRoot, "bin"), join(extDir, "bin"), { recursive: true });
|
|
183
231
|
cpSync(join(packageRoot, "package.json"), join(extDir, "package.json"));
|
|
232
|
+
log(` files copied to ${extDir}`);
|
|
233
|
+
|
|
234
|
+
// Rewrite package.json for the installed extension.
|
|
235
|
+
// The npm-published package.json has bin, scripts, prepare, devDeps —
|
|
236
|
+
// all of which cause problems during `bun install` in the extension dir.
|
|
237
|
+
// We keep only what OMP needs (omp.extensions) and the runtime dependencies.
|
|
238
|
+
const sourcePkg = JSON.parse(readFileSync(join(extDir, "package.json"), "utf8"));
|
|
239
|
+
const runtimePkg = {
|
|
240
|
+
name: sourcePkg.name,
|
|
241
|
+
version: sourcePkg.version,
|
|
242
|
+
type: sourcePkg.type,
|
|
243
|
+
omp: sourcePkg.omp,
|
|
244
|
+
dependencies: {
|
|
245
|
+
// Only packages imported at runtime by src/ code:
|
|
246
|
+
// - config/schema.ts → @sinclair/typebox
|
|
247
|
+
// - commands/model.ts, model-picker.ts → @oh-my-pi/pi-ai
|
|
248
|
+
// - commands/model-picker.ts → @oh-my-pi/pi-tui
|
|
249
|
+
"@sinclair/typebox": "*",
|
|
250
|
+
"@oh-my-pi/pi-ai": "*",
|
|
251
|
+
"@oh-my-pi/pi-tui": "*",
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
writeFileSync(join(extDir, "package.json"), JSON.stringify(runtimePkg, null, 2));
|
|
255
|
+
log(` rewrote package.json: ${JSON.stringify(runtimePkg, null, 2)}`);
|
|
184
256
|
|
|
185
257
|
// Copy skills → ~/<platform>/agent/skills/<skillname>/SKILL.md
|
|
186
258
|
const skillsSource = join(packageRoot, "skills");
|
|
@@ -196,17 +268,19 @@ function installToPlatform(platformDir: string, packageRoot: string): string {
|
|
|
196
268
|
}
|
|
197
269
|
}
|
|
198
270
|
|
|
199
|
-
// Install dependencies so the extension's
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
// packages aren't in Bun's global install (e.g. OMP installed via npm).
|
|
271
|
+
// Install runtime dependencies so the extension's imports resolve.
|
|
272
|
+
// Without node_modules/, external imports (@sinclair/typebox, @oh-my-pi/*)
|
|
273
|
+
// fail on systems where these packages aren't in Bun's global install.
|
|
274
|
+
log(` running: bun install (cwd=${extDir})`);
|
|
204
275
|
s.message("Installing extension dependencies...");
|
|
205
|
-
const install = run("bun", ["install"
|
|
276
|
+
const install = run("bun", ["install"], { cwd: extDir });
|
|
277
|
+
log(` bun install exit code: ${install.status}`);
|
|
278
|
+
log(` bun install stdout: ${install.stdout ?? "(null)"}`);
|
|
279
|
+
log(` bun install stderr: ${install.stderr ?? "(null)"}`);
|
|
280
|
+
if (install.error) log(` bun install error: ${install.error.message}`);
|
|
206
281
|
if (install.status !== 0) {
|
|
207
|
-
// Non-fatal: the extension may still work if OMP provides the deps
|
|
208
|
-
//
|
|
209
|
-
// registry might be temporarily unreachable.
|
|
282
|
+
// Non-fatal: the extension may still work if OMP provides the deps
|
|
283
|
+
// via its own module resolution (e.g. Bun global install on macOS).
|
|
210
284
|
note(
|
|
211
285
|
"Could not install extension dependencies.\n" +
|
|
212
286
|
"If /supi commands don't appear in OMP, run:\n" +
|
|
@@ -215,12 +289,23 @@ function installToPlatform(platformDir: string, packageRoot: string): string {
|
|
|
215
289
|
);
|
|
216
290
|
}
|
|
217
291
|
|
|
292
|
+
// Verify node_modules was created
|
|
293
|
+
const nmExists = existsSync(join(extDir, "node_modules"));
|
|
294
|
+
log(` node_modules exists after install: ${nmExists}`);
|
|
295
|
+
if (nmExists) {
|
|
296
|
+
try {
|
|
297
|
+
const nmContents = readdirSync(join(extDir, "node_modules"));
|
|
298
|
+
log(` node_modules top-level: ${nmContents.join(", ")}`);
|
|
299
|
+
} catch { /* ignore */ }
|
|
300
|
+
}
|
|
301
|
+
|
|
218
302
|
s.stop(
|
|
219
303
|
installedVersion
|
|
220
304
|
? `supipowers updated to v${VERSION} (${platformDir})`
|
|
221
305
|
: `supipowers v${VERSION} installed (${platformDir})`,
|
|
222
306
|
);
|
|
223
307
|
} catch (err: unknown) {
|
|
308
|
+
log(` installToPlatform FAILED: ${err instanceof Error ? err.stack : String(err)}`);
|
|
224
309
|
s.stop(`${action} failed (${platformDir})`);
|
|
225
310
|
const message = err instanceof Error ? err.message : `Failed to copy files to ~/${platformDir}/agent/`;
|
|
226
311
|
bail(message);
|
|
@@ -377,9 +462,14 @@ async function main(): Promise<void> {
|
|
|
377
462
|
const piBin = findPiBinary();
|
|
378
463
|
const ompBin = findOmpBinary();
|
|
379
464
|
|
|
465
|
+
log(`findPiBinary() => ${piBin ?? "null"}`);
|
|
466
|
+
log(`findOmpBinary() => ${ompBin ?? "null"}`);
|
|
467
|
+
|
|
380
468
|
const piVer = piBin ? run(piBin, ["--version"]).stdout?.trim() || "unknown" : null;
|
|
381
469
|
const ompVer = ompBin ? run(ompBin, ["--version"]).stdout?.trim() || "unknown" : null;
|
|
382
470
|
|
|
471
|
+
log(`piVer: ${piVer ?? "N/A"}, ompVer: ${ompVer ?? "N/A"}`);
|
|
472
|
+
|
|
383
473
|
const detected: string[] = [];
|
|
384
474
|
if (piBin) detected.push(`Pi ${piVer}`);
|
|
385
475
|
if (ompBin) detected.push(`OMP ${ompVer}`);
|
|
@@ -448,6 +538,8 @@ async function main(): Promise<void> {
|
|
|
448
538
|
// ── Step 3: Install supipowers to each chosen target ──────
|
|
449
539
|
|
|
450
540
|
const packageRoot = resolve(__dirname, "..");
|
|
541
|
+
log(`packageRoot: ${packageRoot}`);
|
|
542
|
+
log(`targets: ${JSON.stringify(targets)}`);
|
|
451
543
|
|
|
452
544
|
for (const target of targets) {
|
|
453
545
|
installToPlatform(target.dir, packageRoot);
|
|
@@ -456,6 +548,10 @@ async function main(): Promise<void> {
|
|
|
456
548
|
await installContextMode(target.dir);
|
|
457
549
|
}
|
|
458
550
|
|
|
551
|
+
if (DEBUG) {
|
|
552
|
+
note(`Debug log written to:\n${LOG_FILE}`, "Debug");
|
|
553
|
+
}
|
|
554
|
+
|
|
459
555
|
// ── Step 4: Unified dependency check (--skip-deps to skip) ──
|
|
460
556
|
|
|
461
557
|
if (skipDeps) {
|
package/bin/local-install.sh
CHANGED
|
@@ -2,38 +2,42 @@
|
|
|
2
2
|
# Install supipowers locally from the current working tree.
|
|
3
3
|
# Usage: ./bin/local-install.sh
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# Re-run after pulling new changes
|
|
5
|
+
# Creates a global symlink so `supipowers` CLI works, then runs the
|
|
6
|
+
# installer with --debug to deploy the extension and write a log file.
|
|
7
|
+
# Re-run after pulling new changes.
|
|
8
8
|
|
|
9
9
|
set -euo pipefail
|
|
10
10
|
|
|
11
11
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
12
12
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
13
13
|
|
|
14
|
-
echo "
|
|
14
|
+
echo "-> Installing supipowers locally from $PROJECT_DIR"
|
|
15
15
|
|
|
16
16
|
# 1. Install dependencies (fast no-op when lock is current)
|
|
17
|
-
echo "
|
|
17
|
+
echo "-> Installing dependencies..."
|
|
18
18
|
cd "$PROJECT_DIR"
|
|
19
19
|
bun install --frozen-lockfile 2>/dev/null || bun install
|
|
20
20
|
|
|
21
21
|
# 2. Create a global symlink via bun link
|
|
22
|
-
|
|
23
|
-
# and Pi/OMP can resolve it by name.
|
|
24
|
-
echo "→ Linking supipowers globally…"
|
|
22
|
+
echo "-> Linking supipowers globally..."
|
|
25
23
|
bun link
|
|
26
24
|
|
|
27
25
|
# 3. Verify the link
|
|
28
26
|
if command -v supipowers &>/dev/null; then
|
|
29
|
-
echo "
|
|
27
|
+
echo "[OK] 'supipowers' CLI is available at $(which supipowers)"
|
|
30
28
|
else
|
|
31
|
-
echo "
|
|
29
|
+
echo "[WARN] CLI not on PATH -- you may need to add bun's global bin to \$PATH:"
|
|
32
30
|
echo " export PATH=\"\$HOME/.bun/bin:\$PATH\""
|
|
33
31
|
fi
|
|
34
32
|
|
|
35
|
-
# 4.
|
|
33
|
+
# 4. Run the installer with --debug to deploy extension + write log
|
|
34
|
+
echo "-> Running installer (--debug mode)..."
|
|
35
|
+
bun run "$PROJECT_DIR/bin/install.ts" --debug --force
|
|
36
|
+
|
|
37
|
+
# 5. Show version and log location
|
|
36
38
|
VERSION=$(node -e "console.log(require('$PROJECT_DIR/package.json').version)")
|
|
37
39
|
echo ""
|
|
38
|
-
echo "
|
|
39
|
-
|
|
40
|
+
echo "[OK] supipowers v${VERSION} installed locally"
|
|
41
|
+
if [ -f "$PROJECT_DIR/supipowers-install.log" ]; then
|
|
42
|
+
echo " Debug log: $PROJECT_DIR/supipowers-install.log"
|
|
43
|
+
fi
|
package/package.json
CHANGED
package/src/deps/registry.ts
CHANGED
|
@@ -34,10 +34,10 @@ export async function checkBinary(
|
|
|
34
34
|
exec: ExecFn,
|
|
35
35
|
binary: string,
|
|
36
36
|
): Promise<{ installed: boolean; version?: string }> {
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
if (
|
|
37
|
+
// Bun.which() is cross-platform (handles .cmd/.exe/.bat on Windows)
|
|
38
|
+
// and doesn't require shelling out to `which` (Unix) or `where` (Windows).
|
|
39
|
+
const found = Bun.which(binary);
|
|
40
|
+
if (!found) return { installed: false };
|
|
41
41
|
|
|
42
42
|
const ver = await exec(binary, ["--version"]);
|
|
43
43
|
const version = ver.code === 0 ? ver.stdout.trim().split("\n")[0] : undefined;
|