surf-cli 2.7.2 → 2.9.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 +208 -13
- package/dist/content/index.js +116 -0
- package/dist/content/index.js.map +1 -0
- package/dist/manifest.json +2 -11
- package/dist/options/options.js +3 -3
- package/dist/options/options.js.map +1 -1
- package/dist/service-worker/index.js +261 -61
- package/dist/service-worker/index.js.map +1 -1
- package/native/abort.cjs +65 -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 +169 -0
- package/native/chatgpt-client.cjs +63 -30
- package/native/cli.cjs +947 -460
- package/native/client-transport.cjs +168 -0
- package/native/config.cjs +2 -2
- package/native/do-executor.cjs +25 -51
- package/native/do-parser.cjs +12 -0
- package/native/doctor.cjs +633 -0
- package/native/endpoint.cjs +174 -0
- package/native/file-transfer.cjs +734 -0
- package/native/gemini-client.cjs +244 -88
- package/native/grok-client.cjs +321 -212
- package/native/host-helpers.cjs +88 -16
- package/native/host-sessions.cjs +283 -0
- package/native/host.cjs +811 -616
- package/native/listener.cjs +20 -0
- package/native/mcp-server.cjs +60 -62
- package/native/network-export.cjs +113 -0
- package/native/perplexity-client.cjs +46 -17
- 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 +46 -0
- package/package.json +11 -9
- package/scripts/install-native-host.cjs +184 -51
- package/scripts/uninstall-native-host.cjs +93 -15
- package/skills/README.md +11 -5
- package/skills/deep-x-research/SKILL.md +106 -0
- package/skills/surf/SKILL.md +77 -22
- package/dist/content/accessibility-tree.js +0 -11
- package/dist/content/accessibility-tree.js.map +0 -1
- package/dist/content/visual-indicator.js +0 -111
- package/dist/content/visual-indicator.js.map +0 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
const net = require("net");
|
|
2
|
+
const { DEFAULT_SOCKET_PATH } = require("./socket-path.cjs");
|
|
3
|
+
const { authenticateClient } = require("./remote-transport.cjs");
|
|
4
|
+
|
|
5
|
+
function parseRemoteEndpoint(value) {
|
|
6
|
+
if (typeof value !== "string" || !value) throw new Error("--remote requires host:port");
|
|
7
|
+
let host;
|
|
8
|
+
let portText;
|
|
9
|
+
if (value.startsWith("[")) {
|
|
10
|
+
const match = value.match(/^\[([^\]]+)\]:(\d+)$/);
|
|
11
|
+
if (!match || net.isIP(match?.[1]) !== 6) throw new Error("remote endpoint must use a bracketed IPv6 address and port");
|
|
12
|
+
[, host, portText] = match;
|
|
13
|
+
host = new URL(`http://[${host}]`).hostname.slice(1, -1);
|
|
14
|
+
} else {
|
|
15
|
+
const match = value.match(/^([^:]+):(\d+)$/);
|
|
16
|
+
if (!match) throw new Error("remote endpoint must be host:port (IPv6 must be bracketed)");
|
|
17
|
+
[, host, portText] = match;
|
|
18
|
+
if (host.includes("/") || host.includes("@") || host.includes(":") || host === "*" || host.includes("*")) throw new Error("remote endpoint host is invalid");
|
|
19
|
+
if ((/^\d+(?:\.\d+){3}$/.test(host) && net.isIP(host) !== 4) || (net.isIP(host) !== 4 && !/^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)*[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/.test(host))) {
|
|
20
|
+
throw new Error("remote endpoint host is invalid");
|
|
21
|
+
}
|
|
22
|
+
host = host.toLowerCase();
|
|
23
|
+
}
|
|
24
|
+
const port = Number(portText);
|
|
25
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error("remote endpoint port must be between 1 and 65535");
|
|
26
|
+
if ((net.isIP(host) === 4 && host === "0.0.0.0") || (net.isIP(host) === 6 && /^0*:?0*$/.test(host.replace(/:/g, "")))) {
|
|
27
|
+
throw new Error("remote endpoint host must not be unspecified");
|
|
28
|
+
}
|
|
29
|
+
const display = net.isIP(host) === 6 ? `[${host}]:${port}` : `${host}:${port}`;
|
|
30
|
+
return { kind: "remote", host, port, display, key: `tcp:${display}`, connectionOptions: { host, port } };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function selectEndpoint(args, env) {
|
|
34
|
+
const selectedEnv = env === undefined ? process.env : env;
|
|
35
|
+
const remoteIndexes = [];
|
|
36
|
+
const credentialIndexes = [];
|
|
37
|
+
for (let i = 0; i < args.length; i++) {
|
|
38
|
+
if (args[i] === "--remote") remoteIndexes.push(i);
|
|
39
|
+
if (args[i] === "--remote-credential") credentialIndexes.push(i);
|
|
40
|
+
}
|
|
41
|
+
if (remoteIndexes.length > 1) throw new Error("--remote may only be specified once");
|
|
42
|
+
if (credentialIndexes.length > 1) throw new Error("--remote-credential may only be specified once");
|
|
43
|
+
let cliRemote;
|
|
44
|
+
let cliCredential;
|
|
45
|
+
const strippedArgs = [...args];
|
|
46
|
+
if (remoteIndexes.length) {
|
|
47
|
+
const index = remoteIndexes[0];
|
|
48
|
+
cliRemote = args[index + 1];
|
|
49
|
+
if (!cliRemote || cliRemote.startsWith("--")) throw new Error("--remote requires host:port");
|
|
50
|
+
strippedArgs.splice(index, 2);
|
|
51
|
+
}
|
|
52
|
+
if (credentialIndexes.length) {
|
|
53
|
+
const index = credentialIndexes[0];
|
|
54
|
+
cliCredential = args[index + 1];
|
|
55
|
+
if (!cliCredential || cliCredential.startsWith("--")) throw new Error("--remote-credential requires a file path");
|
|
56
|
+
const adjustedIndex = index - (remoteIndexes.length && index > remoteIndexes[0] ? 2 : 0);
|
|
57
|
+
strippedArgs.splice(adjustedIndex, 2);
|
|
58
|
+
}
|
|
59
|
+
const remoteValue = cliRemote || selectedEnv.SURF_REMOTE;
|
|
60
|
+
if (remoteValue) {
|
|
61
|
+
const credentialPath = cliCredential || selectedEnv.SURF_REMOTE_CREDENTIAL;
|
|
62
|
+
if (!credentialPath) throw new Error("remote endpoint requires --remote-credential <path> or SURF_REMOTE_CREDENTIAL");
|
|
63
|
+
return { args: strippedArgs, endpoint: { ...parseRemoteEndpoint(remoteValue), credentialPath } };
|
|
64
|
+
}
|
|
65
|
+
if (cliCredential) throw new Error("--remote-credential requires a remote endpoint");
|
|
66
|
+
const socketPath = selectedEnv.SURF_SOCKET || DEFAULT_SOCKET_PATH;
|
|
67
|
+
return { args: strippedArgs, endpoint: { kind: "local", path: socketPath, display: socketPath, key: `unix:${socketPath}`, connectionOptions: socketPath } };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function createRemoteSocket(endpoint) {
|
|
71
|
+
const rawSocket = net.createConnection(endpoint.connectionOptions, () => {});
|
|
72
|
+
let ready = false;
|
|
73
|
+
let connected = false;
|
|
74
|
+
let destroyed = false;
|
|
75
|
+
const pending = new Map();
|
|
76
|
+
const queue = (event, listener, once) => {
|
|
77
|
+
if (ready) {
|
|
78
|
+
once ? rawSocket.once(event, listener) : rawSocket.on(event, listener);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const listeners = pending.get(event) || [];
|
|
82
|
+
listeners.push({ listener, once });
|
|
83
|
+
pending.set(event, listeners);
|
|
84
|
+
};
|
|
85
|
+
const flush = () => {
|
|
86
|
+
ready = true;
|
|
87
|
+
for (const [event, listeners] of pending) {
|
|
88
|
+
for (const { listener, once } of listeners) {
|
|
89
|
+
once ? rawSocket.once(event, listener) : rawSocket.on(event, listener);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
pending.clear();
|
|
93
|
+
};
|
|
94
|
+
const proxy = {
|
|
95
|
+
on(event, listener) { queue(event, listener, false); return proxy; },
|
|
96
|
+
once(event, listener) { queue(event, listener, true); return proxy; },
|
|
97
|
+
removeListener(event, listener) {
|
|
98
|
+
if (ready) rawSocket.removeListener(event, listener);
|
|
99
|
+
else pending.set(event, (pending.get(event) || []).filter((entry) => entry.listener !== listener));
|
|
100
|
+
return proxy;
|
|
101
|
+
},
|
|
102
|
+
write(...args) { return rawSocket.write(...args); },
|
|
103
|
+
end(...args) { return rawSocket.end(...args); },
|
|
104
|
+
destroy(...args) { destroyed = true; return rawSocket.destroy(...args); },
|
|
105
|
+
setTimeout(...args) { rawSocket.setTimeout(...args); return proxy; },
|
|
106
|
+
get authenticated() { return ready; },
|
|
107
|
+
get connected() { return connected; },
|
|
108
|
+
};
|
|
109
|
+
rawSocket.once("connect", () => { connected = true; });
|
|
110
|
+
rawSocket.on("error", (error) => {
|
|
111
|
+
if (ready || destroyed) return;
|
|
112
|
+
ready = true;
|
|
113
|
+
const listeners = pending.get("error") || [];
|
|
114
|
+
pending.delete("error");
|
|
115
|
+
for (const { listener } of listeners) listener(error);
|
|
116
|
+
if (proxy.__pendingErrors) proxy.__pendingErrors.length = 0;
|
|
117
|
+
flush();
|
|
118
|
+
});
|
|
119
|
+
rawSocket.on("close", () => {
|
|
120
|
+
if (ready) return;
|
|
121
|
+
ready = true;
|
|
122
|
+
const error = new Error("remote authentication connection closed");
|
|
123
|
+
for (const { listener } of pending.get("error") || []) listener(error);
|
|
124
|
+
for (const { listener } of pending.get("close") || []) listener();
|
|
125
|
+
pending.clear();
|
|
126
|
+
if (proxy.__pendingErrors) proxy.__pendingErrors.length = 0;
|
|
127
|
+
});
|
|
128
|
+
return { rawSocket, proxy, flush };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function connectEndpoint(endpoint, onConnect) {
|
|
132
|
+
if (endpoint.kind === "local") {
|
|
133
|
+
return net.createConnection(endpoint.connectionOptions, onConnect || (() => {}));
|
|
134
|
+
}
|
|
135
|
+
const { rawSocket, proxy, flush } = createRemoteSocket(endpoint);
|
|
136
|
+
rawSocket.once("connect", () => {
|
|
137
|
+
authenticateClient(rawSocket, endpoint.credentialPath)
|
|
138
|
+
.then(() => {
|
|
139
|
+
flush();
|
|
140
|
+
if (onConnect) onConnect(proxy);
|
|
141
|
+
})
|
|
142
|
+
.catch((error) => {
|
|
143
|
+
error.code = error.code || "EAUTH";
|
|
144
|
+
const listeners = proxy.__pendingErrors || [];
|
|
145
|
+
for (const listener of listeners) {
|
|
146
|
+
proxy.removeListener("error", listener);
|
|
147
|
+
listener(error);
|
|
148
|
+
}
|
|
149
|
+
proxy.__pendingErrors.length = 0;
|
|
150
|
+
flush();
|
|
151
|
+
proxy.destroy();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
proxy.__pendingErrors = [];
|
|
155
|
+
const originalOn = proxy.on;
|
|
156
|
+
const originalOnce = proxy.once;
|
|
157
|
+
proxy.on = (event, listener) => {
|
|
158
|
+
if (event === "error" && !proxy.authenticated) proxy.__pendingErrors.push(listener);
|
|
159
|
+
return originalOn(event, listener);
|
|
160
|
+
};
|
|
161
|
+
proxy.once = (event, listener) => {
|
|
162
|
+
if (event === "error" && !proxy.authenticated) proxy.__pendingErrors.push(listener);
|
|
163
|
+
return originalOnce(event, listener);
|
|
164
|
+
};
|
|
165
|
+
return proxy;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function formatEndpointError(error, endpoint, formatSocketError) {
|
|
169
|
+
if (endpoint.kind === "local") return formatSocketError(error);
|
|
170
|
+
const message = error?.message || String(error);
|
|
171
|
+
return `Remote endpoint connection failed (${endpoint.display}): ${message}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = { parseRemoteEndpoint, selectEndpoint, connectEndpoint, formatEndpointError };
|