epismo 1.4.0 → 1.5.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 +35 -46
- package/npm/bin/epismo.js +10 -0
- package/npm/launcher.js +104 -0
- package/npm/vendor/darwin-amd64/epismo +0 -0
- package/npm/vendor/darwin-arm64/epismo +0 -0
- package/npm/vendor/linux-amd64/epismo +0 -0
- package/npm/vendor/linux-arm64/epismo +0 -0
- package/npm/vendor/windows-amd64/epismo.exe +0 -0
- package/npm/vendor/windows-arm64/epismo.exe +0 -0
- package/package.json +15 -6
- package/npm/bin/epismo.exe +0 -1
- package/npm/install.js +0 -82
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Epismo CLI
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
as a native executable.
|
|
3
|
+
Native command-line interface for [Epismo](https://epismo.ai). It is written in Go and returns JSON, making it useful in terminals, scripts, and agent workflows.
|
|
5
4
|
|
|
6
5
|
## Install
|
|
7
6
|
|
|
@@ -17,35 +16,41 @@ Windows PowerShell:
|
|
|
17
16
|
irm https://epismo.ai/install.ps1 | iex
|
|
18
17
|
```
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
Or install with Go or npm:
|
|
21
20
|
|
|
22
21
|
```sh
|
|
23
22
|
go install github.com/epismoai/cli/cmd/epismo@latest
|
|
23
|
+
npm install -g epismo
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Prebuilt archives, standalone executables, and `checksums.txt` are available from [GitHub Releases](https://github.com/epismoai/cli/releases).
|
|
27
|
+
|
|
28
|
+
## Updating
|
|
29
|
+
|
|
30
|
+
The CLI checks GitHub Releases for a newer version at most once every 24 hours in interactive terminals. When an update is available, run:
|
|
27
31
|
|
|
28
32
|
```sh
|
|
29
|
-
|
|
33
|
+
epismo update
|
|
30
34
|
```
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
[GitHub Releases](https://github.com/epismoai/cli/releases).
|
|
36
|
+
The command identifies how the active executable was installed and returns the appropriate installation command; it never modifies the executable itself. Default shell installations receive the short command shown above. A custom installation directory is included only when needed. Go and global Node installations receive a manager-specific command when the package manager can be identified. Yarn's global command is returned only for Yarn Classic because modern Yarn does not support that workflow. Set `EPISMO_UPDATE_CHECK=0` to disable automatic checks.
|
|
34
37
|
|
|
35
|
-
|
|
38
|
+
The npm package contains a dependency-free JavaScript launcher and all supported native binaries. It uses no lifecycle installation scripts and performs no additional downloads during installation. The launcher selects the binary for the current platform and passes its package-manager context to the native CLI. Shell and PowerShell installers write an installation receipt next to the executable; Go installations are identified from embedded build metadata. If the method cannot be determined safely, `epismo update` links to the update instructions without modifying the executable.
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
36
41
|
|
|
37
42
|
```sh
|
|
38
43
|
epismo login
|
|
39
|
-
epismo whoami
|
|
40
44
|
epismo workspace list
|
|
45
|
+
epismo workspace use WORKSPACE_ID # optional: save a default workspace
|
|
41
46
|
epismo playbook search --query onboarding
|
|
42
|
-
epismo case list --status open
|
|
43
|
-
epismo task list
|
|
44
|
-
epismo suggestion list
|
|
45
47
|
```
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
Run `epismo --help` for command groups, or append `--help` to any group or command for its options.
|
|
50
|
+
|
|
51
|
+
## Output and input
|
|
52
|
+
|
|
53
|
+
Successful commands write JSON to stdout; warnings and errors write JSON to stderr. This keeps output easy to use from scripts and agents.
|
|
49
54
|
|
|
50
55
|
```json
|
|
51
56
|
{
|
|
@@ -57,54 +62,41 @@ stderr, making the CLI suitable for both people and agents.
|
|
|
57
62
|
}
|
|
58
63
|
```
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
`epismo <group> <command> --help` for the complete command reference.
|
|
62
|
-
|
|
63
|
-
### Structured input
|
|
64
|
-
|
|
65
|
-
Commands that accept request payloads support inline JSON, files, and stdin:
|
|
65
|
+
Commands that accept a request body support inline JSON, a file, or stdin:
|
|
66
66
|
|
|
67
67
|
```sh
|
|
68
68
|
epismo playbook create --input @playbook.json
|
|
69
69
|
epismo case record append CASE_ID --input - < record.json
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
Explicit
|
|
73
|
-
an idempotency key unless `--idempotency-key` or an `idempotencyKey` in input is
|
|
74
|
-
provided.
|
|
72
|
+
Explicit flags override fields supplied through `--input`. Mutations create an idempotency key automatically unless you provide `--idempotency-key` or `idempotencyKey` in the input.
|
|
75
73
|
|
|
76
|
-
|
|
74
|
+
## Authentication and configuration
|
|
77
75
|
|
|
78
|
-
|
|
79
|
-
automatically selects organization SSO or a terminal email-code flow.
|
|
76
|
+
`epismo login` opens a browser-based OAuth login. With `--email`, it automatically uses your organization SSO when available, otherwise it prompts for an email code.
|
|
80
77
|
|
|
81
|
-
For CI,
|
|
78
|
+
For CI or other non-interactive use, create a workspace-scoped token and pass it with `EPISMO_TOKEN`:
|
|
82
79
|
|
|
83
80
|
```sh
|
|
84
81
|
epismo token create --workspace-id WORKSPACE_ID
|
|
85
82
|
EPISMO_TOKEN=... epismo playbook search
|
|
86
83
|
```
|
|
87
84
|
|
|
88
|
-
Configuration
|
|
89
|
-
`~/.epismo`. Set `EPISMO_CONFIG_DIR` to use another location. For local API
|
|
90
|
-
development, set `APP_ENV=dev`, or override endpoints directly with
|
|
91
|
-
`EPISMO_API_URL` and `EPISMO_WEB_URL`.
|
|
85
|
+
Configuration is stored in `~/.epismo` (compatible with the original npm CLI). Set `EPISMO_CONFIG_DIR` to use a different directory. For local API development, set `APP_ENV=dev`, or override `EPISMO_API_URL` and `EPISMO_WEB_URL`.
|
|
92
86
|
|
|
93
87
|
## API contract
|
|
94
88
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
compatible with the API. Releases run the same compatibility checks against the
|
|
103
|
-
deployed contract before publishing.
|
|
89
|
+
[`contracts/openapi.json`](contracts/openapi.json) is a generated snapshot of Epismo's public OpenAPI 3.1 contract. Refresh it after an API deployment:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
sh scripts/sync-openapi.sh
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Do not edit the snapshot manually. Its `info.version` represents API compatibility (for example, `1.0.0` for `/v1`), not the CLI release version. Contract tests ensure remote commands and query options remain compatible with the API.
|
|
104
96
|
|
|
105
97
|
## Develop
|
|
106
98
|
|
|
107
|
-
The runtime uses only the Go standard library.
|
|
99
|
+
The CLI runtime uses only the Go standard library.
|
|
108
100
|
|
|
109
101
|
```sh
|
|
110
102
|
go test -race ./...
|
|
@@ -112,11 +104,8 @@ go vet ./...
|
|
|
112
104
|
go build ./cmd/epismo
|
|
113
105
|
```
|
|
114
106
|
|
|
115
|
-
Releases are built for macOS, Linux, and Windows from semantic-version tags such
|
|
116
|
-
as `v1.3.1`. The tag is the sole source of the release version: the workflow
|
|
117
|
-
injects it into Go binaries and sets the npm package version immediately before
|
|
118
|
-
publishing. The development `package.json` version is intentionally not bumped.
|
|
107
|
+
Releases are built for macOS, Linux, and Windows from semantic-version tags such as `v1.3.1`. The tag is the release version source; the development `package.json` version is intentionally not updated.
|
|
119
108
|
|
|
120
109
|
## License
|
|
121
110
|
|
|
122
|
-
Apache License 2.0.
|
|
111
|
+
[Apache License 2.0](LICENSE).
|
package/npm/launcher.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
3
|
+
import { dirname, join, parse, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
7
|
+
|
|
8
|
+
export function targetFor(platform = process.platform, architecture = process.arch) {
|
|
9
|
+
const os = { darwin: "darwin", linux: "linux", win32: "windows" }[platform];
|
|
10
|
+
const arch = { x64: "amd64", arm64: "arm64" }[architecture];
|
|
11
|
+
return os && arch ? `${os}-${arch}` : null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function binaryFor(platform = process.platform, architecture = process.arch, root = packageRoot) {
|
|
15
|
+
const target = targetFor(platform, architecture);
|
|
16
|
+
if (!target) return null;
|
|
17
|
+
return join(root, "npm", "vendor", target, platform === "win32" ? "epismo.exe" : "epismo");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function detectPackageManager({
|
|
21
|
+
root = packageRoot,
|
|
22
|
+
entrypoint = process.argv[1] || "",
|
|
23
|
+
userAgent = process.env.npm_config_user_agent || "",
|
|
24
|
+
execPath = process.env.npm_execpath || ""
|
|
25
|
+
} = {}) {
|
|
26
|
+
if (pnpmOwnsPackage(root, entrypoint)) return { name: "pnpm", version: versionFromUserAgent(userAgent, "pnpm") };
|
|
27
|
+
const normalizedRoot = root.toLowerCase().replaceAll("\\", "/");
|
|
28
|
+
if (/\bbun\//i.test(userAgent) || /(?:^|[\\/])bun(?:\.exe)?$/i.test(execPath) || normalizedRoot.includes("/.bun/install/global/")) {
|
|
29
|
+
return { name: "bun", version: versionFromUserAgent(userAgent, "bun") };
|
|
30
|
+
}
|
|
31
|
+
if (/\byarn\//i.test(userAgent) || /(?:^|[\\/])yarn(?:\.c?js|\.cmd)?$/i.test(execPath) || normalizedRoot.includes("/.config/yarn/global/")) {
|
|
32
|
+
return { name: "yarn", version: versionFromUserAgent(userAgent, "yarn") || "1" };
|
|
33
|
+
}
|
|
34
|
+
return { name: "npm", version: versionFromUserAgent(userAgent, "npm") };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function installationScope(environment = process.env) {
|
|
38
|
+
return environment.npm_command === "exec" || environment.npm_lifecycle_event === "npx" ? "ephemeral" : "global";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function launch() {
|
|
42
|
+
const binary = binaryFor();
|
|
43
|
+
if (!binary) {
|
|
44
|
+
throw new Error(`Unsupported platform: ${process.platform}-${process.arch}`);
|
|
45
|
+
}
|
|
46
|
+
if (!existsSync(binary)) {
|
|
47
|
+
throw new Error(`Epismo native binary is missing for ${process.platform}-${process.arch}. Reinstall the epismo package.`);
|
|
48
|
+
}
|
|
49
|
+
const manager = detectPackageManager();
|
|
50
|
+
const env = {
|
|
51
|
+
...process.env,
|
|
52
|
+
EPISMO_DISTRIBUTION: "node",
|
|
53
|
+
EPISMO_NODE_MANAGER: manager.name,
|
|
54
|
+
EPISMO_NODE_MANAGER_VERSION: manager.version || "",
|
|
55
|
+
EPISMO_NODE_SCOPE: installationScope()
|
|
56
|
+
};
|
|
57
|
+
const child = spawn(binary, process.argv.slice(2), { stdio: "inherit", env });
|
|
58
|
+
const signalHandlers = new Map();
|
|
59
|
+
const removeSignalHandlers = () => {
|
|
60
|
+
for (const [signal, handler] of signalHandlers) process.off(signal, handler);
|
|
61
|
+
};
|
|
62
|
+
child.on("error", (error) => {
|
|
63
|
+
removeSignalHandlers();
|
|
64
|
+
console.error(error.message);
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
});
|
|
67
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
68
|
+
const handler = () => {
|
|
69
|
+
if (!child.killed) child.kill(signal);
|
|
70
|
+
};
|
|
71
|
+
signalHandlers.set(signal, handler);
|
|
72
|
+
process.on(signal, handler);
|
|
73
|
+
}
|
|
74
|
+
child.on("exit", (code, signal) => {
|
|
75
|
+
removeSignalHandlers();
|
|
76
|
+
if (signal && process.platform !== "win32") {
|
|
77
|
+
process.kill(process.pid, signal);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
process.exitCode = code ?? 1;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function versionFromUserAgent(userAgent, manager) {
|
|
85
|
+
return userAgent.match(new RegExp(`(?:^|\\s)${manager}/([^\\s]+)`, "i"))?.[1] || "";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function pnpmOwnsPackage(root, entrypoint) {
|
|
89
|
+
for (const start of new Set([root, dirname(resolve(entrypoint || root))])) {
|
|
90
|
+
const filesystemRoot = parse(start).root;
|
|
91
|
+
for (let current = start; ; current = dirname(current)) {
|
|
92
|
+
const nodeModules = join(current, "node_modules");
|
|
93
|
+
if (existsSync(join(nodeModules, ".modules.yaml"))) {
|
|
94
|
+
try {
|
|
95
|
+
if (realpathSync(join(nodeModules, "epismo")) === realpathSync(root)) return true;
|
|
96
|
+
} catch {
|
|
97
|
+
// Keep looking for the owning pnpm node_modules directory.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (current === filesystemRoot) break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "epismo",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Dependency-free CLI for discovering, authoring, and coordinating reusable AI Playbooks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
-
"homepage": "https://
|
|
7
|
+
"homepage": "https://epismo.ai",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/epismoai/cli.git"
|
|
@@ -17,16 +17,25 @@
|
|
|
17
17
|
"agent",
|
|
18
18
|
"cli"
|
|
19
19
|
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18"
|
|
22
|
+
},
|
|
20
23
|
"bin": {
|
|
21
|
-
"epismo": "npm/bin/epismo.
|
|
24
|
+
"epismo": "npm/bin/epismo.js"
|
|
22
25
|
},
|
|
23
26
|
"files": [
|
|
24
|
-
"npm/bin/epismo.
|
|
25
|
-
"npm/
|
|
27
|
+
"npm/bin/epismo.js",
|
|
28
|
+
"npm/launcher.js",
|
|
29
|
+
"npm/vendor/darwin-amd64/epismo",
|
|
30
|
+
"npm/vendor/darwin-arm64/epismo",
|
|
31
|
+
"npm/vendor/linux-amd64/epismo",
|
|
32
|
+
"npm/vendor/linux-arm64/epismo",
|
|
33
|
+
"npm/vendor/windows-amd64/epismo.exe",
|
|
34
|
+
"npm/vendor/windows-arm64/epismo.exe",
|
|
26
35
|
"README.md",
|
|
27
36
|
"LICENSE"
|
|
28
37
|
],
|
|
29
38
|
"scripts": {
|
|
30
|
-
"
|
|
39
|
+
"test": "node --test npm/launcher.test.js"
|
|
31
40
|
}
|
|
32
41
|
}
|
package/npm/bin/epismo.exe
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Epismo native binary placeholder. The npm postinstall script replaces this file.
|
package/npm/install.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { chmod, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { get } from "node:https";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
|
|
7
|
-
const repository = "epismoai/cli";
|
|
8
|
-
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
|
-
const target = join(packageRoot, "npm", "bin", "epismo.exe");
|
|
10
|
-
const packageJson = JSON.parse(await readFile(join(packageRoot, "package.json"), "utf8"));
|
|
11
|
-
const version = packageJson.version;
|
|
12
|
-
if (version === "0.0.0-development") {
|
|
13
|
-
console.log("Skipping Epismo binary download for a development checkout.");
|
|
14
|
-
process.exit(0);
|
|
15
|
-
}
|
|
16
|
-
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version)) {
|
|
17
|
-
throw new Error(`Invalid published package version: ${JSON.stringify(version)}`);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const platform = { darwin: "darwin", linux: "linux", win32: "windows" }[process.platform];
|
|
21
|
-
const architecture = { x64: "amd64", arm64: "arm64" }[process.arch];
|
|
22
|
-
if (!platform || !architecture) {
|
|
23
|
-
throw new Error(`Unsupported platform: ${process.platform}-${process.arch}`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const extension = platform === "windows" ? ".exe" : "";
|
|
27
|
-
const asset = `epismo_${platform}_${architecture}${extension}`;
|
|
28
|
-
const releaseBase = process.env.EPISMO_RELEASE_BASE_URL || `https://github.com/${repository}/releases/download/v${version}`;
|
|
29
|
-
const [binary, checksumFile] = await Promise.all([
|
|
30
|
-
download(`${releaseBase}/${asset}`),
|
|
31
|
-
download(`${releaseBase}/checksums.txt`)
|
|
32
|
-
]);
|
|
33
|
-
|
|
34
|
-
const expected = checksumFile
|
|
35
|
-
.toString("utf8")
|
|
36
|
-
.split(/\r?\n/)
|
|
37
|
-
.map((line) => line.trim().split(/\s+/))
|
|
38
|
-
.find((parts) => parts[1] === asset)?.[0];
|
|
39
|
-
if (!expected) {
|
|
40
|
-
throw new Error(`Checksum not found for ${asset}`);
|
|
41
|
-
}
|
|
42
|
-
const actual = createHash("sha256").update(binary).digest("hex");
|
|
43
|
-
if (actual !== expected.toLowerCase()) {
|
|
44
|
-
throw new Error(`Checksum verification failed for ${asset}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const temporary = `${target}.${process.pid}.tmp`;
|
|
48
|
-
await writeFile(temporary, binary, { mode: 0o755 });
|
|
49
|
-
await chmod(temporary, 0o755).catch(() => {});
|
|
50
|
-
await rm(target, { force: true });
|
|
51
|
-
await rename(temporary, target);
|
|
52
|
-
|
|
53
|
-
function download(url, redirects = 0) {
|
|
54
|
-
if (redirects > 5) {
|
|
55
|
-
return Promise.reject(new Error(`Too many redirects while downloading ${url}`));
|
|
56
|
-
}
|
|
57
|
-
return new Promise((resolve, reject) => {
|
|
58
|
-
const request = get(url, { headers: { "user-agent": `epismo-npm/${version}` } }, (response) => {
|
|
59
|
-
if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400) {
|
|
60
|
-
const location = response.headers.location;
|
|
61
|
-
response.resume();
|
|
62
|
-
if (!location) {
|
|
63
|
-
reject(new Error(`Redirect without Location from ${url}`));
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
resolve(download(new URL(location, url).toString(), redirects + 1));
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
if (response.statusCode !== 200) {
|
|
70
|
-
response.resume();
|
|
71
|
-
reject(new Error(`Download failed (${response.statusCode}) for ${url}`));
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
const chunks = [];
|
|
75
|
-
response.on("data", (chunk) => chunks.push(chunk));
|
|
76
|
-
response.on("end", () => resolve(Buffer.concat(chunks)));
|
|
77
|
-
response.on("error", reject);
|
|
78
|
-
});
|
|
79
|
-
request.setTimeout(30_000, () => request.destroy(new Error(`Download timed out: ${url}`)));
|
|
80
|
-
request.on("error", reject);
|
|
81
|
-
});
|
|
82
|
-
}
|