anolisa-tokenless 0.7.7
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/LICENSE +190 -0
- package/README.md +770 -0
- package/adapters/tokenless/claude-code/.claude-plugin/marketplace.json +15 -0
- package/adapters/tokenless/claude-code/.claude-plugin/plugin.json +9 -0
- package/adapters/tokenless/claude-code/hooks/hooks.json +38 -0
- package/adapters/tokenless/claude-code/scripts/detect.sh +193 -0
- package/adapters/tokenless/claude-code/scripts/install.sh +92 -0
- package/adapters/tokenless/claude-code/scripts/uninstall.sh +50 -0
- package/adapters/tokenless/codex/.codex-plugin/plugin.json +20 -0
- package/adapters/tokenless/codex/README.md +160 -0
- package/adapters/tokenless/codex/hooks/hooks.json +52 -0
- package/adapters/tokenless/codex/scripts/_common.sh +25 -0
- package/adapters/tokenless/codex/scripts/check-tokenless +94 -0
- package/adapters/tokenless/codex/scripts/compress-response +441 -0
- package/adapters/tokenless/codex/scripts/detect.sh +61 -0
- package/adapters/tokenless/codex/scripts/install.sh +151 -0
- package/adapters/tokenless/codex/scripts/rewrite-hook +282 -0
- package/adapters/tokenless/codex/scripts/tool-ready +303 -0
- package/adapters/tokenless/codex/scripts/uninstall.sh +78 -0
- package/adapters/tokenless/common/commands/tokenless-stats.toml +2 -0
- package/adapters/tokenless/common/cosh-extension.json +65 -0
- package/adapters/tokenless/common/hooks/compress_response_hook.py +487 -0
- package/adapters/tokenless/common/hooks/compress_schema_hook.py +144 -0
- package/adapters/tokenless/common/hooks/compress_toon_hook.py +160 -0
- package/adapters/tokenless/common/hooks/hook_utils.py +584 -0
- package/adapters/tokenless/common/hooks/rewrite_hook.py +223 -0
- package/adapters/tokenless/common/hooks/run-hook.sh +62 -0
- package/adapters/tokenless/common/hooks/tool_categories.json +99 -0
- package/adapters/tokenless/common/hooks/tool_ready_hook.sh +571 -0
- package/adapters/tokenless/common/tokenless-env-fix.sh +730 -0
- package/adapters/tokenless/common/tool-ready-spec.json +113 -0
- package/adapters/tokenless/dsh/cordis.patch.yml +8 -0
- package/adapters/tokenless/dsh/dist/index.js +399 -0
- package/adapters/tokenless/dsh/package.json +26 -0
- package/adapters/tokenless/hermes/__init__.py +572 -0
- package/adapters/tokenless/hermes/plugin.yaml +9 -0
- package/adapters/tokenless/hermes/scripts/detect.sh +80 -0
- package/adapters/tokenless/hermes/scripts/install.sh +60 -0
- package/adapters/tokenless/hermes/scripts/uninstall.sh +45 -0
- package/adapters/tokenless/manifest.json +147 -0
- package/adapters/tokenless/openclaw/dist/index.d.ts +27 -0
- package/adapters/tokenless/openclaw/dist/index.js +598 -0
- package/adapters/tokenless/openclaw/dist/tool_categories.json +99 -0
- package/adapters/tokenless/openclaw/index.ts +720 -0
- package/adapters/tokenless/openclaw/openclaw.plugin.json +40 -0
- package/adapters/tokenless/openclaw/package.json +24 -0
- package/adapters/tokenless/openclaw/scripts/detect.sh +102 -0
- package/adapters/tokenless/openclaw/scripts/install.sh +75 -0
- package/adapters/tokenless/openclaw/scripts/uninstall.sh +46 -0
- package/adapters/tokenless/openclaw/tsconfig.json +13 -0
- package/adapters/tokenless/opencode/plugin.js +246 -0
- package/adapters/tokenless/opencode/scripts/detect.sh +38 -0
- package/adapters/tokenless/opencode/scripts/install.sh +56 -0
- package/adapters/tokenless/opencode/scripts/uninstall.sh +29 -0
- package/adapters/tokenless/qoder/.qoder-plugin/plugin.json +11 -0
- package/adapters/tokenless/qoder/commands/tokenless-stats.md +8 -0
- package/adapters/tokenless/qoder/hooks/hooks.json +36 -0
- package/adapters/tokenless/qoder/hooks/run-hook.sh +51 -0
- package/adapters/tokenless/qoder/scripts/detect.sh +35 -0
- package/adapters/tokenless/qoder/scripts/install.sh +136 -0
- package/adapters/tokenless/qoder/scripts/uninstall.sh +106 -0
- package/adapters/tokenless/qwencode/qwen-extension.json +69 -0
- package/adapters/tokenless/qwencode/scripts/detect.sh +125 -0
- package/adapters/tokenless/qwencode/scripts/install.sh +128 -0
- package/adapters/tokenless/qwencode/scripts/uninstall.sh +62 -0
- package/bin/rtk +6 -0
- package/bin/tokenless +6 -0
- package/bin/toon +6 -0
- package/package.json +57 -0
- package/scripts/postinstall.js +208 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install.sh — Register tokenless plugin for Qwen Code via `qwen extensions link`.
|
|
3
|
+
#
|
|
4
|
+
# Responsibility boundary:
|
|
5
|
+
# - This script ONLY deploys an already-stamped plugin manifest.
|
|
6
|
+
# - Manifest stamping (qwen-extension.json.in -> qwen-extension.json) is the
|
|
7
|
+
# Makefile's job:
|
|
8
|
+
# make -C src/tokenless stamp-adapter-templates
|
|
9
|
+
# which `make install` runs automatically before `install-adapter-resources`
|
|
10
|
+
# copies the result into $SHARE_DIR/qwencode.
|
|
11
|
+
# - A dev-only fallback stamps the manifest in place when called outside the
|
|
12
|
+
# RPM/Makefile flow, so adapter-install works on a freshly checked-out tree.
|
|
13
|
+
#
|
|
14
|
+
# Qwen Code extensions can be installed via `link` (local path, no npm build
|
|
15
|
+
# needed) or `install` (local path, copies files). We use `link` so that
|
|
16
|
+
# changes to the adapter directory are reflected immediately without reinstall.
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
AGENT="${ANOLISA_TARGET:-qwencode}"
|
|
20
|
+
COMPONENT="${ANOLISA_COMPONENT:-tokenless}"
|
|
21
|
+
ADAPTER_DIR="${ANOLISA_ADAPTER_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}"
|
|
22
|
+
|
|
23
|
+
PLUGIN_SRC="$ADAPTER_DIR/qwencode"
|
|
24
|
+
EXTENSION_NAME="tokenless"
|
|
25
|
+
|
|
26
|
+
QWEN_BIN="${QWEN_BIN:-}"
|
|
27
|
+
export PATH="$HOME/.local/bin:/usr/local/bin:$PATH"
|
|
28
|
+
if [ -z "$QWEN_BIN" ]; then
|
|
29
|
+
QWEN_BIN="$(command -v qwen 2>/dev/null || true)"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
DRY_RUN="${ANOLISA_DRY_RUN:-0}"
|
|
33
|
+
|
|
34
|
+
echo "[${COMPONENT}] Installing ${AGENT} plugin..."
|
|
35
|
+
|
|
36
|
+
if ! command -v "$QWEN_BIN" &>/dev/null; then
|
|
37
|
+
echo "[${COMPONENT}] qwen CLI not found (QWEN_BIN=${QWEN_BIN}) — skipping plugin installation."
|
|
38
|
+
echo "[${COMPONENT}] Install Qwen Code first, then run this script again."
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
if [ ! -d "$PLUGIN_SRC" ]; then
|
|
43
|
+
echo "[${COMPONENT}] Plugin source not found: $PLUGIN_SRC" >&2
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Dev-only fallback: stamp qwen-extension.json from .in template when Makefile
|
|
48
|
+
# hasn't run yet. Production installs (RPM, `make install`) ship a stamped
|
|
49
|
+
# manifest inside SHARE_DIR, so this branch is a no-op in those flows.
|
|
50
|
+
EXTENSION_MANIFEST="$PLUGIN_SRC/qwen-extension.json"
|
|
51
|
+
EXTENSION_TEMPLATE="$PLUGIN_SRC/qwen-extension.json.in"
|
|
52
|
+
if [ ! -f "$EXTENSION_MANIFEST" ] && [ -f "$EXTENSION_TEMPLATE" ]; then
|
|
53
|
+
VERSION="${TOKENLESS_VERSION:-${ANOLISA_VERSION:-0.0.0-dev}}"
|
|
54
|
+
sed "s/@VERSION@/${VERSION}/g" "$EXTENSION_TEMPLATE" > "$EXTENSION_MANIFEST"
|
|
55
|
+
echo "[${COMPONENT}] dev-fallback: stamped qwen-extension.json (version=${VERSION}) — production builds should stamp via Makefile"
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
if [ ! -f "$EXTENSION_MANIFEST" ]; then
|
|
59
|
+
echo "[${COMPONENT}] ERROR: $EXTENSION_MANIFEST missing." >&2
|
|
60
|
+
echo "[${COMPONENT}] Stamp the manifest first:" >&2
|
|
61
|
+
echo "[${COMPONENT}] make -C src/tokenless stamp-adapter-templates" >&2
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Idempotent: unlink first if already linked, then link again.
|
|
66
|
+
# `qwen extensions list` may not reliably show link state, so we
|
|
67
|
+
# always re-link to ensure the latest version is active.
|
|
68
|
+
if "$QWEN_BIN" extensions list 2>/dev/null | grep -qE "(^|[[:space:]])${EXTENSION_NAME}([[:space:]]|$)"; then
|
|
69
|
+
echo "[${COMPONENT}] extension '${EXTENSION_NAME}' already registered, re-linking..."
|
|
70
|
+
if ! "$QWEN_BIN" extensions uninstall "$EXTENSION_NAME" 2>/dev/null; then
|
|
71
|
+
echo "[${COMPONENT}] WARNING: qwen extensions uninstall failed (non-fatal, will re-link)"
|
|
72
|
+
fi
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if [ "$DRY_RUN" = "1" ]; then
|
|
76
|
+
echo "DRY-RUN: $QWEN_BIN extensions link $PLUGIN_SRC"
|
|
77
|
+
exit 0
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
echo "[${COMPONENT}] linking extension from ${PLUGIN_SRC}..."
|
|
81
|
+
"$QWEN_BIN" extensions link "$PLUGIN_SRC" \
|
|
82
|
+
|| { echo "[${COMPONENT}] ERROR: qwen extensions link failed" >&2; exit 1; }
|
|
83
|
+
|
|
84
|
+
# Verify the extension was registered.
|
|
85
|
+
if "$QWEN_BIN" extensions list 2>/dev/null | grep -qE "(^|[[:space:]])${EXTENSION_NAME}([[:space:]]|$)"; then
|
|
86
|
+
echo "[${COMPONENT}] ${AGENT} plugin installed and linked via qwen CLI."
|
|
87
|
+
else
|
|
88
|
+
echo "[${COMPONENT}] WARNING: extension '${EXTENSION_NAME}' may not be visible in extensions list yet."
|
|
89
|
+
echo "[${COMPONENT}] Run 'qwen extensions list' or restart qwen-code to confirm."
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# Clean up tokenless-specific hook entries from settings.json — the
|
|
93
|
+
# extension's qwen-extension.json already provides all hooks, so duplicate
|
|
94
|
+
# entries in settings.json cause double registration (2x invocations per
|
|
95
|
+
# event). Only remove hook entries whose name starts with "tokenless-",
|
|
96
|
+
# preserving any user-defined hooks that may also exist.
|
|
97
|
+
# Qwen Code stores global settings under ~/.qwen/settings.json.
|
|
98
|
+
SETTINGS_JSON="$HOME/.qwen/settings.json"
|
|
99
|
+
if [ -f "$SETTINGS_JSON" ] && command -v python3 &>/dev/null; then
|
|
100
|
+
if python3 -c "
|
|
101
|
+
import json, sys
|
|
102
|
+
path = sys.argv[1]
|
|
103
|
+
with open(path) as f:
|
|
104
|
+
d = json.load(f)
|
|
105
|
+
if 'hooks' not in d:
|
|
106
|
+
sys.exit(1)
|
|
107
|
+
changed = False
|
|
108
|
+
for event, definitions in d['hooks'].items():
|
|
109
|
+
if not isinstance(definitions, list):
|
|
110
|
+
continue
|
|
111
|
+
filtered = [defn for defn in definitions
|
|
112
|
+
if not any(h.get('name', '').startswith('tokenless-')
|
|
113
|
+
for h in defn.get('hooks', []))]
|
|
114
|
+
if len(filtered) != len(definitions):
|
|
115
|
+
d['hooks'][event] = filtered
|
|
116
|
+
changed = True
|
|
117
|
+
# If all hook events are now empty, remove the entire hooks key.
|
|
118
|
+
if all(not v for v in d['hooks'].values()):
|
|
119
|
+
del d['hooks']
|
|
120
|
+
if changed:
|
|
121
|
+
with open(path, 'w') as f:
|
|
122
|
+
json.dump(d, f, indent=2)
|
|
123
|
+
sys.exit(0)
|
|
124
|
+
sys.exit(1)
|
|
125
|
+
" "$SETTINGS_JSON" 2>/dev/null; then
|
|
126
|
+
echo "[${COMPONENT}] Cleaned up tokenless hook entries from ${SETTINGS_JSON} (extension provides them via qwen-extension.json)."
|
|
127
|
+
fi
|
|
128
|
+
fi
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# uninstall.sh — Remove tokenless plugin from Qwen Code via `qwen extensions uninstall`.
|
|
3
|
+
# Falls back to manual cleanup of the extension directory when qwen is unavailable.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
AGENT="${ANOLISA_TARGET:-qwencode}"
|
|
7
|
+
COMPONENT="${ANOLISA_COMPONENT:-tokenless}"
|
|
8
|
+
|
|
9
|
+
EXTENSION_NAME="tokenless"
|
|
10
|
+
|
|
11
|
+
QWEN_BIN="${QWEN_BIN:-}"
|
|
12
|
+
export PATH="$HOME/.local/bin:/usr/local/bin:$PATH"
|
|
13
|
+
|
|
14
|
+
echo "[${COMPONENT}] Uninstalling ${AGENT} plugin..."
|
|
15
|
+
|
|
16
|
+
if [ -z "$QWEN_BIN" ]; then
|
|
17
|
+
QWEN_BIN="$(command -v qwen 2>/dev/null || true)"
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
if [ -n "$QWEN_BIN" ] && [ -x "$QWEN_BIN" ]; then
|
|
21
|
+
"$QWEN_BIN" extensions uninstall "$EXTENSION_NAME" 2>&1 || true
|
|
22
|
+
echo "[${COMPONENT}] ${AGENT} plugin removed via qwen CLI."
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
echo "[${COMPONENT}] qwen CLI not found — falling back to manual cleanup."
|
|
27
|
+
|
|
28
|
+
# Qwen Code stores installed extensions under ~/.qwen/extensions/ and
|
|
29
|
+
# metadata in ~/.qwen/.qwen-extension-install.json.
|
|
30
|
+
QWEN_HOME="${HOME}/.qwen"
|
|
31
|
+
EXTENSIONS_DIR="${QWEN_HOME}/extensions"
|
|
32
|
+
|
|
33
|
+
# Find and remove the tokenless extension directory.
|
|
34
|
+
for subdir in "$EXTENSIONS_DIR"/*; do
|
|
35
|
+
[ -d "$subdir" ] || continue
|
|
36
|
+
# Match by name field in qwen-extension.json.
|
|
37
|
+
manifest="$subdir/qwen-extension.json"
|
|
38
|
+
if [ -f "$manifest" ] && grep -q '"name": *"tokenless"' "$manifest" 2>/dev/null; then
|
|
39
|
+
rm -rf "$subdir"
|
|
40
|
+
echo "[${COMPONENT}] removed $subdir"
|
|
41
|
+
fi
|
|
42
|
+
done
|
|
43
|
+
|
|
44
|
+
# Also clean up the install-metadata file that may reference the link source.
|
|
45
|
+
for subdir in "$EXTENSIONS_DIR"/*; do
|
|
46
|
+
[ -d "$subdir" ] || continue
|
|
47
|
+
metadata="$subdir/.qwen-extension-install.json"
|
|
48
|
+
if [ -f "$metadata" ] && grep -q '"name": *"tokenless"' "$metadata" 2>/dev/null; then
|
|
49
|
+
rm -rf "$subdir"
|
|
50
|
+
echo "[${COMPONENT}] removed $subdir (via install-metadata)"
|
|
51
|
+
fi
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
# Remove from extension-enablement.json if present.
|
|
55
|
+
ENABLEMENT_FILE="${QWEN_HOME}/extension-enablement.json"
|
|
56
|
+
if [ -f "$ENABLEMENT_FILE" ] && command -v jq &>/dev/null; then
|
|
57
|
+
tmp="$(mktemp "${ENABLEMENT_FILE}.XXXXXX")"
|
|
58
|
+
jq 'del(.tokenless)' "$ENABLEMENT_FILE" > "$tmp" && mv "$tmp" "$ENABLEMENT_FILE"
|
|
59
|
+
echo "[${COMPONENT}] cleaned $ENABLEMENT_FILE"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
echo "[${COMPONENT}] manual cleanup complete."
|
package/bin/rtk
ADDED
package/bin/tokenless
ADDED
package/bin/toon
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "anolisa-tokenless",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.7.7",
|
|
5
|
+
"description": "Token-Less — LLM token optimization toolkit (schema/response compression, command rewriting, tool readiness)",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/alibaba/anolisa.git",
|
|
10
|
+
"directory": "src/tokenless"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/alibaba/anolisa/tree/main/src/tokenless",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"anolisa",
|
|
15
|
+
"tokenless",
|
|
16
|
+
"llm",
|
|
17
|
+
"token-optimization",
|
|
18
|
+
"compression",
|
|
19
|
+
"cli"
|
|
20
|
+
],
|
|
21
|
+
"bin": {
|
|
22
|
+
"tokenless": "bin/tokenless",
|
|
23
|
+
"rtk": "bin/rtk",
|
|
24
|
+
"toon": "bin/toon"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin/",
|
|
28
|
+
"scripts/",
|
|
29
|
+
"adapters/",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"postinstall": "node scripts/postinstall.js"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=16.0.0"
|
|
38
|
+
},
|
|
39
|
+
"os": [
|
|
40
|
+
"linux",
|
|
41
|
+
"darwin"
|
|
42
|
+
],
|
|
43
|
+
"cpu": [
|
|
44
|
+
"x64",
|
|
45
|
+
"arm64"
|
|
46
|
+
],
|
|
47
|
+
"optionalDependencies": {
|
|
48
|
+
"@anolisa/tokenless-linux-x64": "0.7.7",
|
|
49
|
+
"@anolisa/tokenless-linux-arm64": "0.7.7",
|
|
50
|
+
"@anolisa/tokenless-darwin-x64": "0.7.7",
|
|
51
|
+
"@anolisa/tokenless-darwin-arm64": "0.7.7"
|
|
52
|
+
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"registry": "https://registry.npmjs.org/",
|
|
55
|
+
"access": "public"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright 2026 Alibaba Cloud
|
|
6
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* postinstall script for anolisa-tokenless
|
|
11
|
+
*
|
|
12
|
+
* Resolves the platform-specific binary package and creates launcher
|
|
13
|
+
* symlinks at bin/ that delegate to the native binaries, then installs the
|
|
14
|
+
* bundled Agent adapters into the user-level data directory searched by
|
|
15
|
+
* the adapter hook dispatcher (run-hook.sh).
|
|
16
|
+
*
|
|
17
|
+
* Platform packages follow the naming convention:
|
|
18
|
+
* @anolisa/tokenless-{os}-{arch}
|
|
19
|
+
*
|
|
20
|
+
* Each platform package ships three native binaries:
|
|
21
|
+
* bin/tokenless, bin/rtk, bin/toon
|
|
22
|
+
*
|
|
23
|
+
* Exit codes: on a supported platform, a missing platform package or a
|
|
24
|
+
* missing binary is a hard failure (non-zero exit) so `npm install` fails
|
|
25
|
+
* loudly instead of leaving broken bin stubs behind. Unsupported platforms
|
|
26
|
+
* are already rejected by the root package's os/cpu constraints.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import {
|
|
30
|
+
existsSync,
|
|
31
|
+
mkdirSync,
|
|
32
|
+
symlinkSync,
|
|
33
|
+
unlinkSync,
|
|
34
|
+
chmodSync,
|
|
35
|
+
cpSync,
|
|
36
|
+
rmSync,
|
|
37
|
+
} from 'node:fs';
|
|
38
|
+
import { execSync } from 'node:child_process';
|
|
39
|
+
import { join, dirname } from 'node:path';
|
|
40
|
+
import { fileURLToPath } from 'node:url';
|
|
41
|
+
import { createRequire } from 'node:module';
|
|
42
|
+
import { platform, arch, homedir } from 'node:os';
|
|
43
|
+
|
|
44
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
45
|
+
const __dirname = dirname(__filename);
|
|
46
|
+
const require = createRequire(import.meta.url);
|
|
47
|
+
const packageRoot = join(__dirname, '..');
|
|
48
|
+
const binDir = join(packageRoot, 'bin');
|
|
49
|
+
|
|
50
|
+
const BINARIES = ['tokenless', 'rtk', 'toon'];
|
|
51
|
+
|
|
52
|
+
// Map Node.js platform/arch to package names
|
|
53
|
+
const PLATFORM_MAP = {
|
|
54
|
+
'linux-x64': '@anolisa/tokenless-linux-x64',
|
|
55
|
+
'linux-arm64': '@anolisa/tokenless-linux-arm64',
|
|
56
|
+
'darwin-x64': '@anolisa/tokenless-darwin-x64',
|
|
57
|
+
'darwin-arm64': '@anolisa/tokenless-darwin-arm64',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function resolvePackageDir(pkgName) {
|
|
61
|
+
// Resolve platform package using createRequire (compatible with Node 16+)
|
|
62
|
+
try {
|
|
63
|
+
const resolved = require.resolve(`${pkgName}/package.json`);
|
|
64
|
+
return dirname(resolved);
|
|
65
|
+
} catch {
|
|
66
|
+
// Fallback: walk up to find node_modules
|
|
67
|
+
let current = packageRoot;
|
|
68
|
+
while (current !== dirname(current)) {
|
|
69
|
+
const candidate = join(current, 'node_modules', ...pkgName.split('/'));
|
|
70
|
+
if (existsSync(candidate)) {
|
|
71
|
+
return candidate;
|
|
72
|
+
}
|
|
73
|
+
current = dirname(current);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function isMusl() {
|
|
80
|
+
// The platform packages declare libc=glibc, but npm CLI versions prior to
|
|
81
|
+
// ~8.3 (e.g. 8.19.4) do not check the libc field in checkPlatform. On a
|
|
82
|
+
// musl-based distribution such as Alpine, the linux-x64 package will still
|
|
83
|
+
// be installed and its glibc-linked ELF will not run. Detect this at
|
|
84
|
+
// postinstall time and fail with a clear message instead of leaving broken
|
|
85
|
+
// bin stubs.
|
|
86
|
+
if (platform() !== 'linux') return false;
|
|
87
|
+
try {
|
|
88
|
+
const out = execSync('ldd --version 2>&1 || true', {
|
|
89
|
+
encoding: 'utf-8',
|
|
90
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
91
|
+
});
|
|
92
|
+
return /\bmusl\b/i.test(out);
|
|
93
|
+
} catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function main() {
|
|
99
|
+
const key = `${platform()}-${arch()}`;
|
|
100
|
+
const pkgName = PLATFORM_MAP[key];
|
|
101
|
+
|
|
102
|
+
if (!pkgName) {
|
|
103
|
+
console.warn(
|
|
104
|
+
`anolisa-tokenless: No prebuilt binary available for ${platform()}-${arch()}.`,
|
|
105
|
+
);
|
|
106
|
+
console.warn('You can build from source: https://github.com/alibaba/anolisa/tree/main/src/tokenless');
|
|
107
|
+
process.exit(0);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (isMusl()) {
|
|
111
|
+
console.error(
|
|
112
|
+
'anolisa-tokenless: musl-based Linux distributions (e.g. Alpine) are not supported by the prebuilt binaries.',
|
|
113
|
+
);
|
|
114
|
+
console.error(
|
|
115
|
+
'Build from source for your libc: https://github.com/alibaba/anolisa/tree/main/src/tokenless',
|
|
116
|
+
);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const pkgDir = resolvePackageDir(pkgName);
|
|
121
|
+
|
|
122
|
+
if (!pkgDir || !existsSync(pkgDir)) {
|
|
123
|
+
console.error(
|
|
124
|
+
`anolisa-tokenless: Platform package ${pkgName} not found.`,
|
|
125
|
+
);
|
|
126
|
+
console.error(
|
|
127
|
+
'This usually means optionalDependencies were skipped during install.',
|
|
128
|
+
);
|
|
129
|
+
console.error(
|
|
130
|
+
'Try: npm install --include=optional or check npm install logs for details.',
|
|
131
|
+
);
|
|
132
|
+
console.error(
|
|
133
|
+
'You can also build from source: https://github.com/alibaba/anolisa/tree/main/src/tokenless',
|
|
134
|
+
);
|
|
135
|
+
// The platform is supported, so the platform package is effectively a
|
|
136
|
+
// required dependency — fail the install instead of leaving bin stubs.
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Ensure bin/ directory exists
|
|
141
|
+
if (!existsSync(binDir)) {
|
|
142
|
+
mkdirSync(binDir, { recursive: true });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const missing = [];
|
|
146
|
+
for (const binary of BINARIES) {
|
|
147
|
+
const nativeBinary = join(pkgDir, 'bin', binary);
|
|
148
|
+
if (!existsSync(nativeBinary)) {
|
|
149
|
+
console.error(`anolisa-tokenless: Binary ${binary} not found in ${pkgName}`);
|
|
150
|
+
missing.push(binary);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const linkPath = join(binDir, binary);
|
|
155
|
+
|
|
156
|
+
// Remove existing symlink or file
|
|
157
|
+
if (existsSync(linkPath)) {
|
|
158
|
+
unlinkSync(linkPath);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
symlinkSync(nativeBinary, linkPath);
|
|
162
|
+
chmodSync(linkPath, 0o755);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (missing.length > 0) {
|
|
166
|
+
console.error(
|
|
167
|
+
`anolisa-tokenless: Incomplete platform package ${pkgName} — missing: ${missing.join(', ')}. Failing install.`,
|
|
168
|
+
);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log(`anolisa-tokenless: Linked ${BINARIES.length} binaries for ${platform()}-${arch()}`);
|
|
173
|
+
|
|
174
|
+
installAdapters();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Install the bundled Agent adapters (hook scripts and install helpers —
|
|
179
|
+
* plain bash/python, OS independent) into the user-level data directory that
|
|
180
|
+
* the hook dispatcher (common/hooks/run-hook.sh) already searches:
|
|
181
|
+
* ~/.local/share/anolisa/adapters/tokenless
|
|
182
|
+
*
|
|
183
|
+
* Fail-open: adapter installation is supplementary — a failure here warns
|
|
184
|
+
* but never fails the npm install, and the files remain available inside
|
|
185
|
+
* the package under adapters/.
|
|
186
|
+
*/
|
|
187
|
+
function installAdapters() {
|
|
188
|
+
const adaptersSrc = join(packageRoot, 'adapters', 'tokenless');
|
|
189
|
+
if (!existsSync(adaptersSrc)) return;
|
|
190
|
+
|
|
191
|
+
const destParent = join(homedir(), '.local', 'share', 'anolisa', 'adapters');
|
|
192
|
+
const dest = join(destParent, 'tokenless');
|
|
193
|
+
try {
|
|
194
|
+
rmSync(dest, { recursive: true, force: true });
|
|
195
|
+
mkdirSync(destParent, { recursive: true });
|
|
196
|
+
cpSync(adaptersSrc, dest, { recursive: true });
|
|
197
|
+
console.log(`anolisa-tokenless: Installed Agent adapters to ${dest}`);
|
|
198
|
+
console.log(
|
|
199
|
+
'anolisa-tokenless: To register an adapter with an Agent product, run its install script, e.g.:',
|
|
200
|
+
);
|
|
201
|
+
console.log(` bash ${join(dest, 'claude-code', 'scripts', 'install.sh')}`);
|
|
202
|
+
} catch (err) {
|
|
203
|
+
console.warn(`anolisa-tokenless: Could not install adapters to ${dest}: ${err.message}`);
|
|
204
|
+
console.warn(`anolisa-tokenless: Adapter files remain available at ${adaptersSrc}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
main();
|