@maximem/synap-cli 0.1.0
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 +28 -0
- package/bin/synap-cli.js +159 -0
- package/package.json +32 -0
- package/vendor/maximem_synap-0.1.0-py3-none-any.whl +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# @maximem/synap-cli
|
|
2
|
+
|
|
3
|
+
One-line installer for the **maximem-synap** developer CLI (the Synap control
|
|
4
|
+
plane). This npm package is a small bootstrap: it installs the actual CLI — a
|
|
5
|
+
Python/Typer program, bundled here as a wheel — onto your PATH and then runs it.
|
|
6
|
+
|
|
7
|
+
> This is **not** the runtime SDK. Agents/apps integrate Synap with
|
|
8
|
+
> [`@maximem/synap-js-sdk`](https://www.npmjs.com/package/@maximem/synap-js-sdk)
|
|
9
|
+
> or the Python SDK. This CLI is for **developers** to provision and operate
|
|
10
|
+
> Synap from a terminal.
|
|
11
|
+
|
|
12
|
+
## Use
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx @maximem/synap-cli # install the CLI onto your PATH
|
|
16
|
+
npx @maximem/synap-cli auth login # bootstrap if needed, then log in
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
After the first run, the `maximem-synap` command is on your PATH:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
maximem-synap auth login
|
|
23
|
+
maximem-synap whoami
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Requires **Node 16+** and **Python 3.10+** on the host. The CLI is installed via
|
|
27
|
+
`pipx` if present, otherwise into an isolated virtualenv at `~/.maximem-synap`
|
|
28
|
+
and symlinked into `~/.local/bin`.
|
package/bin/synap-cli.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* npx bootstrap for the `maximem-synap` developer CLI.
|
|
6
|
+
*
|
|
7
|
+
* The CLI itself is a Python (Typer) program. This Node shim exists only to give
|
|
8
|
+
* JS-first developers a one-liner: `npx maximem-synap`. It:
|
|
9
|
+
* 1. ensures the Python CLI is installed onto your PATH (pipx, or an isolated
|
|
10
|
+
* venv at ~/.maximem-synap symlinked into ~/.local/bin), then
|
|
11
|
+
* 2. forwards your command to it (`npx maximem-synap auth login`).
|
|
12
|
+
*
|
|
13
|
+
* It is NOT the runtime SDK. Agents/apps use @maximem/synap-js-sdk to call Synap.
|
|
14
|
+
*
|
|
15
|
+
* Env:
|
|
16
|
+
* MAXIMEM_SYNAP_SOURCE pip spec to install (default "maximem-synap").
|
|
17
|
+
* Set to a local path/checkout for development.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const { spawnSync } = require("child_process");
|
|
21
|
+
const fs = require("fs");
|
|
22
|
+
const os = require("os");
|
|
23
|
+
const path = require("path");
|
|
24
|
+
|
|
25
|
+
const HOME = os.homedir();
|
|
26
|
+
const LOCAL_BIN = path.join(HOME, ".local", "bin");
|
|
27
|
+
const INSTALL_HOME = path.join(HOME, ".maximem-synap");
|
|
28
|
+
const VENV = path.join(INSTALL_HOME, "venv");
|
|
29
|
+
const VENV_BIN = process.platform === "win32" ? "Scripts" : "bin";
|
|
30
|
+
const EXE = process.platform === "win32" ? ".exe" : "";
|
|
31
|
+
|
|
32
|
+
// What pip should install. Priority:
|
|
33
|
+
// 1. MAXIMEM_SYNAP_SOURCE env (dev: a path/checkout)
|
|
34
|
+
// 2. the wheel bundled in this npm package (vendor/*.whl) — self-contained,
|
|
35
|
+
// so `npx maximem-synap` works without the CLI being on PyPI yet
|
|
36
|
+
// 3. "maximem-synap" from PyPI (once published)
|
|
37
|
+
function resolvePkgSpec() {
|
|
38
|
+
if (process.env.MAXIMEM_SYNAP_SOURCE) return process.env.MAXIMEM_SYNAP_SOURCE;
|
|
39
|
+
const vendor = path.join(__dirname, "..", "vendor");
|
|
40
|
+
try {
|
|
41
|
+
const wheels = fs.readdirSync(vendor).filter((f) => f.endsWith(".whl"));
|
|
42
|
+
if (wheels.length) return path.join(vendor, wheels.sort().reverse()[0]);
|
|
43
|
+
} catch (_) { /* no bundled wheel */ }
|
|
44
|
+
return "maximem-synap";
|
|
45
|
+
}
|
|
46
|
+
const PKG_SPEC = resolvePkgSpec();
|
|
47
|
+
|
|
48
|
+
function log(msg) { process.stderr.write(msg + "\n"); }
|
|
49
|
+
|
|
50
|
+
function shHasCommand(name) {
|
|
51
|
+
if (process.platform === "win32") {
|
|
52
|
+
return spawnSync("where", [name], { stdio: "ignore" }).status === 0;
|
|
53
|
+
}
|
|
54
|
+
return spawnSync("sh", ["-c", `command -v ${name}`], { stdio: "ignore" }).status === 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Resolve the *installed Python CLI* by concrete location, NOT by spawning bare
|
|
58
|
+
// `maximem-synap` (which under npx would recurse into this very shim).
|
|
59
|
+
function findInstalledCli() {
|
|
60
|
+
const candidates = [
|
|
61
|
+
path.join(LOCAL_BIN, "maximem-synap" + EXE),
|
|
62
|
+
path.join(VENV, VENV_BIN, "maximem-synap" + EXE),
|
|
63
|
+
];
|
|
64
|
+
for (const c of candidates) {
|
|
65
|
+
if (!fs.existsSync(c)) continue;
|
|
66
|
+
const r = spawnSync(c, ["--version"], { encoding: "utf8" });
|
|
67
|
+
if (r.status === 0 && /maximem-synap/i.test(r.stdout || "")) return c;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function findPython() {
|
|
73
|
+
const cands = ["python3.13", "python3.12", "python3.11", "python3.10", "python3", "python"];
|
|
74
|
+
for (const py of cands) {
|
|
75
|
+
const r = spawnSync(py, ["-c", "import sys;print('%d.%d' % sys.version_info[:2])"], { encoding: "utf8" });
|
|
76
|
+
if (r.status !== 0) continue;
|
|
77
|
+
const [maj, min] = (r.stdout || "").trim().split(".").map(Number);
|
|
78
|
+
if (maj === 3 && min >= 10) return py;
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function ensureLocalBinOnPath() {
|
|
84
|
+
const parts = (process.env.PATH || "").split(path.delimiter);
|
|
85
|
+
if (!parts.includes(LOCAL_BIN)) {
|
|
86
|
+
log("");
|
|
87
|
+
log(`Note: ${LOCAL_BIN} is not on your PATH. Add this to your shell profile:`);
|
|
88
|
+
log(` export PATH="$HOME/.local/bin:$PATH"`);
|
|
89
|
+
log("");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function symlink(target, linkPath) {
|
|
94
|
+
try { fs.mkdirSync(path.dirname(linkPath), { recursive: true }); } catch (_) {}
|
|
95
|
+
try { fs.unlinkSync(linkPath); } catch (_) {}
|
|
96
|
+
fs.symlinkSync(target, linkPath);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function install() {
|
|
100
|
+
// Preferred: pipx (isolated, manages ~/.local/bin + PATH itself).
|
|
101
|
+
if (shHasCommand("pipx")) {
|
|
102
|
+
log(`Installing maximem-synap via pipx (${PKG_SPEC})…`);
|
|
103
|
+
const r = spawnSync("pipx", ["install", "--force", PKG_SPEC], { stdio: "inherit" });
|
|
104
|
+
if (r.status === 0) { ensureLocalBinOnPath(); return findInstalledCli(); }
|
|
105
|
+
log("pipx install failed; falling back to a managed virtualenv.");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Fallback: a dedicated venv at ~/.maximem-synap, symlinked into ~/.local/bin.
|
|
109
|
+
const py = findPython();
|
|
110
|
+
if (!py) {
|
|
111
|
+
log("");
|
|
112
|
+
log("✗ maximem-synap needs Python 3.10+ on your machine (the CLI is Python).");
|
|
113
|
+
log(" Install Python 3.10+ and re-run, or use Homebrew: brew install maximem-ai/tap/maximem-synap");
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
log(`Installing maximem-synap into ${VENV} using ${py}…`);
|
|
117
|
+
fs.mkdirSync(INSTALL_HOME, { recursive: true });
|
|
118
|
+
if (!fs.existsSync(VENV)) {
|
|
119
|
+
const v = spawnSync(py, ["-m", "venv", VENV], { stdio: "inherit" });
|
|
120
|
+
if (v.status !== 0) { log("✗ Failed to create virtualenv."); process.exit(1); }
|
|
121
|
+
}
|
|
122
|
+
const venvPy = path.join(VENV, VENV_BIN, "python" + EXE);
|
|
123
|
+
spawnSync(venvPy, ["-m", "pip", "install", "--quiet", "--upgrade", "pip"], { stdio: "inherit" });
|
|
124
|
+
const pip = spawnSync(venvPy, ["-m", "pip", "install", "--upgrade", PKG_SPEC], { stdio: "inherit" });
|
|
125
|
+
if (pip.status !== 0) { log("✗ pip install failed."); process.exit(1); }
|
|
126
|
+
|
|
127
|
+
fs.mkdirSync(LOCAL_BIN, { recursive: true });
|
|
128
|
+
for (const name of ["maximem-synap", "synap"]) {
|
|
129
|
+
const src = path.join(VENV, VENV_BIN, name + EXE);
|
|
130
|
+
if (fs.existsSync(src)) symlink(src, path.join(LOCAL_BIN, name + EXE));
|
|
131
|
+
}
|
|
132
|
+
ensureLocalBinOnPath();
|
|
133
|
+
return findInstalledCli();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function main() {
|
|
137
|
+
let args = process.argv.slice(2);
|
|
138
|
+
|
|
139
|
+
// `npx maximem-synap install` (or `setup`) = bootstrap only, don't forward.
|
|
140
|
+
const bootstrapOnly = args[0] === "install" || args[0] === "setup";
|
|
141
|
+
if (bootstrapOnly) args = args.slice(1);
|
|
142
|
+
|
|
143
|
+
let cli = findInstalledCli();
|
|
144
|
+
if (!cli) cli = install();
|
|
145
|
+
if (!cli) { log("✗ Could not locate maximem-synap after install."); process.exit(1); }
|
|
146
|
+
|
|
147
|
+
if (bootstrapOnly || args.length === 0) {
|
|
148
|
+
log("");
|
|
149
|
+
log("✓ maximem-synap is installed.");
|
|
150
|
+
log(" Next: maximem-synap auth login");
|
|
151
|
+
log("");
|
|
152
|
+
if (args.length === 0) return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const r = spawnSync(cli, args, { stdio: "inherit" });
|
|
156
|
+
process.exit(r.status === null ? 1 : r.status);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@maximem/synap-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Installer for the maximem-synap developer CLI. Bootstraps the (Python) Synap control-plane CLI onto your PATH, then runs it. Not the runtime SDK — see @maximem/synap-js-sdk for agent integration.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"synap-cli": "bin/synap-cli.js"
|
|
7
|
+
},
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/",
|
|
13
|
+
"vendor/",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=16"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"synap",
|
|
21
|
+
"maximem",
|
|
22
|
+
"cli",
|
|
23
|
+
"memory",
|
|
24
|
+
"agents"
|
|
25
|
+
],
|
|
26
|
+
"license": "UNLICENSED",
|
|
27
|
+
"homepage": "https://maximem.ai",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/maximem-ai/maximem_synap.git"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
Binary file
|