@averyyy/pi-client 0.80.6-piclient.7 → 0.80.6-piclient.9
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/CHANGELOG.md +1 -0
- package/README.md +8 -0
- package/bin/pi-client.js +6 -1
- package/bin/send.js +49 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
- `pi-client update` command for updating the fork checkout and reinstalling both `pi-client` and `pi-server`.
|
|
8
8
|
- npm global package updates during `pi-client update`.
|
|
9
9
|
- `pi-client web` command that starts the client backend in Tau mirror mode on port `1838` by default.
|
|
10
|
+
- `pi-client send <path>` command for chunked file and folder uploads to pi-server.
|
|
10
11
|
|
|
11
12
|
### Fixed
|
|
12
13
|
|
package/README.md
CHANGED
|
@@ -24,6 +24,14 @@ Send one prompt and exit:
|
|
|
24
24
|
PI_SERVER_URL=https://pi.yreva.asia pi-client -p "Say exactly: ok"
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
Send a file or folder to the server:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
PI_SERVER_URL=https://pi.yreva.asia pi-client send /path/to/file-or-folder
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The server saves it under its configured upload directory, which defaults to `~/.pi/upload_files`.
|
|
34
|
+
|
|
27
35
|
Start the browser UI:
|
|
28
36
|
|
|
29
37
|
```bash
|
package/bin/pi-client.js
CHANGED
|
@@ -4,10 +4,15 @@ import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { runPiClientSend } from "./send.js";
|
|
7
8
|
|
|
8
9
|
const args = process.argv.slice(2);
|
|
9
10
|
const modulePromise =
|
|
10
|
-
args[0] === "
|
|
11
|
+
args[0] === "send"
|
|
12
|
+
? runPiClientSend(args.slice(1)).then((code) => {
|
|
13
|
+
process.exitCode = code;
|
|
14
|
+
})
|
|
15
|
+
: args[0] === "update"
|
|
11
16
|
? import("./update.js").then(async ({ runPiClientUpdate }) => {
|
|
12
17
|
process.exitCode = await runPiClientUpdate(args.slice(1));
|
|
13
18
|
})
|
package/bin/send.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { lstatSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { basename, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { ChunkRequest } from "@earendil-works/pi-coding-agent/pi-server-request";
|
|
4
|
+
|
|
5
|
+
function addDirectoryEntries(root, directory, entries) {
|
|
6
|
+
const path = relative(root, directory).split(sep).join("/");
|
|
7
|
+
entries.push({ path, type: "directory" });
|
|
8
|
+
for (const child of readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
9
|
+
const childPath = join(directory, child.name);
|
|
10
|
+
if (child.isDirectory()) addDirectoryEntries(root, childPath, entries);
|
|
11
|
+
else if (child.isFile()) {
|
|
12
|
+
entries.push({ path: relative(root, childPath).split(sep).join("/"), type: "file", data: readFileSync(childPath).toString("base64") });
|
|
13
|
+
} else {
|
|
14
|
+
throw new Error(`Unsupported file type: ${childPath}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createUploadBody(sourcePath) {
|
|
20
|
+
const source = resolve(sourcePath);
|
|
21
|
+
const stat = lstatSync(source);
|
|
22
|
+
if (stat.isSymbolicLink()) throw new Error(`Symbolic links are not supported: ${source}`);
|
|
23
|
+
if (stat.isFile()) {
|
|
24
|
+
return { name: basename(source), entries: [{ path: "", type: "file", data: readFileSync(source).toString("base64") }] };
|
|
25
|
+
}
|
|
26
|
+
if (!stat.isDirectory()) throw new Error(`Unsupported file type: ${source}`);
|
|
27
|
+
const entries = [];
|
|
28
|
+
addDirectoryEntries(source, source, entries);
|
|
29
|
+
return { name: basename(source), entries };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function runPiClientSend(args) {
|
|
33
|
+
if (args.length !== 1) {
|
|
34
|
+
console.error("Usage: pi-client send /path/to/file-or-folder");
|
|
35
|
+
return 1;
|
|
36
|
+
}
|
|
37
|
+
const request = new ChunkRequest({
|
|
38
|
+
serverUrl: process.env.PI_SERVER_URL ?? "http://127.0.0.1:4217",
|
|
39
|
+
authToken: process.env.PI_SERVER_AUTH_TOKEN ?? "",
|
|
40
|
+
});
|
|
41
|
+
const response = await request.postJson("/api/receive", createUploadBody(args[0]));
|
|
42
|
+
const body = await response.json();
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
console.error(`pi-client send failed (${response.status}): ${body.error ?? response.statusText}`);
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
console.log(`Saved to ${body.path}`);
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@averyyy/pi-client",
|
|
3
|
-
"version": "0.80.6-piclient.
|
|
3
|
+
"version": "0.80.6-piclient.9",
|
|
4
4
|
"description": "Lightweight CLI wrapper that connects to a pi-server instance",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piClient": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"prepublishOnly": "npm run build"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@earendil-works/pi-coding-agent": "npm:@averyyy/pi-coding-agent@0.80.6-piclient.
|
|
28
|
+
"@earendil-works/pi-coding-agent": "npm:@averyyy/pi-coding-agent@0.80.6-piclient.9"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"shx": "0.4.0",
|