mishmar 0.2.0 → 0.3.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 +15 -3
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +4 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/signer.d.ts +10 -1
- package/dist/signer.js +35 -11
- package/dist/wrapper.d.ts +17 -0
- package/dist/wrapper.js +58 -0
- package/package.json +15 -7
package/README.md
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
# mishmar (TypeScript)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
[Mishmar](https://mishmar.io) is a risk management layer for trading bots
|
|
4
|
+
and AI agents: every order is checked against your limits before it reaches
|
|
5
|
+
the exchange, large orders wait for your approval, and no key can trade
|
|
6
|
+
alone.
|
|
7
|
+
|
|
8
|
+
This is the TypeScript client: build a proposal, sign it with the agent's
|
|
9
|
+
key, send it to the engine, read the decision. No runtime dependencies; Node
|
|
10
|
+
20 or later.
|
|
6
11
|
|
|
7
12
|
```sh
|
|
8
13
|
npm install mishmar
|
|
9
14
|
```
|
|
10
15
|
|
|
16
|
+
The package also puts `mishmar` on the path, so `npx -y mishmar mcp` runs
|
|
17
|
+
the MCP server and `npx -y mishmar signer run` the signer without the CLI
|
|
18
|
+
installed: the command runs the binary found through `MISHMAR_BIN`, the
|
|
19
|
+
PATH or `~/.mishmar/bin`, and when there is none fetches the pinned release
|
|
20
|
+
(v0.2.2) into `~/.mishmar/bin` first, checked against the release's
|
|
21
|
+
checksums.
|
|
22
|
+
|
|
11
23
|
```ts
|
|
12
24
|
import { AgentKey, Client } from "mishmar";
|
|
13
25
|
|
package/dist/bin.d.ts
ADDED
package/dist/bin.js
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -5,5 +5,5 @@ export { Client, DEFAULT_RETRIES, EngineError, resolved, retryWait } from "./cli
|
|
|
5
5
|
export type { ClientOptions, Decision, DecisionPage, Retries, SignRequest, SignerAnswer } from "./client.ts";
|
|
6
6
|
export { fromWire, isDecimal, rfc3339, sign, signingBytes, toWire, validate } from "./proposal.ts";
|
|
7
7
|
export type { Kind, OrderType, Proposal, Scheme, Side, VenueMessage, WireProposal } from "./proposal.ts";
|
|
8
|
-
export { Signer, downloadBinary, findBinary, platformName, RELEASES } from "./signer.ts";
|
|
9
|
-
export type { ConnectOptions, OrderIntent, SignerOptions } from "./signer.ts";
|
|
8
|
+
export { Signer, downloadBinary, findBinary, platformName, RELEASE, RELEASES } from "./signer.ts";
|
|
9
|
+
export type { ConnectOptions, FindBinaryOptions, OrderIntent, SignerOptions } from "./signer.ts";
|
package/dist/index.js
CHANGED
|
@@ -2,4 +2,4 @@ export { AgentKey, HEADER_KEY_ID, HEADER_NONCE, HEADER_SIGNATURE, HEADER_SIGNED_
|
|
|
2
2
|
export { canonicalBytes, canonicalJson } from "./canon.js";
|
|
3
3
|
export { Client, DEFAULT_RETRIES, EngineError, resolved, retryWait } from "./client.js";
|
|
4
4
|
export { fromWire, isDecimal, rfc3339, sign, signingBytes, toWire, validate } from "./proposal.js";
|
|
5
|
-
export { Signer, downloadBinary, findBinary, platformName, RELEASES } from "./signer.js";
|
|
5
|
+
export { Signer, downloadBinary, findBinary, platformName, RELEASE, RELEASES } from "./signer.js";
|
package/dist/signer.d.ts
CHANGED
|
@@ -31,13 +31,22 @@
|
|
|
31
31
|
import type { ChildProcess } from "node:child_process";
|
|
32
32
|
import type { Decision, DecisionPage } from "./client.ts";
|
|
33
33
|
export declare const RELEASES = "https://github.com/mishmar-io/mishmar/releases/download";
|
|
34
|
+
/** The release the SDK fetches when it has to (the npm bin: `npx -y mishmar …`); bumped with each release. */
|
|
35
|
+
export declare const RELEASE = "v0.2.2";
|
|
34
36
|
/** The release asset's os_arch for this machine: linux_amd64, darwin_arm64, … */
|
|
35
37
|
export declare function platformName(): string;
|
|
38
|
+
/** What `findBinary` passes over on the PATH; the argument and `MISHMAR_BIN` are always taken as given. */
|
|
39
|
+
export interface FindBinaryOptions {
|
|
40
|
+
/** Skip a `mishmar` that is a script (starts with `#!`): the binary is native, and a script by that name is a wrapper such as the npm or PyPI one. */
|
|
41
|
+
skipScripts?: boolean;
|
|
42
|
+
/** Real paths never to return: a wrapper's own, so it does not run itself. */
|
|
43
|
+
exclude?: string[];
|
|
44
|
+
}
|
|
36
45
|
/**
|
|
37
46
|
* Where the mishmar binary is: the argument, `MISHMAR_BIN`, the PATH, or a
|
|
38
47
|
* release `downloadBinary` fetched. Throws with what to do when there is none.
|
|
39
48
|
*/
|
|
40
|
-
export declare function findBinary(explicit?: string): Promise<string>;
|
|
49
|
+
export declare function findBinary(explicit?: string, options?: FindBinaryOptions): Promise<string>;
|
|
41
50
|
/**
|
|
42
51
|
* Fetch the release `version` (a tag, v0.1.0) for this machine into `destDir`
|
|
43
52
|
* (default `~/.mishmar/bin/<version>`), after checking its SHA-256 against the
|
package/dist/signer.js
CHANGED
|
@@ -31,13 +31,15 @@
|
|
|
31
31
|
import { execFile, spawn } from "node:child_process";
|
|
32
32
|
import { createHash, randomBytes } from "node:crypto";
|
|
33
33
|
import { constants as fsConstants } from "node:fs";
|
|
34
|
-
import { access, chmod, mkdir, mkdtemp, readdir, rm, writeFile } from "node:fs/promises";
|
|
34
|
+
import { access, chmod, mkdir, mkdtemp, open, readdir, realpath, rm, writeFile } from "node:fs/promises";
|
|
35
35
|
import * as http from "node:http";
|
|
36
36
|
import * as net from "node:net";
|
|
37
37
|
import { arch, homedir, platform, tmpdir } from "node:os";
|
|
38
38
|
import * as path from "node:path";
|
|
39
39
|
import { EngineError, resolved } from "./client.js";
|
|
40
40
|
export const RELEASES = "https://github.com/mishmar-io/mishmar/releases/download";
|
|
41
|
+
/** The release the SDK fetches when it has to (the npm bin: `npx -y mishmar …`); bumped with each release. */
|
|
42
|
+
export const RELEASE = "v0.2.2";
|
|
41
43
|
const ENV_BINARY = "MISHMAR_BIN";
|
|
42
44
|
const ENV_LOCAL_TOKEN = "MISHMAR_LOCAL_TOKEN";
|
|
43
45
|
/** The release asset's os_arch for this machine: linux_amd64, darwin_arm64, … */
|
|
@@ -62,27 +64,49 @@ async function executable(p) {
|
|
|
62
64
|
return false;
|
|
63
65
|
}
|
|
64
66
|
}
|
|
67
|
+
/** Whether the file starts with `#!`: a script, never the mishmar binary itself. */
|
|
68
|
+
async function isScript(p) {
|
|
69
|
+
const f = await open(p, "r");
|
|
70
|
+
try {
|
|
71
|
+
const { bytesRead, buffer } = await f.read(Buffer.alloc(2), 0, 2, 0);
|
|
72
|
+
return bytesRead === 2 && buffer[0] === 0x23 && buffer[1] === 0x21;
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
await f.close();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
65
78
|
/**
|
|
66
79
|
* Where the mishmar binary is: the argument, `MISHMAR_BIN`, the PATH, or a
|
|
67
80
|
* release `downloadBinary` fetched. Throws with what to do when there is none.
|
|
68
81
|
*/
|
|
69
|
-
export async function findBinary(explicit) {
|
|
70
|
-
const
|
|
82
|
+
export async function findBinary(explicit, options = {}) {
|
|
83
|
+
for (const c of [explicit, process.env[ENV_BINARY]])
|
|
84
|
+
if (await executable(c))
|
|
85
|
+
return c;
|
|
71
86
|
const name = exe("mishmar");
|
|
72
|
-
for (const dir of (process.env.PATH ?? "").split(path.delimiter))
|
|
73
|
-
if (dir)
|
|
74
|
-
|
|
87
|
+
for (const dir of (process.env.PATH ?? "").split(path.delimiter)) {
|
|
88
|
+
if (!dir)
|
|
89
|
+
continue;
|
|
90
|
+
const c = path.join(dir, name);
|
|
91
|
+
if (!(await executable(c)))
|
|
92
|
+
continue;
|
|
93
|
+
if (options.skipScripts && (await isScript(c)))
|
|
94
|
+
continue;
|
|
95
|
+
if (options.exclude?.length && options.exclude.includes(await realpath(c).catch(() => c)))
|
|
96
|
+
continue;
|
|
97
|
+
return c;
|
|
98
|
+
}
|
|
75
99
|
const home = path.join(homedir(), ".mishmar", "bin");
|
|
76
100
|
try {
|
|
77
|
-
for (const version of (await readdir(home)).sort().reverse())
|
|
78
|
-
|
|
101
|
+
for (const version of (await readdir(home)).sort().reverse()) {
|
|
102
|
+
const c = path.join(home, version, name);
|
|
103
|
+
if (await executable(c))
|
|
104
|
+
return c;
|
|
105
|
+
}
|
|
79
106
|
}
|
|
80
107
|
catch {
|
|
81
108
|
// no downloads yet
|
|
82
109
|
}
|
|
83
|
-
for (const c of candidates)
|
|
84
|
-
if (await executable(c))
|
|
85
|
-
return c;
|
|
86
110
|
throw new Error("no mishmar binary: install it from https://github.com/mishmar-io/mishmar/releases (or call downloadBinary(version)), put it on the PATH or in MISHMAR_BIN");
|
|
87
111
|
}
|
|
88
112
|
/**
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mishmar` as an npm bin: `npx -y mishmar mcp`, or any other mishmar
|
|
3
|
+
* command, without installing the CLI first. It finds the real binary
|
|
4
|
+
* (`MISHMAR_BIN`, the PATH less any wrapper such as this one, then
|
|
5
|
+
* `~/.mishmar/bin`), fetches the pinned release when there is none, and runs
|
|
6
|
+
* it with the same arguments, stdin, stdout and stderr, so stdout stays the
|
|
7
|
+
* MCP channel; it writes nothing there itself. It forwards SIGINT and SIGTERM
|
|
8
|
+
* and exits with the binary's code.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The binary for the wrapper at `self` (the running script's path) to run:
|
|
12
|
+
* found, or fetched into `~/.mishmar/bin/<release>`. `log` gets one line
|
|
13
|
+
* when a download starts.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveBinary(self: string, log?: (line: string) => void): Promise<string>;
|
|
16
|
+
/** Run the binary with `argv`; the exit code to end this process with. */
|
|
17
|
+
export declare function main(selfUrl: string, argv?: string[]): Promise<number>;
|
package/dist/wrapper.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `mishmar` as an npm bin: `npx -y mishmar mcp`, or any other mishmar
|
|
3
|
+
* command, without installing the CLI first. It finds the real binary
|
|
4
|
+
* (`MISHMAR_BIN`, the PATH less any wrapper such as this one, then
|
|
5
|
+
* `~/.mishmar/bin`), fetches the pinned release when there is none, and runs
|
|
6
|
+
* it with the same arguments, stdin, stdout and stderr, so stdout stays the
|
|
7
|
+
* MCP channel; it writes nothing there itself. It forwards SIGINT and SIGTERM
|
|
8
|
+
* and exits with the binary's code.
|
|
9
|
+
*/
|
|
10
|
+
import { spawn } from "node:child_process";
|
|
11
|
+
import { realpath } from "node:fs/promises";
|
|
12
|
+
import { constants as osConstants } from "node:os";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { RELEASE, downloadBinary, findBinary, platformName } from "./signer.js";
|
|
15
|
+
/** Set on the binary this wrapper starts; a wrapper that finds it set has been started by another wrapper, which means it resolved to itself. */
|
|
16
|
+
const ENV_WRAPPED = "MISHMAR_WRAPPED";
|
|
17
|
+
/**
|
|
18
|
+
* The binary for the wrapper at `self` (the running script's path) to run:
|
|
19
|
+
* found, or fetched into `~/.mishmar/bin/<release>`. `log` gets one line
|
|
20
|
+
* when a download starts.
|
|
21
|
+
*/
|
|
22
|
+
export async function resolveBinary(self, log = () => { }) {
|
|
23
|
+
const exclude = [await realpath(self).catch(() => self)];
|
|
24
|
+
try {
|
|
25
|
+
return await findBinary(undefined, { skipScripts: true, exclude });
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
log(`mishmar: fetching release ${RELEASE} for ${platformName()} into ~/.mishmar/bin/${RELEASE}`);
|
|
29
|
+
return await downloadBinary(RELEASE);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Run the binary with `argv`; the exit code to end this process with. */
|
|
33
|
+
export async function main(selfUrl, argv = process.argv.slice(2)) {
|
|
34
|
+
const stderr = (line) => process.stderr.write(line + "\n");
|
|
35
|
+
if (process.env[ENV_WRAPPED]) {
|
|
36
|
+
stderr("mishmar: this wrapper was started by another wrapper, so the PATH holds no mishmar binary; set MISHMAR_BIN to it");
|
|
37
|
+
return 2;
|
|
38
|
+
}
|
|
39
|
+
let binary;
|
|
40
|
+
try {
|
|
41
|
+
binary = await resolveBinary(fileURLToPath(selfUrl), stderr);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
stderr(`mishmar: ${err instanceof Error ? err.message : String(err)}`);
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
const child = spawn(binary, argv, { stdio: "inherit", env: { ...process.env, [ENV_WRAPPED]: "1" } });
|
|
48
|
+
const forward = (signal) => child.kill(signal);
|
|
49
|
+
process.on("SIGINT", forward);
|
|
50
|
+
process.on("SIGTERM", forward);
|
|
51
|
+
return new Promise((resolve) => {
|
|
52
|
+
child.on("error", (err) => {
|
|
53
|
+
stderr(`mishmar: ${binary}: ${err.message}`);
|
|
54
|
+
resolve(1);
|
|
55
|
+
});
|
|
56
|
+
child.on("exit", (code, signal) => resolve(code ?? (signal ? 128 + (osConstants.signals[signal] ?? 0) : 1)));
|
|
57
|
+
});
|
|
58
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mishmar",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"mcpName": "io.github.mishmar-io/mishmar",
|
|
5
|
+
"description": "Mishmar is a risk management layer for trading bots and AI agents: every order is checked against your limits before it reaches the exchange, large orders wait for your approval, and no key can trade alone. The TypeScript SDK: run the agent's signer, propose orders, read decisions.",
|
|
5
6
|
"license": "Apache-2.0",
|
|
6
7
|
"type": "module",
|
|
7
8
|
"exports": {
|
|
@@ -10,6 +11,9 @@
|
|
|
10
11
|
"import": "./dist/index.js"
|
|
11
12
|
}
|
|
12
13
|
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"mishmar": "./dist/bin.js"
|
|
16
|
+
},
|
|
13
17
|
"files": [
|
|
14
18
|
"dist"
|
|
15
19
|
],
|
|
@@ -17,7 +21,7 @@
|
|
|
17
21
|
"node": ">=20"
|
|
18
22
|
},
|
|
19
23
|
"scripts": {
|
|
20
|
-
"build": "tsc -p tsconfig.build.json",
|
|
24
|
+
"build": "tsc -p tsconfig.build.json && chmod +x dist/bin.js",
|
|
21
25
|
"prepublishOnly": "npm run build",
|
|
22
26
|
"typecheck": "tsc --noEmit",
|
|
23
27
|
"test": "node --test \"tests/**/*.test.ts\""
|
|
@@ -34,10 +38,14 @@
|
|
|
34
38
|
"homepage": "https://mishmar.io",
|
|
35
39
|
"keywords": [
|
|
36
40
|
"trading",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
41
|
+
"trading-bot",
|
|
42
|
+
"ai-agents",
|
|
43
|
+
"risk-management",
|
|
44
|
+
"pre-trade-risk",
|
|
40
45
|
"hyperliquid",
|
|
41
|
-
"binance"
|
|
46
|
+
"binance",
|
|
47
|
+
"mpc",
|
|
48
|
+
"threshold-signatures",
|
|
49
|
+
"mcp"
|
|
42
50
|
]
|
|
43
51
|
}
|