arcana-ai 0.2.27 → 0.2.28
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/arcana.js +133 -133
- package/package.json +28 -28
package/bin/arcana.js
CHANGED
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// SPDX-License-Identifier: MIT OR LicenseRef-arcana-Commercial
|
|
3
|
-
// Copyright (c) 2026 arcana contributors
|
|
4
|
-
// arcana launcher — downloads the binary from R2 if needed, then runs it.
|
|
5
|
-
// Entrypoint for: npx arcana-ai
|
|
6
|
-
const { spawnSync, execSync } = require("child_process")
|
|
7
|
-
const { existsSync, mkdirSync, chmodSync, writeFileSync, unlinkSync } = require("fs")
|
|
8
|
-
const path = require("path")
|
|
9
|
-
const crypto = require("crypto")
|
|
10
|
-
const os = require("os")
|
|
11
|
-
|
|
12
|
-
const RELEASES_URL = "https://releases.otnelhq.com/arcana"
|
|
13
|
-
const VERSION = "v0.2.
|
|
14
|
-
|
|
15
|
-
const PLATFORM_MAP = {
|
|
16
|
-
"win32-x64": { asset: "arcana-windows-x64.zip", binary: "arcana.exe" },
|
|
17
|
-
"win32-arm64": { asset: "arcana-windows-arm64.zip", binary: "arcana.exe" },
|
|
18
|
-
"linux-x64": { asset: "arcana-linux-x64.tar.gz", binary: "arcana" },
|
|
19
|
-
"linux-arm64": { asset: "arcana-linux-arm64.tar.gz", binary: "arcana" },
|
|
20
|
-
"darwin-x64": { asset: "arcana-darwin-x64.zip", binary: "arcana" },
|
|
21
|
-
"darwin-arm64": { asset: "arcana-darwin-arm64.zip", binary: "arcana" },
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const platform = `${os.platform()}-${os.arch()}`
|
|
25
|
-
const entry = PLATFORM_MAP[platform]
|
|
26
|
-
|
|
27
|
-
if (!entry) {
|
|
28
|
-
console.error(`arcana: unsupported platform ${platform}`)
|
|
29
|
-
console.error(`arcana: supported platforms: ${Object.keys(PLATFORM_MAP).join(", ")}`)
|
|
30
|
-
process.exit(1)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const CACHE_DIR = process.env.ARCANA_CACHE || path.join(os.homedir(), ".arcana", "bin")
|
|
34
|
-
const CACHED_BINARY = path.join(CACHE_DIR, entry.binary)
|
|
35
|
-
const VERSION_FILE = path.join(CACHE_DIR, ".version")
|
|
36
|
-
|
|
37
|
-
async function downloadAndExtract() {
|
|
38
|
-
// Clean up any stale temp file from previous failed attempts
|
|
39
|
-
try { unlinkSync(path.join(CACHE_DIR, entry.asset)) } catch {}
|
|
40
|
-
|
|
41
|
-
const url = `${RELEASES_URL}/${VERSION}/${entry.asset}`
|
|
42
|
-
console.error(`arcana: ${VERSION} — downloading ${entry.asset}...`)
|
|
43
|
-
|
|
44
|
-
mkdirSync(CACHE_DIR, { recursive: true })
|
|
45
|
-
|
|
46
|
-
const res = await fetch(url)
|
|
47
|
-
if (!res.ok) {
|
|
48
|
-
console.error(`arcana: download failed: ${res.status} ${res.statusText}`)
|
|
49
|
-
process.exit(1)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const tmp = path.join(CACHE_DIR, entry.asset)
|
|
53
|
-
const buf = Buffer.from(await res.arrayBuffer())
|
|
54
|
-
writeFileSync(tmp, buf)
|
|
55
|
-
console.error(`arcana: ${(buf.length / 1e6).toFixed(1)}MB, verifying...`)
|
|
56
|
-
|
|
57
|
-
// Verify binary integrity — checksum is mandatory
|
|
58
|
-
const shaUrl = url + ".sha256"
|
|
59
|
-
const shaRes = await fetch(shaUrl)
|
|
60
|
-
if (!shaRes.ok) {
|
|
61
|
-
console.error(`arcana: FATAL — checksum unavailable (${shaUrl})`)
|
|
62
|
-
console.error(`arcana: the binary cannot be verified and will not be executed`)
|
|
63
|
-
try { unlinkSync(tmp) } catch {}
|
|
64
|
-
process.exit(1)
|
|
65
|
-
}
|
|
66
|
-
const shaText = (await shaRes.text()).trim()
|
|
67
|
-
const expectedHash = shaText.split(/\s+/)[0]
|
|
68
|
-
const actualHash = crypto.createHash("sha256").update(buf).digest("hex")
|
|
69
|
-
if (expectedHash !== actualHash) {
|
|
70
|
-
console.error(`arcana: CHECKSUM MISMATCH`)
|
|
71
|
-
console.error(` expected: ${expectedHash}`)
|
|
72
|
-
console.error(` actual: ${actualHash}`)
|
|
73
|
-
console.error(`arcana: binary may be corrupted or tampered — deleting`)
|
|
74
|
-
try { unlinkSync(tmp) } catch {}
|
|
75
|
-
process.exit(1)
|
|
76
|
-
}
|
|
77
|
-
console.error(`arcana: checksum OK`)
|
|
78
|
-
|
|
79
|
-
console.error(`arcana: extracting...`)
|
|
80
|
-
try {
|
|
81
|
-
if (entry.asset.endsWith(".tar.gz")) {
|
|
82
|
-
execSync(`tar xzf "${tmp}" -C "${CACHE_DIR}"`, { stdio: "pipe" })
|
|
83
|
-
unlinkSync(tmp)
|
|
84
|
-
} else if (entry.asset.endsWith(".zip")) {
|
|
85
|
-
if (os.platform() === "win32") {
|
|
86
|
-
const safeTmp = tmp.replace(/'/g, "''")
|
|
87
|
-
const safeDir = CACHE_DIR.replace(/'/g, "''")
|
|
88
|
-
execSync(
|
|
89
|
-
`powershell -Command "[System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null; [System.IO.Compression.ZipFile]::ExtractToDirectory('${safeTmp}', '${safeDir}')"`,
|
|
90
|
-
{ stdio: "pipe" },
|
|
91
|
-
)
|
|
92
|
-
} else {
|
|
93
|
-
execSync(`unzip -o "${tmp}" -d "${CACHE_DIR}"`, { stdio: "pipe" })
|
|
94
|
-
}
|
|
95
|
-
unlinkSync(tmp)
|
|
96
|
-
}
|
|
97
|
-
} catch (e) {
|
|
98
|
-
console.error(`arcana: extraction failed: ${e.message}`)
|
|
99
|
-
process.exit(1)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (os.platform() !== "win32") {
|
|
103
|
-
try { chmodSync(CACHED_BINARY, 0o755) } catch {}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Write version file so we can detect staleness on next run
|
|
107
|
-
try { writeFileSync(VERSION_FILE, VERSION, "utf8") } catch {}
|
|
108
|
-
console.error(`arcana: ready`)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function main() {
|
|
112
|
-
// Check if cached binary is stale (wrong version)
|
|
113
|
-
let cachedVersion = ""
|
|
114
|
-
try { cachedVersion = require("fs").readFileSync(VERSION_FILE, "utf8").trim() } catch {}
|
|
115
|
-
|
|
116
|
-
if (!existsSync(CACHED_BINARY) || cachedVersion !== VERSION) {
|
|
117
|
-
await downloadAndExtract()
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (!existsSync(CACHED_BINARY)) {
|
|
121
|
-
console.error(`arcana: binary not found after download`)
|
|
122
|
-
process.exit(1)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const args = process.argv.slice(2)
|
|
126
|
-
const child = spawnSync(CACHED_BINARY, args, { stdio: "inherit" })
|
|
127
|
-
process.exit(child.status ?? 0)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
main().then((code) => process.exit(code), (err) => {
|
|
131
|
-
console.error(`arcana: ${err.message}`)
|
|
132
|
-
process.exit(1)
|
|
133
|
-
})
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SPDX-License-Identifier: MIT OR LicenseRef-arcana-Commercial
|
|
3
|
+
// Copyright (c) 2026 arcana contributors
|
|
4
|
+
// arcana launcher — downloads the binary from R2 if needed, then runs it.
|
|
5
|
+
// Entrypoint for: npx arcana-ai
|
|
6
|
+
const { spawnSync, execSync } = require("child_process")
|
|
7
|
+
const { existsSync, mkdirSync, chmodSync, writeFileSync, unlinkSync } = require("fs")
|
|
8
|
+
const path = require("path")
|
|
9
|
+
const crypto = require("crypto")
|
|
10
|
+
const os = require("os")
|
|
11
|
+
|
|
12
|
+
const RELEASES_URL = "https://releases.otnelhq.com/arcana"
|
|
13
|
+
const VERSION = "v0.2.28"
|
|
14
|
+
|
|
15
|
+
const PLATFORM_MAP = {
|
|
16
|
+
"win32-x64": { asset: "arcana-windows-x64.zip", binary: "arcana.exe" },
|
|
17
|
+
"win32-arm64": { asset: "arcana-windows-arm64.zip", binary: "arcana.exe" },
|
|
18
|
+
"linux-x64": { asset: "arcana-linux-x64.tar.gz", binary: "arcana" },
|
|
19
|
+
"linux-arm64": { asset: "arcana-linux-arm64.tar.gz", binary: "arcana" },
|
|
20
|
+
"darwin-x64": { asset: "arcana-darwin-x64.zip", binary: "arcana" },
|
|
21
|
+
"darwin-arm64": { asset: "arcana-darwin-arm64.zip", binary: "arcana" },
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const platform = `${os.platform()}-${os.arch()}`
|
|
25
|
+
const entry = PLATFORM_MAP[platform]
|
|
26
|
+
|
|
27
|
+
if (!entry) {
|
|
28
|
+
console.error(`arcana: unsupported platform ${platform}`)
|
|
29
|
+
console.error(`arcana: supported platforms: ${Object.keys(PLATFORM_MAP).join(", ")}`)
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const CACHE_DIR = process.env.ARCANA_CACHE || path.join(os.homedir(), ".arcana", "bin")
|
|
34
|
+
const CACHED_BINARY = path.join(CACHE_DIR, entry.binary)
|
|
35
|
+
const VERSION_FILE = path.join(CACHE_DIR, ".version")
|
|
36
|
+
|
|
37
|
+
async function downloadAndExtract() {
|
|
38
|
+
// Clean up any stale temp file from previous failed attempts
|
|
39
|
+
try { unlinkSync(path.join(CACHE_DIR, entry.asset)) } catch {}
|
|
40
|
+
|
|
41
|
+
const url = `${RELEASES_URL}/${VERSION}/${entry.asset}`
|
|
42
|
+
console.error(`arcana: ${VERSION} — downloading ${entry.asset}...`)
|
|
43
|
+
|
|
44
|
+
mkdirSync(CACHE_DIR, { recursive: true })
|
|
45
|
+
|
|
46
|
+
const res = await fetch(url)
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
console.error(`arcana: download failed: ${res.status} ${res.statusText}`)
|
|
49
|
+
process.exit(1)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const tmp = path.join(CACHE_DIR, entry.asset)
|
|
53
|
+
const buf = Buffer.from(await res.arrayBuffer())
|
|
54
|
+
writeFileSync(tmp, buf)
|
|
55
|
+
console.error(`arcana: ${(buf.length / 1e6).toFixed(1)}MB, verifying...`)
|
|
56
|
+
|
|
57
|
+
// Verify binary integrity — checksum is mandatory
|
|
58
|
+
const shaUrl = url + ".sha256"
|
|
59
|
+
const shaRes = await fetch(shaUrl)
|
|
60
|
+
if (!shaRes.ok) {
|
|
61
|
+
console.error(`arcana: FATAL — checksum unavailable (${shaUrl})`)
|
|
62
|
+
console.error(`arcana: the binary cannot be verified and will not be executed`)
|
|
63
|
+
try { unlinkSync(tmp) } catch {}
|
|
64
|
+
process.exit(1)
|
|
65
|
+
}
|
|
66
|
+
const shaText = (await shaRes.text()).trim()
|
|
67
|
+
const expectedHash = shaText.split(/\s+/)[0]
|
|
68
|
+
const actualHash = crypto.createHash("sha256").update(buf).digest("hex")
|
|
69
|
+
if (expectedHash !== actualHash) {
|
|
70
|
+
console.error(`arcana: CHECKSUM MISMATCH`)
|
|
71
|
+
console.error(` expected: ${expectedHash}`)
|
|
72
|
+
console.error(` actual: ${actualHash}`)
|
|
73
|
+
console.error(`arcana: binary may be corrupted or tampered — deleting`)
|
|
74
|
+
try { unlinkSync(tmp) } catch {}
|
|
75
|
+
process.exit(1)
|
|
76
|
+
}
|
|
77
|
+
console.error(`arcana: checksum OK`)
|
|
78
|
+
|
|
79
|
+
console.error(`arcana: extracting...`)
|
|
80
|
+
try {
|
|
81
|
+
if (entry.asset.endsWith(".tar.gz")) {
|
|
82
|
+
execSync(`tar xzf "${tmp}" -C "${CACHE_DIR}"`, { stdio: "pipe" })
|
|
83
|
+
unlinkSync(tmp)
|
|
84
|
+
} else if (entry.asset.endsWith(".zip")) {
|
|
85
|
+
if (os.platform() === "win32") {
|
|
86
|
+
const safeTmp = tmp.replace(/'/g, "''")
|
|
87
|
+
const safeDir = CACHE_DIR.replace(/'/g, "''")
|
|
88
|
+
execSync(
|
|
89
|
+
`powershell -Command "[System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null; [System.IO.Compression.ZipFile]::ExtractToDirectory('${safeTmp}', '${safeDir}')"`,
|
|
90
|
+
{ stdio: "pipe" },
|
|
91
|
+
)
|
|
92
|
+
} else {
|
|
93
|
+
execSync(`unzip -o "${tmp}" -d "${CACHE_DIR}"`, { stdio: "pipe" })
|
|
94
|
+
}
|
|
95
|
+
unlinkSync(tmp)
|
|
96
|
+
}
|
|
97
|
+
} catch (e) {
|
|
98
|
+
console.error(`arcana: extraction failed: ${e.message}`)
|
|
99
|
+
process.exit(1)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (os.platform() !== "win32") {
|
|
103
|
+
try { chmodSync(CACHED_BINARY, 0o755) } catch {}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Write version file so we can detect staleness on next run
|
|
107
|
+
try { writeFileSync(VERSION_FILE, VERSION, "utf8") } catch {}
|
|
108
|
+
console.error(`arcana: ready`)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function main() {
|
|
112
|
+
// Check if cached binary is stale (wrong version)
|
|
113
|
+
let cachedVersion = ""
|
|
114
|
+
try { cachedVersion = require("fs").readFileSync(VERSION_FILE, "utf8").trim() } catch {}
|
|
115
|
+
|
|
116
|
+
if (!existsSync(CACHED_BINARY) || cachedVersion !== VERSION) {
|
|
117
|
+
await downloadAndExtract()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!existsSync(CACHED_BINARY)) {
|
|
121
|
+
console.error(`arcana: binary not found after download`)
|
|
122
|
+
process.exit(1)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const args = process.argv.slice(2)
|
|
126
|
+
const child = spawnSync(CACHED_BINARY, args, { stdio: "inherit" })
|
|
127
|
+
process.exit(child.status ?? 0)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
main().then((code) => process.exit(code), (err) => {
|
|
131
|
+
console.error(`arcana: ${err.message}`)
|
|
132
|
+
process.exit(1)
|
|
133
|
+
})
|
package/package.json
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "arcana-ai",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Self-improving AI agent CLI — opencode TUI + skills. Installs the arcana binary.",
|
|
5
|
-
"bin": {
|
|
6
|
-
"arcana": "./bin/arcana.js"
|
|
7
|
-
},
|
|
8
|
-
"files": [
|
|
9
|
-
"bin/arcana.js",
|
|
10
|
-
"README.md"
|
|
11
|
-
],
|
|
12
|
-
"repository": "github:Lento47/arcana",
|
|
13
|
-
"homepage": "https://github.com/Lento47/arcana",
|
|
14
|
-
"keywords": [
|
|
15
|
-
"ai",
|
|
16
|
-
"agent",
|
|
17
|
-
"cli",
|
|
18
|
-
"tui",
|
|
19
|
-
"opencode",
|
|
20
|
-
"arcana",
|
|
21
|
-
"llm",
|
|
22
|
-
"coding-assistant"
|
|
23
|
-
],
|
|
24
|
-
"license": "MIT",
|
|
25
|
-
"engines": {
|
|
26
|
-
"node": ">=18"
|
|
27
|
-
}
|
|
28
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "arcana-ai",
|
|
3
|
+
"version": "0.2.28",
|
|
4
|
+
"description": "Self-improving AI agent CLI — opencode TUI + skills. Installs the arcana binary.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"arcana": "./bin/arcana.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/arcana.js",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"repository": "github:Lento47/arcana",
|
|
13
|
+
"homepage": "https://github.com/Lento47/arcana",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"ai",
|
|
16
|
+
"agent",
|
|
17
|
+
"cli",
|
|
18
|
+
"tui",
|
|
19
|
+
"opencode",
|
|
20
|
+
"arcana",
|
|
21
|
+
"llm",
|
|
22
|
+
"coding-assistant"
|
|
23
|
+
],
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
}
|
|
28
|
+
}
|