@yefengr/pi-reach 0.0.6 → 0.0.8
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 +62 -48
- package/dist/files/content-type.d.ts +16 -0
- package/dist/files/content-type.js +98 -0
- package/dist/files/content-type.js.map +1 -0
- package/dist/files/image-structure.d.ts +21 -0
- package/dist/files/image-structure.js +269 -0
- package/dist/files/image-structure.js.map +1 -0
- package/dist/files/publications.d.ts +26 -0
- package/dist/files/publications.js +136 -0
- package/dist/files/publications.js.map +1 -0
- package/dist/files/publish-tool.d.ts +23 -0
- package/dist/files/publish-tool.js +70 -0
- package/dist/files/publish-tool.js.map +1 -0
- package/dist/files/reader.d.ts +66 -0
- package/dist/files/reader.js +416 -0
- package/dist/files/reader.js.map +1 -0
- package/dist/files/safe-open.d.ts +22 -0
- package/dist/files/safe-open.js +119 -0
- package/dist/files/safe-open.js.map +1 -0
- package/dist/index.js +12 -17
- package/dist/index.js.map +1 -1
- package/dist/runtime/file_binding.d.ts +25 -0
- package/dist/runtime/file_binding.js +49 -0
- package/dist/runtime/file_binding.js.map +1 -0
- package/dist/runtime/session_frame_router.d.ts +11 -0
- package/dist/runtime/session_frame_router.js +25 -0
- package/dist/runtime/session_frame_router.js.map +1 -0
- package/dist/timeline/runtime.d.ts +1 -1
- package/dist/timeline/runtime.js +26 -54
- package/dist/timeline/runtime.js.map +1 -1
- package/dist/timeline/system_event.d.ts +7 -0
- package/dist/timeline/system_event.js +58 -0
- package/dist/timeline/system_event.js.map +1 -0
- package/dist/timeline/tool_lifecycle.js +8 -2
- package/dist/timeline/tool_lifecycle.js.map +1 -1
- package/dist/timeline/v2_service.js +3 -0
- package/dist/timeline/v2_service.js.map +1 -1
- package/dist/vendor/protocol/session/files.d.ts +83 -0
- package/dist/vendor/protocol/session/files.js +162 -0
- package/dist/vendor/protocol/session/frames.d.ts +293 -18
- package/dist/vendor/protocol/session/frames.js +52 -2
- package/dist/vendor/protocol/session/index.d.ts +1 -0
- package/dist/vendor/protocol/session/index.js +1 -0
- package/dist/vendor/protocol/session/schema.d.ts +2 -2
- package/dist/vendor/protocol/session/schema.js +2 -0
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -1,62 +1,73 @@
|
|
|
1
|
-
|
|
1
|
+
# Pi Reach
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Control your running Pi coding agent from your phone or any browser.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
When you step away from your computer, Pi Reach lets you follow Pi's live responses and tool calls, send another prompt or a file, and stop the current task. Pi keeps running on your computer; the browser is its remote interface.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+

|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
device -> endpoint -> runtime -> session / history generation
|
|
11
|
-
```
|
|
9
|
+
## Features
|
|
12
10
|
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
11
|
+
- **Pair without an account**: scan a QR code or enter an 8-character pairing code. Pair each computer once; later Pi processes on that computer appear automatically.
|
|
12
|
+
- **Switch between computers and Pi sessions**: connect to multiple computers and choose among the Pi processes currently running on each one.
|
|
13
|
+
- **Follow and control a live conversation**: stream responses and tool calls, send prompts and file attachments, or stop the current task. Attachments reach your computer as original files for Pi to read; image previews do not automatically become model vision input.
|
|
14
|
+
- **Manage the current session**: start a new conversation, compact context, or change the model and thinking level.
|
|
15
|
+
- **Use an installable PWA**: add the browser app to your home screen, choose light or dark mode, and use English or Chinese. Previously received conversations remain available for read-only viewing in that browser while offline.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+

|
|
18
18
|
|
|
19
19
|
## Quick start
|
|
20
20
|
|
|
21
|
-
Install the Extension
|
|
21
|
+
1. Install the Extension:
|
|
22
22
|
|
|
23
|
-
```bash
|
|
24
|
-
pi install npm:@yefengr/pi-reach
|
|
25
|
-
```
|
|
23
|
+
```bash
|
|
24
|
+
pi install npm:@yefengr/pi-reach
|
|
25
|
+
```
|
|
26
26
|
|
|
27
|
-
Open Pi in the project you want to control.
|
|
27
|
+
2. Open Pi in the project you want to control. The Extension automatically connects to the configured Relay when the session starts.
|
|
28
|
+
3. Open the public [Pi Reach PWA](https://pi-reach.yefengr.cn/app) on your phone or another browser.
|
|
29
|
+
4. Run this command in Pi, then scan the terminal QR code from the PWA or enter the 8-character pairing code:
|
|
28
30
|
|
|
29
|
-
```text
|
|
30
|
-
/pi-reach pair
|
|
31
|
-
```
|
|
31
|
+
```text
|
|
32
|
+
/pi-reach pair
|
|
33
|
+
```
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
5. Pick an online Pi in the PWA and send your first prompt.
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/pi-reach
|
|
38
|
-
|
|
37
|
+
Each computer only needs to be paired once. Pairings apply to the computer that creates them, not to every computer you use.
|
|
38
|
+
|
|
39
|
+
The public PWA and the default Relay are run by the maintainer. For sensitive work, [self-host the PWA and Relay](https://github.com/yefengr/pi-reach/blob/main/README.en.md#self-hosting).
|
|
40
|
+
|
|
41
|
+
## Security and limits
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
**Pi Reach has no application-layer end-to-end encryption. The Relay is fully trusted.** TLS protects transport, but the Relay operator can read conversation content, including code, commands, and output. The operator could also impersonate a paired browser and send prompts that Pi executes on your computer. Use a Relay you control for sensitive work.
|
|
44
|
+
|
|
45
|
+
- Pi must already be running with the Extension loaded. Pi Reach cannot remotely start, wake, or keep Pi running in the background.
|
|
46
|
+
- Browser history is a local, read-only cache of received conversations, not a cloud backup or a way to browse and resume old Pi sessions on your computer.
|
|
47
|
+
- Sending prompts and files requires a live connection. There is no offline send queue or push notifications, and phone lock-screen connectivity is not guaranteed.
|
|
48
|
+
- Browser identity, pairings, and received history stay in that browser's local storage. Clearing site data removes them and requires pairing again. There are no cloud accounts or cross-browser history synchronization.
|
|
49
|
+
- Revoke browsers you no longer use from Pi with `/pi-reach revoke <shortid>`; list them with `/pi-reach devices`.
|
|
50
|
+
|
|
51
|
+
Report vulnerabilities through GitHub's [private vulnerability reporting](https://github.com/yefengr/pi-reach/security/advisories/new); see the [security policy](https://github.com/yefengr/pi-reach/blob/main/SECURITY.md).
|
|
41
52
|
|
|
42
53
|
## Commands
|
|
43
54
|
|
|
44
55
|
| Command | Description |
|
|
45
56
|
|---|---|
|
|
46
|
-
| `/pi-reach` |
|
|
47
|
-
| `/pi-reach start` / `/pi-reach stop` | Connect or disconnect this
|
|
48
|
-
| `/pi-reach status` | Show Relay, endpoint, runtime, and
|
|
49
|
-
| `/pi-reach pair` | Show
|
|
50
|
-
| `/pi-reach devices` | List
|
|
51
|
-
| `/pi-reach revoke <shortid>` | Revoke
|
|
52
|
-
| `/pi-reach set-relay <url>` |
|
|
57
|
+
| `/pi-reach` | Reconnect this Pi after `/pi-reach stop` |
|
|
58
|
+
| `/pi-reach start` / `/pi-reach stop` | Connect or disconnect this Pi |
|
|
59
|
+
| `/pi-reach status` | Show Relay, endpoint, runtime, and paired-browser state |
|
|
60
|
+
| `/pi-reach pair` | Show a pairing QR code and pairing code for this Pi |
|
|
61
|
+
| `/pi-reach devices` | List browsers paired with this computer |
|
|
62
|
+
| `/pi-reach revoke <shortid>` | Revoke a browser's pairing on this computer |
|
|
63
|
+
| `/pi-reach set-relay <url>` | Save the Relay URL |
|
|
53
64
|
| `/pi-reach config` | Show the resolved Relay URL |
|
|
54
65
|
|
|
55
|
-
The Extension handles remote `session_new` requests in-process through Pi's session API. There is no standalone `pi-reach` CLI, background process, scheduler, or service installation command.
|
|
56
|
-
|
|
57
66
|
## Relay configuration
|
|
58
67
|
|
|
59
|
-
|
|
68
|
+
Pi and the PWA must use the same Relay. The pairing QR code does not carry a Relay address.
|
|
69
|
+
|
|
70
|
+
The Extension resolves its Relay URL in this order:
|
|
60
71
|
|
|
61
72
|
1. `PI_REACH_RELAY`
|
|
62
73
|
2. `~/.pi/pi-reach/config.json`
|
|
@@ -69,27 +80,30 @@ Set and inspect it from Pi:
|
|
|
69
80
|
/pi-reach config
|
|
70
81
|
```
|
|
71
82
|
|
|
72
|
-
|
|
83
|
+
In the PWA, set the same URL under **Settings → Connection → Relay URL**. Use `https://` for a deployed Relay; `http://` is accepted for local development. The Extension converts the URL to WebSocket form internally. The Relay retains routing state in memory and does not persist conversations.
|
|
84
|
+
|
|
85
|
+
## Endpoint model and local state
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
device -> endpoint -> runtime -> session / history generation
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
- **Device**: the computer's Ed25519 identity and the scope of pairing authorization.
|
|
92
|
+
- **Endpoint and runtime**: generated randomly when a Pi process loads the Extension. They remain stable across Extension reloads in that process and are regenerated for the next Pi process. The endpoint is not derived from the working directory.
|
|
93
|
+
- **Session / generation**: the active Pi conversation and its current history branch. Pi Reach does not list or resume historical Pi sessions.
|
|
73
94
|
|
|
74
|
-
|
|
95
|
+
Pairing QR codes target the current endpoint and runtime; the resulting browser authorization is stored at device scope. Owner messages are trusted only through the Relay-injected `source_owner_id`. Pairing and revocation update the Relay endpoint ACL with `authorized_owner_ids`.
|
|
75
96
|
|
|
76
|
-
|
|
77
|
-
- Report vulnerabilities privately through GitHub's [private vulnerability reporting](https://github.com/yefengr/pi-reach/security/advisories/new); see the [security policy](https://github.com/yefengr/pi-reach/blob/main/SECURITY.md).
|
|
78
|
-
- `device_id` is the Host Ed25519 public key in canonical Base64 form.
|
|
79
|
-
- Owner messages are trusted only through the Relay-injected `source_owner_id`.
|
|
80
|
-
- Pairing and revocation update the Relay endpoint ACL with `authorized_owner_ids`.
|
|
81
|
-
- Relay loss enters reconnecting state; the Extension does not restart Pi to recover.
|
|
82
|
-
- Device private keys, pairing tokens, encrypted payloads, and message bodies are not logged.
|
|
83
|
-
- Concurrent Pi processes coordinate device identity initialization through a local lock. If initialization is interrupted, follow the [identity storage and lock recovery rules](../docs/reference/protocol/pairing.md#host); do not delete identity or pairing data to retry.
|
|
97
|
+
The Extension handles remote `session_new` requests in-process through Pi's session API. There is no standalone `pi-reach` CLI, background process, scheduler, or service installation command. Relay loss triggers reconnection, not a Pi restart.
|
|
84
98
|
|
|
85
|
-
|
|
99
|
+
Pi Reach stores global configuration, identity files, and pairings under `~/.pi/pi-reach`, and project display configuration under `.pi/pi-reach`. Its platform keyring service is `dev.pireach.pi`. Device private keys, pairing tokens, and message bodies are not logged.
|
|
86
100
|
|
|
87
|
-
Pi
|
|
101
|
+
Concurrent Pi processes coordinate device identity initialization through a local lock. If initialization is interrupted, follow the [identity storage and lock recovery rules](https://github.com/yefengr/pi-reach/blob/main/docs/reference/protocol/pairing.md#host); do not delete identity or pairing data to retry.
|
|
88
102
|
|
|
89
103
|
## Development
|
|
90
104
|
|
|
91
105
|
Install dependencies from the repository root with `pnpm install --frozen-lockfile`.
|
|
92
|
-
The root workspace owns dependency catalogs, build approvals, and the lockfile. The private workspace package [`@pi-reach/protocol`](
|
|
106
|
+
The root workspace owns dependency catalogs, build approvals, and the lockfile. The private workspace package [`@pi-reach/protocol`](https://github.com/yefengr/pi-reach/tree/main/packages/protocol) provides the shared protocol build artifacts; the Extension uses it as a development dependency, while installed users receive vendored artifacts and need neither the workspace nor a separately published shared package. Its module and distribution boundary is defined in [ARCHITECTURE](https://github.com/yefengr/pi-reach/blob/main/docs/ARCHITECTURE.md#工程与构建边界).
|
|
93
107
|
|
|
94
108
|
The root `prepare` script and an Extension `pnpm build` build the shared package first. After changing shared sources, run `pnpm --filter @pi-reach/protocol build` from the repository root before an Extension-only `typecheck` or `test`, or use the corresponding root command.
|
|
95
109
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FileHandle } from "node:fs/promises";
|
|
2
|
+
import { type Dimensions } from "./image-structure.js";
|
|
3
|
+
type Preview = ({
|
|
4
|
+
kind: "image";
|
|
5
|
+
} & Dimensions) | {
|
|
6
|
+
kind: "text";
|
|
7
|
+
} | {
|
|
8
|
+
kind: "none";
|
|
9
|
+
};
|
|
10
|
+
type Inspection = {
|
|
11
|
+
mimeType: string;
|
|
12
|
+
preview: Preview;
|
|
13
|
+
};
|
|
14
|
+
/** 有界头识别不是完整解码认证;HTML/SVG 与所有文本都只能由消费端转义显示。 */
|
|
15
|
+
export declare function inspectFile(handle: FileHandle, byteLength: number, fileName: string): Promise<Inspection>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { extname } from "node:path";
|
|
2
|
+
import { FileAccessError } from "./safe-open.js";
|
|
3
|
+
import { ImageStructureReader, MAX_INSPECTION_READS, staticGif, staticPng, staticWebp } from "./image-structure.js";
|
|
4
|
+
const HEAD_MAX_BYTES = 256 * 1024;
|
|
5
|
+
const MAX_IMAGE_PIXELS = 20_000_000;
|
|
6
|
+
const NONE = { kind: "none" };
|
|
7
|
+
function image(mimeType, dimensions) {
|
|
8
|
+
if (!dimensions || dimensions.width <= 0 || dimensions.height <= 0 || dimensions.width * dimensions.height > MAX_IMAGE_PIXELS) {
|
|
9
|
+
return { mimeType, preview: NONE };
|
|
10
|
+
}
|
|
11
|
+
return { mimeType, preview: { kind: "image", ...dimensions } };
|
|
12
|
+
}
|
|
13
|
+
function jpegDimensions(head) {
|
|
14
|
+
let offset = 2;
|
|
15
|
+
while (offset < head.length) {
|
|
16
|
+
if (head[offset++] !== 0xff)
|
|
17
|
+
return undefined;
|
|
18
|
+
while (head[offset] === 0xff)
|
|
19
|
+
offset++;
|
|
20
|
+
if (offset >= head.length)
|
|
21
|
+
return undefined;
|
|
22
|
+
const marker = head[offset++];
|
|
23
|
+
if (marker === 0xd9 || marker === 0xda || marker === 0x00 || marker === 0xd8)
|
|
24
|
+
return undefined;
|
|
25
|
+
if (marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7))
|
|
26
|
+
continue;
|
|
27
|
+
if (offset + 2 > head.length)
|
|
28
|
+
return undefined;
|
|
29
|
+
const length = head.readUInt16BE(offset);
|
|
30
|
+
if (length < 2 || offset + length > head.length)
|
|
31
|
+
return undefined;
|
|
32
|
+
const sof = marker >= 0xc0 && marker <= 0xcf && ![0xc4, 0xc8, 0xcc].includes(marker);
|
|
33
|
+
if (sof) {
|
|
34
|
+
if (length < 8)
|
|
35
|
+
return undefined;
|
|
36
|
+
const components = head[offset + 7];
|
|
37
|
+
if (components < 1 || components > 4 || length !== 8 + 3 * components || ![8, 12, 16].includes(head[offset + 2]))
|
|
38
|
+
return undefined;
|
|
39
|
+
return { width: head.readUInt16BE(offset + 5), height: head.readUInt16BE(offset + 3) };
|
|
40
|
+
}
|
|
41
|
+
offset += length;
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
function isText(head, truncated) {
|
|
46
|
+
for (const byte of head) {
|
|
47
|
+
if ((byte < 0x20 && ![9, 10, 13].includes(byte)) || byte === 0x7f)
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
// 仅因头预算截断时允许末尾码点未完整;完整文件仍须 fatal 验证尾部。
|
|
52
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(head, { stream: truncated });
|
|
53
|
+
return !/[\u0080-\u009f]/u.test(text);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** 有界头识别不是完整解码认证;HTML/SVG 与所有文本都只能由消费端转义显示。 */
|
|
60
|
+
export async function inspectFile(handle, byteLength, fileName) {
|
|
61
|
+
if (!Number.isSafeInteger(byteLength) || byteLength < 0)
|
|
62
|
+
throw new FileAccessError("io_error");
|
|
63
|
+
const buffer = Buffer.alloc(Math.min(byteLength, HEAD_MAX_BYTES));
|
|
64
|
+
let offset = 0;
|
|
65
|
+
let reads = 0;
|
|
66
|
+
// 保留短 read 的续读语义;实读头字节受 buffer 限制,重试另受操作预算限制。
|
|
67
|
+
while (offset < buffer.length && reads < MAX_INSPECTION_READS) {
|
|
68
|
+
const requested = buffer.length - offset;
|
|
69
|
+
reads++;
|
|
70
|
+
const { bytesRead } = await handle.read(buffer, offset, requested, offset);
|
|
71
|
+
if (bytesRead === 0)
|
|
72
|
+
throw new FileAccessError("file_changed");
|
|
73
|
+
if (!Number.isInteger(bytesRead) || bytesRead < 0 || bytesRead > requested)
|
|
74
|
+
throw new FileAccessError("io_error");
|
|
75
|
+
offset += bytesRead;
|
|
76
|
+
}
|
|
77
|
+
const head = buffer.subarray(0, offset);
|
|
78
|
+
const completeHead = offset === buffer.length;
|
|
79
|
+
const reader = new ImageStructureReader(handle, head, byteLength, reads);
|
|
80
|
+
if (head.length >= 8 && head.toString("hex", 0, 8) === "89504e470d0a1a0a")
|
|
81
|
+
return image("image/png", completeHead ? await staticPng(reader) : undefined);
|
|
82
|
+
if (head.length >= 3 && head[0] === 0xff && head[1] === 0xd8 && head[2] === 0xff)
|
|
83
|
+
return image("image/jpeg", completeHead ? jpegDimensions(head) : undefined);
|
|
84
|
+
if (head.length >= 6 && ["GIF87a", "GIF89a"].includes(head.toString("latin1", 0, 6)))
|
|
85
|
+
return image("image/gif", completeHead ? staticGif(head, byteLength) : undefined);
|
|
86
|
+
if (head.length >= 12 && head.toString("latin1", 0, 4) === "RIFF" && head.toString("latin1", 8, 12) === "WEBP") {
|
|
87
|
+
return image("image/webp", completeHead ? await staticWebp(reader) : undefined);
|
|
88
|
+
}
|
|
89
|
+
if (head.length >= 5 && head.toString("latin1", 0, 5) === "%PDF-")
|
|
90
|
+
return { mimeType: "application/pdf", preview: NONE };
|
|
91
|
+
if (head.length >= 4 && ["504b0304", "504b0506", "504b0708"].includes(head.toString("hex", 0, 4)))
|
|
92
|
+
return { mimeType: "application/zip", preview: NONE };
|
|
93
|
+
if (!completeHead || !isText(head, byteLength > head.length))
|
|
94
|
+
return { mimeType: "application/octet-stream", preview: NONE };
|
|
95
|
+
const markdown = [".md", ".markdown"].includes(extname(fileName).toLowerCase());
|
|
96
|
+
return { mimeType: markdown ? "text/markdown" : "text/plain", preview: { kind: "text" } };
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=content-type.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-type.js","sourceRoot":"","sources":["../../src/files/content-type.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FileHandle } from "node:fs/promises";
|
|
2
|
+
export type Dimensions = {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
};
|
|
6
|
+
export declare const MAX_INSPECTION_READS = 4096;
|
|
7
|
+
/** 只读取结构元数据;压缩数据按长度跳过,不认证像素内容或 CRC。 */
|
|
8
|
+
export declare class ImageStructureReader {
|
|
9
|
+
private readonly handle;
|
|
10
|
+
readonly head: Buffer;
|
|
11
|
+
readonly byteLength: number;
|
|
12
|
+
private reads;
|
|
13
|
+
private steps;
|
|
14
|
+
private metadataBytes;
|
|
15
|
+
constructor(handle: FileHandle, head: Buffer, byteLength: number, reads: number);
|
|
16
|
+
step(): boolean;
|
|
17
|
+
get(position: number, length: number): Promise<Buffer | undefined>;
|
|
18
|
+
}
|
|
19
|
+
export declare function staticPng(reader: ImageStructureReader): Promise<Dimensions | undefined>;
|
|
20
|
+
export declare function staticWebp(reader: ImageStructureReader): Promise<Dimensions | undefined>;
|
|
21
|
+
export declare function staticGif(head: Buffer, byteLength: number): Dimensions | undefined;
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { FileAccessError } from "./safe-open.js";
|
|
2
|
+
export const MAX_INSPECTION_READS = 4096;
|
|
3
|
+
const MAX_STRUCTURE_STEPS = 4096;
|
|
4
|
+
const MAX_METADATA_BYTES = 64 * 1024;
|
|
5
|
+
const PNG_ANCILLARY = new Set(["cHRM", "gAMA", "iCCP", "sBIT", "sRGB", "bKGD", "hIST", "tRNS", "pHYs", "sPLT", "tIME", "iTXt", "tEXt", "zTXt", "eXIf", "cICP", "mDCV", "cLLI"]);
|
|
6
|
+
/** 只读取结构元数据;压缩数据按长度跳过,不认证像素内容或 CRC。 */
|
|
7
|
+
export class ImageStructureReader {
|
|
8
|
+
handle;
|
|
9
|
+
head;
|
|
10
|
+
byteLength;
|
|
11
|
+
reads;
|
|
12
|
+
steps = 0;
|
|
13
|
+
metadataBytes = 0;
|
|
14
|
+
constructor(handle, head, byteLength, reads) {
|
|
15
|
+
this.handle = handle;
|
|
16
|
+
this.head = head;
|
|
17
|
+
this.byteLength = byteLength;
|
|
18
|
+
this.reads = reads;
|
|
19
|
+
}
|
|
20
|
+
step() { return ++this.steps <= MAX_STRUCTURE_STEPS; }
|
|
21
|
+
async get(position, length) {
|
|
22
|
+
if (!Number.isSafeInteger(position) || position < 0 || length < 0 || position + length > this.byteLength)
|
|
23
|
+
return undefined;
|
|
24
|
+
if (position + length <= this.head.length)
|
|
25
|
+
return this.head.subarray(position, position + length);
|
|
26
|
+
const buffer = Buffer.alloc(length);
|
|
27
|
+
let offset = Math.max(0, Math.min(length, this.head.length - position));
|
|
28
|
+
if (offset)
|
|
29
|
+
this.head.copy(buffer, 0, position, position + offset);
|
|
30
|
+
while (offset < length) {
|
|
31
|
+
const requested = length - offset;
|
|
32
|
+
// 按请求字节计费,短 read 也不能反复请求而突破 metadata I/O 预算。
|
|
33
|
+
if (this.reads >= MAX_INSPECTION_READS || this.metadataBytes + requested > MAX_METADATA_BYTES)
|
|
34
|
+
return undefined;
|
|
35
|
+
this.reads++;
|
|
36
|
+
this.metadataBytes += requested;
|
|
37
|
+
const { bytesRead } = await this.handle.read(buffer, offset, requested, position + offset);
|
|
38
|
+
if (bytesRead === 0)
|
|
39
|
+
throw new FileAccessError("file_changed");
|
|
40
|
+
if (!Number.isInteger(bytesRead) || bytesRead < 0 || bytesRead > requested)
|
|
41
|
+
throw new FileAccessError("io_error");
|
|
42
|
+
offset += bytesRead;
|
|
43
|
+
}
|
|
44
|
+
return buffer;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function pngHeader(head) {
|
|
48
|
+
if (head.length < 33 || head.readUInt32BE(8) !== 13 || head.toString("latin1", 12, 16) !== "IHDR")
|
|
49
|
+
return undefined;
|
|
50
|
+
const depths = { 0: [1, 2, 4, 8, 16], 2: [8, 16], 3: [1, 2, 4, 8], 4: [8, 16], 6: [8, 16] };
|
|
51
|
+
if (!depths[head[25]]?.includes(head[24]) || head[26] !== 0 || head[27] !== 0 || head[28] > 1)
|
|
52
|
+
return undefined;
|
|
53
|
+
const width = head.readUInt32BE(16);
|
|
54
|
+
const height = head.readUInt32BE(20);
|
|
55
|
+
if (width === 0 || height === 0 || width > 0x7fffffff || height > 0x7fffffff)
|
|
56
|
+
return undefined;
|
|
57
|
+
return { width, height };
|
|
58
|
+
}
|
|
59
|
+
export async function staticPng(reader) {
|
|
60
|
+
const dimensions = pngHeader(reader.head);
|
|
61
|
+
if (!dimensions)
|
|
62
|
+
return undefined;
|
|
63
|
+
let position = 33;
|
|
64
|
+
let dataSeen = false;
|
|
65
|
+
let dataStarted = false;
|
|
66
|
+
let dataEnded = false;
|
|
67
|
+
let paletteSeen = false;
|
|
68
|
+
while (position < reader.byteLength && reader.step()) {
|
|
69
|
+
const header = await reader.get(position, 8);
|
|
70
|
+
if (!header)
|
|
71
|
+
return undefined;
|
|
72
|
+
const length = header.readUInt32BE(0);
|
|
73
|
+
const type = header.toString("latin1", 4, 8);
|
|
74
|
+
const end = position + 12 + length;
|
|
75
|
+
if (length > 0x7fffffff || end > reader.byteLength)
|
|
76
|
+
return undefined;
|
|
77
|
+
// 动画声明在任何位置都不能放行;IEND 后也不得保留浏览器可解释的尾部。
|
|
78
|
+
if (["acTL", "fcTL", "fdAT"].includes(type))
|
|
79
|
+
return undefined;
|
|
80
|
+
if (type === "IEND") {
|
|
81
|
+
if (length !== 0 || !dataSeen || end !== reader.byteLength)
|
|
82
|
+
return undefined;
|
|
83
|
+
return await reader.get(position + 8, 4) ? dimensions : undefined;
|
|
84
|
+
}
|
|
85
|
+
if (type === "IDAT") {
|
|
86
|
+
if (dataEnded || (reader.head[25] === 3 && !paletteSeen))
|
|
87
|
+
return undefined;
|
|
88
|
+
dataStarted = true;
|
|
89
|
+
if (length > 0)
|
|
90
|
+
dataSeen = true;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
if (dataStarted)
|
|
94
|
+
dataEnded = true;
|
|
95
|
+
if (type === "PLTE") {
|
|
96
|
+
if (paletteSeen || dataStarted || !length || length > 768 || length % 3 || [0, 4].includes(reader.head[25]))
|
|
97
|
+
return undefined;
|
|
98
|
+
paletteSeen = true;
|
|
99
|
+
}
|
|
100
|
+
else if (!PNG_ANCILLARY.has(type))
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
// 中间 payload/checksum 按长度跳过,最终 IEND checksum 稀疏读确认 EOF。
|
|
104
|
+
position = end;
|
|
105
|
+
}
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
function webpPayload(type, data) {
|
|
109
|
+
if (type === "VP8 ") {
|
|
110
|
+
if (data.length < 10 || data[0] & 1 || data.toString("hex", 3, 6) !== "9d012a")
|
|
111
|
+
return undefined;
|
|
112
|
+
return { width: data.readUInt16LE(6) & 0x3fff, height: data.readUInt16LE(8) & 0x3fff };
|
|
113
|
+
}
|
|
114
|
+
if (data.length < 5 || data[0] !== 0x2f)
|
|
115
|
+
return undefined;
|
|
116
|
+
const bits = data.readUInt32LE(1);
|
|
117
|
+
if (bits >>> 29 !== 0)
|
|
118
|
+
return undefined;
|
|
119
|
+
return { width: (bits & 0x3fff) + 1, height: ((bits >>> 14) & 0x3fff) + 1 };
|
|
120
|
+
}
|
|
121
|
+
export async function staticWebp(reader) {
|
|
122
|
+
if (reader.head.readUInt32LE(4) + 8 !== reader.byteLength)
|
|
123
|
+
return undefined;
|
|
124
|
+
let position = 12;
|
|
125
|
+
let canvas;
|
|
126
|
+
let dimensions;
|
|
127
|
+
let flags = 0;
|
|
128
|
+
let imageType;
|
|
129
|
+
const seen = new Set();
|
|
130
|
+
let losslessAlpha = false;
|
|
131
|
+
while (position < reader.byteLength && reader.step()) {
|
|
132
|
+
const header = await reader.get(position, 8);
|
|
133
|
+
if (!header)
|
|
134
|
+
return undefined;
|
|
135
|
+
const type = header.toString("latin1", 0, 4);
|
|
136
|
+
const length = header.readUInt32LE(4);
|
|
137
|
+
const start = position + 8;
|
|
138
|
+
const end = start + length + (length & 1);
|
|
139
|
+
if (end > reader.byteLength || seen.has(type) || type === "ANIM" || type === "ANMF")
|
|
140
|
+
return undefined;
|
|
141
|
+
seen.add(type);
|
|
142
|
+
if (type === "VP8X") {
|
|
143
|
+
const data = length === 10 && position === 12 ? await reader.get(start, 10) : undefined;
|
|
144
|
+
if (!data || data[0] & 0xc3 || data[1] || data[2] || data[3])
|
|
145
|
+
return undefined;
|
|
146
|
+
flags = data[0];
|
|
147
|
+
canvas = { width: data.readUIntLE(4, 3) + 1, height: data.readUIntLE(7, 3) + 1 };
|
|
148
|
+
}
|
|
149
|
+
else if (type === "VP8 " || type === "VP8L") {
|
|
150
|
+
if (dimensions || (seen.has("ALPH") && type !== "VP8 "))
|
|
151
|
+
return undefined;
|
|
152
|
+
const required = type === "VP8 " ? 10 : 5;
|
|
153
|
+
const data = length >= required ? await reader.get(start, required) : undefined;
|
|
154
|
+
if (!data)
|
|
155
|
+
return undefined;
|
|
156
|
+
dimensions = webpPayload(type, data);
|
|
157
|
+
if (!dimensions || !dimensions.width || !dimensions.height)
|
|
158
|
+
return undefined;
|
|
159
|
+
imageType = type;
|
|
160
|
+
losslessAlpha = type === "VP8L" && !!(data[4] & 0x10);
|
|
161
|
+
if (canvas && (canvas.width !== dimensions.width || canvas.height !== dimensions.height))
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
else if (type === "ICCP" || type === "ALPH" || type === "EXIF" || type === "XMP ") {
|
|
165
|
+
if (!canvas || !length)
|
|
166
|
+
return undefined;
|
|
167
|
+
if ((type === "ICCP" || type === "ALPH") && dimensions)
|
|
168
|
+
return undefined;
|
|
169
|
+
if ((type === "EXIF" || type === "XMP ") && !dimensions)
|
|
170
|
+
return undefined;
|
|
171
|
+
if (type === "ALPH") {
|
|
172
|
+
const data = await reader.get(start, 1);
|
|
173
|
+
if (!data || data[0] & 0xc0 || (data[0] & 3) > 1 || ((data[0] >> 4) & 3) > 1)
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else
|
|
178
|
+
return undefined;
|
|
179
|
+
if (length & 1) {
|
|
180
|
+
const padding = await reader.get(end - 1, 1);
|
|
181
|
+
if (!padding || padding[0] !== 0)
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
position = end;
|
|
185
|
+
}
|
|
186
|
+
if (position !== reader.byteLength || !dimensions)
|
|
187
|
+
return undefined;
|
|
188
|
+
if (canvas && (Boolean(flags & 0x20) !== seen.has("ICCP") || Boolean(flags & 0x08) !== seen.has("EXIF")
|
|
189
|
+
|| Boolean(flags & 0x04) !== seen.has("XMP ") || Boolean(flags & 0x10) !== (seen.has("ALPH") || losslessAlpha)))
|
|
190
|
+
return undefined;
|
|
191
|
+
if (!canvas && (seen.size !== 1 || (imageType !== "VP8 " && imageType !== "VP8L")))
|
|
192
|
+
return undefined;
|
|
193
|
+
// 即使最后一个压缩 payload 没有 padding,也确认声明的文件末字节存在。
|
|
194
|
+
return await reader.get(reader.byteLength - 1, 1) ? dimensions : undefined;
|
|
195
|
+
}
|
|
196
|
+
export function staticGif(head, byteLength) {
|
|
197
|
+
// GIF 的压缩块不能稀疏跳过证明帧数;超过已有头预算时仅提供下载。
|
|
198
|
+
if (head.length !== byteLength || head.length < 13)
|
|
199
|
+
return undefined;
|
|
200
|
+
const dimensions = { width: head.readUInt16LE(6), height: head.readUInt16LE(8) };
|
|
201
|
+
if (!dimensions.width || !dimensions.height)
|
|
202
|
+
return undefined;
|
|
203
|
+
const globalTable = !!(head[10] & 0x80);
|
|
204
|
+
let position = 13 + (globalTable ? 3 * 2 ** ((head[10] & 7) + 1) : 0);
|
|
205
|
+
let steps = 0;
|
|
206
|
+
let frames = 0;
|
|
207
|
+
let controlPending = false;
|
|
208
|
+
const step = () => ++steps <= MAX_STRUCTURE_STEPS;
|
|
209
|
+
const subblocks = (requireData) => {
|
|
210
|
+
let dataSeen = false;
|
|
211
|
+
while (position < head.length && step()) {
|
|
212
|
+
const length = head[position++];
|
|
213
|
+
if (!length)
|
|
214
|
+
return !requireData || dataSeen;
|
|
215
|
+
dataSeen = true;
|
|
216
|
+
position += length;
|
|
217
|
+
if (position > head.length)
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
return false;
|
|
221
|
+
};
|
|
222
|
+
while (position < head.length && step()) {
|
|
223
|
+
const type = head[position++];
|
|
224
|
+
if (type === 0x3b)
|
|
225
|
+
return frames === 1 && !controlPending && position === head.length ? dimensions : undefined;
|
|
226
|
+
if (type === 0x21) {
|
|
227
|
+
const label = head[position++];
|
|
228
|
+
if (label === 0xf9) {
|
|
229
|
+
if (controlPending || position + 6 > head.length || head[position] !== 4 || head[position + 5] !== 0)
|
|
230
|
+
return undefined;
|
|
231
|
+
const packed = head[position + 1];
|
|
232
|
+
if (packed & 0xe0 || ((packed >> 2) & 7) > 3)
|
|
233
|
+
return undefined;
|
|
234
|
+
controlPending = true;
|
|
235
|
+
position += 6;
|
|
236
|
+
}
|
|
237
|
+
else if (label === 0xfe) {
|
|
238
|
+
if (!subblocks(false))
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
// Plain Text 可产生额外渲染;应用扩展语义未知,不能证明静态。
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (type !== 0x2c || ++frames !== 1 || position + 9 > head.length)
|
|
248
|
+
return undefined;
|
|
249
|
+
const left = head.readUInt16LE(position);
|
|
250
|
+
const top = head.readUInt16LE(position + 2);
|
|
251
|
+
const width = head.readUInt16LE(position + 4);
|
|
252
|
+
const height = head.readUInt16LE(position + 6);
|
|
253
|
+
const packed = head[position + 8];
|
|
254
|
+
if (!width || !height || left + width > dimensions.width || top + height > dimensions.height || packed & 0x18)
|
|
255
|
+
return undefined;
|
|
256
|
+
const localTable = !!(packed & 0x80);
|
|
257
|
+
if (!globalTable && !localTable)
|
|
258
|
+
return undefined;
|
|
259
|
+
position += 9 + (localTable ? 3 * 2 ** ((packed & 7) + 1) : 0);
|
|
260
|
+
if (position >= head.length || head[position] < 2 || head[position] > 8)
|
|
261
|
+
return undefined;
|
|
262
|
+
position++;
|
|
263
|
+
if (!subblocks(true))
|
|
264
|
+
return undefined;
|
|
265
|
+
controlPending = false;
|
|
266
|
+
}
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=image-structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-structure.js","sourceRoot":"","sources":["../../src/files/image-structure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type SessionManager } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type PublishedFileMetadata } from "../vendor/protocol/session/index.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
export declare const PUBLISHED_FILE_TOOL_NAME: "publish_file";
|
|
5
|
+
export type ConfirmedPublication = {
|
|
6
|
+
id: string;
|
|
7
|
+
sourcePath: string;
|
|
8
|
+
metadata: PublishedFileMetadata;
|
|
9
|
+
groupId?: string;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
branchPosition: number;
|
|
12
|
+
};
|
|
13
|
+
/** 只存于原生 custom;不能把此 schema 当作 wire payload。 */
|
|
14
|
+
export declare const publicationDataSchema: z.ZodObject<{
|
|
15
|
+
file_name: z.ZodString;
|
|
16
|
+
mime_type: z.ZodString;
|
|
17
|
+
byte_length: z.ZodNumber;
|
|
18
|
+
tool_call_id: z.ZodString;
|
|
19
|
+
source_path: z.ZodString;
|
|
20
|
+
group_id: z.ZodOptional<z.ZodString>;
|
|
21
|
+
}, z.core.$strict>;
|
|
22
|
+
/**
|
|
23
|
+
* 原生成功 toolResult 是确认点,且 custom/result 两者均须已落盘。
|
|
24
|
+
* 失败 append 留下的内部孤立记录无资格;不尝试回滚 SDK 的 append-only 树。
|
|
25
|
+
*/
|
|
26
|
+
export declare function collectPublications(manager: SessionManager): Map<string, ConfirmedPublication>;
|