arcana-ai 0.1.2 → 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 +33 -31
- 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,38 +32,36 @@ 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
|
-
try { unlinkSync(path.join(CACHE_DIR, entry.name)) } catch {}
|
|
49
|
-
|
|
50
|
-
// Write to temp file
|
|
51
|
-
const tmp = path.join(CACHE_DIR, entry.name)
|
|
49
|
+
const tmp = path.join(CACHE_DIR, entry.asset)
|
|
52
50
|
const buf = Buffer.from(await res.arrayBuffer())
|
|
53
51
|
writeFileSync(tmp, buf)
|
|
54
|
-
console.error(`arcana:
|
|
52
|
+
console.error(`arcana: ${(buf.length / 1e6).toFixed(1)}MB downloaded, extracting...`)
|
|
55
53
|
|
|
56
|
-
// Extract
|
|
57
54
|
try {
|
|
58
|
-
if (entry.
|
|
55
|
+
if (entry.asset.endsWith(".tar.gz")) {
|
|
59
56
|
execSync(`tar xzf "${tmp}" -C "${CACHE_DIR}"`, { stdio: "pipe" })
|
|
60
57
|
unlinkSync(tmp)
|
|
61
|
-
} else if (entry.
|
|
58
|
+
} else if (entry.asset.endsWith(".zip")) {
|
|
62
59
|
if (os.platform() === "win32") {
|
|
63
|
-
//
|
|
60
|
+
// .NET ZipFile — built into .NET, no PowerShell module needed
|
|
61
|
+
const safeTmp = tmp.replace(/'/g, "''")
|
|
62
|
+
const safeDir = CACHE_DIR.replace(/'/g, "''")
|
|
64
63
|
execSync(
|
|
65
|
-
`powershell -Command "[System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null; [System.IO.Compression.ZipFile]::ExtractToDirectory('${
|
|
64
|
+
`powershell -Command "[System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null; [System.IO.Compression.ZipFile]::ExtractToDirectory('${safeTmp}', '${safeDir}')"`,
|
|
66
65
|
{ stdio: "pipe" },
|
|
67
66
|
)
|
|
68
67
|
} else {
|
|
@@ -72,30 +71,33 @@ async function downloadAndExtract() {
|
|
|
72
71
|
}
|
|
73
72
|
} catch (e) {
|
|
74
73
|
console.error(`arcana: extraction failed: ${e.message}`)
|
|
75
|
-
// Leave tmp file for manual debugging
|
|
76
74
|
process.exit(1)
|
|
77
75
|
}
|
|
78
76
|
|
|
79
|
-
//
|
|
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)
|
|
82
|
+
}
|
|
83
|
+
|
|
80
84
|
if (os.platform() !== "win32") {
|
|
81
85
|
try { chmodSync(CACHED_BINARY, 0o755) } catch {}
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
console.error(`arcana:
|
|
88
|
+
console.error(`arcana: ready — ${CACHED_BINARY}`)
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
async function main() {
|
|
88
|
-
// Download if needed
|
|
89
92
|
if (!existsSync(CACHED_BINARY)) {
|
|
90
93
|
await downloadAndExtract()
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
if (!existsSync(CACHED_BINARY)) {
|
|
94
|
-
console.error(`arcana: binary not found
|
|
97
|
+
console.error(`arcana: binary not found: ${CACHED_BINARY}`)
|
|
95
98
|
process.exit(1)
|
|
96
99
|
}
|
|
97
100
|
|
|
98
|
-
// Run binary with same args
|
|
99
101
|
const args = process.argv.slice(2)
|
|
100
102
|
const child = spawnSync(CACHED_BINARY, args, { stdio: "inherit" })
|
|
101
103
|
process.exit(child.status ?? 0)
|