@nickname4th/pura-cli 0.1.0 → 0.1.1
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 +14 -12
- package/package.json +1 -1
- package/server/dist/cli.js +96 -3
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Open the Hub:
|
|
|
20
20
|
http://<hub-lan-ip>:8787
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
On each developer machine, connect an Agent:
|
|
23
|
+
On each developer machine, connect an Agent. After this command starts, the Hub page shows every authorized Android device attached to this machine:
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
npx @nickname4th/pura-cli connect <hub-lan-ip>:8787 --name "Zhang San"
|
|
@@ -33,13 +33,7 @@ npm install -g @nickname4th/pura-cli
|
|
|
33
33
|
pura-cli connect <hub-lan-ip>:8787 --name "Zhang San" --background
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npx @nickname4th/pura-cli connect device --name "Zhang San Pixel 8" --owner "Zhang San" --note "login branch"
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Designers can now pick the published machine on the Hub homepage, open the live screen, and click on it with a mouse.
|
|
36
|
+
Open the Hub page, find the device under devices to publish, and publish it from the web UI. Designers can then pick the published machine on the Hub homepage, open the live screen, and click on it with a mouse.
|
|
43
37
|
|
|
44
38
|
## Project Site
|
|
45
39
|
|
|
@@ -105,13 +99,13 @@ pura-cli hub --host 0.0.0.0 --port 8787
|
|
|
105
99
|
|
|
106
100
|
## Developer Agent
|
|
107
101
|
|
|
108
|
-
Each developer connects their local Agent to the Hub:
|
|
102
|
+
Each developer connects their local Agent to the Hub. Once connected, the Hub web UI lists all authorized local Android devices, including devices that are not published yet:
|
|
109
103
|
|
|
110
104
|
```bash
|
|
111
105
|
pura-cli connect 192.168.100.128:8787 --name "Zhang San"
|
|
112
106
|
```
|
|
113
107
|
|
|
114
|
-
The Agent listens on `8788` by default and continuously reports local ADB devices to the Hub.
|
|
108
|
+
The Agent listens on `8788` by default and continuously reports local ADB devices to the Hub. Use the web UI to publish, rename, unpublish, and manage devices.
|
|
115
109
|
|
|
116
110
|
If the Hub cannot reach the auto-detected Agent URL, specify it:
|
|
117
111
|
|
|
@@ -140,7 +134,15 @@ Connect a phone over USB and confirm it is authorized:
|
|
|
140
134
|
adb devices -l
|
|
141
135
|
```
|
|
142
136
|
|
|
143
|
-
Then
|
|
137
|
+
Then run or install the Agent:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
pura-cli connect 192.168.100.128:8787 --name "Zhang San" --background
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Open the Hub page and publish the device from the web UI.
|
|
144
|
+
|
|
145
|
+
The CLI also has a shortcut for scripts:
|
|
144
146
|
|
|
145
147
|
```bash
|
|
146
148
|
pura-cli connect device --name "Zhang San Pixel 8" --owner "Zhang San" --note "login branch"
|
|
@@ -211,7 +213,7 @@ Release flow:
|
|
|
211
213
|
1. Update `version` in `package.json`.
|
|
212
214
|
2. Run `npm run check`, `npm run build`, and `npm pack --dry-run`.
|
|
213
215
|
3. Push a tag like `v0.1.0`.
|
|
214
|
-
4. GitHub Actions publishes
|
|
216
|
+
4. GitHub Actions publishes `@nickname4th/pura-cli` to npm and `ghcr.io/liutianjie/pura` to GHCR.
|
|
215
217
|
|
|
216
218
|
The release workflow requires an `NPM_TOKEN` repository secret.
|
|
217
219
|
|
package/package.json
CHANGED
package/server/dist/cli.js
CHANGED
|
@@ -59,7 +59,7 @@ async function handleConnect() {
|
|
|
59
59
|
console.log(`pura-cli saved hub: ${hubUrl}`);
|
|
60
60
|
console.log(`agent URL announced to hub: ${publicUrl}`);
|
|
61
61
|
if (hasFlag("--background") || hasFlag("--install")) {
|
|
62
|
-
installLaunchAgent();
|
|
62
|
+
await installLaunchAgent();
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
65
|
console.log(`pura-cli starting agent: ${agentName} (${agentId})`);
|
|
@@ -77,7 +77,7 @@ async function handleConnect() {
|
|
|
77
77
|
}
|
|
78
78
|
async function handleAutoConnect() {
|
|
79
79
|
if (hasFlag("--install")) {
|
|
80
|
-
installLaunchAgent();
|
|
80
|
+
await installLaunchAgent();
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
83
|
if (hasFlag("--uninstall")) {
|
|
@@ -119,6 +119,7 @@ async function publishLocalDevice() {
|
|
|
119
119
|
process.exit(1);
|
|
120
120
|
}
|
|
121
121
|
console.log(`Published ${label} (${serial}) to ${config.hubUrl ?? "configured hub"}`);
|
|
122
|
+
await printPublicationVisibility(config, agentPort, serial);
|
|
122
123
|
}
|
|
123
124
|
function startSavedAgent(config) {
|
|
124
125
|
const port = readFlag("--port") ?? config.agentPort ?? "8788";
|
|
@@ -150,7 +151,7 @@ function startSavedAgent(config) {
|
|
|
150
151
|
DATA_DIR: dataDir
|
|
151
152
|
});
|
|
152
153
|
}
|
|
153
|
-
function installLaunchAgent() {
|
|
154
|
+
async function installLaunchAgent() {
|
|
154
155
|
const config = readConfig();
|
|
155
156
|
if (!config.hubUrl) {
|
|
156
157
|
console.error("No saved hub found. Run `pura-cli connect <hub-url> --name <name>` once first.");
|
|
@@ -174,6 +175,7 @@ function installLaunchAgent() {
|
|
|
174
175
|
if (cliPath.includes(`${path.sep}_npx${path.sep}`)) {
|
|
175
176
|
console.warn("This was installed from an npx cache path. For long-term use, install pura-cli globally and run the install command again.");
|
|
176
177
|
}
|
|
178
|
+
await printAgentVisibility(config);
|
|
177
179
|
}
|
|
178
180
|
function uninstallLaunchAgent() {
|
|
179
181
|
if (process.platform !== "darwin") {
|
|
@@ -264,6 +266,97 @@ function startServer(env) {
|
|
|
264
266
|
});
|
|
265
267
|
child.on("exit", (code) => process.exit(code ?? 0));
|
|
266
268
|
}
|
|
269
|
+
async function printAgentVisibility(config) {
|
|
270
|
+
const agentId = config.agentId;
|
|
271
|
+
const hubUrl = config.hubUrl;
|
|
272
|
+
const port = config.agentPort ?? "8788";
|
|
273
|
+
if (!agentId || !hubUrl)
|
|
274
|
+
return;
|
|
275
|
+
const result = await waitForAgentVisibility({ agentId, hubUrl, port });
|
|
276
|
+
if (result.visible) {
|
|
277
|
+
console.log(`Agent is online. Local devices: ${result.localDevices?.length ?? 0}. Devices visible on Hub: ${result.hubDevices?.length ?? 0}.`);
|
|
278
|
+
console.log(`Open ${hubUrl} to publish or control devices.`);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
console.warn("Agent was installed, but the Hub did not report the local device list yet.");
|
|
282
|
+
if (result.localError)
|
|
283
|
+
console.warn(`Local Agent check: ${result.localError}`);
|
|
284
|
+
if (result.hubError)
|
|
285
|
+
console.warn(`Hub check: ${result.hubError}`);
|
|
286
|
+
console.warn(`Check logs: tail -f ${path.join(os.homedir(), "Library", "Logs", "pura-agent.err.log")}`);
|
|
287
|
+
}
|
|
288
|
+
async function printPublicationVisibility(config, agentPort, serial) {
|
|
289
|
+
if (!config.hubUrl || !config.agentId) {
|
|
290
|
+
console.warn("Device metadata was saved locally, but no Hub connection is configured.");
|
|
291
|
+
console.warn("Run `pura-cli connect <hub-ip>:8787 --name <your-name> --background` first.");
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const result = await waitForAgentVisibility({
|
|
295
|
+
agentId: config.agentId,
|
|
296
|
+
hubUrl: config.hubUrl,
|
|
297
|
+
port: agentPort,
|
|
298
|
+
serial
|
|
299
|
+
});
|
|
300
|
+
if (result.publishedVisible) {
|
|
301
|
+
console.log("Hub confirmed this device is visible and published.");
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (result.deviceVisible) {
|
|
305
|
+
console.warn("Hub can see this device, but it has not received the published state yet. Refresh the page in a few seconds.");
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
console.warn("The device was published locally, but the Hub does not see it yet.");
|
|
309
|
+
console.warn(`Make sure the Agent is connected: pura-cli connect ${config.hubUrl} --name "${config.agentName ?? "your name"}" --background`);
|
|
310
|
+
}
|
|
311
|
+
async function waitForAgentVisibility(options) {
|
|
312
|
+
let localDevices;
|
|
313
|
+
let hubDevices;
|
|
314
|
+
let localError = "";
|
|
315
|
+
let hubError = "";
|
|
316
|
+
for (const delayMs of [250, 500, 750, 1000, 1500, 2500]) {
|
|
317
|
+
await sleep(delayMs);
|
|
318
|
+
try {
|
|
319
|
+
localDevices = (await fetchJson(`http://127.0.0.1:${options.port}/api/devices`)).devices;
|
|
320
|
+
localError = "";
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
localError = error instanceof Error ? error.message : String(error);
|
|
324
|
+
}
|
|
325
|
+
try {
|
|
326
|
+
const allHubDevices = (await fetchJson(`${options.hubUrl}/api/devices`)).devices;
|
|
327
|
+
hubDevices = allHubDevices.filter((device) => device.agentId === options.agentId);
|
|
328
|
+
hubError = "";
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
hubError = error instanceof Error ? error.message : String(error);
|
|
332
|
+
}
|
|
333
|
+
const deviceVisible = options.serial ? Boolean(hubDevices?.some((device) => device.remoteSerial === options.serial)) : false;
|
|
334
|
+
const publishedVisible = options.serial
|
|
335
|
+
? Boolean(hubDevices?.some((device) => device.remoteSerial === options.serial && device.publication?.published))
|
|
336
|
+
: false;
|
|
337
|
+
if (publishedVisible || deviceVisible) {
|
|
338
|
+
return { visible: true, deviceVisible, publishedVisible, localDevices, hubDevices, localError, hubError };
|
|
339
|
+
}
|
|
340
|
+
if (!options.serial && localDevices && hubDevices && (localDevices.length === 0 || hubDevices.length > 0)) {
|
|
341
|
+
return { visible: true, deviceVisible, publishedVisible, localDevices, hubDevices, localError, hubError };
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const deviceVisible = options.serial ? Boolean(hubDevices?.some((device) => device.remoteSerial === options.serial)) : false;
|
|
345
|
+
const publishedVisible = options.serial
|
|
346
|
+
? Boolean(hubDevices?.some((device) => device.remoteSerial === options.serial && device.publication?.published))
|
|
347
|
+
: false;
|
|
348
|
+
return { visible: false, deviceVisible, publishedVisible, localDevices, hubDevices, localError, hubError };
|
|
349
|
+
}
|
|
350
|
+
async function fetchJson(url) {
|
|
351
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(1500) });
|
|
352
|
+
if (!response.ok) {
|
|
353
|
+
throw new Error(`${response.status} ${response.statusText}`);
|
|
354
|
+
}
|
|
355
|
+
return (await response.json());
|
|
356
|
+
}
|
|
357
|
+
function sleep(ms) {
|
|
358
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
359
|
+
}
|
|
267
360
|
function readFlag(name) {
|
|
268
361
|
const index = args.indexOf(name);
|
|
269
362
|
if (index === -1)
|