@pajamadot/pajama 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/README.md +51 -0
- package/bin/pajama.js +32 -0
- package/package.json +35 -0
- package/scripts/postinstall.js +101 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Pajama CLI (npm)
|
|
2
|
+
|
|
3
|
+
Install the `pajama` CLI via npm (recommended for end users):
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm i -g @pajamadot/pajama
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Login (OAuth PKCE):
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pajama login
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If your environment cannot open a browser automatically:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pajama login --no-open
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then use the Memory API:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pajama projects list
|
|
25
|
+
pajama projects create --name "UE5 Prototype" --engine unreal --description "Memory sandbox"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Environment Variables
|
|
29
|
+
|
|
30
|
+
- `PAJAMA_API_URL`: Memory API base URL (defaults to `https://api-game-dev-memory.pajamadot.com`)
|
|
31
|
+
- `PAJAMA_TOKEN`: Bearer token override (API key)
|
|
32
|
+
- `PAJAMA_OAUTH_CALLBACK_TIMEOUT_SECS`: loopback callback wait (default: 900)
|
|
33
|
+
|
|
34
|
+
Installer-only:
|
|
35
|
+
|
|
36
|
+
- `PAJAMA_DOWNLOAD_BASE_URL`: override binary download base URL (defaults to `https://api-game-dev-memory.pajamadot.com/downloads/pajama`)
|
|
37
|
+
|
|
38
|
+
## Prebuilt Binary Support
|
|
39
|
+
|
|
40
|
+
This package downloads a platform-specific prebuilt binary at install time.
|
|
41
|
+
|
|
42
|
+
Current prebuilt support (initial release):
|
|
43
|
+
|
|
44
|
+
- Windows x64 (`win32/x64`)
|
|
45
|
+
|
|
46
|
+
If you are on macOS or Linux, install from source for now:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cd pajama
|
|
50
|
+
cargo install --path . --force
|
|
51
|
+
```
|
package/bin/pajama.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
const { spawn } = require("node:child_process");
|
|
7
|
+
|
|
8
|
+
function binaryName() {
|
|
9
|
+
return process.platform === "win32" ? "pajama.exe" : "pajama";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function binaryPath() {
|
|
13
|
+
return path.join(__dirname, binaryName());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const bin = binaryPath();
|
|
17
|
+
if (!fs.existsSync(bin)) {
|
|
18
|
+
console.error("[pajama] CLI binary not found.");
|
|
19
|
+
console.error("[pajama] Try reinstalling: npm i -g @pajamadot/pajama");
|
|
20
|
+
console.error("[pajama] Or build from source: cargo install --path pajama --force");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const child = spawn(bin, process.argv.slice(2), { stdio: "inherit" });
|
|
25
|
+
child.on("exit", (code, signal) => {
|
|
26
|
+
if (signal) {
|
|
27
|
+
process.kill(process.pid, signal);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
process.exit(code == null ? 1 : code);
|
|
31
|
+
});
|
|
32
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pajamadot/pajama",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "PajamaDot CLI for Game Dev Memory (prebuilt binary installer)",
|
|
5
|
+
"private": false,
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+ssh://git@github.com/pajamadot/game-dev-memory.git",
|
|
10
|
+
"directory": "packages/pajama"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"pajama": "bin/pajama.js"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"postinstall": "node scripts/postinstall.js"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"os": [
|
|
22
|
+
"win32"
|
|
23
|
+
],
|
|
24
|
+
"cpu": [
|
|
25
|
+
"x64"
|
|
26
|
+
],
|
|
27
|
+
"files": [
|
|
28
|
+
"bin/pajama.js",
|
|
29
|
+
"scripts/postinstall.js",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
const crypto = require("node:crypto");
|
|
7
|
+
|
|
8
|
+
const pkg = require("../package.json");
|
|
9
|
+
|
|
10
|
+
function platformAssetName() {
|
|
11
|
+
const p = process.platform;
|
|
12
|
+
const a = process.arch;
|
|
13
|
+
|
|
14
|
+
if (p === "win32" && a === "x64") return "pajama-win32-x64.exe";
|
|
15
|
+
if (p === "win32" && a === "arm64") return "pajama-win32-arm64.exe";
|
|
16
|
+
if (p === "darwin" && a === "x64") return "pajama-darwin-x64";
|
|
17
|
+
if (p === "darwin" && a === "arm64") return "pajama-darwin-arm64";
|
|
18
|
+
if (p === "linux" && a === "x64") return "pajama-linux-x64";
|
|
19
|
+
if (p === "linux" && a === "arm64") return "pajama-linux-arm64";
|
|
20
|
+
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function binaryName() {
|
|
25
|
+
return process.platform === "win32" ? "pajama.exe" : "pajama";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function baseUrl() {
|
|
29
|
+
const fromEnv = (process.env.PAJAMA_DOWNLOAD_BASE_URL || "").trim();
|
|
30
|
+
return fromEnv || "https://api-game-dev-memory.pajamadot.com/downloads/pajama";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function fetchOrNull(url) {
|
|
34
|
+
const res = await fetch(url, { redirect: "follow" });
|
|
35
|
+
if (res.status === 404) return null;
|
|
36
|
+
if (!res.ok) {
|
|
37
|
+
const text = await res.text().catch(() => "");
|
|
38
|
+
throw new Error(`HTTP ${res.status} downloading ${url}: ${text}`);
|
|
39
|
+
}
|
|
40
|
+
return res;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function downloadToFile(url, dstPath) {
|
|
44
|
+
const res = await fetchOrNull(url);
|
|
45
|
+
if (!res) throw new Error(`Not found: ${url}`);
|
|
46
|
+
|
|
47
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
48
|
+
|
|
49
|
+
fs.mkdirSync(path.dirname(dstPath), { recursive: true });
|
|
50
|
+
|
|
51
|
+
// Atomic write: write to tmp, then rename.
|
|
52
|
+
const tmp = `${dstPath}.tmp-${process.pid}`;
|
|
53
|
+
fs.writeFileSync(tmp, buf);
|
|
54
|
+
fs.renameSync(tmp, dstPath);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function maybeVerifySha256(url, filePath) {
|
|
58
|
+
const res = await fetchOrNull(url);
|
|
59
|
+
if (!res) return; // optional sidecar
|
|
60
|
+
|
|
61
|
+
const text = (await res.text()).trim();
|
|
62
|
+
const expected = text.split(/\s+/)[0]?.toLowerCase();
|
|
63
|
+
if (!expected || expected.length < 32) return;
|
|
64
|
+
|
|
65
|
+
const data = fs.readFileSync(filePath);
|
|
66
|
+
const actual = crypto.createHash("sha256").update(data).digest("hex").toLowerCase();
|
|
67
|
+
if (actual !== expected) {
|
|
68
|
+
throw new Error(`SHA256 mismatch for ${path.basename(filePath)} (expected ${expected}, got ${actual})`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function main() {
|
|
73
|
+
const asset = platformAssetName();
|
|
74
|
+
if (!asset) {
|
|
75
|
+
console.error(`[pajama] No prebuilt binary for ${process.platform}/${process.arch}.`);
|
|
76
|
+
console.error("[pajama] Build from source: cargo install --path pajama --force");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const tag = `v${pkg.version}`;
|
|
81
|
+
const url = `${baseUrl()}/${tag}/${asset}`;
|
|
82
|
+
const shaUrl = `${url}.sha256`;
|
|
83
|
+
const dst = path.join(__dirname, "..", "bin", binaryName());
|
|
84
|
+
|
|
85
|
+
// Skip if already present.
|
|
86
|
+
if (fs.existsSync(dst)) return;
|
|
87
|
+
|
|
88
|
+
console.error(`[pajama] Downloading ${url}`);
|
|
89
|
+
await downloadToFile(url, dst);
|
|
90
|
+
await maybeVerifySha256(shaUrl, dst);
|
|
91
|
+
|
|
92
|
+
if (process.platform !== "win32") {
|
|
93
|
+
fs.chmodSync(dst, 0o755);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
main().catch((err) => {
|
|
98
|
+
console.error("[pajama] Install failed:", err && err.message ? err.message : String(err));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
});
|
|
101
|
+
|