codex-to-im 0.1.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/LICENSE +21 -0
- package/README.md +163 -0
- package/README_CN.md +161 -0
- package/SECURITY.md +38 -0
- package/SKILL.md +79 -0
- package/config.env.example +106 -0
- package/dist/cli.mjs +182 -0
- package/dist/daemon.mjs +206122 -0
- package/dist/ui-server.mjs +7725 -0
- package/docs/codex-to-im-prd.md +424 -0
- package/docs/codex-to-im-shared-thread-design.md +572 -0
- package/docs/install-windows.md +287 -0
- package/package.json +55 -0
- package/references/setup-guides.md +329 -0
- package/references/token-validation.md +44 -0
- package/references/troubleshooting.md +88 -0
- package/references/usage.md +46 -0
- package/scripts/build.js +36 -0
- package/scripts/daemon.ps1 +16 -0
- package/scripts/daemon.sh +225 -0
- package/scripts/doctor.ps1 +27 -0
- package/scripts/doctor.sh +450 -0
- package/scripts/install-codex.sh +65 -0
- package/scripts/run-tests.js +44 -0
- package/scripts/supervisor-linux.sh +49 -0
- package/scripts/supervisor-macos.sh +154 -0
- package/scripts/supervisor-windows.ps1 +481 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Token Validation Commands
|
|
2
|
+
|
|
3
|
+
After writing config.env, validate each enabled platform's credentials to catch typos and configuration errors early.
|
|
4
|
+
|
|
5
|
+
## Telegram
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
curl -s "https://api.telegram.org/bot${TOKEN}/getMe"
|
|
9
|
+
```
|
|
10
|
+
Expected: response contains `"ok":true`. If not, the Bot Token is invalid — re-check with @BotFather.
|
|
11
|
+
|
|
12
|
+
## Discord
|
|
13
|
+
|
|
14
|
+
Verify token format matches: `[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+`
|
|
15
|
+
|
|
16
|
+
A format mismatch means the token was copied incorrectly from the Discord Developer Portal.
|
|
17
|
+
|
|
18
|
+
## Feishu / Lark
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
curl -s -X POST "${DOMAIN}/open-apis/auth/v3/tenant_access_token/internal" \
|
|
22
|
+
-H "Content-Type: application/json" \
|
|
23
|
+
-d '{"app_id":"...","app_secret":"..."}'
|
|
24
|
+
```
|
|
25
|
+
Expected: response contains `"code":0`. If not, check that App ID and App Secret match the Feishu Developer Console.
|
|
26
|
+
|
|
27
|
+
## QQ
|
|
28
|
+
|
|
29
|
+
Step 1 — Get access token:
|
|
30
|
+
```bash
|
|
31
|
+
curl -s -X POST "https://bots.qq.com/app/getAppAccessToken" \
|
|
32
|
+
-H "Content-Type: application/json" \
|
|
33
|
+
-d '{"appId":"...","clientSecret":"..."}'
|
|
34
|
+
```
|
|
35
|
+
Expected: response contains `access_token`.
|
|
36
|
+
|
|
37
|
+
Step 2 — Verify gateway connectivity:
|
|
38
|
+
```bash
|
|
39
|
+
curl -s "https://api.sgroup.qq.com/gateway" \
|
|
40
|
+
-H "Authorization: QQBot <access_token>"
|
|
41
|
+
```
|
|
42
|
+
Expected: response contains a gateway URL.
|
|
43
|
+
|
|
44
|
+
If either step fails, verify the App ID and App Secret from https://q.qq.com.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
## Bridge won't start
|
|
4
|
+
|
|
5
|
+
**Symptoms**: Starting the bridge from the local workbench fails, or the daemon exits immediately.
|
|
6
|
+
|
|
7
|
+
**Steps**:
|
|
8
|
+
|
|
9
|
+
1. Run the local doctor script or inspect the workbench logs to identify the issue
|
|
10
|
+
2. Check that Node.js >= 20 is installed: `node --version`
|
|
11
|
+
3. Check that Claude Code CLI is available: `claude --version`
|
|
12
|
+
4. Verify config exists: `ls -la ~/.codex-to-im/config.env`
|
|
13
|
+
5. Check logs for startup errors in `~/.codex-to-im/logs/`
|
|
14
|
+
|
|
15
|
+
**Common causes**:
|
|
16
|
+
- Missing or invalid config.env -- open the local workbench and save a valid configuration
|
|
17
|
+
- Node.js not found or wrong version -- install Node.js >= 20
|
|
18
|
+
- Port or resource conflict -- check if another bridge instance is already running
|
|
19
|
+
|
|
20
|
+
## Messages not received
|
|
21
|
+
|
|
22
|
+
**Symptoms**: Bot is online but doesn't respond to messages.
|
|
23
|
+
|
|
24
|
+
**Steps**:
|
|
25
|
+
|
|
26
|
+
1. Verify the bot token is valid with the local workbench test tools or doctor script
|
|
27
|
+
2. Check allowed user IDs in config -- if set, only listed users can interact
|
|
28
|
+
3. For Telegram: ensure you've sent `/start` to the bot first
|
|
29
|
+
4. For Discord: verify the bot has been invited to the server with message read permissions
|
|
30
|
+
5. For Feishu: confirm the app has been approved and event subscriptions are configured
|
|
31
|
+
6. Check recent logs for incoming message events under `~/.codex-to-im/logs/`
|
|
32
|
+
|
|
33
|
+
## Feishu streaming cards not working
|
|
34
|
+
|
|
35
|
+
**Symptoms**: Feishu responds only with a final message, or the streaming card never appears even though `Enable Feishu streaming response cards` is checked.
|
|
36
|
+
|
|
37
|
+
**Steps**:
|
|
38
|
+
|
|
39
|
+
1. Check `~/.codex-to-im/logs/bridge.log` or `~/.claude-to-im/logs/bridge.log`
|
|
40
|
+
2. Look for Feishu error `99991672`
|
|
41
|
+
3. If the error mentions `cardkit:card:write`, add and publish the missing Feishu permissions:
|
|
42
|
+
- `cardkit:card:write`
|
|
43
|
+
- `cardkit:card:read`
|
|
44
|
+
- `im:message:update`
|
|
45
|
+
4. Restart the bridge after the Feishu app version is approved
|
|
46
|
+
|
|
47
|
+
**Important behavior note**:
|
|
48
|
+
|
|
49
|
+
- If card creation is denied, the bridge falls back to a normal final-result message
|
|
50
|
+
- Even after the Feishu permissions are fixed, the current `codex` runtime usually delivers assistant text when the turn completes, not token-by-token
|
|
51
|
+
- That means Feishu streaming cards currently work best for `Thinking` / tool-progress updates, while the final response text may still appear all at once
|
|
52
|
+
|
|
53
|
+
## Permission timeout
|
|
54
|
+
|
|
55
|
+
**Symptoms**: Claude Code session starts but times out waiting for tool approval.
|
|
56
|
+
|
|
57
|
+
**Steps**:
|
|
58
|
+
|
|
59
|
+
1. The bridge runs Claude Code in non-interactive mode; ensure your Claude Code configuration allows the necessary tools
|
|
60
|
+
2. Consider using `--allowedTools` in your configuration to pre-approve common tools
|
|
61
|
+
3. Check network connectivity if the timeout occurs during API calls
|
|
62
|
+
|
|
63
|
+
## High memory usage
|
|
64
|
+
|
|
65
|
+
**Symptoms**: The daemon process consumes increasing memory over time.
|
|
66
|
+
|
|
67
|
+
**Steps**:
|
|
68
|
+
|
|
69
|
+
1. Check current bridge status from the local workbench
|
|
70
|
+
2. Restart the daemon to reset memory:
|
|
71
|
+
```
|
|
72
|
+
Stop the bridge, then start it again from the local workbench
|
|
73
|
+
```
|
|
74
|
+
3. If the issue persists, check how many concurrent sessions are active -- each Claude Code session consumes memory
|
|
75
|
+
4. Review logs for error loops that may cause memory leaks
|
|
76
|
+
|
|
77
|
+
## Stale PID file
|
|
78
|
+
|
|
79
|
+
**Symptoms**: Status shows "running" but the process doesn't exist, or start refuses because it thinks a daemon is already running.
|
|
80
|
+
|
|
81
|
+
The daemon management script (`daemon.sh`) handles stale PID files automatically. If you still encounter issues:
|
|
82
|
+
|
|
83
|
+
1. Stop the bridge from the local workbench — this should clean up the stale PID file
|
|
84
|
+
2. If stop also fails, manually remove the PID file:
|
|
85
|
+
```bash
|
|
86
|
+
rm ~/.codex-to-im/runtime/bridge.pid
|
|
87
|
+
```
|
|
88
|
+
3. Start the bridge again from the local workbench
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Optional Codex Integration Usage
|
|
2
|
+
|
|
3
|
+
This repository no longer uses a full bridge-management skill as its primary workflow.
|
|
4
|
+
|
|
5
|
+
The main product is the local `codex-to-im` app and web workbench.
|
|
6
|
+
|
|
7
|
+
`SKILL.md` is only an optional Codex integration entry with two actions:
|
|
8
|
+
|
|
9
|
+
- `open`
|
|
10
|
+
- `share-feishu`
|
|
11
|
+
|
|
12
|
+
## open
|
|
13
|
+
|
|
14
|
+
Open the local `codex-to-im` workbench.
|
|
15
|
+
|
|
16
|
+
Preferred command:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
codex-to-im open
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Fallback:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
node dist/cli.mjs open
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## share-feishu
|
|
29
|
+
|
|
30
|
+
Open the Feishu handoff flow for the current workflow.
|
|
31
|
+
|
|
32
|
+
Preferred command:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
codex-to-im share-feishu
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Fallback:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
node dist/cli.mjs share-feishu
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Non-goals
|
|
45
|
+
|
|
46
|
+
If the user asks to configure credentials, start or stop the bridge, inspect logs, or diagnose problems, do not route them through the optional Codex integration. Open the local app instead.
|
package/scripts/build.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as esbuild from 'esbuild';
|
|
2
|
+
|
|
3
|
+
const common = {
|
|
4
|
+
bundle: true,
|
|
5
|
+
platform: 'node',
|
|
6
|
+
format: 'esm',
|
|
7
|
+
target: 'node20',
|
|
8
|
+
external: [
|
|
9
|
+
// SDK must stay external — it spawns a CLI subprocess and resolves
|
|
10
|
+
// dist/cli.js relative to its own package location. Bundling it
|
|
11
|
+
// breaks that path resolution.
|
|
12
|
+
'@anthropic-ai/claude-agent-sdk',
|
|
13
|
+
'@openai/codex-sdk',
|
|
14
|
+
// discord.js optional native deps
|
|
15
|
+
'bufferutil', 'utf-8-validate', 'zlib-sync', 'erlpack',
|
|
16
|
+
// Node.js built-ins
|
|
17
|
+
'fs', 'path', 'os', 'crypto', 'http', 'https', 'net', 'tls',
|
|
18
|
+
'stream', 'events', 'url', 'util', 'child_process', 'worker_threads',
|
|
19
|
+
'node:*',
|
|
20
|
+
],
|
|
21
|
+
banner: { js: "import { createRequire } from 'module'; const require = createRequire(import.meta.url);" },
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
async function build(entryPoint, outfile) {
|
|
25
|
+
await esbuild.build({
|
|
26
|
+
...common,
|
|
27
|
+
entryPoints: [entryPoint],
|
|
28
|
+
outfile,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await build('src/main.ts', 'dist/daemon.mjs');
|
|
33
|
+
await build('src/ui-server.ts', 'dist/ui-server.mjs');
|
|
34
|
+
await build('src/cli.ts', 'dist/cli.mjs');
|
|
35
|
+
|
|
36
|
+
console.log('Built dist/daemon.mjs, dist/ui-server.mjs, dist/cli.mjs');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Windows entry point — delegates to supervisor-windows.ps1.
|
|
4
|
+
.DESCRIPTION
|
|
5
|
+
Usage: powershell -File scripts\daemon.ps1 start|stop|status|logs|install-service|uninstall-service
|
|
6
|
+
#>
|
|
7
|
+
param(
|
|
8
|
+
[Parameter(Position=0)]
|
|
9
|
+
[string]$Command = 'help',
|
|
10
|
+
|
|
11
|
+
[Parameter(Position=1)]
|
|
12
|
+
[int]$LogLines = 50
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
$supervisorScript = Join-Path (Split-Path -Parent $PSCommandPath) 'supervisor-windows.ps1'
|
|
16
|
+
& $supervisorScript $Command $LogLines
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
CTI_HOME="${CTI_HOME:-$HOME/.claude-to-im}"
|
|
4
|
+
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
5
|
+
PID_FILE="$CTI_HOME/runtime/bridge.pid"
|
|
6
|
+
STATUS_FILE="$CTI_HOME/runtime/status.json"
|
|
7
|
+
LOG_FILE="$CTI_HOME/logs/bridge.log"
|
|
8
|
+
|
|
9
|
+
# ── Common helpers ──
|
|
10
|
+
|
|
11
|
+
ensure_dirs() { mkdir -p "$CTI_HOME"/{data,logs,runtime,data/messages}; }
|
|
12
|
+
|
|
13
|
+
ensure_built() {
|
|
14
|
+
local need_build=0
|
|
15
|
+
if [ ! -f "$SKILL_DIR/dist/daemon.mjs" ]; then
|
|
16
|
+
need_build=1
|
|
17
|
+
else
|
|
18
|
+
# Check if any source file is newer than the bundle
|
|
19
|
+
local newest_src
|
|
20
|
+
newest_src=$(find "$SKILL_DIR/src" -name '*.ts' -newer "$SKILL_DIR/dist/daemon.mjs" 2>/dev/null | head -1)
|
|
21
|
+
if [ -n "$newest_src" ]; then
|
|
22
|
+
need_build=1
|
|
23
|
+
fi
|
|
24
|
+
# Also check if node_modules/claude-to-im was updated (npm update)
|
|
25
|
+
# — its code is bundled into dist, so changes require a rebuild
|
|
26
|
+
if [ "$need_build" = "0" ] && [ -d "$SKILL_DIR/node_modules/claude-to-im/src" ]; then
|
|
27
|
+
local newest_dep
|
|
28
|
+
newest_dep=$(find "$SKILL_DIR/node_modules/claude-to-im/src" -name '*.ts' -newer "$SKILL_DIR/dist/daemon.mjs" 2>/dev/null | head -1)
|
|
29
|
+
if [ -n "$newest_dep" ]; then
|
|
30
|
+
need_build=1
|
|
31
|
+
fi
|
|
32
|
+
fi
|
|
33
|
+
fi
|
|
34
|
+
if [ "$need_build" = "1" ]; then
|
|
35
|
+
echo "Building daemon bundle..."
|
|
36
|
+
(cd "$SKILL_DIR" && npm run build)
|
|
37
|
+
fi
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Clean environment for subprocess isolation.
|
|
41
|
+
clean_env() {
|
|
42
|
+
unset CLAUDECODE 2>/dev/null || true
|
|
43
|
+
|
|
44
|
+
local runtime
|
|
45
|
+
runtime=$(grep "^CTI_RUNTIME=" "$CTI_HOME/config.env" 2>/dev/null | head -1 | cut -d= -f2- | tr -d "'" | tr -d '"' || true)
|
|
46
|
+
runtime="${runtime:-claude}"
|
|
47
|
+
|
|
48
|
+
local mode="${CTI_ENV_ISOLATION:-inherit}"
|
|
49
|
+
if [ "$mode" = "strict" ]; then
|
|
50
|
+
case "$runtime" in
|
|
51
|
+
codex)
|
|
52
|
+
while IFS='=' read -r name _; do
|
|
53
|
+
case "$name" in ANTHROPIC_*) unset "$name" 2>/dev/null || true ;; esac
|
|
54
|
+
done < <(env)
|
|
55
|
+
;;
|
|
56
|
+
claude)
|
|
57
|
+
# Keep ANTHROPIC_* (from config.env) — needed for third-party API providers.
|
|
58
|
+
# Strip OPENAI_* to avoid cross-runtime leakage.
|
|
59
|
+
while IFS='=' read -r name _; do
|
|
60
|
+
case "$name" in OPENAI_*) unset "$name" 2>/dev/null || true ;; esac
|
|
61
|
+
done < <(env)
|
|
62
|
+
;;
|
|
63
|
+
auto)
|
|
64
|
+
# Keep both ANTHROPIC_* and OPENAI_* for auto mode
|
|
65
|
+
;;
|
|
66
|
+
esac
|
|
67
|
+
fi
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
read_pid() {
|
|
71
|
+
[ -f "$PID_FILE" ] && cat "$PID_FILE" 2>/dev/null || echo ""
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
pid_alive() {
|
|
75
|
+
local pid="$1"
|
|
76
|
+
[ -n "$pid" ] && kill -0 "$pid" 2>/dev/null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
status_running() {
|
|
80
|
+
[ -f "$STATUS_FILE" ] && grep -q '"running"[[:space:]]*:[[:space:]]*true' "$STATUS_FILE" 2>/dev/null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
show_last_exit_reason() {
|
|
84
|
+
if [ -f "$STATUS_FILE" ]; then
|
|
85
|
+
local reason
|
|
86
|
+
reason=$(grep -o '"lastExitReason"[[:space:]]*:[[:space:]]*"[^"]*"' "$STATUS_FILE" 2>/dev/null | head -1 | sed 's/.*: *"//;s/"$//')
|
|
87
|
+
[ -n "$reason" ] && echo "Last exit reason: $reason"
|
|
88
|
+
fi
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
show_failure_help() {
|
|
92
|
+
echo ""
|
|
93
|
+
echo "Recent logs:"
|
|
94
|
+
tail -20 "$LOG_FILE" 2>/dev/null || echo " (no log file)"
|
|
95
|
+
echo ""
|
|
96
|
+
echo "Next steps:"
|
|
97
|
+
echo " 1. Run diagnostics: bash \"$SKILL_DIR/scripts/doctor.sh\""
|
|
98
|
+
echo " 2. Check full logs: bash \"$SKILL_DIR/scripts/daemon.sh\" logs 100"
|
|
99
|
+
echo " 3. Rebuild bundle: cd \"$SKILL_DIR\" && npm run build"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# ── Load platform-specific supervisor ──
|
|
103
|
+
|
|
104
|
+
case "$(uname -s)" in
|
|
105
|
+
Darwin)
|
|
106
|
+
# shellcheck source=supervisor-macos.sh
|
|
107
|
+
source "$SKILL_DIR/scripts/supervisor-macos.sh"
|
|
108
|
+
;;
|
|
109
|
+
MINGW*|MSYS*|CYGWIN*)
|
|
110
|
+
# Windows detected via Git Bash / MSYS2 / Cygwin — delegate to PowerShell
|
|
111
|
+
echo "Windows detected. Delegating to supervisor-windows.ps1..."
|
|
112
|
+
powershell.exe -ExecutionPolicy Bypass -File "$SKILL_DIR/scripts/supervisor-windows.ps1" "$@"
|
|
113
|
+
exit $?
|
|
114
|
+
;;
|
|
115
|
+
*)
|
|
116
|
+
# shellcheck source=supervisor-linux.sh
|
|
117
|
+
source "$SKILL_DIR/scripts/supervisor-linux.sh"
|
|
118
|
+
;;
|
|
119
|
+
esac
|
|
120
|
+
|
|
121
|
+
# ── Commands ──
|
|
122
|
+
|
|
123
|
+
case "${1:-help}" in
|
|
124
|
+
start)
|
|
125
|
+
ensure_dirs
|
|
126
|
+
ensure_built
|
|
127
|
+
|
|
128
|
+
# Check if already running (supervisor-aware: launchctl on macOS, PID on Linux)
|
|
129
|
+
if supervisor_is_running; then
|
|
130
|
+
EXISTING_PID=$(read_pid)
|
|
131
|
+
echo "Bridge already running${EXISTING_PID:+ (PID: $EXISTING_PID)}"
|
|
132
|
+
cat "$STATUS_FILE" 2>/dev/null
|
|
133
|
+
exit 1
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Source config.env BEFORE clean_env so that CTI_ANTHROPIC_PASSTHROUGH
|
|
137
|
+
# and other CTI_* flags are available when clean_env checks them.
|
|
138
|
+
[ -f "$CTI_HOME/config.env" ] && set -a && source "$CTI_HOME/config.env" && set +a
|
|
139
|
+
|
|
140
|
+
clean_env
|
|
141
|
+
echo "Starting bridge..."
|
|
142
|
+
supervisor_start
|
|
143
|
+
|
|
144
|
+
# Poll for up to 10 seconds waiting for status.json to report running
|
|
145
|
+
STARTED=false
|
|
146
|
+
for _ in $(seq 1 10); do
|
|
147
|
+
sleep 1
|
|
148
|
+
if status_running; then
|
|
149
|
+
STARTED=true
|
|
150
|
+
break
|
|
151
|
+
fi
|
|
152
|
+
# If supervisor process already died, stop waiting
|
|
153
|
+
if ! supervisor_is_running; then
|
|
154
|
+
break
|
|
155
|
+
fi
|
|
156
|
+
done
|
|
157
|
+
|
|
158
|
+
if [ "$STARTED" = "true" ]; then
|
|
159
|
+
NEW_PID=$(read_pid)
|
|
160
|
+
echo "Bridge started${NEW_PID:+ (PID: $NEW_PID)}"
|
|
161
|
+
cat "$STATUS_FILE" 2>/dev/null
|
|
162
|
+
else
|
|
163
|
+
echo "Failed to start bridge."
|
|
164
|
+
supervisor_is_running || echo " Process not running."
|
|
165
|
+
status_running || echo " status.json not reporting running=true."
|
|
166
|
+
show_last_exit_reason
|
|
167
|
+
show_failure_help
|
|
168
|
+
exit 1
|
|
169
|
+
fi
|
|
170
|
+
;;
|
|
171
|
+
|
|
172
|
+
stop)
|
|
173
|
+
if supervisor_is_managed; then
|
|
174
|
+
echo "Stopping bridge..."
|
|
175
|
+
supervisor_stop
|
|
176
|
+
echo "Bridge stopped"
|
|
177
|
+
else
|
|
178
|
+
PID=$(read_pid)
|
|
179
|
+
if [ -z "$PID" ]; then echo "No bridge running"; exit 0; fi
|
|
180
|
+
if pid_alive "$PID"; then
|
|
181
|
+
kill "$PID"
|
|
182
|
+
for _ in $(seq 1 10); do
|
|
183
|
+
pid_alive "$PID" || break
|
|
184
|
+
sleep 1
|
|
185
|
+
done
|
|
186
|
+
pid_alive "$PID" && kill -9 "$PID"
|
|
187
|
+
echo "Bridge stopped"
|
|
188
|
+
else
|
|
189
|
+
echo "Bridge was not running (stale PID file)"
|
|
190
|
+
fi
|
|
191
|
+
rm -f "$PID_FILE"
|
|
192
|
+
fi
|
|
193
|
+
;;
|
|
194
|
+
|
|
195
|
+
status)
|
|
196
|
+
# Platform-specific status info (prints launchd/service state)
|
|
197
|
+
supervisor_status_extra
|
|
198
|
+
|
|
199
|
+
# Process status: supervisor-aware (launchctl on macOS, PID on Linux)
|
|
200
|
+
if supervisor_is_running; then
|
|
201
|
+
PID=$(read_pid)
|
|
202
|
+
echo "Bridge process is running${PID:+ (PID: $PID)}"
|
|
203
|
+
# Business status from status.json
|
|
204
|
+
if status_running; then
|
|
205
|
+
echo "Bridge status: running"
|
|
206
|
+
else
|
|
207
|
+
echo "Bridge status: process alive but status.json not reporting running"
|
|
208
|
+
fi
|
|
209
|
+
cat "$STATUS_FILE" 2>/dev/null
|
|
210
|
+
else
|
|
211
|
+
echo "Bridge is not running"
|
|
212
|
+
[ -f "$PID_FILE" ] && rm -f "$PID_FILE"
|
|
213
|
+
show_last_exit_reason
|
|
214
|
+
fi
|
|
215
|
+
;;
|
|
216
|
+
|
|
217
|
+
logs)
|
|
218
|
+
N="${2:-50}"
|
|
219
|
+
tail -n "$N" "$LOG_FILE" 2>/dev/null | sed -E 's/(token|secret|password)(["\\x27]?\s*[:=]\s*["\\x27]?)[^ "]+/\1\2*****/gi'
|
|
220
|
+
;;
|
|
221
|
+
|
|
222
|
+
*)
|
|
223
|
+
echo "Usage: daemon.sh {start|stop|status|logs [N]}"
|
|
224
|
+
;;
|
|
225
|
+
esac
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Windows wrapper for the existing bash-based doctor script.
|
|
4
|
+
.DESCRIPTION
|
|
5
|
+
Prefers Git Bash / bash.exe when available. Falls back to a clear message
|
|
6
|
+
when bash is missing, because the full diagnostics live in doctor.sh.
|
|
7
|
+
#>
|
|
8
|
+
|
|
9
|
+
$ErrorActionPreference = 'Stop'
|
|
10
|
+
|
|
11
|
+
$doctorScript = Join-Path (Split-Path -Parent $PSCommandPath) 'doctor.sh'
|
|
12
|
+
|
|
13
|
+
if (-not (Test-Path $doctorScript)) {
|
|
14
|
+
Write-Error "doctor.sh not found at $doctorScript"
|
|
15
|
+
exit 1
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
$bash = Get-Command bash -ErrorAction SilentlyContinue
|
|
19
|
+
if (-not $bash) {
|
|
20
|
+
Write-Host "bash was not found in PATH."
|
|
21
|
+
Write-Host "Install Git Bash or another bash environment, then run:"
|
|
22
|
+
Write-Host " bash `"$doctorScript`""
|
|
23
|
+
exit 1
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
& $bash.Source $doctorScript
|
|
27
|
+
exit $LASTEXITCODE
|