arcana-ai 0.1.1 → 0.1.3
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 +12 -0
- package/bin/arcana.js +48 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,3 +20,15 @@ Zero dependencies — the binary bundles the Bun runtime. Just download and run.
|
|
|
20
20
|
```bash
|
|
21
21
|
npx arcana-ai@latest
|
|
22
22
|
```
|
|
23
|
+
|
|
24
|
+
## Thanks
|
|
25
|
+
|
|
26
|
+
Arcana builds on open-source giants:
|
|
27
|
+
|
|
28
|
+
- **[OpenCode](https://github.com/anomalyco/opencode)** — TUI engine, provider system, tools, CLI architecture
|
|
29
|
+
- **[Hermes Agent](https://github.com/Lento47/hermes-agent)** — autonomous AI agent framework
|
|
30
|
+
- **[Bun](https://bun.sh)** — runtime + compiler producing the standalone binary
|
|
31
|
+
- **[models.dev](https://models.dev)** — community model catalog (200+ models, 33 providers)
|
|
32
|
+
- **[Effect](https://effect.website)** — typed functional effect system
|
|
33
|
+
- **[AI SDK](https://sdk.vercel.ai)** — unified LLM provider interface
|
|
34
|
+
- 174 community-contributed skills
|
package/bin/arcana.js
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// arcana launcher — downloads the
|
|
3
|
-
//
|
|
2
|
+
// arcana launcher — downloads the binary from GitHub releases if needed, then runs it.
|
|
3
|
+
// Entrypoint for: npx arcana-ai
|
|
4
4
|
const { spawnSync, execSync } = require("child_process")
|
|
5
|
-
const { existsSync, mkdirSync, chmodSync, writeFileSync, unlinkSync } = require("fs")
|
|
5
|
+
const { existsSync, mkdirSync, chmodSync, writeFileSync, unlinkSync, renameSync } = require("fs")
|
|
6
6
|
const path = require("path")
|
|
7
7
|
const os = require("os")
|
|
8
8
|
|
|
9
9
|
const REPO = "anomalyco/opencode"
|
|
10
10
|
const VERSION = "v1.17.8" // TODO: fetch latest via GitHub API
|
|
11
11
|
|
|
12
|
+
// Asset name = what GitHub release calls it. Binary name = what we rename it to.
|
|
12
13
|
const PLATFORM_MAP = {
|
|
13
|
-
"win32-x64":
|
|
14
|
-
"win32-arm64":
|
|
15
|
-
"linux-x64":
|
|
16
|
-
"linux-arm64":
|
|
17
|
-
"darwin-x64":
|
|
18
|
-
"darwin-arm64":
|
|
14
|
+
"win32-x64": { asset: "opencode-windows-x64.zip", binary: "arcana.exe" },
|
|
15
|
+
"win32-arm64": { asset: "opencode-windows-arm64.zip", binary: "arcana.exe" },
|
|
16
|
+
"linux-x64": { asset: "opencode-linux-x64.tar.gz", binary: "arcana" },
|
|
17
|
+
"linux-arm64": { asset: "opencode-linux-arm64.tar.gz", binary: "arcana" },
|
|
18
|
+
"darwin-x64": { asset: "opencode-darwin-x64.zip", binary: "arcana" },
|
|
19
|
+
"darwin-arm64": { asset: "opencode-darwin-arm64.zip", binary: "arcana" },
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
const platform = `${os.platform()}-${os.arch()}`
|
|
@@ -23,7 +24,7 @@ const entry = PLATFORM_MAP[platform]
|
|
|
23
24
|
|
|
24
25
|
if (!entry) {
|
|
25
26
|
console.error(`arcana: unsupported platform ${platform}`)
|
|
26
|
-
console.error(`arcana: try installing from source: https://github.com
|
|
27
|
+
console.error(`arcana: try installing from source: https://github.com/Lento47/arcana`)
|
|
27
28
|
process.exit(1)
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -31,58 +32,72 @@ const CACHE_DIR = process.env.ARCANA_CACHE || path.join(os.homedir(), ".arcana",
|
|
|
31
32
|
const CACHED_BINARY = path.join(CACHE_DIR, entry.binary)
|
|
32
33
|
|
|
33
34
|
async function downloadAndExtract() {
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
// Clean up any stale temp file from previous failed attempts
|
|
36
|
+
try { unlinkSync(path.join(CACHE_DIR, entry.asset)) } catch {}
|
|
37
|
+
|
|
38
|
+
const url = `https://github.com/${REPO}/releases/download/${VERSION}/${entry.asset}`
|
|
39
|
+
console.error(`arcana: downloading ${entry.asset}...`)
|
|
36
40
|
|
|
37
41
|
mkdirSync(CACHE_DIR, { recursive: true })
|
|
38
42
|
|
|
39
|
-
// Download via Node.js fetch
|
|
40
43
|
const res = await fetch(url)
|
|
41
44
|
if (!res.ok) {
|
|
42
45
|
console.error(`arcana: download failed: ${res.status} ${res.statusText}`)
|
|
43
|
-
console.error(`arcana: URL: ${url}`)
|
|
44
46
|
process.exit(1)
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
const tmp = path.join(CACHE_DIR, entry.name)
|
|
49
|
+
const tmp = path.join(CACHE_DIR, entry.asset)
|
|
49
50
|
const buf = Buffer.from(await res.arrayBuffer())
|
|
50
51
|
writeFileSync(tmp, buf)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
console.error(`arcana: ${(buf.length / 1e6).toFixed(1)}MB downloaded, extracting...`)
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
if (entry.asset.endsWith(".tar.gz")) {
|
|
56
|
+
execSync(`tar xzf "${tmp}" -C "${CACHE_DIR}"`, { stdio: "pipe" })
|
|
57
|
+
unlinkSync(tmp)
|
|
58
|
+
} else if (entry.asset.endsWith(".zip")) {
|
|
59
|
+
if (os.platform() === "win32") {
|
|
60
|
+
// .NET ZipFile — built into .NET, no PowerShell module needed
|
|
61
|
+
const safeTmp = tmp.replace(/'/g, "''")
|
|
62
|
+
const safeDir = CACHE_DIR.replace(/'/g, "''")
|
|
63
|
+
execSync(
|
|
64
|
+
`powershell -Command "[System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null; [System.IO.Compression.ZipFile]::ExtractToDirectory('${safeTmp}', '${safeDir}')"`,
|
|
65
|
+
{ stdio: "pipe" },
|
|
66
|
+
)
|
|
67
|
+
} else {
|
|
68
|
+
execSync(`unzip -o "${tmp}" -d "${CACHE_DIR}"`, { stdio: "pipe" })
|
|
69
|
+
}
|
|
70
|
+
unlinkSync(tmp)
|
|
62
71
|
}
|
|
63
|
-
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.error(`arcana: extraction failed: ${e.message}`)
|
|
74
|
+
process.exit(1)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Rename extracted binary from opencode → arcana
|
|
78
|
+
const extracted = path.join(CACHE_DIR, os.platform() === "win32" ? "opencode.exe" : "opencode")
|
|
79
|
+
if (existsSync(extracted) && extracted !== CACHED_BINARY) {
|
|
80
|
+
try { unlinkSync(CACHED_BINARY) } catch {}
|
|
81
|
+
renameSync(extracted, CACHED_BINARY)
|
|
64
82
|
}
|
|
65
83
|
|
|
66
|
-
// Ensure executable
|
|
67
84
|
if (os.platform() !== "win32") {
|
|
68
85
|
try { chmodSync(CACHED_BINARY, 0o755) } catch {}
|
|
69
86
|
}
|
|
70
87
|
|
|
71
|
-
console.error(`arcana:
|
|
88
|
+
console.error(`arcana: ready — ${CACHED_BINARY}`)
|
|
72
89
|
}
|
|
73
90
|
|
|
74
91
|
async function main() {
|
|
75
|
-
// Download if needed
|
|
76
92
|
if (!existsSync(CACHED_BINARY)) {
|
|
77
93
|
await downloadAndExtract()
|
|
78
94
|
}
|
|
79
95
|
|
|
80
96
|
if (!existsSync(CACHED_BINARY)) {
|
|
81
|
-
console.error(`arcana: binary not found
|
|
97
|
+
console.error(`arcana: binary not found: ${CACHED_BINARY}`)
|
|
82
98
|
process.exit(1)
|
|
83
99
|
}
|
|
84
100
|
|
|
85
|
-
// Run binary with same args
|
|
86
101
|
const args = process.argv.slice(2)
|
|
87
102
|
const child = spawnSync(CACHED_BINARY, args, { stdio: "inherit" })
|
|
88
103
|
process.exit(child.status ?? 0)
|