@mmmbuto/anthmorph 0.1.0 → 0.1.1

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/Cargo.lock CHANGED
@@ -4,7 +4,7 @@ version = 4
4
4
 
5
5
  [[package]]
6
6
  name = "AnthMorph"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  dependencies = [
9
9
  "anyhow",
10
10
  "async-stream",
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "AnthMorph"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  edition = "2021"
5
5
  description = "Anthropic to OpenAI-compatible proxy"
6
6
  license = "MIT"
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 DioNanos
3
+ Copyright (c) 2026 Davide A. Guglielmi
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # AnthMorph
2
2
 
3
- [![Status](https://img.shields.io/badge/Status-0.1.0-blue.svg)](#project-status)
3
+ [![Status](https://img.shields.io/badge/Status-0.1.1-blue.svg)](#project-status)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
5
  [![Rust](https://img.shields.io/badge/Rust-1.94%2B-orange.svg)](https://www.rust-lang.org)
6
6
  [![Target](https://img.shields.io/badge/Target-Termux%20%2F%20Linux-green.svg)](https://termux.dev)
@@ -15,15 +15,15 @@ Core capabilities:
15
15
  - `openai_generic` profile for conservative compatibility with generic OpenAI-style providers
16
16
  - Streaming SSE translation with fragmented tool-call handling
17
17
  - Local control CLI for init, start, stop, restart, status, and logs
18
- - Termux-friendly workflow with optional npm distribution
18
+ - Termux-first npm distribution with bundled prebuilt binary and local self-build on Linux/macOS
19
19
 
20
20
  ## Project Status
21
21
 
22
- - Current line: `0.1.0`
22
+ - Current line: `0.1.1`
23
23
  - Primary target: `chutes.ai`
24
24
  - Secondary target: generic OpenAI-compatible endpoints
25
25
  - Tested locally against Chutes, MiniMax, and Alibaba Coding Plan rejection handling
26
- - Distribution paths: source build and npm package under `@mmmbuto/anthmorph`
26
+ - Distribution paths: Termux-first npm package with bundled prebuilt binary, plus source builds
27
27
  - Repository metadata is aligned for GitHub and npm publication
28
28
 
29
29
  ## Quickstart
@@ -65,7 +65,8 @@ anthmorphctl stop
65
65
  ## CLI Control
66
66
 
67
67
  `anthmorphctl` is the operator entrypoint.
68
- It stores runtime state under `.anthmorph/` in the project root.
68
+ By default it stores runtime state under `.anthmorph/` inside the installed package root.
69
+ For shell wrappers and daily usage, prefer setting `ANTHMORPH_STATE_DIR` to a dedicated writable path.
69
70
 
70
71
  Common commands:
71
72
 
@@ -135,8 +136,9 @@ This repository already includes npm packaging files:
135
136
 
136
137
  Packaging behavior:
137
138
  - `npm install -g @mmmbuto/anthmorph` exposes `anthmorph` and `anthmorphctl`
138
- - `postinstall` tries to build the Rust binary with `cargo build --release`
139
- - if the binary is missing later, the `anthmorph` shim will try to build it on demand
139
+ - Termux uses the bundled `prebuilt/anthmorph` from the npm tarball
140
+ - Linux and macOS use `postinstall` to build locally with Cargo
141
+ - if no binary is available later, the `anthmorph` shim still falls back to a local release build
140
142
 
141
143
  ## Build And Test
142
144
 
@@ -179,6 +181,6 @@ tests/ protocol and real-backend integration tests
179
181
 
180
182
  MIT License
181
183
  <p>
182
- Copyright (c) 2026 DioNanos<br>
184
+ Copyright (c) 2026 Davide A. Guglielmi<br>
183
185
  Made in Italy
184
186
  </p>
package/bin/anthmorph CHANGED
@@ -1,9 +1,20 @@
1
1
  #!/bin/sh
2
2
  set -eu
3
3
 
4
- ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
4
+ SCRIPT_PATH=$(readlink -f -- "$0" 2>/dev/null || printf "%s" "$0")
5
+ ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$SCRIPT_PATH")/.." && pwd)
6
+ PREBUILT_BIN=$ROOT_DIR/prebuilt/anthmorph
5
7
  RELEASE_BIN=$ROOT_DIR/target/release/anthmorph
6
8
  DEBUG_BIN=$ROOT_DIR/target/debug/anthmorph
9
+ IS_TERMUX=0
10
+
11
+ if [ -n "${TERMUX_VERSION:-}" ] || [ "${PREFIX:-}" = "/data/data/com.termux/files/usr" ]; then
12
+ IS_TERMUX=1
13
+ fi
14
+
15
+ if [ "$IS_TERMUX" -eq 1 ] && [ -x "$PREBUILT_BIN" ]; then
16
+ exec "$PREBUILT_BIN" "$@"
17
+ fi
7
18
 
8
19
  if [ -x "$RELEASE_BIN" ]; then
9
20
  exec "$RELEASE_BIN" "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mmmbuto/anthmorph",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Chutes-first Anthropic /v1/messages proxy for Chutes and OpenAI-compatible backends",
5
5
  "license": "MIT",
6
6
  "author": "DioNanos <noreply@github.com>",
@@ -10,6 +10,7 @@
10
10
  },
11
11
  "files": [
12
12
  "bin/",
13
+ "prebuilt/",
13
14
  "scripts/anthmorphctl",
14
15
  "scripts/postinstall.js",
15
16
  "scripts/smoke_test.sh",
Binary file
@@ -1,31 +1,33 @@
1
1
  #!/bin/sh
2
2
  set -eu
3
3
 
4
- ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
4
+ SCRIPT_PATH=$(readlink -f -- "$0" 2>/dev/null || printf "%s" "$0")
5
+ ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "$SCRIPT_PATH")/.." && pwd)
5
6
  STATE_DIR=${ANTHMORPH_STATE_DIR:-$ROOT_DIR/.anthmorph}
6
7
  CONFIG_FILE=$STATE_DIR/config.env
7
8
  PID_FILE=$STATE_DIR/anthmorph.pid
8
9
  LOG_FILE=$STATE_DIR/anthmorph.log
9
- BIN_PATH_DEFAULT=$ROOT_DIR/target/release/anthmorph
10
+ PREBUILT_BIN=$ROOT_DIR/prebuilt/anthmorph
11
+ BIN_PATH_DEFAULT=${PREBUILT_BIN:-$ROOT_DIR/target/release/anthmorph}
10
12
 
11
13
  mkdir -p "$STATE_DIR"
12
14
 
13
15
  usage() {
14
16
  cat <<USAGE
15
17
  usage:
16
- scripts/anthmorphctl init chutes [options]
17
- scripts/anthmorphctl init minimax [options]
18
- scripts/anthmorphctl init openai --backend-url URL --model MODEL [options]
19
- scripts/anthmorphctl set key-env ENV_NAME
20
- scripts/anthmorphctl set key VALUE --save
21
- scripts/anthmorphctl unset key
22
- scripts/anthmorphctl start
23
- scripts/anthmorphctl stop
24
- scripts/anthmorphctl restart
25
- scripts/anthmorphctl status
26
- scripts/anthmorphctl logs
27
- scripts/anthmorphctl print-config
28
- scripts/anthmorphctl help
18
+ anthmorphctl init chutes [options]
19
+ anthmorphctl init minimax [options]
20
+ anthmorphctl init openai --backend-url URL --model MODEL [options]
21
+ anthmorphctl set key-env ENV_NAME
22
+ anthmorphctl set key VALUE --save
23
+ anthmorphctl unset key
24
+ anthmorphctl start
25
+ anthmorphctl stop
26
+ anthmorphctl restart
27
+ anthmorphctl status
28
+ anthmorphctl logs
29
+ anthmorphctl print-config
30
+ anthmorphctl help
29
31
 
30
32
  options for init:
31
33
  --port PORT
@@ -46,7 +48,7 @@ USAGE
46
48
 
47
49
  ensure_config() {
48
50
  if [ ! -f "$CONFIG_FILE" ]; then
49
- echo "missing config: run 'scripts/anthmorphctl init ...' first" >&2
51
+ echo "missing config: run 'anthmorphctl init ...' first" >&2
50
52
  exit 1
51
53
  fi
52
54
  }
@@ -102,6 +104,33 @@ health_on_port() {
102
104
  curl -fsS "http://127.0.0.1:$1/health" 2>/dev/null
103
105
  }
104
106
 
107
+ bin_supports_profile() {
108
+ [ -x "$1" ] || return 1
109
+ "$1" --help 2>&1 | grep -q -- '--backend-profile'
110
+ }
111
+
112
+ ensure_bin_ready() {
113
+ if bin_supports_profile "$BIN_PATH"; then
114
+ return 0
115
+ fi
116
+
117
+ if bin_supports_profile "$PREBUILT_BIN"; then
118
+ BIN_PATH="$PREBUILT_BIN"
119
+ save_config
120
+ return 0
121
+ fi
122
+
123
+ if ! command -v cargo >/dev/null 2>&1; then
124
+ echo "cargo not found and anthmorph binary is missing or outdated" >&2
125
+ return 1
126
+ fi
127
+
128
+ (cd "$ROOT_DIR" && cargo build --release --quiet) || return 1
129
+ BIN_PATH=$ROOT_DIR/target/release/anthmorph
130
+ save_config
131
+ bin_supports_profile "$BIN_PATH"
132
+ }
133
+
105
134
  join_allow_origin() {
106
135
  value=$1
107
136
  if [ -z "${ALLOWED_ORIGINS:-}" ]; then
@@ -297,9 +326,7 @@ start_cmd() {
297
326
  exit 1
298
327
  fi
299
328
 
300
- if [ ! -x "$BIN_PATH" ]; then
301
- (cd "$ROOT_DIR" && cargo build --release --quiet)
302
- fi
329
+ ensure_bin_ready || exit 1
303
330
 
304
331
  api_key=$(resolve_api_key || true)
305
332
  if [ -z "$api_key" ]; then
@@ -1,23 +1,77 @@
1
- const { spawnSync } = require("node:child_process");
1
+ const fs = require("node:fs");
2
+ const os = require("node:os");
2
3
  const path = require("node:path");
4
+ const { spawnSync } = require("node:child_process");
5
+ const packageJson = require("../package.json");
3
6
 
4
7
  const root = path.resolve(__dirname, "..");
8
+ const prebuiltDir = path.join(root, "prebuilt");
9
+ const prebuiltBin = path.join(prebuiltDir, "anthmorph");
10
+ const releaseBin = path.join(root, "target", "release", "anthmorph");
11
+ const expectedVersion = packageJson.version;
12
+ const isTermux =
13
+ process.env.TERMUX_VERSION !== undefined ||
14
+ process.env.PREFIX === "/data/data/com.termux/files/usr";
5
15
 
6
16
  function hasCargo() {
7
17
  const probe = spawnSync("cargo", ["--version"], { stdio: "ignore" });
8
18
  return probe.status === 0;
9
19
  }
10
20
 
11
- if (!hasCargo()) {
12
- console.log("[anthmorph] cargo not found; skipping Rust build");
13
- process.exit(0);
21
+ function isExecutable(file) {
22
+ try {
23
+ fs.accessSync(file, fs.constants.X_OK);
24
+ return true;
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
29
+
30
+ function binaryVersion(file) {
31
+ try {
32
+ const out = spawnSync(file, ["--version"], { encoding: "utf8" });
33
+ if (out.status !== 0) return null;
34
+ const match = out.stdout.trim().match(/(\d+\.\d+\.\d+)$/);
35
+ return match ? match[1] : null;
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ function ensurePrebuiltPermissions() {
42
+ if (fs.existsSync(prebuiltBin)) {
43
+ fs.chmodSync(prebuiltBin, 0o755);
44
+ }
45
+ }
46
+
47
+ function buildRelease() {
48
+ if (!hasCargo()) {
49
+ console.error("[anthmorph] cargo not found; cannot build local binary");
50
+ process.exit(1);
51
+ }
52
+
53
+ const build = spawnSync("cargo", ["build", "--release", "--quiet"], {
54
+ cwd: root,
55
+ stdio: "inherit",
56
+ });
57
+
58
+ if (build.status !== 0) {
59
+ process.exit(build.status || 1);
60
+ }
14
61
  }
15
62
 
16
- const build = spawnSync("cargo", ["build", "--release", "--quiet"], {
17
- cwd: root,
18
- stdio: "inherit",
19
- });
63
+ fs.mkdirSync(prebuiltDir, { recursive: true });
64
+ ensurePrebuiltPermissions();
65
+
66
+ if (isTermux && isExecutable(prebuiltBin) && binaryVersion(prebuiltBin) === expectedVersion) {
67
+ console.log(`[anthmorph] using packaged Termux prebuilt ${expectedVersion}`);
68
+ process.exit(0);
69
+ }
20
70
 
21
- if (build.status !== 0) {
22
- process.exit(build.status || 1);
71
+ if (os.platform() === "linux" || os.platform() === "darwin") {
72
+ console.log("[anthmorph] building local release binary for this platform");
73
+ buildRelease();
74
+ process.exit(0);
23
75
  }
76
+
77
+ console.log("[anthmorph] unsupported platform for automatic setup; packaged files kept as-is");