@keychat-io/keychat 0.1.18

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.
@@ -0,0 +1,180 @@
1
+ #!/bin/bash
2
+ # Keychat — one-line installer
3
+ # Usage: curl -fsSL https://raw.githubusercontent.com/keychat-io/keychat-openclaw/main/scripts/install.sh | bash
4
+ set -e
5
+
6
+ REPO="keychat-io/keychat-openclaw"
7
+ INSTALL_DIR="${OPENCLAW_EXTENSIONS:-$HOME/.openclaw/extensions}/keychat"
8
+ BINARY="$INSTALL_DIR/bridge/target/release/keychat-bridge"
9
+
10
+ echo "🔑 Installing Keychat"
11
+ echo ""
12
+
13
+ # ── Check OpenClaw ──
14
+ if ! command -v openclaw &>/dev/null; then
15
+ echo "❌ OpenClaw not found. Install it first: https://docs.openclaw.ai"
16
+ exit 1
17
+ fi
18
+
19
+ # ── Detect platform ──
20
+ detect_artifact() {
21
+ local arch=$(uname -m)
22
+ local os=$(uname -s | tr '[:upper:]' '[:lower:]')
23
+ case "$os-$arch" in
24
+ darwin-arm64) echo "keychat-openclaw-darwin-arm64" ;;
25
+ darwin-x86_64) echo "keychat-openclaw-darwin-x64" ;;
26
+ linux-x86_64) echo "keychat-openclaw-linux-x64" ;;
27
+ linux-aarch64) echo "keychat-openclaw-linux-arm64" ;;
28
+ *) echo "" ;;
29
+ esac
30
+ }
31
+
32
+ # ── Spinner helper ──
33
+ spinner() {
34
+ local pid=$1
35
+ local msg=$2
36
+ local chars='⣾⣽⣻⢿⡿⣟⣯⣷'
37
+ local i=0
38
+ while kill -0 "$pid" 2>/dev/null; do
39
+ printf "\r %s %s" "${chars:i++%${#chars}:1}" "$msg"
40
+ sleep 0.1
41
+ done
42
+ printf "\r"
43
+ }
44
+
45
+ # ── Clone or update repo ──
46
+ if [ -d "$INSTALL_DIR/.git" ]; then
47
+ echo "📦 Updating existing installation..."
48
+ cd "$INSTALL_DIR"
49
+ git pull --ff-only 2>/dev/null || true
50
+ else
51
+ echo "📦 Downloading Keychat source..."
52
+ mkdir -p "$(dirname "$INSTALL_DIR")"
53
+ git clone --depth 1 "https://github.com/$REPO.git" "$INSTALL_DIR" 2>/dev/null &
54
+ CLONE_PID=$!
55
+ spinner $CLONE_PID "Cloning repository..."
56
+ wait $CLONE_PID
57
+ echo " ✅ Source downloaded"
58
+ cd "$INSTALL_DIR"
59
+ fi
60
+
61
+ # ── Install npm dependencies ──
62
+ echo "📦 Installing dependencies..."
63
+ npm install --omit=dev --silent 2>/dev/null &
64
+ NPM_PID=$!
65
+ spinner $NPM_PID "Installing npm packages (this may take a moment)..."
66
+ wait $NPM_PID || true
67
+ echo " ✅ Dependencies installed"
68
+
69
+ # ── Get binary ──
70
+ if [ -f "$BINARY" ]; then
71
+ echo "✅ Bridge binary already exists"
72
+ else
73
+ ARTIFACT=$(detect_artifact)
74
+ DOWNLOADED=false
75
+
76
+ if [ -n "$ARTIFACT" ]; then
77
+ echo "📦 Downloading bridge binary ($ARTIFACT)..."
78
+ echo " ⏳ This may take 30-60 seconds depending on your connection..."
79
+ URL="https://github.com/$REPO/releases/latest/download/$ARTIFACT"
80
+ mkdir -p "$(dirname "$BINARY")"
81
+ if curl -fSL --progress-bar "$URL" -o "$BINARY" 2>&1; then
82
+ chmod +x "$BINARY"
83
+ echo " ✅ Binary downloaded"
84
+ DOWNLOADED=true
85
+ else
86
+ echo " ⚠️ Download failed, will try building from source..."
87
+ fi
88
+ fi
89
+
90
+ if [ "$DOWNLOADED" = false ]; then
91
+ if command -v cargo &>/dev/null; then
92
+ echo "🔨 Building from source..."
93
+ echo " ⏳ This takes 3-5 minutes on most machines. Please wait..."
94
+ cd "$INSTALL_DIR/bridge"
95
+ cargo build --release 2>&1 &
96
+ BUILD_PID=$!
97
+ spinner $BUILD_PID "Compiling Rust code (be patient)..."
98
+ wait $BUILD_PID
99
+ cd "$INSTALL_DIR"
100
+ if [ ! -f "$BINARY" ]; then
101
+ echo " ❌ Build failed"
102
+ exit 1
103
+ fi
104
+ echo " ✅ Built from source"
105
+ else
106
+ echo "❌ No pre-compiled binary for your platform and Rust not installed."
107
+ echo " Install Rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
108
+ exit 1
109
+ fi
110
+ fi
111
+ fi
112
+
113
+ # ── Register plugin ──
114
+ echo ""
115
+ echo "📦 Registering plugin..."
116
+ openclaw plugins install "$INSTALL_DIR" 2>&1 || true
117
+
118
+ # ── Auto-configure ──
119
+ CONFIG_FILE="$HOME/.openclaw/openclaw.json"
120
+ if [ -f "$CONFIG_FILE" ]; then
121
+ if grep -q '"keychat"' "$CONFIG_FILE" 2>/dev/null; then
122
+ echo "ℹ️ Keychat already in config"
123
+ else
124
+ if command -v python3 &>/dev/null; then
125
+ python3 -c "
126
+ import json, sys
127
+ try:
128
+ with open('$CONFIG_FILE', 'r') as f:
129
+ cfg = json.load(f)
130
+ if 'channels' not in cfg:
131
+ cfg['channels'] = {}
132
+ cfg['channels']['keychat'] = {'enabled': True}
133
+ with open('$CONFIG_FILE', 'w') as f:
134
+ json.dump(cfg, f, indent=2, ensure_ascii=False)
135
+ print(' ✅ Keychat enabled in config')
136
+ except Exception as e:
137
+ print(f' ⚠️ Could not auto-configure: {e}')
138
+ print(' Add manually: \"keychat\": {{\"enabled\": true}} under channels')
139
+ "
140
+ elif command -v node &>/dev/null; then
141
+ node -e "
142
+ const fs = require('fs');
143
+ try {
144
+ const cfg = JSON.parse(fs.readFileSync('$CONFIG_FILE', 'utf8'));
145
+ if (!cfg.channels) cfg.channels = {};
146
+ cfg.channels.keychat = { enabled: true };
147
+ fs.writeFileSync('$CONFIG_FILE', JSON.stringify(cfg, null, 2));
148
+ console.log(' ✅ Keychat enabled in config');
149
+ } catch(e) {
150
+ console.log(' ⚠️ Could not auto-configure:', e.message);
151
+ console.log(' Add manually: \"keychat\": {\"enabled\": true} under channels');
152
+ }
153
+ "
154
+ else
155
+ echo "⚠️ Add to $CONFIG_FILE under \"channels\":"
156
+ echo ' "keychat": { "enabled": true }'
157
+ fi
158
+ fi
159
+ else
160
+ echo "⚠️ Config not found at $CONFIG_FILE"
161
+ echo " Run 'openclaw init' first, then re-run this installer"
162
+ fi
163
+
164
+ # ── Restart gateway ──
165
+ echo ""
166
+ echo "🔄 Restarting gateway..."
167
+ openclaw gateway restart 2>&1 || true
168
+
169
+ # ── Done ──
170
+ echo ""
171
+ echo "════════════════════════════════════════"
172
+ echo " 🎉 Keychat installed successfully!"
173
+ echo "════════════════════════════════════════"
174
+ echo ""
175
+ echo "Your agent's Keychat ID will appear in the gateway logs."
176
+ echo "Run 'openclaw status' to see it."
177
+ echo ""
178
+ echo "To connect: open the Keychat app and scan the QR code."
179
+ echo ""
180
+ echo "Docs: https://github.com/$REPO"
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * postinstall — download pre-compiled keychat-bridge binary.
4
+ * Runs automatically after `npm install` / `openclaw plugins install`.
5
+ * Uses native fetch/https — no child_process dependency.
6
+ */
7
+ import { existsSync, mkdirSync, chmodSync, writeFileSync, readFileSync, rmSync } from "node:fs";
8
+ import { join, dirname } from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+ import https from "node:https";
11
+
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const REPO = "keychat-io/keychat-openclaw";
14
+ const BINARY_DIR = join(__dirname, "..", "bridge", "target", "release");
15
+ const BINARY_PATH = join(BINARY_DIR, "keychat-bridge");
16
+
17
+ import { statSync } from "node:fs";
18
+
19
+ // Read expected version from package.json
20
+ const pkgPath = join(__dirname, "..", "package.json");
21
+ const pkgVersion = JSON.parse(readFileSync(pkgPath, "utf-8")).version;
22
+ const versionFile = join(BINARY_DIR, ".version");
23
+
24
+ const currentVersion = existsSync(versionFile)
25
+ ? readFileSync(versionFile, "utf-8").trim()
26
+ : null;
27
+
28
+ // Clean up conflicting script-installed copy (extensions/keychat vs extensions/keychat-openclaw)
29
+ const pluginDir = join(__dirname, "..");
30
+ const pluginDirName = pluginDir.split("/").pop();
31
+ const scriptInstallDir = join(pluginDir, "..", "keychat");
32
+ if (pluginDirName === "keychat-bridge" && existsSync(scriptInstallDir)) {
33
+ console.log(`[keychat] Removing conflicting script-installed copy...`);
34
+ try { rmSync(scriptInstallDir, { recursive: true, force: true }); } catch {}
35
+ }
36
+
37
+ if (existsSync(BINARY_PATH) && currentVersion === pkgVersion) {
38
+ console.log(`[keychat] Binary already exists (v${pkgVersion}), skipping download`);
39
+ process.exit(0);
40
+ }
41
+
42
+ if (existsSync(BINARY_PATH)) {
43
+ console.log(`[keychat] Binary exists but version mismatch (${currentVersion || "unknown"} → ${pkgVersion}), re-downloading...`);
44
+ }
45
+
46
+ const platform = process.platform; // darwin, linux
47
+ const arch = process.arch; // arm64, x64
48
+
49
+ const ARTIFACTS = {
50
+ "darwin-arm64": "keychat-openclaw-darwin-arm64",
51
+ "darwin-x64": "keychat-openclaw-darwin-x64",
52
+ "linux-x64": "keychat-openclaw-linux-x64",
53
+ "linux-arm64": "keychat-openclaw-linux-arm64",
54
+ };
55
+
56
+ const artifact = ARTIFACTS[`${platform}-${arch}`];
57
+ if (!artifact) {
58
+ console.warn(`[keychat] No pre-compiled binary for ${platform}-${arch}`);
59
+ console.warn("[keychat] Build from source: cd bridge && cargo build --release");
60
+ process.exit(0); // Don't fail install
61
+ }
62
+
63
+ const url = `https://github.com/${REPO}/releases/latest/download/${artifact}`;
64
+ console.log(`[keychat] Downloading ${artifact}...`);
65
+
66
+ /**
67
+ * Download a URL following redirects, return a Buffer.
68
+ */
69
+ function download(downloadUrl) {
70
+ return new Promise((resolve, reject) => {
71
+ https.get(downloadUrl, (res) => {
72
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
73
+ return download(res.headers.location).then(resolve, reject);
74
+ }
75
+ if (res.statusCode !== 200) {
76
+ return reject(new Error(`HTTP ${res.statusCode}`));
77
+ }
78
+ const chunks = [];
79
+ res.on("data", (chunk) => chunks.push(chunk));
80
+ res.on("end", () => resolve(Buffer.concat(chunks)));
81
+ res.on("error", reject);
82
+ }).on("error", reject);
83
+ });
84
+ }
85
+
86
+ try {
87
+ mkdirSync(BINARY_DIR, { recursive: true });
88
+ const buffer = await download(url);
89
+ writeFileSync(BINARY_PATH, buffer);
90
+ chmodSync(BINARY_PATH, 0o755);
91
+ writeFileSync(versionFile, pkgVersion + "\n");
92
+ console.log(`[keychat] ✅ Binary installed (v${pkgVersion})`);
93
+ } catch (err) {
94
+ console.warn(`[keychat] Download failed: ${err.message}`);
95
+ console.warn("[keychat] Build from source: cd bridge && cargo build --release");
96
+ // Don't fail install — user can build manually
97
+ }
98
+
99
+ // Auto-initialize config if channels["keychat"] not set
100
+ import { homedir } from "node:os";
101
+
102
+ const configPath = join(homedir(), ".openclaw", "openclaw.json");
103
+ try {
104
+ let config = {};
105
+ if (existsSync(configPath)) {
106
+ config = JSON.parse(readFileSync(configPath, "utf-8"));
107
+ }
108
+
109
+ if (config.channels?.["keychat"] || config.channels?.keychat) {
110
+ console.log("[keychat] Config already contains keychat settings, skipping init");
111
+ // Migrate old channels.keychat → channels.keychat
112
+ if (config.channels?.keychat && !config.channels?.["keychat"]) {
113
+ config.channels["keychat"] = config.channels.keychat;
114
+ delete config.channels.keychat;
115
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
116
+ console.log("[keychat] ✅ Migrated channels.keychat → channels.keychat");
117
+ }
118
+ } else {
119
+ if (!config.channels) config.channels = {};
120
+ config.channels["keychat"] = { enabled: true, dmPolicy: "open" };
121
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
122
+ console.log('[keychat] ✅ Config initialized (channels.keychat.enabled = true)');
123
+ console.log("[keychat] Restart gateway to activate: openclaw gateway restart");
124
+ }
125
+ } catch (err) {
126
+ console.warn(`[keychat] Could not auto-configure: ${err.message}`);
127
+ console.warn('[keychat] Run manually: openclaw config set channels.keychat.enabled true');
128
+ }
@@ -0,0 +1,25 @@
1
+ #!/bin/bash
2
+ # Publish @keychat-io/keychat to npm
3
+ # Usage: ./scripts/publish.sh [--dry-run]
4
+
5
+ set -e
6
+ cd "$(dirname "$0")/.."
7
+
8
+ # Load .env
9
+ if [ -f .env ]; then
10
+ export $(grep -v '^#' .env | xargs)
11
+ fi
12
+
13
+ if [ -z "$NPM_TOKEN" ]; then
14
+ echo "❌ NPM_TOKEN not set. Create .env with: NPM_TOKEN=npm_xxxxx"
15
+ exit 1
16
+ fi
17
+
18
+ # Set token for this publish
19
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc.tmp
20
+
21
+ echo "📦 Publishing @keychat-io/keychat v$(node -p "require('./package.json').version")..."
22
+ npm publish --access public --tag latest --userconfig .npmrc.tmp "$@"
23
+
24
+ rm -f .npmrc.tmp
25
+ echo "✅ Published!"
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env bash
2
+ # Keychat OpenClaw plugin setup
3
+ # Downloads the bridge binary and initializes config.
4
+ # Run after: openclaw plugins install @keychat-io/keychat
5
+ set -euo pipefail
6
+
7
+ # --- Locate plugin directory ---
8
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
9
+ PLUGIN_DIR="$(dirname "$SCRIPT_DIR")"
10
+
11
+ # Also check common install locations
12
+ if [ ! -f "$PLUGIN_DIR/package.json" ]; then
13
+ for d in \
14
+ "$HOME/.openclaw/extensions/keychat" \
15
+ "$HOME/.openclaw/plugins/@keychat-io/keychat"; do
16
+ if [ -f "$d/package.json" ]; then
17
+ PLUGIN_DIR="$d"
18
+ break
19
+ fi
20
+ done
21
+ fi
22
+
23
+ BINARY_DIR="$PLUGIN_DIR/bridge/target/release"
24
+ BINARY_PATH="$BINARY_DIR/keychat-bridge"
25
+
26
+ # --- Download binary ---
27
+ if [ -f "$BINARY_PATH" ]; then
28
+ echo "[keychat] ✅ Binary already exists: $BINARY_PATH"
29
+ else
30
+ REPO="keychat-io/keychat-openclaw"
31
+ OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
32
+ ARCH="$(uname -m)"
33
+
34
+ case "$OS-$ARCH" in
35
+ darwin-arm64) ARTIFACT="keychat-openclaw-darwin-arm64" ;;
36
+ darwin-x86_64) ARTIFACT="keychat-openclaw-darwin-x64" ;;
37
+ linux-x86_64) ARTIFACT="keychat-openclaw-linux-x64" ;;
38
+ linux-aarch64) ARTIFACT="keychat-openclaw-linux-arm64" ;;
39
+ *)
40
+ echo "[keychat] ❌ No pre-compiled binary for $OS-$ARCH"
41
+ echo "[keychat] Build from source: cd $PLUGIN_DIR/bridge && cargo build --release"
42
+ exit 1
43
+ ;;
44
+ esac
45
+
46
+ URL="https://github.com/$REPO/releases/latest/download/$ARTIFACT"
47
+ echo "[keychat] Downloading $ARTIFACT..."
48
+ mkdir -p "$BINARY_DIR"
49
+ curl -fsSL -o "$BINARY_PATH" "$URL"
50
+ chmod +x "$BINARY_PATH"
51
+ echo "[keychat] ✅ Binary installed: $BINARY_PATH"
52
+ fi
53
+
54
+ # --- Initialize config ---
55
+ CONFIG_PATH="$HOME/.openclaw/openclaw.json"
56
+
57
+ if [ ! -f "$CONFIG_PATH" ]; then
58
+ echo '{}' > "$CONFIG_PATH"
59
+ fi
60
+
61
+ # Check if channels.keychat already exists
62
+ if node -e "
63
+ const c = JSON.parse(require('fs').readFileSync('$CONFIG_PATH','utf-8'));
64
+ process.exit(c.channels?.keychat ? 0 : 1);
65
+ " 2>/dev/null; then
66
+ echo "[keychat] ✅ Config already has channels.keychat"
67
+ else
68
+ node -e "
69
+ const fs = require('fs');
70
+ const c = JSON.parse(fs.readFileSync('$CONFIG_PATH','utf-8'));
71
+ if (!c.channels) c.channels = {};
72
+ c.channels.keychat = { enabled: true, dmPolicy: 'open' };
73
+ fs.writeFileSync('$CONFIG_PATH', JSON.stringify(c, null, 2) + '\n');
74
+ "
75
+ echo "[keychat] ✅ Config initialized (channels.keychat.enabled = true, dmPolicy = open)"
76
+ fi
77
+
78
+ echo ""
79
+ echo "[keychat] Setup complete! Restart gateway to activate:"
80
+ echo " openclaw gateway install && openclaw gateway start"