clawdex-mobile 3.0.0 → 5.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.
@@ -29,14 +29,15 @@ jobs:
29
29
  - "clawdex-mobile"
30
30
  steps:
31
31
  - name: Checkout
32
- uses: actions/checkout@v4
32
+ uses: actions/checkout@v5
33
33
 
34
34
  - name: Setup Node.js
35
- uses: actions/setup-node@v4
35
+ uses: actions/setup-node@v5
36
36
  with:
37
37
  node-version: "20"
38
38
  cache: npm
39
39
  cache-dependency-path: package-lock.json
40
+ package-manager-cache: false
40
41
 
41
42
  - name: Install dependencies
42
43
  run: npm ci
@@ -56,7 +57,7 @@ jobs:
56
57
  timeout-minutes: 20
57
58
  steps:
58
59
  - name: Checkout
59
- uses: actions/checkout@v4
60
+ uses: actions/checkout@v5
60
61
 
61
62
  - name: Setup Rust toolchain
62
63
  uses: dtolnay/rust-toolchain@stable
@@ -16,23 +16,80 @@ permissions:
16
16
  contents: write
17
17
 
18
18
  jobs:
19
+ build_bridge_binaries:
20
+ name: Build bridge binary (${{ matrix.package_target }})
21
+ runs-on: ${{ matrix.runner }}
22
+ timeout-minutes: 30
23
+ strategy:
24
+ fail-fast: false
25
+ matrix:
26
+ include:
27
+ - runner: macos-26
28
+ package_target: darwin-arm64
29
+ rust_target: aarch64-apple-darwin
30
+ binary_path: services/rust-bridge/target/aarch64-apple-darwin/release/codex-rust-bridge
31
+ - runner: macos-26-intel
32
+ package_target: darwin-x64
33
+ rust_target: x86_64-apple-darwin
34
+ binary_path: services/rust-bridge/target/x86_64-apple-darwin/release/codex-rust-bridge
35
+ - runner: ubuntu-latest
36
+ package_target: linux-x64
37
+ rust_target: x86_64-unknown-linux-gnu
38
+ binary_path: services/rust-bridge/target/x86_64-unknown-linux-gnu/release/codex-rust-bridge
39
+ - runner: windows-latest
40
+ package_target: win32-x64
41
+ rust_target: x86_64-pc-windows-msvc
42
+ binary_path: services/rust-bridge/target/x86_64-pc-windows-msvc/release/codex-rust-bridge.exe
43
+ steps:
44
+ - name: Checkout
45
+ uses: actions/checkout@v5
46
+
47
+ - name: Setup Node.js
48
+ uses: actions/setup-node@v5
49
+ with:
50
+ node-version: "20"
51
+ package-manager-cache: false
52
+
53
+ - name: Setup Rust toolchain
54
+ uses: dtolnay/rust-toolchain@stable
55
+ with:
56
+ targets: ${{ matrix.rust_target }}
57
+
58
+ - name: Cache Rust dependencies
59
+ uses: Swatinem/rust-cache@v2
60
+
61
+ - name: Build bridge binary
62
+ working-directory: services/rust-bridge
63
+ run: cargo build --release --locked --target ${{ matrix.rust_target }}
64
+
65
+ - name: Stage packaged bridge binary
66
+ run: node scripts/bridge-binary.js stage --target ${{ matrix.package_target }} --from ${{ matrix.binary_path }}
67
+
68
+ - name: Upload bridge artifact
69
+ uses: actions/upload-artifact@v6
70
+ with:
71
+ name: bridge-binaries-${{ matrix.package_target }}
72
+ path: vendor/bridge-binaries/${{ matrix.package_target }}/
73
+
19
74
  publish:
20
75
  name: Publish package to npm
76
+ needs: build_bridge_binaries
21
77
  runs-on: ubuntu-latest
22
78
  timeout-minutes: 30
23
79
  steps:
24
80
  - name: Checkout
25
- uses: actions/checkout@v4
81
+ uses: actions/checkout@v5
26
82
  with:
27
83
  fetch-depth: 0
28
84
 
29
85
  - name: Setup Node.js
30
- uses: actions/setup-node@v4
86
+ uses: actions/setup-node@v5
31
87
  with:
32
88
  node-version: "20"
33
89
  registry-url: "https://registry.npmjs.org"
34
90
  cache: npm
35
91
  cache-dependency-path: package-lock.json
92
+ package-manager-cache: false
36
93
 
37
94
  - name: Setup Rust toolchain
38
95
  uses: dtolnay/rust-toolchain@stable
@@ -43,6 +100,13 @@ jobs:
43
100
  - name: Install dependencies
44
101
  run: npm ci
45
102
 
103
+ - name: Download packaged bridge binaries
104
+ uses: actions/download-artifact@v7
105
+ with:
106
+ pattern: bridge-binaries-*
107
+ path: .
108
+ merge-multiple: true
109
+
46
110
  - name: Verify Rust bridge compiles
47
111
  working-directory: services/rust-bridge
48
112
  run: cargo check --locked
@@ -26,7 +26,7 @@ jobs:
26
26
  url: ${{ steps.deployment.outputs.page_url }}
27
27
  steps:
28
28
  - name: Checkout
29
- uses: actions/checkout@v4
29
+ uses: actions/checkout@v5
30
30
 
31
31
  - name: Configure Pages
32
32
  uses: actions/configure-pages@v5
package/README.md CHANGED
@@ -41,6 +41,10 @@ clawdex init
41
41
 
42
42
  This is the primary starting point.
43
43
 
44
+ Published npm releases bundle prebuilt Rust bridge binaries for `darwin-arm64`, `darwin-x64`, `linux-x64`, and `win32-x64`, so supported hosts do not need to compile the bridge during normal startup. The interactive shell-based setup helpers are still macOS/Linux-oriented today.
45
+
46
+ `clawdex init` does not run a project-local `npm install`. The global package install is the only npm install needed for the published bridge flow.
47
+
44
48
  `clawdex init` guides you through:
45
49
 
46
50
  1. bridge mode selection: `Local (LAN)` or `Tailscale`
@@ -48,6 +52,8 @@ This is the primary starting point.
48
52
  3. phone readiness checks for selected mode
49
53
  4. optional bridge launch in the foreground (release build)
50
54
 
55
+ This flow is bridge-only. The mobile app is already shipped, so no Expo dev server is required to pair your phone.
56
+
51
57
  When the bridge starts, it prints a pairing QR:
52
58
 
53
59
  - preferred: QR contains both `bridgeUrl + bridgeToken` (one-scan onboarding)
@@ -103,7 +109,7 @@ Optional for simulators/emulators:
103
109
  From repo root:
104
110
 
105
111
  - `npm run setup:wizard` — guided secure setup + optional bridge start
106
- - `npm run stop:services` — stop running bridge (and Expo if running)
112
+ - `npm run stop:services` — stop running bridge (and local Expo if running)
107
113
  - `npm run secure:setup` — generate/update secure env
108
114
  - `npm run secure:bridge` — start rust bridge from `.env.secure` (release profile)
109
115
  - `npm run secure:bridge:dev` — start rust bridge in dev profile
@@ -140,7 +146,14 @@ npm run secure:bridge
140
146
 
141
147
  Keep `npm run secure:bridge` running in foreground. It prints pairing QR and bridge logs.
142
148
 
143
- In a second terminal, start the app runtime:
149
+ On supported published installs, this uses the bundled bridge binary directly instead of compiling Rust on startup. Source checkouts still build from Cargo when a packaged binary is unavailable.
150
+
151
+ Then open the installed mobile app and pair it:
152
+
153
+ - preferred: scan the bridge QR to autofill URL + token
154
+ - fallback: enter the bridge URL and token manually
155
+
156
+ For local mobile development only, start Expo from the repo:
144
157
 
145
158
  ```bash
146
159
  npm run mobile
@@ -150,7 +163,7 @@ npm run mobile
150
163
 
151
164
  On first launch (or after reset):
152
165
 
153
- 1. choose mode (`Local` or `Tailscale`)
166
+ 1. start the bridge on your server
154
167
  2. scan bridge QR to autofill URL + token
155
168
  3. use `Test Connection` (health + authenticated RPC check)
156
169
  4. tap `Continue`
@@ -3,7 +3,7 @@
3
3
  "name": "Clawdex Mobile",
4
4
  "slug": "clawdex-mobile",
5
5
  "scheme": "clawdex",
6
- "version": "3.0.0",
6
+ "version": "5.0.0",
7
7
  "userInterfaceStyle": "dark",
8
8
  "orientation": "portrait",
9
9
  "icon": "./assets/brand/app-icon.png",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawdex-mobile",
3
- "version": "3.0.0",
3
+ "version": "5.0.0",
4
4
  "private": true,
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -20,6 +20,7 @@
20
20
  "expo-audio": "~55.0.9",
21
21
  "expo-blur": "~55.0.10",
22
22
  "expo-camera": "~55.0.10",
23
+ "expo-clipboard": "~55.0.9",
23
24
  "expo-document-picker": "~55.0.9",
24
25
  "expo-file-system": "~55.0.9",
25
26
  "expo-image-picker": "~55.0.13",
@@ -383,9 +383,9 @@ describe('HostBridgeApiClient', () => {
383
383
  bridgeRoot: '/Users/mohit/work',
384
384
  allowOutsideRootCwd: true,
385
385
  workspaces: [
386
- { path: '/Users/mohit/work/app', chatCount: 3 },
387
- { path: '/Users/mohit/work/docs', chatCount: '1' },
388
- { path: '', chatCount: 99 },
386
+ { path: '/Users/mohit/work/app', chatCount: 3, updatedAt: 1700000000 },
387
+ { path: '/Users/mohit/work/docs', chatCount: '1', updatedAt: '1700001000' },
388
+ { path: '', chatCount: 99, updatedAt: 1700002000 },
389
389
  ],
390
390
  });
391
391
 
@@ -397,8 +397,16 @@ describe('HostBridgeApiClient', () => {
397
397
  bridgeRoot: '/Users/mohit/work',
398
398
  allowOutsideRootCwd: true,
399
399
  workspaces: [
400
- { path: '/Users/mohit/work/app', chatCount: 3 },
401
- { path: '/Users/mohit/work/docs', chatCount: 1 },
400
+ {
401
+ path: '/Users/mohit/work/app',
402
+ chatCount: 3,
403
+ updatedAt: new Date(1700000000 * 1000).toISOString(),
404
+ },
405
+ {
406
+ path: '/Users/mohit/work/docs',
407
+ chatCount: 1,
408
+ updatedAt: new Date(1700001000 * 1000).toISOString(),
409
+ },
402
410
  ],
403
411
  });
404
412
  });
@@ -955,6 +955,29 @@ function normalizeCwd(cwd: string | null | undefined): string | null {
955
955
  return trimmed.length > 0 ? trimmed : null;
956
956
  }
957
957
 
958
+ function readTimestampIso(value: unknown): string | null {
959
+ if (typeof value === 'string') {
960
+ const trimmed = value.trim();
961
+ if (!trimmed) {
962
+ return null;
963
+ }
964
+
965
+ const numeric = Number(trimmed);
966
+ if (Number.isFinite(numeric) && numeric > 0) {
967
+ return new Date((numeric > 1_000_000_000_000 ? numeric : numeric * 1000)).toISOString();
968
+ }
969
+
970
+ const parsedMs = Date.parse(trimmed);
971
+ return Number.isFinite(parsedMs) ? new Date(parsedMs).toISOString() : null;
972
+ }
973
+
974
+ if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
975
+ return new Date((value > 1_000_000_000_000 ? value : value * 1000)).toISOString();
976
+ }
977
+
978
+ return null;
979
+ }
980
+
958
981
  function readWorkspaceListResponse(value: unknown): WorkspaceListResponse {
959
982
  const record = toRecord(value) ?? {};
960
983
  const workspacesRaw = Array.isArray(record.workspaces) ? record.workspaces : [];
@@ -981,10 +1004,12 @@ function readWorkspaceListResponse(value: unknown): WorkspaceListResponse {
981
1004
  : typeof rawChatCount === 'string'
982
1005
  ? Math.max(0, Number.parseInt(rawChatCount, 10) || 0)
983
1006
  : 0;
1007
+ const updatedAt = readTimestampIso(workspace.updatedAt);
984
1008
 
985
1009
  return {
986
1010
  path,
987
1011
  chatCount,
1012
+ ...(updatedAt ? { updatedAt } : {}),
988
1013
  };
989
1014
  })
990
1015
  .filter((entry): entry is WorkspaceListResponse['workspaces'][number] => entry !== null),
@@ -117,6 +117,7 @@ export interface UploadAttachmentResponse {
117
117
  export interface WorkspaceSummary {
118
118
  path: string;
119
119
  chatCount: number;
120
+ updatedAt?: string;
120
121
  }
121
122
 
122
123
  export interface WorkspaceListResponse {