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,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tokenless",
|
|
3
|
+
"version": "0.7.7",
|
|
4
|
+
"description": "Token-Less LLM Token Optimization Toolkit for Qoder CLI",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Shile Zhang",
|
|
7
|
+
"email": "shile.zhang@linux.alibaba.com"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/alibaba/anolisa",
|
|
10
|
+
"license": "Apache-2.0"
|
|
11
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tokenless-stats
|
|
3
|
+
description: Show Tokenless compression statistics
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Run `tokenless stats` and summarize the compression statistics. If the
|
|
7
|
+
`tokenless` command is unavailable, explain that Tokenless must be installed
|
|
8
|
+
before this command can report statistics.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Bash",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "TOKENLESS_AGENT_ID=qoder-cli bash \"${QODER_PLUGIN_ROOT}/hooks/run-hook.sh\" rewrite_hook.py",
|
|
10
|
+
"timeout": 10
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"hooks": [
|
|
16
|
+
{
|
|
17
|
+
"type": "command",
|
|
18
|
+
"command": "TOKENLESS_AGENT_ID=qoder-cli bash \"${QODER_PLUGIN_ROOT}/hooks/run-hook.sh\" tool_ready_hook.sh",
|
|
19
|
+
"timeout": 15
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"PostToolUse": [
|
|
25
|
+
{
|
|
26
|
+
"hooks": [
|
|
27
|
+
{
|
|
28
|
+
"type": "command",
|
|
29
|
+
"command": "TOKENLESS_AGENT_ID=qoder-cli bash \"${QODER_PLUGIN_ROOT}/hooks/run-hook.sh\" compress_response_hook.py",
|
|
30
|
+
"timeout": 15
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# run-hook.sh — Locate and exec a shared Tokenless hook script.
|
|
3
|
+
#
|
|
4
|
+
# Prefer hooks from the wrapper's own adapter tree so source and staged
|
|
5
|
+
# installations stay version-aligned. A qodercli cache contains only the
|
|
6
|
+
# Qoder plugin, so packaged plugins use the ANOLISA FHS fallbacks.
|
|
7
|
+
#
|
|
8
|
+
# Fail open when the shared hook or its interpreter is unavailable so a
|
|
9
|
+
# Tokenless packaging problem never blocks a Qoder tool call.
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(CDPATH='' cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
13
|
+
SCRIPT="${1:?usage: run-hook.sh <hook-script-basename> [args...]}"
|
|
14
|
+
shift
|
|
15
|
+
|
|
16
|
+
fail_open() { echo "{}"; exit 0; }
|
|
17
|
+
|
|
18
|
+
case "$SCRIPT" in
|
|
19
|
+
compress_response_hook.py|rewrite_hook.py|tool_ready_hook.sh) ;;
|
|
20
|
+
*) fail_open ;;
|
|
21
|
+
esac
|
|
22
|
+
|
|
23
|
+
CANDIDATES=(
|
|
24
|
+
"${SCRIPT_DIR}/../../common/hooks/${SCRIPT}"
|
|
25
|
+
"/usr/local/share/anolisa/adapters/tokenless/common/hooks/${SCRIPT}"
|
|
26
|
+
"/usr/share/anolisa/adapters/tokenless/common/hooks/${SCRIPT}"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Minimal runners may omit HOME. Keep the user-local fallback conditional so
|
|
30
|
+
# an environment omission cannot bypass the fail-open behavior below.
|
|
31
|
+
if [ -n "${HOME:-}" ]; then
|
|
32
|
+
CANDIDATES+=("${HOME}/.local/share/anolisa/adapters/tokenless/common/hooks/${SCRIPT}")
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
for candidate in "${CANDIDATES[@]}"; do
|
|
36
|
+
[ -f "$candidate" ] || continue
|
|
37
|
+
case "$candidate" in
|
|
38
|
+
*.py)
|
|
39
|
+
command -v python3 >/dev/null 2>&1 || fail_open
|
|
40
|
+
exec python3 "$candidate" "$@"
|
|
41
|
+
;;
|
|
42
|
+
*.sh)
|
|
43
|
+
exec bash "$candidate" "$@"
|
|
44
|
+
;;
|
|
45
|
+
*)
|
|
46
|
+
fail_open
|
|
47
|
+
;;
|
|
48
|
+
esac
|
|
49
|
+
done
|
|
50
|
+
|
|
51
|
+
fail_open
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# detect.sh — Check whether Qoder CLI supports the native plugin lifecycle.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
AGENT="${ANOLISA_TARGET:-qoder}"
|
|
6
|
+
COMPONENT="${ANOLISA_COMPONENT:-tokenless}"
|
|
7
|
+
|
|
8
|
+
versioned_glob="$HOME/.qoder/bin/qodercli/qodercli-${ANOLISA_QODER_VERSION:-*}"
|
|
9
|
+
# shellcheck disable=SC2086 # Intentional version glob expansion.
|
|
10
|
+
latest_versioned="$(ls -d $versioned_glob 2>/dev/null | sort -V | tail -1 || true)"
|
|
11
|
+
|
|
12
|
+
QODERCLI=""
|
|
13
|
+
for candidate in "$latest_versioned" \
|
|
14
|
+
"$HOME/.qoder/bin/qodercli/qodercli" \
|
|
15
|
+
"qodercli"; do
|
|
16
|
+
[ -n "$candidate" ] || continue
|
|
17
|
+
if [ -x "$candidate" ] || command -v "$candidate" >/dev/null 2>&1; then
|
|
18
|
+
QODERCLI="$candidate"
|
|
19
|
+
break
|
|
20
|
+
fi
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
if [ -z "$QODERCLI" ]; then
|
|
24
|
+
echo "[${COMPONENT}] ${AGENT}: qodercli not found in standard locations" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
for subcommand in install list uninstall; do
|
|
29
|
+
if ! "$QODERCLI" plugins "$subcommand" --help >/dev/null 2>&1; then
|
|
30
|
+
echo "[${COMPONENT}] ${AGENT}: qodercli lacks 'plugins ${subcommand}' support" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
done
|
|
34
|
+
|
|
35
|
+
echo "[${COMPONENT}] ${AGENT}: detected compatible qodercli at ${QODERCLI}"
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install.sh — Install the native Tokenless plugin for Qoder CLI.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
AGENT="${ANOLISA_TARGET:-qoder}"
|
|
6
|
+
COMPONENT="${ANOLISA_COMPONENT:-tokenless}"
|
|
7
|
+
ADAPTER_DIR="${ANOLISA_ADAPTER_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}"
|
|
8
|
+
PLUGIN_DIR="$ADAPTER_DIR/qoder"
|
|
9
|
+
PLUGIN_JSON="$PLUGIN_DIR/.qoder-plugin/plugin.json"
|
|
10
|
+
|
|
11
|
+
find_qodercli() {
|
|
12
|
+
local versioned_glob latest_versioned candidate
|
|
13
|
+
versioned_glob="$HOME/.qoder/bin/qodercli/qodercli-${ANOLISA_QODER_VERSION:-*}"
|
|
14
|
+
# shellcheck disable=SC2086 # Intentional version glob expansion.
|
|
15
|
+
latest_versioned="$(ls -d $versioned_glob 2>/dev/null | sort -V | tail -1 || true)"
|
|
16
|
+
|
|
17
|
+
for candidate in "$latest_versioned" \
|
|
18
|
+
"$HOME/.qoder/bin/qodercli/qodercli" \
|
|
19
|
+
"qodercli"; do
|
|
20
|
+
[ -n "$candidate" ] || continue
|
|
21
|
+
if [ -x "$candidate" ] || command -v "$candidate" >/dev/null 2>&1; then
|
|
22
|
+
printf '%s\n' "$candidate"
|
|
23
|
+
return 0
|
|
24
|
+
fi
|
|
25
|
+
done
|
|
26
|
+
return 1
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
QODERCLI="$(find_qodercli || true)"
|
|
30
|
+
if [ -z "$QODERCLI" ]; then
|
|
31
|
+
echo "[${COMPONENT}] ERROR: qodercli not found, aborting plugin registration" >&2
|
|
32
|
+
echo " Install Qoder CLI first: https://qoder.com/cli" >&2
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
for subcommand in install list uninstall; do
|
|
37
|
+
if ! "$QODERCLI" plugins "$subcommand" --help >/dev/null 2>&1; then
|
|
38
|
+
echo "[${COMPONENT}] ERROR: qodercli lacks required 'plugins ${subcommand}' support" >&2
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
done
|
|
42
|
+
|
|
43
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
44
|
+
echo "[${COMPONENT}] ERROR: python3 is required by the Tokenless hooks" >&2
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
if [ ! -f "$PLUGIN_JSON" ]; then
|
|
48
|
+
echo "[${COMPONENT}] ERROR: Qoder plugin manifest not found: ${PLUGIN_JSON}" >&2
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
VERSION="$(PLUGIN_JSON="$PLUGIN_JSON" python3 - <<'PYEOF'
|
|
53
|
+
import json
|
|
54
|
+
import os
|
|
55
|
+
|
|
56
|
+
with open(os.environ["PLUGIN_JSON"], encoding="utf-8") as manifest_file:
|
|
57
|
+
print(json.load(manifest_file).get("version", "unknown"))
|
|
58
|
+
PYEOF
|
|
59
|
+
)"
|
|
60
|
+
|
|
61
|
+
echo "[${COMPONENT}] Installing ${AGENT} plugin v${VERSION}..."
|
|
62
|
+
|
|
63
|
+
# Validation was added after the core plugin lifecycle commands. Keep it
|
|
64
|
+
# optional so older compatible clients can still install, while treating a
|
|
65
|
+
# validation failure as a malformed bundle when the capability exists.
|
|
66
|
+
if "$QODERCLI" plugins validate --help >/dev/null 2>&1; then
|
|
67
|
+
if ! VALIDATE_OUT="$("$QODERCLI" plugins validate "$PLUGIN_DIR" 2>&1)"; then
|
|
68
|
+
echo "[${COMPONENT}] ERROR: qodercli rejected the native plugin bundle" >&2
|
|
69
|
+
echo " Output: $VALIDATE_OUT" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
echo "[${COMPONENT}] Registering native plugin with qodercli..."
|
|
75
|
+
if ! INSTALL_OUT="$("$QODERCLI" plugins install "$PLUGIN_DIR" --scope user 2>&1)"; then
|
|
76
|
+
echo "[${COMPONENT}] ERROR: qodercli plugins install failed" >&2
|
|
77
|
+
echo " Output: $INSTALL_OUT" >&2
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
[ -z "$INSTALL_OUT" ] || echo "$INSTALL_OUT"
|
|
81
|
+
|
|
82
|
+
if ! PLUGIN_LIST_OUT="$("$QODERCLI" plugins list --json)"; then
|
|
83
|
+
echo "[${COMPONENT}] ERROR: qodercli plugins list failed after installation" >&2
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
if ! QODER_PLUGIN_LIST="$PLUGIN_LIST_OUT" python3 - <<'PYEOF'
|
|
88
|
+
import json
|
|
89
|
+
import os
|
|
90
|
+
import sys
|
|
91
|
+
|
|
92
|
+
expected_id = "tokenless@local"
|
|
93
|
+
try:
|
|
94
|
+
inventory = json.loads(os.environ["QODER_PLUGIN_LIST"])
|
|
95
|
+
except json.JSONDecodeError as error:
|
|
96
|
+
print(f"invalid JSON from qodercli plugins list: {error}", file=sys.stderr)
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
|
|
99
|
+
if isinstance(inventory, dict):
|
|
100
|
+
inventory = inventory.get("plugins")
|
|
101
|
+
if not isinstance(inventory, list):
|
|
102
|
+
print("qodercli plugin inventory is not a list", file=sys.stderr)
|
|
103
|
+
sys.exit(1)
|
|
104
|
+
|
|
105
|
+
matches = [
|
|
106
|
+
item
|
|
107
|
+
for item in inventory
|
|
108
|
+
if isinstance(item, dict) and item.get("id") == expected_id
|
|
109
|
+
]
|
|
110
|
+
if len(matches) != 1:
|
|
111
|
+
print(
|
|
112
|
+
f"expected exactly one {expected_id} inventory entry, found {len(matches)}",
|
|
113
|
+
file=sys.stderr,
|
|
114
|
+
)
|
|
115
|
+
sys.exit(1)
|
|
116
|
+
|
|
117
|
+
plugin = matches[0]
|
|
118
|
+
if plugin.get("scope") != "user":
|
|
119
|
+
print(f"{expected_id} has unexpected scope: {plugin.get('scope')!r}", file=sys.stderr)
|
|
120
|
+
sys.exit(1)
|
|
121
|
+
if plugin.get("enabled") is not True:
|
|
122
|
+
print(f"{expected_id} is not enabled", file=sys.stderr)
|
|
123
|
+
sys.exit(1)
|
|
124
|
+
|
|
125
|
+
resources = plugin.get("resources")
|
|
126
|
+
hooks = resources.get("hooks") if isinstance(resources, dict) else None
|
|
127
|
+
if not isinstance(hooks, list) or not hooks:
|
|
128
|
+
print(f"{expected_id} loaded no hook resources", file=sys.stderr)
|
|
129
|
+
sys.exit(1)
|
|
130
|
+
PYEOF
|
|
131
|
+
then
|
|
132
|
+
echo "[${COMPONENT}] ERROR: installed Qoder plugin failed inventory verification" >&2
|
|
133
|
+
exit 1
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
echo "[${COMPONENT}] ${AGENT} plugin v${VERSION} installed and activated."
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# uninstall.sh — Remove the native Tokenless plugin from Qoder CLI.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
AGENT="${ANOLISA_TARGET:-qoder}"
|
|
6
|
+
COMPONENT="${ANOLISA_COMPONENT:-tokenless}"
|
|
7
|
+
|
|
8
|
+
find_qodercli() {
|
|
9
|
+
local versioned_glob latest_versioned candidate
|
|
10
|
+
versioned_glob="$HOME/.qoder/bin/qodercli/qodercli-${ANOLISA_QODER_VERSION:-*}"
|
|
11
|
+
# shellcheck disable=SC2086 # Intentional version glob expansion.
|
|
12
|
+
latest_versioned="$(ls -d $versioned_glob 2>/dev/null | sort -V | tail -1 || true)"
|
|
13
|
+
|
|
14
|
+
for candidate in "$latest_versioned" \
|
|
15
|
+
"$HOME/.qoder/bin/qodercli/qodercli" \
|
|
16
|
+
"qodercli"; do
|
|
17
|
+
[ -n "$candidate" ] || continue
|
|
18
|
+
if [ -x "$candidate" ] || command -v "$candidate" >/dev/null 2>&1; then
|
|
19
|
+
printf '%s\n' "$candidate"
|
|
20
|
+
return 0
|
|
21
|
+
fi
|
|
22
|
+
done
|
|
23
|
+
return 1
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
QODERCLI="$(find_qodercli || true)"
|
|
27
|
+
echo "[${COMPONENT}] Removing ${AGENT} plugin..."
|
|
28
|
+
|
|
29
|
+
if [ -z "$QODERCLI" ]; then
|
|
30
|
+
echo "[${COMPONENT}] WARNING: qodercli not found, cannot unregister plugin" >&2
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
for subcommand in list uninstall; do
|
|
34
|
+
if ! "$QODERCLI" plugins "$subcommand" --help >/dev/null 2>&1; then
|
|
35
|
+
echo "[${COMPONENT}] ERROR: qodercli lacks required 'plugins ${subcommand}' support" >&2
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
done
|
|
39
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
40
|
+
echo "[${COMPONENT}] ERROR: python3 is required to verify Qoder plugin state" >&2
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
read_plugin_state() {
|
|
45
|
+
local plugin_list_out
|
|
46
|
+
if ! plugin_list_out="$("$QODERCLI" plugins list --json 2>&1)"; then
|
|
47
|
+
echo "[${COMPONENT}] ERROR: qodercli plugins list failed during uninstall" >&2
|
|
48
|
+
echo " Output: $plugin_list_out" >&2
|
|
49
|
+
return 1
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
QODER_PLUGIN_LIST="$plugin_list_out" python3 - <<'PYEOF'
|
|
53
|
+
import json
|
|
54
|
+
import os
|
|
55
|
+
import sys
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
inventory = json.loads(os.environ["QODER_PLUGIN_LIST"])
|
|
59
|
+
except json.JSONDecodeError as error:
|
|
60
|
+
print(f"invalid JSON from qodercli plugins list: {error}", file=sys.stderr)
|
|
61
|
+
sys.exit(1)
|
|
62
|
+
|
|
63
|
+
if isinstance(inventory, dict):
|
|
64
|
+
inventory = inventory.get("plugins")
|
|
65
|
+
if not isinstance(inventory, list):
|
|
66
|
+
print("qodercli plugin inventory is not a list", file=sys.stderr)
|
|
67
|
+
sys.exit(1)
|
|
68
|
+
|
|
69
|
+
matches = [
|
|
70
|
+
item
|
|
71
|
+
for item in inventory
|
|
72
|
+
if isinstance(item, dict)
|
|
73
|
+
and item.get("id") == "tokenless@local"
|
|
74
|
+
and item.get("scope") == "user"
|
|
75
|
+
]
|
|
76
|
+
if len(matches) > 1:
|
|
77
|
+
print("multiple user-scoped tokenless@local entries found", file=sys.stderr)
|
|
78
|
+
sys.exit(1)
|
|
79
|
+
print("present" if matches else "absent")
|
|
80
|
+
PYEOF
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if ! PLUGIN_STATE="$(read_plugin_state)"; then
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
if [ "$PLUGIN_STATE" = "absent" ]; then
|
|
87
|
+
echo "[${COMPONENT}] ${AGENT} plugin is already absent."
|
|
88
|
+
exit 0
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
if ! UNINSTALL_OUT="$("$QODERCLI" plugins uninstall tokenless --scope user 2>&1)"; then
|
|
92
|
+
echo "[${COMPONENT}] ERROR: qodercli plugins uninstall failed" >&2
|
|
93
|
+
echo " Output: $UNINSTALL_OUT" >&2
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
[ -z "$UNINSTALL_OUT" ] || echo "$UNINSTALL_OUT"
|
|
97
|
+
|
|
98
|
+
if ! PLUGIN_STATE="$(read_plugin_state)"; then
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
if [ "$PLUGIN_STATE" != "absent" ]; then
|
|
102
|
+
echo "[${COMPONENT}] ERROR: user-scoped tokenless plugin remains after uninstall" >&2
|
|
103
|
+
exit 1
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
echo "[${COMPONENT}] ${AGENT} plugin removed."
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tokenless",
|
|
3
|
+
"version": "0.7.7",
|
|
4
|
+
"description": "Token-Less context compression for Qwen Code — RTK command rewriting, response/TOON/schema compression, and registered but hard-disabled Tool Ready",
|
|
5
|
+
"author": { "name": "ANOLISA" },
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"homepage": "https://github.com/alibaba/anolisa/tree/main/src/tokenless",
|
|
8
|
+
"keywords": ["token-optimization", "rtk", "compression", "toon", "tool-ready"],
|
|
9
|
+
"hooks": {
|
|
10
|
+
"PreToolUse": [
|
|
11
|
+
{
|
|
12
|
+
"matcher": "",
|
|
13
|
+
"sequential": true,
|
|
14
|
+
"hooks": [
|
|
15
|
+
{
|
|
16
|
+
"type": "command",
|
|
17
|
+
"name": "tokenless-tool-ready",
|
|
18
|
+
"description": "Registered hard-disabled Tool Ready pass-through; performs no check, repair, or block",
|
|
19
|
+
"command": "bash \"${extensionPath}/hooks/run-hook.sh\" tool_ready_hook.sh",
|
|
20
|
+
"timeout": 10000,
|
|
21
|
+
"env": { "TOKENLESS_AGENT_ID": "qwencode" }
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"matcher": "^(Bash|run_shell_command|terminal|Shell|exec|process)$",
|
|
27
|
+
"hooks": [
|
|
28
|
+
{
|
|
29
|
+
"type": "command",
|
|
30
|
+
"name": "tokenless-rewrite",
|
|
31
|
+
"description": "Rewrites shell commands via rtk for token savings",
|
|
32
|
+
"command": "bash \"${extensionPath}/hooks/run-hook.sh\" rewrite_hook.py",
|
|
33
|
+
"timeout": 5000,
|
|
34
|
+
"env": { "TOKENLESS_AGENT_ID": "qwencode" }
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"PostToolUse": [
|
|
40
|
+
{
|
|
41
|
+
"matcher": "^(?!(?:Read|Glob|NotebookRead)$).+",
|
|
42
|
+
"hooks": [
|
|
43
|
+
{
|
|
44
|
+
"type": "command",
|
|
45
|
+
"name": "tokenless-compress-response",
|
|
46
|
+
"description": "Compresses tool responses and encodes to TOON format",
|
|
47
|
+
"command": "bash \"${extensionPath}/hooks/run-hook.sh\" compress_response_hook.py",
|
|
48
|
+
"timeout": 10000,
|
|
49
|
+
"env": { "TOKENLESS_AGENT_ID": "qwencode" }
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"BeforeModel": [
|
|
55
|
+
{
|
|
56
|
+
"hooks": [
|
|
57
|
+
{
|
|
58
|
+
"type": "command",
|
|
59
|
+
"name": "tokenless-compress-schema",
|
|
60
|
+
"description": "Compresses tool schema definitions for token savings",
|
|
61
|
+
"command": "bash \"${extensionPath}/hooks/run-hook.sh\" compress_schema_hook.py",
|
|
62
|
+
"timeout": 10000,
|
|
63
|
+
"env": { "TOKENLESS_AGENT_ID": "qwencode" }
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# detect.sh — Inspect Qwen Code presence and the tokenless plugin state.
|
|
3
|
+
# Read-only. Tri-state exit aligns with claude-code/openclaw detect.sh:
|
|
4
|
+
# 0 = installed and ready
|
|
5
|
+
# 1 = not installed but installable (prereqs OK)
|
|
6
|
+
# 2 = missing prerequisites
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
COMPONENT="${ANOLISA_COMPONENT:-tokenless}"
|
|
10
|
+
AGENT="${ANOLISA_TARGET:-qwencode}"
|
|
11
|
+
ADAPTER_DIR="${ANOLISA_ADAPTER_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}"
|
|
12
|
+
|
|
13
|
+
PLUGIN_ID="tokenless"
|
|
14
|
+
PLUGIN_SRC="$ADAPTER_DIR/qwencode"
|
|
15
|
+
|
|
16
|
+
QWEN_BIN="${QWEN_BIN:-}"
|
|
17
|
+
export PATH="$HOME/.local/bin:/usr/local/bin:$PATH"
|
|
18
|
+
|
|
19
|
+
line() { printf '[%s] %s\n' "$COMPONENT" "$*"; }
|
|
20
|
+
field() { printf '[%s] %-26s %s\n' "$COMPONENT" "$1" "$2"; }
|
|
21
|
+
|
|
22
|
+
PREREQ_MISSING=()
|
|
23
|
+
INSTALL_MISSING=()
|
|
24
|
+
note_prereq_missing() { PREREQ_MISSING+=("$1"); }
|
|
25
|
+
note_install_missing() { INSTALL_MISSING+=("$1"); }
|
|
26
|
+
|
|
27
|
+
if [ -z "$QWEN_BIN" ]; then
|
|
28
|
+
QWEN_BIN="$(command -v qwen 2>/dev/null || true)"
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
line "${AGENT} detect"
|
|
32
|
+
if [ -n "$QWEN_BIN" ] && [ -x "$QWEN_BIN" ]; then
|
|
33
|
+
QWEN_VER="$("$QWEN_BIN" --version 2>/dev/null | awk '{print $1}' || echo unknown)"
|
|
34
|
+
field "qwen CLI" "present (${QWEN_BIN}, v${QWEN_VER})"
|
|
35
|
+
else
|
|
36
|
+
field "qwen CLI" "missing"
|
|
37
|
+
note_prereq_missing "qwen CLI"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Informational only: Qwen Code stores global settings, extensions, and
|
|
41
|
+
# session data under ~/.qwen/ (upstream default). Absence is not a
|
|
42
|
+
# prerequisite failure — the directory is created on first run.
|
|
43
|
+
if [ -d "$HOME/.qwen" ]; then
|
|
44
|
+
field "qwen config dir" "present ($HOME/.qwen)"
|
|
45
|
+
else
|
|
46
|
+
field "qwen config dir" "missing (created on first Qwen Code run)"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
if [ -f "$PLUGIN_SRC/qwen-extension.json" ]; then
|
|
50
|
+
field "qwen-extension.json" "present"
|
|
51
|
+
else
|
|
52
|
+
field "qwen-extension.json" "missing"
|
|
53
|
+
note_prereq_missing "qwen-extension.json"
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
if [ -n "$QWEN_BIN" ] && [ -x "$QWEN_BIN" ]; then
|
|
57
|
+
if "$QWEN_BIN" extensions list 2>/dev/null | grep -qE "(^|[[:space:]])${PLUGIN_ID}([[:space:]]|$)"; then
|
|
58
|
+
field "plugin install" "installed (${PLUGIN_ID})"
|
|
59
|
+
else
|
|
60
|
+
field "plugin install" "not installed"
|
|
61
|
+
note_install_missing "${PLUGIN_ID}"
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
if command -v python3 &>/dev/null; then
|
|
66
|
+
field "python3" "present ($(command -v python3))"
|
|
67
|
+
else
|
|
68
|
+
field "python3" "missing"
|
|
69
|
+
note_prereq_missing "python3"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
if command -v jq &>/dev/null; then
|
|
73
|
+
field "jq" "present ($(command -v jq))"
|
|
74
|
+
else
|
|
75
|
+
field "jq" "missing (tool-ready hook disabled)"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
runtime_bin="$(command -v tokenless 2>/dev/null || true)"
|
|
79
|
+
if [ -n "$runtime_bin" ]; then
|
|
80
|
+
field "tokenless binary" "present (${runtime_bin})"
|
|
81
|
+
else
|
|
82
|
+
field "tokenless binary" "missing"
|
|
83
|
+
note_prereq_missing "tokenless binary"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
rtk_bin="$(command -v rtk 2>/dev/null || true)"
|
|
87
|
+
if [ -n "$rtk_bin" ]; then
|
|
88
|
+
field "rtk binary" "present (${rtk_bin})"
|
|
89
|
+
else
|
|
90
|
+
field "rtk binary" "missing"
|
|
91
|
+
note_prereq_missing "rtk binary"
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# Shared hook scripts live under FHS; warn when missing so user knows to run
|
|
95
|
+
# `make install` (or install the RPM) before adapter actually fires.
|
|
96
|
+
SHARED_HOOKS_DIR=""
|
|
97
|
+
for d in /usr/local/share/anolisa/adapters/tokenless/common/hooks \
|
|
98
|
+
/usr/share/anolisa/adapters/tokenless/common/hooks \
|
|
99
|
+
"$HOME/.local/share/anolisa/adapters/tokenless/common/hooks"; do
|
|
100
|
+
if [ -d "$d" ]; then SHARED_HOOKS_DIR="$d"; break; fi
|
|
101
|
+
done
|
|
102
|
+
if [ -n "$SHARED_HOOKS_DIR" ]; then
|
|
103
|
+
field "shared hooks dir" "present ($SHARED_HOOKS_DIR)"
|
|
104
|
+
else
|
|
105
|
+
field "shared hooks dir" "missing (run: make -C src/tokenless install)"
|
|
106
|
+
note_prereq_missing "shared hooks dir"
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
if [ -f "$PLUGIN_SRC/hooks/run-hook.sh" ]; then
|
|
110
|
+
field "hook dispatcher" "present"
|
|
111
|
+
else
|
|
112
|
+
field "hook dispatcher" "missing (hooks/run-hook.sh)"
|
|
113
|
+
note_prereq_missing "hook dispatcher"
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
if [ ${#PREREQ_MISSING[@]} -gt 0 ]; then
|
|
117
|
+
line "${AGENT}: missing prerequisites (${PREREQ_MISSING[*]})"
|
|
118
|
+
exit 2
|
|
119
|
+
fi
|
|
120
|
+
if [ ${#INSTALL_MISSING[@]} -gt 0 ]; then
|
|
121
|
+
line "${AGENT}: not installed (ready to install)"
|
|
122
|
+
exit 1
|
|
123
|
+
fi
|
|
124
|
+
line "${AGENT}: ready"
|
|
125
|
+
exit 0
|