clawdex-mobile 3.0.0 → 4.0.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.
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { spawn, spawnSync } = require("node:child_process");
5
+ const fs = require("node:fs");
6
+ const os = require("node:os");
7
+ const path = require("node:path");
8
+
9
+ const {
10
+ builtBinaryPath,
11
+ ensureExecutable,
12
+ packagedBinaryPath,
13
+ resolveRuntimeTarget,
14
+ } = require("./bridge-binary");
15
+
16
+ function resolveRootDir() {
17
+ let rootDir = process.env.INIT_CWD ? path.resolve(process.env.INIT_CWD) : path.resolve(__dirname, "..");
18
+ if (!fs.existsSync(path.join(rootDir, "package.json"))) {
19
+ rootDir = path.resolve(__dirname, "..");
20
+ }
21
+ return rootDir;
22
+ }
23
+
24
+ function readEnvFile(filePath) {
25
+ const contents = fs.readFileSync(filePath, "utf8");
26
+ const nextEnv = {};
27
+
28
+ for (const rawLine of contents.split(/\r?\n/)) {
29
+ const line = rawLine.trim();
30
+ if (!line || line.startsWith("#")) {
31
+ continue;
32
+ }
33
+
34
+ const match = line.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
35
+ if (!match) {
36
+ continue;
37
+ }
38
+
39
+ const [, key, rawValue] = match;
40
+ let value = rawValue;
41
+ if (
42
+ (value.startsWith('"') && value.endsWith('"')) ||
43
+ (value.startsWith("'") && value.endsWith("'"))
44
+ ) {
45
+ value = value.slice(1, -1);
46
+ }
47
+ nextEnv[key] = value;
48
+ }
49
+
50
+ return nextEnv;
51
+ }
52
+
53
+ function commandExists(command) {
54
+ const checker = process.platform === "win32" ? "where" : "which";
55
+ const result = spawnSync(checker, [command], { stdio: "ignore" });
56
+ return result.status === 0;
57
+ }
58
+
59
+ function walkFiles(directory) {
60
+ const entries = fs.readdirSync(directory, { withFileTypes: true });
61
+ const files = [];
62
+
63
+ for (const entry of entries) {
64
+ const entryPath = path.join(directory, entry.name);
65
+ if (entry.isDirectory()) {
66
+ files.push(...walkFiles(entryPath));
67
+ continue;
68
+ }
69
+ if (entry.isFile()) {
70
+ files.push(entryPath);
71
+ }
72
+ }
73
+
74
+ return files;
75
+ }
76
+
77
+ function isBuiltBinaryFresh(rootDir, binaryPath) {
78
+ if (!fs.existsSync(binaryPath)) {
79
+ return false;
80
+ }
81
+
82
+ const binaryMtime = fs.statSync(binaryPath).mtimeMs;
83
+ const watchPaths = [
84
+ path.join(rootDir, "services", "rust-bridge", "Cargo.toml"),
85
+ path.join(rootDir, "services", "rust-bridge", "Cargo.lock"),
86
+ ];
87
+ const sourceDir = path.join(rootDir, "services", "rust-bridge", "src");
88
+
89
+ if (fs.existsSync(sourceDir)) {
90
+ watchPaths.push(...walkFiles(sourceDir));
91
+ }
92
+
93
+ return watchPaths.every((watchPath) => {
94
+ if (!fs.existsSync(watchPath)) {
95
+ return true;
96
+ }
97
+ return fs.statSync(watchPath).mtimeMs <= binaryMtime;
98
+ });
99
+ }
100
+
101
+ function printMissingCompilerHint() {
102
+ if (process.platform === "win32") {
103
+ console.error("Install Visual Studio Build Tools (Desktop development with C++) and Rust, then retry.");
104
+ return;
105
+ }
106
+ if (commandExists("apt-get")) {
107
+ console.error("Install on Ubuntu/Debian: sudo apt-get update && sudo apt-get install -y build-essential");
108
+ return;
109
+ }
110
+ if (commandExists("dnf")) {
111
+ console.error("Install on Fedora/RHEL: sudo dnf install -y gcc gcc-c++ make");
112
+ return;
113
+ }
114
+ if (commandExists("yum")) {
115
+ console.error("Install on CentOS/RHEL: sudo yum install -y gcc gcc-c++ make");
116
+ return;
117
+ }
118
+ if (commandExists("apk")) {
119
+ console.error("Install on Alpine: sudo apk add build-base");
120
+ return;
121
+ }
122
+ if (commandExists("xcode-select")) {
123
+ console.error("Install on macOS: xcode-select --install");
124
+ }
125
+ }
126
+
127
+ function spawnAndRelay(command, args, options) {
128
+ const child = spawn(command, args, {
129
+ stdio: "inherit",
130
+ ...options,
131
+ });
132
+
133
+ child.on("error", (error) => {
134
+ console.error(`error: failed to start ${command}: ${error.message}`);
135
+ process.exit(1);
136
+ });
137
+
138
+ child.on("exit", (code, signal) => {
139
+ if (signal) {
140
+ process.kill(process.pid, signal);
141
+ return;
142
+ }
143
+ process.exit(code ?? 0);
144
+ });
145
+ }
146
+
147
+ function buildBridgeFromSource(rootDir, env) {
148
+ const cargoCmd = "cargo";
149
+ const args = ["build", "--release", "--locked"];
150
+ const result = spawnSync(cargoCmd, args, {
151
+ cwd: path.join(rootDir, "services", "rust-bridge"),
152
+ env,
153
+ stdio: "inherit",
154
+ });
155
+
156
+ if (result.error) {
157
+ console.error(`error: failed to run cargo build: ${result.error.message}`);
158
+ process.exit(1);
159
+ }
160
+
161
+ if ((result.status ?? 1) !== 0) {
162
+ process.exit(result.status ?? 1);
163
+ }
164
+ }
165
+
166
+ function start() {
167
+ const rootDir = resolveRootDir();
168
+ const secureEnvFile = path.join(rootDir, ".env.secure");
169
+ if (!fs.existsSync(secureEnvFile)) {
170
+ console.error(`error: ${secureEnvFile} not found. Run: npm run secure:setup`);
171
+ process.exit(1);
172
+ }
173
+
174
+ const fileEnv = readEnvFile(secureEnvFile);
175
+ const env = { ...fileEnv, ...process.env };
176
+ const devMode = process.argv.includes("--dev") || env.BRIDGE_RUN_MODE === "dev";
177
+ const forceSourceBuild = env.CLAWDEX_BRIDGE_FORCE_SOURCE_BUILD === "true";
178
+
179
+ if (devMode) {
180
+ if (!commandExists("cargo")) {
181
+ console.error("error: missing Rust/Cargo toolchain for dev bridge mode.");
182
+ process.exit(1);
183
+ }
184
+
185
+ spawnAndRelay("cargo", ["run"], {
186
+ cwd: path.join(rootDir, "services", "rust-bridge"),
187
+ env,
188
+ });
189
+ return;
190
+ }
191
+
192
+ const overrideBinary = env.CLAWDEX_BRIDGE_BINARY ? path.resolve(env.CLAWDEX_BRIDGE_BINARY) : "";
193
+ if (overrideBinary) {
194
+ if (!fs.existsSync(overrideBinary)) {
195
+ console.error(`error: CLAWDEX_BRIDGE_BINARY not found at ${overrideBinary}`);
196
+ process.exit(1);
197
+ }
198
+ ensureExecutable(overrideBinary);
199
+ spawnAndRelay(overrideBinary, [], { cwd: rootDir, env });
200
+ return;
201
+ }
202
+
203
+ const packagedBinary = packagedBinaryPath(rootDir, resolveRuntimeTarget());
204
+ if (!forceSourceBuild && packagedBinary && fs.existsSync(packagedBinary)) {
205
+ ensureExecutable(packagedBinary);
206
+ spawnAndRelay(packagedBinary, [], { cwd: rootDir, env });
207
+ return;
208
+ }
209
+
210
+ const builtBinary = builtBinaryPath(rootDir, os.platform());
211
+ if (isBuiltBinaryFresh(rootDir, builtBinary)) {
212
+ ensureExecutable(builtBinary);
213
+ spawnAndRelay(builtBinary, [], { cwd: rootDir, env });
214
+ return;
215
+ }
216
+
217
+ if (!commandExists("cargo")) {
218
+ console.error("error: no packaged bridge binary was found for this host, and cargo is not installed.");
219
+ console.error("Reinstall a published clawdex-mobile package with bundled bridge binaries, or install Rust and retry.");
220
+ process.exit(1);
221
+ }
222
+
223
+ if (process.platform !== "win32" && !commandExists("cc")) {
224
+ console.error("error: missing system C compiler/linker ('cc'). Rust bridge cannot compile without it.");
225
+ printMissingCompilerHint();
226
+ process.exit(1);
227
+ }
228
+
229
+ buildBridgeFromSource(rootDir, env);
230
+
231
+ if (!fs.existsSync(builtBinary)) {
232
+ console.error(`error: expected built bridge binary at ${builtBinary}, but it was not created.`);
233
+ process.exit(1);
234
+ }
235
+
236
+ ensureExecutable(builtBinary);
237
+ spawnAndRelay(builtBinary, [], { cwd: rootDir, env });
238
+ }
239
+
240
+ start();
@@ -2,43 +2,4 @@
2
2
  set -euo pipefail
3
3
 
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -L)"
5
- ROOT_DIR="${INIT_CWD:-$(cd "$SCRIPT_DIR/.." && pwd -L)}"
6
- if [[ ! -f "$ROOT_DIR/package.json" ]]; then
7
- ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd -L)"
8
- fi
9
- SECURE_ENV_FILE="$ROOT_DIR/.env.secure"
10
-
11
- if [[ ! -f "$SECURE_ENV_FILE" ]]; then
12
- echo "error: $SECURE_ENV_FILE not found. Run: npm run secure:setup" >&2
13
- exit 1
14
- fi
15
-
16
- if ! command -v cc >/dev/null 2>&1; then
17
- echo "error: missing system C compiler/linker ('cc'). Rust bridge cannot compile without it." >&2
18
- if command -v apt-get >/dev/null 2>&1; then
19
- echo "Install on Ubuntu/Debian: sudo apt-get update && sudo apt-get install -y build-essential" >&2
20
- elif command -v dnf >/dev/null 2>&1; then
21
- echo "Install on Fedora/RHEL: sudo dnf install -y gcc gcc-c++ make" >&2
22
- elif command -v yum >/dev/null 2>&1; then
23
- echo "Install on CentOS/RHEL: sudo yum install -y gcc gcc-c++ make" >&2
24
- elif command -v apk >/dev/null 2>&1; then
25
- echo "Install on Alpine: sudo apk add build-base" >&2
26
- elif command -v xcode-select >/dev/null 2>&1; then
27
- echo "Install on macOS: xcode-select --install" >&2
28
- fi
29
- exit 1
30
- fi
31
-
32
- set -a
33
- # shellcheck disable=SC1090
34
- source "$SECURE_ENV_FILE"
35
- set +a
36
-
37
- BRIDGE_RUN_MODE="${BRIDGE_RUN_MODE:-release}"
38
-
39
- cd "$ROOT_DIR"
40
- if [[ "$BRIDGE_RUN_MODE" == "dev" ]]; then
41
- exec npm run -w @codex/rust-bridge dev
42
- fi
43
-
44
- exec npm run -w @codex/rust-bridge start
5
+ exec node "$SCRIPT_DIR/start-bridge-secure.js" "$@"
@@ -149,7 +149,7 @@ dependencies = [
149
149
 
150
150
  [[package]]
151
151
  name = "codex-rust-bridge"
152
- version = "3.0.0"
152
+ version = "4.0.0"
153
153
  dependencies = [
154
154
  "axum",
155
155
  "base64",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "codex-rust-bridge"
3
- version = "3.0.0"
3
+ version = "4.0.0"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codex/rust-bridge",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "private": true,
5
5
  "scripts": {
6
6
  "dev": "cargo run",
@@ -1907,6 +1907,7 @@ struct WorkspaceListRequest {
1907
1907
  struct WorkspaceSummary {
1908
1908
  path: String,
1909
1909
  chat_count: usize,
1910
+ updated_at: Option<u64>,
1910
1911
  }
1911
1912
 
1912
1913
  #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -2806,7 +2807,16 @@ async fn list_workspace_roots(
2806
2807
 
2807
2808
  let mut workspaces = workspaces_by_path
2808
2809
  .into_iter()
2809
- .map(|(path, (chat_count, updated_at))| (WorkspaceSummary { path, chat_count }, updated_at))
2810
+ .map(|(path, (chat_count, updated_at))| {
2811
+ (
2812
+ WorkspaceSummary {
2813
+ path,
2814
+ chat_count,
2815
+ updated_at: (updated_at > 0).then_some(updated_at),
2816
+ },
2817
+ updated_at,
2818
+ )
2819
+ })
2810
2820
  .collect::<Vec<_>>();
2811
2821
 
2812
2822
  workspaces.sort_by(|(left, left_updated_at), (right, right_updated_at)| {