surf-cli 2.8.0 → 2.10.0
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 +146 -8
- package/native/abort.cjs +65 -0
- package/native/activity-journal.cjs +55 -0
- package/native/ai-queue.cjs +64 -0
- package/native/aistudio-build.cjs +21 -13
- package/native/aistudio-client.cjs +40 -20
- package/native/browser-lock.cjs +2 -2
- package/native/chatgpt-client.cjs +49 -31
- package/native/cli.cjs +352 -482
- package/native/client-transport.cjs +168 -0
- package/native/do-executor.cjs +68 -510
- package/native/do-parser.cjs +8 -249
- package/native/doctor.cjs +55 -5
- package/native/endpoint.cjs +174 -0
- package/native/file-transfer.cjs +734 -0
- package/native/gemini-client.cjs +156 -71
- package/native/grok-client.cjs +98 -89
- package/native/host-helpers.cjs +43 -26
- package/native/host-sessions.cjs +287 -0
- package/native/host.cjs +998 -620
- package/native/listener.cjs +20 -0
- package/native/mcp-server.cjs +60 -65
- package/native/network-export.cjs +116 -0
- package/native/network-store.cjs +38 -58
- package/native/perplexity-client.cjs +46 -17
- package/native/playbook-authoring.cjs +44 -0
- package/native/playbook-cli.cjs +157 -0
- package/native/playbook-client.cjs +259 -0
- package/native/playbook-receipts.cjs +109 -0
- package/native/playbook-records.cjs +208 -0
- package/native/playbook-runtime.cjs +177 -0
- package/native/playbooks.cjs +235 -0
- package/native/private-state.cjs +156 -0
- package/native/redaction.cjs +104 -0
- package/native/remote-auth.cjs +279 -0
- package/native/remote-transport.cjs +337 -0
- package/native/request-pending.cjs +148 -0
- package/native/socket-path.cjs +1 -1
- package/native/workflow-definition.cjs +368 -0
- package/native/workflow-runtime.cjs +225 -0
- package/package.json +9 -6
- package/playbooks/page/ops/read.json +22 -0
- package/playbooks/page/playbook.json +7 -0
- package/scripts/install-native-host.cjs +36 -5
- package/skills/README.md +11 -5
- package/skills/deep-x-research/SKILL.md +106 -0
- package/skills/surf/SKILL.md +72 -5
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
const { connectEndpoint } = require("./endpoint.cjs");
|
|
2
|
+
const { createFrameParser, createSocketWriter } = require("./remote-transport.cjs");
|
|
3
|
+
const { createClientTransferController } = require("./file-transfer.cjs");
|
|
4
|
+
|
|
5
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
6
|
+
|
|
7
|
+
async function openClientTransport(endpoint, { requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS } = {}) {
|
|
8
|
+
let socket;
|
|
9
|
+
let ready;
|
|
10
|
+
let readyReject;
|
|
11
|
+
let readyErrorListener;
|
|
12
|
+
ready = new Promise((resolve, reject) => {
|
|
13
|
+
readyReject = reject;
|
|
14
|
+
readyErrorListener = (error) => reject(error);
|
|
15
|
+
socket = connectEndpoint(endpoint, () => {
|
|
16
|
+
socket.removeListener("error", readyErrorListener);
|
|
17
|
+
resolve();
|
|
18
|
+
});
|
|
19
|
+
socket.once("error", readyErrorListener);
|
|
20
|
+
});
|
|
21
|
+
await ready;
|
|
22
|
+
const pending = new Map();
|
|
23
|
+
const writer = createSocketWriter(socket, { onOverflow: ({ error }) => socket.destroy(error) });
|
|
24
|
+
const transfers = endpoint.kind === "remote"
|
|
25
|
+
? createClientTransferController({ writer, onActivity: () => {} })
|
|
26
|
+
: null;
|
|
27
|
+
let transferChain = Promise.resolve();
|
|
28
|
+
let transferCleanupPromise = null;
|
|
29
|
+
const cleanupTransfers = (error) => {
|
|
30
|
+
if (!transfers) return Promise.resolve();
|
|
31
|
+
transferCleanupPromise ||= transfers.cleanup(error);
|
|
32
|
+
return transferCleanupPromise;
|
|
33
|
+
};
|
|
34
|
+
const rejectPending = (error) => {
|
|
35
|
+
for (const entry of pending.values()) {
|
|
36
|
+
clearTimeout(entry.timer);
|
|
37
|
+
entry.reject(error);
|
|
38
|
+
}
|
|
39
|
+
pending.clear();
|
|
40
|
+
};
|
|
41
|
+
const parser = createFrameParser({
|
|
42
|
+
onFrame(message) {
|
|
43
|
+
if (transfers && message.type && message.type.startsWith("transfer_")) {
|
|
44
|
+
transferChain = transferChain.then(() => transfers.handle(message)).catch((error) => {
|
|
45
|
+
rejectPending(error);
|
|
46
|
+
return writer.send({ type: "transfer_error", version: 1, transferId: message.transferId, error: error.message }).finally(() => socket.destroy(error)).then(() => { throw error; });
|
|
47
|
+
});
|
|
48
|
+
transferChain.catch(() => undefined);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (message.type === "extension_disconnected") {
|
|
52
|
+
transferChain = transferChain.then(() => {
|
|
53
|
+
const error = new Error(message.message || "Surf extension disconnected");
|
|
54
|
+
rejectPending(error);
|
|
55
|
+
socket.end();
|
|
56
|
+
});
|
|
57
|
+
transferChain.catch(() => undefined);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (message.id === undefined || !pending.has(message.id)) return;
|
|
61
|
+
transferChain = transferChain.then(() => {
|
|
62
|
+
const entry = pending.get(message.id);
|
|
63
|
+
if (!entry) return;
|
|
64
|
+
pending.delete(message.id);
|
|
65
|
+
clearTimeout(entry.timer);
|
|
66
|
+
entry.resolve(message);
|
|
67
|
+
}).catch((error) => {
|
|
68
|
+
rejectPending(error);
|
|
69
|
+
socket.destroy(error);
|
|
70
|
+
});
|
|
71
|
+
transferChain.catch(() => undefined);
|
|
72
|
+
},
|
|
73
|
+
onError(error) {
|
|
74
|
+
rejectPending(error);
|
|
75
|
+
readyReject?.(error);
|
|
76
|
+
socket.destroy();
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
socket.on("data", (chunk) => parser.push(chunk));
|
|
80
|
+
let closed = false;
|
|
81
|
+
socket.on("close", () => {
|
|
82
|
+
parser.close();
|
|
83
|
+
const error = new Error("connection closed");
|
|
84
|
+
rejectPending(error);
|
|
85
|
+
writer.close(error);
|
|
86
|
+
cleanupTransfers(error);
|
|
87
|
+
closed = true;
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
socket,
|
|
91
|
+
transfers,
|
|
92
|
+
async request(message, timeoutMs = requestTimeoutMs, transferPlan = {}) {
|
|
93
|
+
if (closed) throw new Error("client transport is closed");
|
|
94
|
+
if (message.id === undefined || message.id === null) throw new Error("request id is required");
|
|
95
|
+
if (pending.has(message.id)) throw new Error("duplicate request id");
|
|
96
|
+
const downloadIds = (transferPlan.downloads || []).map((download) => download.transferId);
|
|
97
|
+
const timeoutError = new Error("request timed out");
|
|
98
|
+
let rejectTimeout;
|
|
99
|
+
const timeout = new Promise((_, reject) => { rejectTimeout = reject; });
|
|
100
|
+
timeout.catch(() => undefined);
|
|
101
|
+
const timer = setTimeout(() => {
|
|
102
|
+
closed = true;
|
|
103
|
+
rejectPending(timeoutError);
|
|
104
|
+
cleanupTransfers(timeoutError).catch(() => {});
|
|
105
|
+
writer.close(timeoutError);
|
|
106
|
+
socket.destroy();
|
|
107
|
+
rejectTimeout(timeoutError);
|
|
108
|
+
}, timeoutMs);
|
|
109
|
+
const execute = async () => {
|
|
110
|
+
try {
|
|
111
|
+
if (transfers) {
|
|
112
|
+
for (const download of transferPlan.downloads || []) await transfers.expectDownload(download);
|
|
113
|
+
const uploadDescriptors = [];
|
|
114
|
+
for (const upload of transferPlan.uploads || []) {
|
|
115
|
+
const result = await transfers.upload(upload.path, upload);
|
|
116
|
+
uploadDescriptors.push({ transferId: result.transferId, field: upload.field, original: upload.original, kind: "upload" });
|
|
117
|
+
}
|
|
118
|
+
message = { ...message, _surfTransfers: { uploads: uploadDescriptors, downloads: (transferPlan.downloads || []).map(({ transferId, field, original }) => ({ transferId, field, original, kind: "download" })) }, _surfPaths: transferPlan.pathRefs || [] };
|
|
119
|
+
}
|
|
120
|
+
const response = new Promise((resolve, reject) => {
|
|
121
|
+
pending.set(message.id, { resolve, reject, timer });
|
|
122
|
+
});
|
|
123
|
+
response.catch(() => undefined);
|
|
124
|
+
try {
|
|
125
|
+
await writer.send(message);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
const entry = pending.get(message.id);
|
|
128
|
+
if (entry) { pending.delete(message.id); clearTimeout(entry.timer); entry.reject(error); }
|
|
129
|
+
await transfers?.cancelDownloads(downloadIds);
|
|
130
|
+
await response.catch(() => undefined);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
const result = await response;
|
|
134
|
+
if (result.error || (transfers && downloadIds.some((id) => transfers.hasDownload(id)))) {
|
|
135
|
+
await transfers?.cancelDownloads(downloadIds);
|
|
136
|
+
if (!result.error) throw new Error("request completed without completing output download");
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
await transfers?.cancelDownloads(downloadIds);
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
try {
|
|
145
|
+
return await Promise.race([execute(), timeout]);
|
|
146
|
+
} finally {
|
|
147
|
+
clearTimeout(timer);
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
async closeAsync() {
|
|
151
|
+
if (closed) return transferCleanupPromise;
|
|
152
|
+
closed = true;
|
|
153
|
+
const error = new Error("client transport is closed");
|
|
154
|
+
parser.close();
|
|
155
|
+
rejectPending(error);
|
|
156
|
+
writer.close(error);
|
|
157
|
+
const cleanup = cleanupTransfers(error);
|
|
158
|
+
socket.end();
|
|
159
|
+
await cleanup;
|
|
160
|
+
return cleanup;
|
|
161
|
+
},
|
|
162
|
+
close() {
|
|
163
|
+
return this.closeAsync();
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
module.exports = { DEFAULT_REQUEST_TIMEOUT_MS, openClientTransport };
|