wtt-connect 0.2.38 → 0.2.40
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/events.js +101 -0
- package/src/main.js +2 -1
- package/src/runner.js +7 -2
package/package.json
CHANGED
package/src/events.js
CHANGED
|
@@ -20,6 +20,9 @@ export function normalizeAgentEvent(adapterName, event) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
const generic = normalizeGenericCliEvent(event, now);
|
|
24
|
+
if (generic) return generic;
|
|
25
|
+
|
|
23
26
|
if (type === 'item.started') {
|
|
24
27
|
const kind = item.type || 'item';
|
|
25
28
|
if (kind === 'command_execution') return { kind: 'command', status: 'started', text: item.command || 'command', time: now, raw: event };
|
|
@@ -35,6 +38,103 @@ export function normalizeAgentEvent(adapterName, event) {
|
|
|
35
38
|
return null;
|
|
36
39
|
}
|
|
37
40
|
|
|
41
|
+
function normalizeGenericCliEvent(event, now) {
|
|
42
|
+
if (!event || typeof event !== 'object') return null;
|
|
43
|
+
const type = String(event.type || event.event || event.kind || '').trim();
|
|
44
|
+
const status = statusFromEvent(event, type);
|
|
45
|
+
const item = event.item || event.delta || event.message || event.data || {};
|
|
46
|
+
const typeText = `${type} ${String(item.type || item.kind || '').trim()}`.toLowerCase();
|
|
47
|
+
|
|
48
|
+
const error = event.error?.message || event.error || event.message?.error || '';
|
|
49
|
+
if (error && (typeText.includes('error') || String(error).trim())) {
|
|
50
|
+
return { kind: 'error', status: 'failed', text: String(error), time: now, raw: event };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const command = firstString(
|
|
54
|
+
event.command,
|
|
55
|
+
event.cmd,
|
|
56
|
+
event.args && Array.isArray(event.args) ? event.args.join(' ') : '',
|
|
57
|
+
item.command,
|
|
58
|
+
item.cmd,
|
|
59
|
+
item.input?.command,
|
|
60
|
+
item.arguments?.command,
|
|
61
|
+
);
|
|
62
|
+
if (command || includesAny(typeText, ['command', 'exec', 'shell', 'terminal'])) {
|
|
63
|
+
return { kind: 'command', status, text: command || labelFromEvent(event, item) || 'command', time: now, raw: event };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const toolName = firstString(
|
|
67
|
+
event.tool,
|
|
68
|
+
event.tool_name,
|
|
69
|
+
event.name,
|
|
70
|
+
event.call?.name,
|
|
71
|
+
item.tool,
|
|
72
|
+
item.tool_name,
|
|
73
|
+
item.name,
|
|
74
|
+
item.call?.name,
|
|
75
|
+
item.function?.name,
|
|
76
|
+
);
|
|
77
|
+
if (toolName || includesAny(typeText, ['tool', 'function_call', 'mcp'])) {
|
|
78
|
+
const summary = summarizeToolInput(event.input || item.input || item.arguments || event.arguments);
|
|
79
|
+
return { kind: 'tool', status, text: summary ? `${toolName || 'tool'} ${summary}` : (toolName || labelFromEvent(event, item) || 'tool'), time: now, raw: event };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const query = firstString(event.query, item.query, event.search_query, item.search_query);
|
|
83
|
+
if (query || includesAny(typeText, ['web_search', 'search'])) {
|
|
84
|
+
return { kind: 'web_search', status, text: query || labelFromEvent(event, item) || 'web search', time: now, raw: event };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (includesAny(typeText, ['reason', 'thinking', 'analysis'])) {
|
|
88
|
+
return { kind: 'reasoning', status, text: labelFromEvent(event, item) || 'thinking', time: now, raw: event };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (includesAny(typeText, ['message', 'assistant', 'response', 'output', 'answer'])) {
|
|
92
|
+
const text = firstString(event.text, event.content, event.delta, item.text, item.content);
|
|
93
|
+
return { kind: 'response', status, text: text ? String(text).slice(0, 120) : 'writing response', time: now, raw: event };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (includesAny(typeText, ['session', 'thread', 'turn'])) {
|
|
97
|
+
return { kind: 'session', status, text: labelFromEvent(event, item) || 'session', time: now, raw: event };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function statusFromEvent(event, type) {
|
|
104
|
+
const raw = `${String(event.status || event.state || '')} ${type}`.toLowerCase();
|
|
105
|
+
if (includesAny(raw, ['fail', 'error'])) return 'failed';
|
|
106
|
+
if (includesAny(raw, ['complete', 'completed', 'done', 'finish', 'finished', 'end', 'ended', 'stop', 'stopped'])) return 'completed';
|
|
107
|
+
return 'started';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function labelFromEvent(event, item) {
|
|
111
|
+
return firstString(
|
|
112
|
+
event.title,
|
|
113
|
+
event.label,
|
|
114
|
+
event.message,
|
|
115
|
+
event.summary,
|
|
116
|
+
item.title,
|
|
117
|
+
item.label,
|
|
118
|
+
item.message,
|
|
119
|
+
item.summary,
|
|
120
|
+
item.type,
|
|
121
|
+
event.type,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function firstString(...values) {
|
|
126
|
+
for (const value of values) {
|
|
127
|
+
if (value == null) continue;
|
|
128
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
129
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
130
|
+
}
|
|
131
|
+
return '';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function includesAny(value, needles) {
|
|
135
|
+
return needles.some((needle) => value.includes(needle));
|
|
136
|
+
}
|
|
137
|
+
|
|
38
138
|
export function renderStatusLine(evt) {
|
|
39
139
|
if (!evt) return '';
|
|
40
140
|
return `[TASK_STATUS] time=${evt.time} status=${evt.status} action=${evt.kind}:${String(evt.text || '').slice(0, 180)}`;
|
|
@@ -49,6 +149,7 @@ export function renderActivityText(evt, adapterName = 'agent') {
|
|
|
49
149
|
if (evt.kind === 'command') return `${adapter} 正在执行命令:${text || 'command'}`;
|
|
50
150
|
if (evt.kind === 'tool') return `${adapter} 正在调用工具:${text || 'tool'}`;
|
|
51
151
|
if (evt.kind === 'web_search') return `${adapter} 正在搜索:${text || 'web search'}`;
|
|
152
|
+
if (evt.kind === 'reasoning') return `${adapter} 正在推理:${text || 'thinking'}`;
|
|
52
153
|
if (evt.kind === 'response') return `${adapter} 正在组织回复`;
|
|
53
154
|
if (evt.kind === 'session') return `${adapter} 会话已启动`;
|
|
54
155
|
return `${adapter} 正在执行:${text || labelKind(evt.kind)}`;
|
package/src/main.js
CHANGED
|
@@ -150,7 +150,8 @@ async function previewPort(config, argv) {
|
|
|
150
150
|
});
|
|
151
151
|
const url = preview.preview_url || preview.url;
|
|
152
152
|
if (!url) throw new Error('Cloud Sandbox preview API returned no URL');
|
|
153
|
-
const
|
|
153
|
+
const title = String(argv.title || argv.previewName || preview.name || 'Cloud Sandbox Preview').trim();
|
|
154
|
+
const markdown = preview.markdown || `[preview_url:${title}](${url})`;
|
|
154
155
|
const topicId = String(argv.topicId || argv.sourceId || '').trim();
|
|
155
156
|
let published = null;
|
|
156
157
|
if (topicId) {
|
package/src/runner.js
CHANGED
|
@@ -937,10 +937,15 @@ function renderCloudSandboxStorageInstruction(config, topicId = '') {
|
|
|
937
937
|
'- Do not install large dependencies, clone large repositories, download datasets, or write bulky generated files into the workspace unless the user explicitly asks for local temporary storage.',
|
|
938
938
|
'- Keep only source edits, small manifests, notes, and temporary scratch files in the workspace.',
|
|
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
|
-
'- If
|
|
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
|
+
'- 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
|
+
'- Before publishing a preview URL, verify the server is actually listening and serving content with `curl -fsS http://127.0.0.1:<port>/ >/dev/null`.',
|
|
944
|
+
'- If local curl fails, fix or restart the web server first. Never publish a preview URL for a dead port.',
|
|
945
|
+
'- If you start a web server in the sandbox and the user should preview it, call the Cloudflare Sandbox outbound Worker directly with curl and include the returned `preview_url` in your reply as `[preview_url:Short Title](<preview_url>)`.',
|
|
941
946
|
'- Preview ports must be 1024-65535 and cannot be 3000. For Vite/Next-style dev servers prefer 5173, 4173, or 8080.',
|
|
942
947
|
`- Preview curl rule: curl -sS -X POST "\${WTT_SANDBOX_OUTBOX_URL:-http://wtt.preview}/preview-port" -H 'content-type: application/json' -d '{"agent_id":"'\${WTT_AGENT_ID:-cloud-agent}'","port":<port>}'`,
|
|
943
|
-
`-
|
|
948
|
+
`- Prefer automatic WTT publishing when topic id is available: wtt-connect preview-port --port <port>${topicId ? ` --topic-id ${topicId}` : ' --topic-id <topic_id>'} --title "Short Title". This publishes a cloud_sandbox_preview card and is a convenience wrapper around the same sandbox outbound Worker.`,
|
|
944
949
|
);
|
|
945
950
|
return lines.join('\n');
|
|
946
951
|
}
|