palmier 0.8.9 → 0.8.10
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/README.md +8 -7
- package/dist/commands/info.js +5 -0
- package/dist/commands/init.js +6 -6
- package/dist/commands/pair.js +9 -6
- package/dist/network.d.ts +9 -0
- package/dist/network.js +34 -0
- package/dist/pwa/assets/{index-1gs4vwFo.js → index-iL_NTbsT.js} +36 -36
- package/dist/pwa/assets/{web-DrSNtZ3i.js → web-C9g_YGd8.js} +1 -1
- package/dist/pwa/assets/{web-BqVsIFtP.js → web-CaRUL7Kz.js} +1 -1
- package/dist/pwa/assets/{web-lefgO9YR.js → web-D4ty3qtI.js} +1 -1
- package/dist/pwa/index.html +1 -1
- package/dist/pwa/service-worker.js +1 -1
- package/dist/rpc-handler.js +2 -0
- package/dist/transports/http-transport.d.ts +0 -1
- package/dist/transports/http-transport.js +8 -11
- package/dist/types.d.ts +4 -0
- package/package.json +2 -1
- package/palmier-server/pwa/src/components/ConnectionStatusIcon.tsx +0 -1
- package/palmier-server/pwa/src/constants.ts +1 -1
- package/palmier-server/pwa/src/contexts/HostConnectionContext.tsx +13 -4
- package/palmier-server/pwa/src/contexts/HostStoreContext.tsx +12 -0
- package/palmier-server/pwa/src/pages/Dashboard.tsx +6 -2
- package/palmier-server/pwa/src/pages/PairHost.tsx +0 -2
- package/palmier-server/pwa/src/pages/PairSetup.tsx +5 -2
- package/palmier-server/pwa/src/types.ts +1 -1
- package/src/commands/info.ts +6 -0
- package/src/commands/init.ts +6 -7
- package/src/commands/pair.ts +9 -6
- package/src/network.ts +32 -0
- package/src/rpc-handler.ts +2 -0
- package/src/transports/http-transport.ts +8 -11
- package/src/types.ts +5 -0
package/README.md
CHANGED
|
@@ -110,14 +110,15 @@ All device tools work while the Palmier Android app is in the background — the
|
|
|
110
110
|
|
|
111
111
|
## Access Modes
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
Three ways to reach your host, ordered by setup effort:
|
|
114
114
|
|
|
115
|
-
| Mode |
|
|
116
|
-
|
|
117
|
-
| **Local** | `http://localhost:<port>`
|
|
118
|
-
| **
|
|
115
|
+
| Mode | Where | Pairing | Notes |
|
|
116
|
+
|------|-------|---------|-------|
|
|
117
|
+
| **Local** | `http://localhost:<port>` in a browser on the host machine | Not required | Loopback only. No internet needed. |
|
|
118
|
+
| **Remote (web)** | [https://app.palmier.me](https://app.palmier.me) in any browser | Required | Always goes through the cloud relay. |
|
|
119
|
+
| **Remote (app)** | [Android APK](https://github.com/caihongxu/palmier-android/releases) | Required | Push notifications, background device capabilities, and **auto-LAN**. |
|
|
119
120
|
|
|
120
|
-
**Auto-LAN (native app only).** When the
|
|
121
|
+
**Auto-LAN (native app only).** When the Android app is on the same network as the host, it transparently routes RPC over direct LAN HTTP (`http://<host-ip>:<port>/rpc/...`) instead of through the relay — lower latency, no protocol change. Events still flow over the relay. Pairing always goes through the relay regardless. Browser PWAs can't do this (Private Network Access / mixed-content restrictions) and stay on the relay.
|
|
121
122
|
|
|
122
123
|
## Security & Privacy
|
|
123
124
|
|
|
@@ -135,7 +136,7 @@ In all modes, client tokens are generated and validated entirely on your host. T
|
|
|
135
136
|
|
|
136
137
|
Local access (`http://localhost:<port>`) works immediately — no pairing needed.
|
|
137
138
|
|
|
138
|
-
For
|
|
139
|
+
For remote access (web or app), run `palmier pair` on the host to generate a code, then enter it at [https://app.palmier.me](https://app.palmier.me) or in the Android app. Pairing always goes through the relay; auto-LAN kicks in transparently afterward in the native app when on the same network.
|
|
139
140
|
|
|
140
141
|
### Managing Clients
|
|
141
142
|
|
package/dist/commands/info.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { loadConfig } from "../config.js";
|
|
2
2
|
import { loadClients } from "../client-store.js";
|
|
3
|
+
import { buildLanUrl } from "../network.js";
|
|
3
4
|
export async function infoCommand() {
|
|
4
5
|
const config = loadConfig();
|
|
5
6
|
const clients = loadClients();
|
|
7
|
+
const port = config.httpPort ?? 7256;
|
|
8
|
+
const lanUrl = buildLanUrl(port, config.defaultInterface);
|
|
6
9
|
console.log(`Host ID: ${config.hostId}`);
|
|
7
10
|
console.log(`Project root: ${config.projectRoot}`);
|
|
11
|
+
console.log(`Local URL: http://localhost:${port}`);
|
|
12
|
+
console.log(`LAN URL: ${lanUrl ?? "(default route interface unavailable — pair a device or check network)"}`);
|
|
8
13
|
if (config.agents && config.agents.length > 0) {
|
|
9
14
|
console.log(`Agents: ${config.agents.map((a) => a.label).join(", ")}`);
|
|
10
15
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -3,7 +3,6 @@ import { loadConfig, saveConfig } from "../config.js";
|
|
|
3
3
|
import { detectAgents } from "../agents/agent.js";
|
|
4
4
|
import { getPlatform } from "../platform/index.js";
|
|
5
5
|
import { pairCommand } from "./pair.js";
|
|
6
|
-
import { detectLanIp } from "../transports/http-transport.js";
|
|
7
6
|
import { listTasks } from "../task.js";
|
|
8
7
|
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
9
8
|
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
@@ -32,15 +31,16 @@ export async function initCommand() {
|
|
|
32
31
|
const parsed = parseInt(portAnswer.trim(), 10);
|
|
33
32
|
if (parsed > 0 && parsed < 65536)
|
|
34
33
|
httpPort = parsed;
|
|
35
|
-
const lanIp = detectLanIp();
|
|
36
34
|
console.log(`\n${bold("Setup summary:")}\n`);
|
|
37
35
|
console.log(` ${dim("Task storage:")} ${bold(process.cwd())}`);
|
|
38
36
|
console.log(` All tasks and execution data will be stored here.\n`);
|
|
39
|
-
console.log(` ${dim("Local
|
|
37
|
+
console.log(` ${dim("Local:")} ${cyan(`http://localhost:${httpPort}`)}`);
|
|
40
38
|
console.log(` Open in a browser on this machine — no internet required.\n`);
|
|
41
|
-
console.log(` ${dim("Remote
|
|
42
|
-
console.log(` Pair
|
|
43
|
-
console.log(`
|
|
39
|
+
console.log(` ${dim("Remote (web):")} ${cyan("https://app.palmier.me")}`);
|
|
40
|
+
console.log(` Pair a browser on any device. Traffic always goes through the relay.\n`);
|
|
41
|
+
console.log(` ${dim("Remote (app):")} ${cyan("https://github.com/caihongxu/palmier-android/releases")}`);
|
|
42
|
+
console.log(` Download the Android APK. The app uses LAN for direct RPC`);
|
|
43
|
+
console.log(` when on the same network, otherwise the relay.\n`);
|
|
44
44
|
console.log(` ${dim("Agents:")} ${agents.map((a) => a.label).join(", ")}\n`);
|
|
45
45
|
const existingTasks = listTasks(process.cwd());
|
|
46
46
|
if (existingTasks.length > 0) {
|
package/dist/commands/pair.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as http from "node:http";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import { StringCodec } from "nats";
|
|
4
|
-
import { loadConfig } from "../config.js";
|
|
4
|
+
import { loadConfig, saveConfig } from "../config.js";
|
|
5
5
|
import { connectNats } from "../nats-client.js";
|
|
6
6
|
import { addClient } from "../client-store.js";
|
|
7
|
-
import {
|
|
7
|
+
import { detectDefaultInterface } from "../network.js";
|
|
8
8
|
const CODE_CHARS = "ABCDEFGHJKMNPQRSTUVWXYZ23456789"; // no O/0/I/1/L
|
|
9
9
|
const CODE_LENGTH = 6;
|
|
10
10
|
export const PAIRING_EXPIRY_MS = 60 * 1000; // 1 minute
|
|
@@ -13,13 +13,16 @@ export function generatePairingCode() {
|
|
|
13
13
|
crypto.getRandomValues(bytes);
|
|
14
14
|
return Array.from(bytes, (b) => CODE_CHARS[b % CODE_CHARS.length]).join("");
|
|
15
15
|
}
|
|
16
|
-
function buildPairResponse(config, label) {
|
|
16
|
+
async function buildPairResponse(config, label) {
|
|
17
17
|
const client = addClient(label);
|
|
18
|
-
const
|
|
18
|
+
const iface = await detectDefaultInterface();
|
|
19
|
+
if (iface && iface !== config.defaultInterface) {
|
|
20
|
+
config.defaultInterface = iface;
|
|
21
|
+
saveConfig(config);
|
|
22
|
+
}
|
|
19
23
|
return {
|
|
20
24
|
hostId: config.hostId,
|
|
21
25
|
clientToken: client.token,
|
|
22
|
-
directUrl: `http://${detectLanIp()}:${port}`,
|
|
23
26
|
hostName: os.hostname(),
|
|
24
27
|
};
|
|
25
28
|
}
|
|
@@ -88,7 +91,7 @@ export async function pairCommand() {
|
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
catch { /* empty body is fine */ }
|
|
91
|
-
const response = buildPairResponse(config, label);
|
|
94
|
+
const response = await buildPairResponse(config, label);
|
|
92
95
|
if (msg.reply) {
|
|
93
96
|
msg.respond(sc.encode(JSON.stringify(response)));
|
|
94
97
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the name of the network interface used for the IPv4 default route.
|
|
3
|
+
* Returns null when no default route is found (e.g. fully offline host) or
|
|
4
|
+
* when the OS platform isn't supported by `default-gateway`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function detectDefaultInterface(): Promise<string | null>;
|
|
7
|
+
export declare function getInterfaceIpv4(interfaceName: string): string | null;
|
|
8
|
+
export declare function buildLanUrl(port: number, interfaceName: string | undefined): string | null;
|
|
9
|
+
//# sourceMappingURL=network.d.ts.map
|
package/dist/network.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as os from "node:os";
|
|
2
|
+
// @ts-expect-error - default-gateway ships no types
|
|
3
|
+
import { gateway4async } from "default-gateway";
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the name of the network interface used for the IPv4 default route.
|
|
6
|
+
* Returns null when no default route is found (e.g. fully offline host) or
|
|
7
|
+
* when the OS platform isn't supported by `default-gateway`.
|
|
8
|
+
*/
|
|
9
|
+
export async function detectDefaultInterface() {
|
|
10
|
+
try {
|
|
11
|
+
const result = await gateway4async();
|
|
12
|
+
return result.int ?? null;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function getInterfaceIpv4(interfaceName) {
|
|
19
|
+
const addrs = os.networkInterfaces()[interfaceName];
|
|
20
|
+
if (!addrs)
|
|
21
|
+
return null;
|
|
22
|
+
for (const addr of addrs) {
|
|
23
|
+
if (addr.family === "IPv4" && !addr.internal)
|
|
24
|
+
return addr.address;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
export function buildLanUrl(port, interfaceName) {
|
|
29
|
+
if (!interfaceName)
|
|
30
|
+
return null;
|
|
31
|
+
const ip = getInterfaceIpv4(interfaceName);
|
|
32
|
+
return ip ? `http://${ip}:${port}` : null;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=network.js.map
|