wtt-connect 0.2.53 → 0.2.55
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/package.json +1 -1
- package/src/main.js +3 -2
- package/src/runner.js +4 -3
- package/src/terminal-session.js +92 -7
package/package.json
CHANGED
package/src/main.js
CHANGED
|
@@ -208,7 +208,8 @@ async function previewPort(config, argv) {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
async function assertLocalPreviewPort(port, timeoutMs = 5000) {
|
|
211
|
-
const
|
|
211
|
+
const host = String(process.env.WTT_CONNECT_PREVIEW_CHECK_HOST || '10.0.0.1').trim() || '10.0.0.1';
|
|
212
|
+
const url = `http://${host}:${port}/`;
|
|
212
213
|
try {
|
|
213
214
|
await fetch(url, {
|
|
214
215
|
method: 'GET',
|
|
@@ -216,7 +217,7 @@ async function assertLocalPreviewPort(port, timeoutMs = 5000) {
|
|
|
216
217
|
});
|
|
217
218
|
} catch (error) {
|
|
218
219
|
const message = error instanceof Error ? error.message : String(error);
|
|
219
|
-
throw new Error(`preview-port
|
|
220
|
+
throw new Error(`preview-port server is not reachable from the Cloudflare preview network at ${url}. Bind the dev server to 0.0.0.0 instead of 127.0.0.1 or localhost: ${message}`);
|
|
220
221
|
}
|
|
221
222
|
}
|
|
222
223
|
|
package/src/runner.js
CHANGED
|
@@ -939,9 +939,10 @@ function renderCloudSandboxStorageInstruction(config, topicId = '') {
|
|
|
939
939
|
'- If a generated user-facing file is stored in R2/persistent output storage and should appear in WTT chat, still publish it with `wtt-connect upload-file` or a WTT artifact marker.',
|
|
940
940
|
'- If the user asks for a visual page, HTML artifact, chart, dashboard, animation, or browser preview, build it in the workspace, start a local web server, create a Cloudflare Sandbox preview URL, and return that preview URL to WTT.',
|
|
941
941
|
'- The Cloud Sandbox preview URL feature is only for WTT Cloud Sandbox agents. Do not use WTT backend artifact/media preview flows for live sandbox web servers.',
|
|
942
|
-
'- Preview servers must keep running after your command finishes. Start them in the background with `nohup ... >/tmp/<name>.log 2>&1 &` or an equivalent long-lived process, and bind to `0.0.0.0`.',
|
|
943
|
-
'-
|
|
944
|
-
'-
|
|
942
|
+
'- Preview servers must keep running after your command finishes. Start them in the background with `nohup ... >/tmp/<name>.log 2>&1 &` or an equivalent long-lived process, and bind to `0.0.0.0`, not `127.0.0.1` or `localhost`.',
|
|
943
|
+
'- For Vite use `npm run dev -- --host 0.0.0.0 --port <port>`; for Next use `next dev -H 0.0.0.0 -p <port>`; for Python use `python3 -m http.server <port> --bind 0.0.0.0`.',
|
|
944
|
+
'- Before publishing a preview URL, verify the server is reachable from the Cloudflare preview network with `curl -fsS http://10.0.0.1:<port>/ >/dev/null`.',
|
|
945
|
+
'- If that curl fails, fix or restart the web server first. Never publish a preview URL for a dead or localhost-only port.',
|
|
945
946
|
'- `wtt-connect preview-port` automatically keeps the most recent preview servers for this agent and stops older registered previews beyond the retention limit. Default retention is 3 live previews; use `--keep-last <n>` only when the user asks.',
|
|
946
947
|
'- Do not pass `--snapshot-dir` for normal live previews. Live Cloud Sandbox previews should render as inline WTT preview cards, not as ordinary index.html/document artifacts.',
|
|
947
948
|
'- If the user explicitly asks for a persistent static artifact or downloadable HTML, publish that separately with `wtt-connect upload-artifact --dir <dir> --title "Short Title"` instead of mixing it with the live preview URL.',
|
package/src/terminal-session.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
4
5
|
import { createRequire } from 'node:module';
|
|
5
6
|
import { log } from './logger.js';
|
|
6
7
|
|
|
7
8
|
const require = createRequire(import.meta.url);
|
|
9
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
8
10
|
let nodePty = null;
|
|
11
|
+
let nodePtyPackageJson = null;
|
|
9
12
|
|
|
10
13
|
export class TerminalSessionManager {
|
|
11
14
|
constructor(config, sendJson) {
|
|
@@ -31,8 +34,8 @@ export class TerminalSessionManager {
|
|
|
31
34
|
const rows = clampInt(msg.rows, 10, 80, 28);
|
|
32
35
|
|
|
33
36
|
try {
|
|
34
|
-
ensureNodePtyHelperExecutable();
|
|
35
37
|
const pty = await loadNodePty();
|
|
38
|
+
ensureNodePtyHelperExecutable();
|
|
36
39
|
const proc = pty.spawn(shell, [], {
|
|
37
40
|
name: 'xterm-256color',
|
|
38
41
|
cols,
|
|
@@ -122,12 +125,24 @@ export class TerminalSessionManager {
|
|
|
122
125
|
|
|
123
126
|
async function loadNodePty() {
|
|
124
127
|
if (nodePty) return nodePty;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
const errors = [];
|
|
129
|
+
for (const candidate of nodePtyCandidates()) {
|
|
130
|
+
try {
|
|
131
|
+
if (candidate.kind === 'require') {
|
|
132
|
+
nodePty = require(candidate.spec);
|
|
133
|
+
} else {
|
|
134
|
+
nodePty = await import(candidate.spec);
|
|
135
|
+
}
|
|
136
|
+
nodePtyPackageJson = candidate.packageJson || resolveNodePtyPackageJson(candidate.spec);
|
|
137
|
+
return nodePty.default && nodePty.default.spawn ? nodePty.default : nodePty;
|
|
138
|
+
} catch (err) {
|
|
139
|
+
errors.push(`${candidate.label}: ${err.message}`);
|
|
140
|
+
}
|
|
130
141
|
}
|
|
142
|
+
|
|
143
|
+
throw new Error(
|
|
144
|
+
`node-pty is not installed or failed to load; terminal sessions are unavailable in this runtime: ${errors.join('; ')}`,
|
|
145
|
+
);
|
|
131
146
|
}
|
|
132
147
|
|
|
133
148
|
function resolveDir(dir) {
|
|
@@ -153,7 +168,7 @@ function clampInt(value, min, max, fallback) {
|
|
|
153
168
|
function ensureNodePtyHelperExecutable() {
|
|
154
169
|
if (os.platform() !== 'darwin') return;
|
|
155
170
|
try {
|
|
156
|
-
const pkg =
|
|
171
|
+
const pkg = nodePtyPackageJson || resolveNodePtyPackageJson('node-pty');
|
|
157
172
|
const arch = os.arch() === 'arm64' ? 'arm64' : 'x64';
|
|
158
173
|
const helper = path.join(path.dirname(pkg), 'prebuilds', `darwin-${arch}`, 'spawn-helper');
|
|
159
174
|
if (fs.existsSync(helper)) fs.chmodSync(helper, 0o755);
|
|
@@ -161,3 +176,73 @@ function ensureNodePtyHelperExecutable() {
|
|
|
161
176
|
// node-pty may be source-built or installed in a different layout.
|
|
162
177
|
}
|
|
163
178
|
}
|
|
179
|
+
|
|
180
|
+
function nodePtyCandidates() {
|
|
181
|
+
const candidates = [
|
|
182
|
+
{ kind: 'import', spec: 'node-pty', label: 'default node resolution', packageJson: null },
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
for (const root of globalModuleRoots()) {
|
|
186
|
+
addNodePtyCandidate(candidates, path.join(root, 'node-pty'));
|
|
187
|
+
addNodePtyCandidate(candidates, path.join(root, '@google', 'gemini-cli', 'node_modules', 'node-pty'));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return candidates;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function addNodePtyCandidate(candidates, packageDir) {
|
|
194
|
+
const packageJson = path.join(packageDir, 'package.json');
|
|
195
|
+
if (!fs.existsSync(packageJson)) return;
|
|
196
|
+
candidates.push({
|
|
197
|
+
kind: 'require',
|
|
198
|
+
spec: packageDir,
|
|
199
|
+
label: packageDir,
|
|
200
|
+
packageJson,
|
|
201
|
+
});
|
|
202
|
+
const libIndex = path.join(packageDir, 'lib', 'index.js');
|
|
203
|
+
if (fs.existsSync(libIndex)) {
|
|
204
|
+
candidates.push({
|
|
205
|
+
kind: 'import',
|
|
206
|
+
spec: pathToFileURL(libIndex).href,
|
|
207
|
+
label: libIndex,
|
|
208
|
+
packageJson,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function globalModuleRoots() {
|
|
214
|
+
const roots = new Set();
|
|
215
|
+
const addRoot = (root) => {
|
|
216
|
+
if (root && fs.existsSync(root)) roots.add(fs.realpathSync(root));
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const localRoot = path.resolve(moduleDir, '..', '..');
|
|
220
|
+
addRoot(localRoot);
|
|
221
|
+
addRoot(path.join(process.execPath ? path.dirname(path.dirname(process.execPath)) : '', 'lib', 'node_modules'));
|
|
222
|
+
|
|
223
|
+
if (process.env.npm_config_prefix) {
|
|
224
|
+
addRoot(path.join(process.env.npm_config_prefix, 'lib', 'node_modules'));
|
|
225
|
+
}
|
|
226
|
+
if (process.env.PREFIX) {
|
|
227
|
+
addRoot(path.join(process.env.PREFIX, 'lib', 'node_modules'));
|
|
228
|
+
}
|
|
229
|
+
if (process.env.HOME) {
|
|
230
|
+
addRoot(path.join(process.env.HOME, '.local', 'lib', 'node_modules'));
|
|
231
|
+
addRoot(path.join(process.env.HOME, '.npm-global', 'lib', 'node_modules'));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return Array.from(roots);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function resolveNodePtyPackageJson(spec) {
|
|
238
|
+
try {
|
|
239
|
+
return require.resolve('node-pty/package.json');
|
|
240
|
+
} catch {
|
|
241
|
+
if (spec && spec !== 'node-pty') {
|
|
242
|
+
const packageDir = spec.endsWith('node-pty') ? spec : path.dirname(path.dirname(fileURLToPath(spec)));
|
|
243
|
+
const packageJson = path.join(packageDir, 'package.json');
|
|
244
|
+
if (fs.existsSync(packageJson)) return packageJson;
|
|
245
|
+
}
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
}
|