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 CHANGED
@@ -1,13 +1,25 @@
1
1
  # mishmar (TypeScript)
2
2
 
3
- The TypeScript client for [Mishmar](https://mishmar.io): build a proposal,
4
- sign it with the agent's key, send it to the engine, read the decision. No
5
- runtime dependencies; Node 20 or later.
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
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ // `mishmar` on npm: runs the real binary with these arguments (see wrapper.ts).
3
+ import { main } from "./wrapper.js";
4
+ process.exitCode = await main(import.meta.url);
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 candidates = [explicit, process.env[ENV_BINARY]];
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
- candidates.push(path.join(dir, name));
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
- candidates.push(path.join(home, version, name));
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>;
@@ -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.2.0",
4
- "description": "Mishmar client: sign and submit proposals to the engine that guards your trading agent",
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
- "agents",
38
- "risk",
39
- "guardrails",
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
  }