@averyyy/pi-client 0.85.1-piclient.1 → 0.85.1-piclient.3

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 CHANGED
@@ -3,6 +3,8 @@
3
3
  ### Fixed
4
4
 
5
5
  - Fixed automatic update checks to compare the installed `pi-client` version instead of the upstream `pi` version.
6
+ - Used `pi-client` in fork help and documented `web`, `send`, server configuration, global package updates, and `/reload` without changing upstream Pi help.
7
+ - Reported upload HTTP and connection failures with the target, response metadata, and a bounded, token-redacted body excerpt instead of JSON or network stack traces.
6
8
 
7
9
  ### Added
8
10
 
package/bin/send.js CHANGED
@@ -2,6 +2,8 @@ import { lstatSync, readdirSync, readFileSync } from "node:fs";
2
2
  import { basename, join, relative, resolve, sep } from "node:path";
3
3
  import { ChunkRequest } from "@earendil-works/pi-coding-agent/pi-server-request";
4
4
 
5
+ const MAX_ERROR_BODY_CHARS = 300;
6
+
5
7
  function addDirectoryEntries(root, directory, entries) {
6
8
  const path = relative(root, directory).split(sep).join("/");
7
9
  entries.push({ path, type: "directory" });
@@ -34,16 +36,85 @@ export async function runPiClientSend(args) {
34
36
  console.error("Usage: pi-client send /path/to/file-or-folder");
35
37
  return 1;
36
38
  }
39
+ const serverUrl = process.env.PI_SERVER_URL ?? "http://127.0.0.1:4217";
40
+ const authToken = process.env.PI_SERVER_AUTH_TOKEN ?? "";
37
41
  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 ?? "",
42
+ serverUrl,
43
+ authToken,
40
44
  });
41
- const response = await request.postJson("/api/receive", createUploadBody(args[0]));
42
- const body = await response.json();
45
+ let response;
46
+ try {
47
+ response = await request.postJson("/api/receive", createUploadBody(args[0]));
48
+ } catch (error) {
49
+ console.error(formatRequestFailure(serverUrl, error, authToken));
50
+ return 1;
51
+ }
52
+
53
+ let bodyText;
54
+ try {
55
+ bodyText = await response.text();
56
+ } catch (error) {
57
+ console.error(formatResponseFailure(serverUrl, response, error, authToken, "response body read failed"));
58
+ return 1;
59
+ }
43
60
  if (!response.ok) {
44
- console.error(`pi-client send failed (${response.status}): ${body.error ?? response.statusText}`);
61
+ console.error(formatResponseFailure(serverUrl, response, bodyText, authToken));
62
+ return 1;
63
+ }
64
+
65
+ let body;
66
+ try {
67
+ body = JSON.parse(bodyText);
68
+ } catch {
69
+ console.error(formatResponseFailure(serverUrl, response, bodyText, authToken, "expected JSON upload response"));
70
+ return 1;
71
+ }
72
+ if (!body || typeof body !== "object" || typeof body.path !== "string") {
73
+ console.error(
74
+ formatResponseFailure(
75
+ serverUrl,
76
+ response,
77
+ bodyText,
78
+ authToken,
79
+ "invalid upload payload; expected JSON response with a string path",
80
+ ),
81
+ );
45
82
  return 1;
46
83
  }
47
84
  console.log(`Saved to ${body.path}`);
48
85
  return 0;
49
86
  }
87
+
88
+ function formatResponseFailure(serverUrl, response, body, authToken, detail) {
89
+ const status = response.statusText ? `${response.status} ${response.statusText}` : String(response.status);
90
+ const contentType = response.headers.get("content-type") ?? "unknown";
91
+ const detailText = detail ? `; ${detail}` : "";
92
+ return `pi-client send failed for ${formatTarget(serverUrl)}: status ${status}; content-type: ${contentType}${detailText}; body excerpt: ${formatBodyExcerpt(body, authToken)}`;
93
+ }
94
+
95
+ function formatRequestFailure(serverUrl, error, authToken) {
96
+ const message = error instanceof Error ? error.message : String(error);
97
+ return `pi-client send failed for ${formatTarget(serverUrl)}: status unavailable; content-type: unavailable; body excerpt: ${formatBodyExcerpt(message, authToken)}`;
98
+ }
99
+
100
+ function formatTarget(serverUrl) {
101
+ try {
102
+ const target = new URL(`${serverUrl}/api/receive`);
103
+ target.username = "";
104
+ target.password = "";
105
+ target.search = "";
106
+ target.hash = "";
107
+ return target.toString();
108
+ } catch {
109
+ return "/api/receive";
110
+ }
111
+ }
112
+
113
+ function formatBodyExcerpt(body, authToken) {
114
+ const compacted = String(body ?? "")
115
+ .replace(/\s+/g, " ")
116
+ .trim();
117
+ const redacted = authToken ? compacted.replaceAll(authToken, "[redacted]") : compacted;
118
+ if (redacted.length <= MAX_ERROR_BODY_CHARS) return redacted || "(empty)";
119
+ return `${redacted.slice(0, MAX_ERROR_BODY_CHARS)}...`;
120
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@averyyy/pi-client",
3
- "version": "0.85.1-piclient.1",
3
+ "version": "0.85.1-piclient.3",
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.85.1-piclient.1"
28
+ "@earendil-works/pi-coding-agent": "npm:@averyyy/pi-coding-agent@0.85.1-piclient.3"
29
29
  },
30
30
  "devDependencies": {
31
31
  "shx": "0.4.0",