wtt-connect 0.1.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 +295 -0
- package/bin/wtt-connect.js +7 -0
- package/package.json +32 -0
- package/scripts/install-agent.sh +4 -0
- package/scripts/install-launchd.sh +51 -0
- package/scripts/install-systemd-user.sh +189 -0
- package/scripts/uninstall-launchd.sh +18 -0
- package/scripts/uninstall-systemd-user.sh +42 -0
- package/src/adapter-registry.js +58 -0
- package/src/adapters/acp.js +196 -0
- package/src/adapters/claude-code.js +161 -0
- package/src/adapters/codex.js +88 -0
- package/src/adapters/generic-cli.js +258 -0
- package/src/adapters/index.js +28 -0
- package/src/adapters/openclaw.js +32 -0
- package/src/artifacts.js +68 -0
- package/src/attachments.js +181 -0
- package/src/config.js +118 -0
- package/src/env.js +42 -0
- package/src/events.js +23 -0
- package/src/logger.js +18 -0
- package/src/main.js +143 -0
- package/src/mime.js +12 -0
- package/src/permissions.js +28 -0
- package/src/runner.js +562 -0
- package/src/runtime-info.js +71 -0
- package/src/service-manager.js +510 -0
- package/src/session-manager.js +31 -0
- package/src/setup.js +71 -0
- package/src/shell-runner.js +184 -0
- package/src/smoke.js +99 -0
- package/src/store.js +58 -0
- package/src/stt.js +146 -0
- package/src/terminal-session.js +152 -0
- package/src/tts.js +40 -0
- package/src/wtt-api.js +79 -0
- package/src/wtt-client.js +144 -0
package/src/config.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseBool, parseIntEnv } from './env.js';
|
|
4
|
+
|
|
5
|
+
function wsUrl(base, agentId) {
|
|
6
|
+
let b = String(base || '').replace(/\/$/, '');
|
|
7
|
+
if (b.endsWith('/api/v1')) b = b.slice(0, -7);
|
|
8
|
+
return `${b.replace(/^https:/, 'wss:').replace(/^http:/, 'ws:')}/ws/${agentId}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function apiUrl(base) {
|
|
12
|
+
return String(base || '').replace(/\/$/, '');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function readJsonConfig(file) {
|
|
16
|
+
if (!file) return {};
|
|
17
|
+
const resolved = path.resolve(file);
|
|
18
|
+
if (!fs.existsSync(resolved)) return {};
|
|
19
|
+
return JSON.parse(fs.readFileSync(resolved, 'utf8'));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function loadConfig(argv = {}) {
|
|
23
|
+
const fileConfig = readJsonConfig(argv.config || process.env.WTT_CONNECT_CONFIG);
|
|
24
|
+
const base = process.env.WTT_BASE_URL || fileConfig.wttBaseUrl || 'https://www.waxbyte.com';
|
|
25
|
+
const agentId = process.env.WTT_AGENT_ID || fileConfig.agentId || '';
|
|
26
|
+
const token = process.env.WTT_TOKEN || process.env.WTT_AGENT_TOKEN || fileConfig.token || '';
|
|
27
|
+
const adapter = process.env.WTT_CONNECT_ADAPTER || fileConfig.adapter || 'codex';
|
|
28
|
+
const adapters = String(process.env.WTT_CONNECT_ADAPTERS || fileConfig.adapters || adapter).split(',').map((x) => x.trim()).filter(Boolean);
|
|
29
|
+
const workDir = process.env.WTT_CONNECT_WORKDIR || fileConfig.workDir || process.cwd();
|
|
30
|
+
const stateDir = process.env.WTT_CONNECT_STATE_DIR || fileConfig.stateDir || path.join(workDir, '.wtt-connect');
|
|
31
|
+
return {
|
|
32
|
+
configFile: argv.config || process.env.WTT_CONNECT_CONFIG || '',
|
|
33
|
+
envFile: argv.envFile || process.env.WTT_CONNECT_ENV_FILE || '',
|
|
34
|
+
wttBaseUrl: base,
|
|
35
|
+
wttWsUrl: process.env.WTT_WS_URL || fileConfig.wttWsUrl || wsUrl(base, agentId),
|
|
36
|
+
agentId,
|
|
37
|
+
token,
|
|
38
|
+
httpToken: process.env.WTT_HTTP_TOKEN || fileConfig.httpToken || '',
|
|
39
|
+
adapter,
|
|
40
|
+
adapters,
|
|
41
|
+
adapterPolicy: fileConfig.adapterPolicy || parseJsonEnv(process.env.WTT_CONNECT_ADAPTER_POLICY, {}),
|
|
42
|
+
workDir,
|
|
43
|
+
model: process.env.WTT_CONNECT_MODEL || fileConfig.model || '',
|
|
44
|
+
mode: process.env.WTT_CONNECT_MODE || fileConfig.mode || 'full-auto',
|
|
45
|
+
allowYolo: parseBool(process.env.WTT_CONNECT_ALLOW_YOLO, fileConfig.allowYolo ?? false),
|
|
46
|
+
reasoningEffort: process.env.WTT_CONNECT_REASONING_EFFORT || fileConfig.reasoningEffort || 'low',
|
|
47
|
+
codexBin: process.env.WTT_CODEX_BIN || fileConfig.codexBin || 'codex',
|
|
48
|
+
claudeBin: process.env.WTT_CLAUDE_BIN || fileConfig.claudeBin || 'claude',
|
|
49
|
+
cursorBin: process.env.WTT_CURSOR_BIN || fileConfig.cursorBin || 'agent',
|
|
50
|
+
geminiBin: process.env.WTT_GEMINI_BIN || fileConfig.geminiBin || 'gemini',
|
|
51
|
+
qoderBin: process.env.WTT_QODER_BIN || fileConfig.qoderBin || 'qodercli',
|
|
52
|
+
opencodeBin: process.env.WTT_OPENCODE_BIN || fileConfig.opencodeBin || 'opencode',
|
|
53
|
+
crushBin: process.env.WTT_CRUSH_BIN || fileConfig.crushBin || 'crush',
|
|
54
|
+
iflowBin: process.env.WTT_IFLOW_BIN || fileConfig.iflowBin || 'iflow',
|
|
55
|
+
kimiBin: process.env.WTT_KIMI_BIN || fileConfig.kimiBin || 'kimi',
|
|
56
|
+
piBin: process.env.WTT_PI_BIN || fileConfig.piBin || 'pi',
|
|
57
|
+
acpBin: process.env.WTT_ACP_BIN || process.env.WTT_CONNECT_ACP_COMMAND || fileConfig.acpBin || fileConfig.acpCommand || 'acp-agent',
|
|
58
|
+
acpCommand: process.env.WTT_CONNECT_ACP_COMMAND || process.env.WTT_ACP_BIN || fileConfig.acpCommand || fileConfig.acpBin || 'acp-agent',
|
|
59
|
+
acpArgs: parseListEnv(process.env.WTT_CONNECT_ACP_ARGS || fileConfig.acpArgs || ''),
|
|
60
|
+
acpDisplayName: process.env.WTT_CONNECT_ACP_DISPLAY_NAME || fileConfig.acpDisplayName || 'ACP Agent',
|
|
61
|
+
devinBin: process.env.WTT_DEVIN_BIN || fileConfig.devinBin || 'devin',
|
|
62
|
+
devinArgs: parseListEnv(process.env.WTT_DEVIN_ARGS || fileConfig.devinArgs || 'acp'),
|
|
63
|
+
openclawBin: process.env.WTT_OPENCLAW_BIN || fileConfig.openclawBin || 'openclaw',
|
|
64
|
+
enableChat: parseBool(process.env.WTT_CONNECT_ENABLE_CHAT, fileConfig.enableChat ?? true),
|
|
65
|
+
publishProgress: parseBool(process.env.WTT_CONNECT_PUBLISH_PROGRESS, fileConfig.publishProgress ?? false),
|
|
66
|
+
requireCommitPush: parseBool(process.env.WTT_REQUIRE_COMMIT_PUSH, fileConfig.requireCommitPush ?? true),
|
|
67
|
+
heartbeatSeconds: parseIntEnv(process.env.WTT_CONNECT_HEARTBEAT_SECONDS, fileConfig.heartbeatSeconds || 25),
|
|
68
|
+
taskTimeoutSeconds: parseIntEnv(process.env.WTT_CONNECT_TASK_TIMEOUT_SECONDS, fileConfig.taskTimeoutSeconds || 3600),
|
|
69
|
+
enableShell: parseBool(process.env.WTT_CONNECT_ENABLE_SHELL, fileConfig.enableShell ?? true),
|
|
70
|
+
shellMode: process.env.WTT_CONNECT_SHELL_MODE || fileConfig.shellMode || 'unsafe',
|
|
71
|
+
shellTimeoutSeconds: parseIntEnv(process.env.WTT_CONNECT_SHELL_TIMEOUT_SECONDS, fileConfig.shellTimeoutSeconds || 30),
|
|
72
|
+
shellMaxOutputChars: parseIntEnv(process.env.WTT_CONNECT_SHELL_MAX_OUTPUT_CHARS, fileConfig.shellMaxOutputChars || 20000),
|
|
73
|
+
dryRun: parseBool(process.env.WTT_CONNECT_DRY_RUN, fileConfig.dryRun ?? false),
|
|
74
|
+
stateDir,
|
|
75
|
+
storeFile: process.env.WTT_CONNECT_STORE_FILE || fileConfig.storeFile || path.join(stateDir, 'state.json'),
|
|
76
|
+
artifactDir: process.env.WTT_CONNECT_ARTIFACT_DIR || fileConfig.artifactDir || path.join(stateDir, 'artifacts'),
|
|
77
|
+
inboxDir: process.env.WTT_CONNECT_INBOX_DIR || fileConfig.inboxDir || path.join(stateDir, 'inbox'),
|
|
78
|
+
uploadArtifacts: parseBool(process.env.WTT_CONNECT_UPLOAD_ARTIFACTS, fileConfig.uploadArtifacts ?? false),
|
|
79
|
+
setupDisplayName: process.env.WTT_CONNECT_DISPLAY_NAME || fileConfig.setupDisplayName || 'wtt-connect',
|
|
80
|
+
agentAliases: parseListEnv(process.env.WTT_CONNECT_AGENT_ALIASES || fileConfig.agentAliases || ''),
|
|
81
|
+
stt: {
|
|
82
|
+
provider: process.env.WTT_CONNECT_STT_PROVIDER || fileConfig.stt?.provider || 'none',
|
|
83
|
+
command: process.env.WTT_CONNECT_STT_COMMAND || fileConfig.stt?.command || '',
|
|
84
|
+
openaiApiKey: process.env.WTT_CONNECT_STT_OPENAI_API_KEY || process.env.OPENAI_API_KEY || fileConfig.stt?.openaiApiKey || '',
|
|
85
|
+
openaiBaseUrl: process.env.WTT_CONNECT_STT_OPENAI_BASE_URL || process.env.OPENAI_BASE_URL || fileConfig.stt?.openaiBaseUrl || 'https://api.openai.com/v1',
|
|
86
|
+
openaiModel: process.env.WTT_CONNECT_STT_OPENAI_MODEL || fileConfig.stt?.openaiModel || 'whisper-1',
|
|
87
|
+
openaiTransport: process.env.WTT_CONNECT_STT_OPENAI_TRANSPORT || fileConfig.stt?.openaiTransport || 'auto',
|
|
88
|
+
language: process.env.WTT_CONNECT_STT_LANGUAGE || fileConfig.stt?.language || '',
|
|
89
|
+
prompt: process.env.WTT_CONNECT_STT_PROMPT || fileConfig.stt?.prompt || '',
|
|
90
|
+
responseFormat: process.env.WTT_CONNECT_STT_RESPONSE_FORMAT || fileConfig.stt?.responseFormat || 'json',
|
|
91
|
+
timeoutMs: parseIntEnv(process.env.WTT_CONNECT_STT_TIMEOUT_MS, fileConfig.stt?.timeoutMs || 120000),
|
|
92
|
+
},
|
|
93
|
+
tts: {
|
|
94
|
+
provider: process.env.WTT_CONNECT_TTS_PROVIDER || fileConfig.tts?.provider || 'none',
|
|
95
|
+
voice: process.env.WTT_CONNECT_TTS_VOICE || fileConfig.tts?.voice || '',
|
|
96
|
+
outDir: process.env.WTT_CONNECT_TTS_OUT_DIR || fileConfig.tts?.outDir || path.join(workDir, '.wtt-connect', 'tts'),
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parseJsonEnv(value, fallback) {
|
|
102
|
+
if (!value) return fallback;
|
|
103
|
+
try { return JSON.parse(value); } catch { return fallback; }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function parseListEnv(value) {
|
|
107
|
+
if (!value) return [];
|
|
108
|
+
if (Array.isArray(value)) return value.map((x) => String(x).trim()).filter(Boolean);
|
|
109
|
+
const raw = String(value).trim();
|
|
110
|
+
if (!raw) return [];
|
|
111
|
+
if (raw.startsWith('[')) {
|
|
112
|
+
try {
|
|
113
|
+
const parsed = JSON.parse(raw);
|
|
114
|
+
if (Array.isArray(parsed)) return parsed.map((x) => String(x).trim()).filter(Boolean);
|
|
115
|
+
} catch {}
|
|
116
|
+
}
|
|
117
|
+
return raw.split(',').map((x) => x.trim()).filter(Boolean);
|
|
118
|
+
}
|
package/src/env.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
export function parseBool(value, fallback = false) {
|
|
6
|
+
if (value === undefined || value === null || value === '') return fallback;
|
|
7
|
+
return ['1', 'true', 'yes', 'y', 'on'].includes(String(value).trim().toLowerCase());
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function parseIntEnv(value, fallback) {
|
|
11
|
+
const n = Number.parseInt(String(value || ''), 10);
|
|
12
|
+
return Number.isFinite(n) ? n : fallback;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function loadEnvFile(file) {
|
|
16
|
+
if (!fs.existsSync(file)) return;
|
|
17
|
+
for (const raw of fs.readFileSync(file, 'utf8').split(/\r?\n/)) {
|
|
18
|
+
const line = raw.trim();
|
|
19
|
+
if (!line || line.startsWith('#') || !line.includes('=')) continue;
|
|
20
|
+
const idx = line.indexOf('=');
|
|
21
|
+
const key = line.slice(0, idx).trim();
|
|
22
|
+
const value = line.slice(idx + 1).trim().replace(/^['"]|['"]$/g, '');
|
|
23
|
+
if (key && process.env[key] === undefined) process.env[key] = value;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function loadDefaultEnvFiles(options = {}) {
|
|
28
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
const root = path.resolve(here, '..');
|
|
30
|
+
const explicit = options.envFile || process.env.WTT_CONNECT_ENV_FILE || '';
|
|
31
|
+
const loaded = new Set();
|
|
32
|
+
if (explicit) {
|
|
33
|
+
const resolved = path.resolve(process.cwd(), explicit);
|
|
34
|
+
loadEnvFile(resolved);
|
|
35
|
+
loaded.add(resolved);
|
|
36
|
+
process.env.WTT_CONNECT_ENV_FILE = resolved;
|
|
37
|
+
}
|
|
38
|
+
for (const file of [path.join(root, '.env'), path.join(process.cwd(), '.env')]) {
|
|
39
|
+
const resolved = path.resolve(file);
|
|
40
|
+
if (!loaded.has(resolved)) loadEnvFile(resolved);
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/events.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function normalizeAgentEvent(adapterName, event) {
|
|
2
|
+
const item = event?.item || {};
|
|
3
|
+
const type = event?.type || '';
|
|
4
|
+
const now = new Date().toISOString();
|
|
5
|
+
if (type === 'item.started') {
|
|
6
|
+
const kind = item.type || 'item';
|
|
7
|
+
if (kind === 'command_execution') return { kind: 'command', status: 'started', text: item.command || 'command', time: now, raw: event };
|
|
8
|
+
if (kind === 'web_search') return { kind: 'web_search', status: 'started', text: item.query || 'web search', time: now, raw: event };
|
|
9
|
+
return { kind, status: 'started', text: kind, time: now, raw: event };
|
|
10
|
+
}
|
|
11
|
+
if (type === 'item.completed') {
|
|
12
|
+
const kind = item.type || 'item';
|
|
13
|
+
const text = item.command || item.query || item.title || kind;
|
|
14
|
+
return { kind, status: 'completed', text, time: now, raw: event };
|
|
15
|
+
}
|
|
16
|
+
if (type === 'error') return { kind: 'error', status: 'failed', text: String(event.error || event.message || 'error'), time: now, raw: event };
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function renderStatusLine(evt) {
|
|
21
|
+
if (!evt) return '';
|
|
22
|
+
return `[TASK_STATUS] time=${evt.time} status=${evt.status} action=${evt.kind}:${String(evt.text || '').slice(0, 180)}`;
|
|
23
|
+
}
|
package/src/logger.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function nowIso() {
|
|
2
|
+
return new Date().toISOString();
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function redact(value) {
|
|
6
|
+
const s = String(value || '');
|
|
7
|
+
if (!s) return s;
|
|
8
|
+
if (s.length <= 10) return '***';
|
|
9
|
+
return `${s.slice(0, 4)}…${s.slice(-4)}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function log(level, message, fields = {}) {
|
|
13
|
+
const suffix = Object.entries(fields)
|
|
14
|
+
.filter(([, v]) => v !== undefined && v !== null && v !== '')
|
|
15
|
+
.map(([k, v]) => `${k}=${String(v).replace(/\s+/g, ' ')}`)
|
|
16
|
+
.join(' ');
|
|
17
|
+
process.stdout.write(`${nowIso()} [${level.toUpperCase()}] ${message}${suffix ? ` ${suffix}` : ''}\n`);
|
|
18
|
+
}
|
package/src/main.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { loadDefaultEnvFiles } from './env.js';
|
|
3
|
+
import { loadConfig } from './config.js';
|
|
4
|
+
import { Runner } from './runner.js';
|
|
5
|
+
import { PermissionBroker } from './permissions.js';
|
|
6
|
+
import { setup } from './setup.js';
|
|
7
|
+
import { smokeChat, smokeTask } from './smoke.js';
|
|
8
|
+
import { log, redact } from './logger.js';
|
|
9
|
+
import { adapterBin, normalizeProfileName } from './adapters/generic-cli.js';
|
|
10
|
+
import { normalizeAdapterName } from './adapters/index.js';
|
|
11
|
+
import { down, listProfiles, logs, resolveProfileEnvFile, restart, status, up } from './service-manager.js';
|
|
12
|
+
|
|
13
|
+
export async function main(args) {
|
|
14
|
+
const cmd = args[0] || 'help';
|
|
15
|
+
const argv = parseArgs(args.slice(1));
|
|
16
|
+
if (!argv.envFile && argv.profile) argv.envFile = resolveProfileEnvFile(argv.profile);
|
|
17
|
+
loadDefaultEnvFiles({ envFile: argv.envFile });
|
|
18
|
+
if (cmd === 'help' || cmd === '--help' || cmd === '-h') return printHelp();
|
|
19
|
+
if (cmd === 'up' || cmd === 'link') return up(argv);
|
|
20
|
+
if (cmd === 'list') return listProfiles(argv);
|
|
21
|
+
if (cmd === 'status') return status(argv);
|
|
22
|
+
if (cmd === 'restart') return restart(argv);
|
|
23
|
+
if (cmd === 'logs') return logs(argv);
|
|
24
|
+
if (cmd === 'down' || cmd === 'unlink') return down(argv);
|
|
25
|
+
if (cmd === 'doctor') return doctor(loadConfig(argv));
|
|
26
|
+
if (cmd === 'setup') return setup(loadConfig(argv), argv);
|
|
27
|
+
if (cmd === 'claim-code') return setup(loadConfig(argv), { ...argv, noRegister: true, claimCode: true });
|
|
28
|
+
if (cmd === 'smoke-task') return smokeTask(loadConfig(argv), argv);
|
|
29
|
+
if (cmd === 'smoke-chat') return smokeChat(loadConfig(argv), argv);
|
|
30
|
+
if (cmd === 'start') {
|
|
31
|
+
const config = loadConfig(argv);
|
|
32
|
+
if (!config.agentId) throw new Error('WTT_AGENT_ID is required');
|
|
33
|
+
const runner = new Runner(config);
|
|
34
|
+
process.on('SIGINT', () => runner.close().finally(() => process.exit(0)));
|
|
35
|
+
process.on('SIGTERM', () => runner.close().finally(() => process.exit(0)));
|
|
36
|
+
await runner.start();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`unknown command: ${cmd}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseArgs(args) {
|
|
43
|
+
const out = { _: [] };
|
|
44
|
+
for (let i = 0; i < args.length; i++) {
|
|
45
|
+
const a = args[i];
|
|
46
|
+
if (a === '--config') out.config = args[++i];
|
|
47
|
+
else if (a === '--env-file') out.envFile = args[++i];
|
|
48
|
+
else if (a === '--profile' || a === '--name') out.profile = args[++i];
|
|
49
|
+
else if (a === '--claim-code') out.claimCode = true;
|
|
50
|
+
else if (a === '--no-register') out.noRegister = true;
|
|
51
|
+
else if (a === '--agent-id') out.agentId = args[++i];
|
|
52
|
+
else if (a === '--token') out.token = args[++i];
|
|
53
|
+
else if (a === '--adapter') out.adapter = args[++i];
|
|
54
|
+
else if (a === '--base-url') out.baseUrl = args[++i];
|
|
55
|
+
else if (a === '--workdir' || a === '--work-dir') out.workDir = args[++i];
|
|
56
|
+
else if (a === '--state-dir') out.stateDir = args[++i];
|
|
57
|
+
else if (a === '--mode') out.mode = args[++i];
|
|
58
|
+
else if (a === '--allow-yolo' || a === '--allow-dangerous-permissions') out.allowYolo = true;
|
|
59
|
+
else if (a === '--yes' || a === '-y') out.yes = true;
|
|
60
|
+
else if (a === '--publish-progress') out.publishProgress = true;
|
|
61
|
+
else if (a === '--enable-linger') out.enableLinger = true;
|
|
62
|
+
else if (a === '--no-start') out.noStart = true;
|
|
63
|
+
else if (a === '--node-bin') out.nodeBin = args[++i];
|
|
64
|
+
else if (a === '--codex-bin') out.codexBin = args[++i];
|
|
65
|
+
else if (a === '--claude-bin') out.claudeBin = args[++i];
|
|
66
|
+
else if (a === '--lines') out.lines = Number(args[++i]);
|
|
67
|
+
else if (a === '--display-name') out.displayName = args[++i];
|
|
68
|
+
else if (a === '--expected') out.expected = args[++i];
|
|
69
|
+
else if (a === '--prompt') out.prompt = args[++i];
|
|
70
|
+
else if (a === '--message') out.message = args[++i];
|
|
71
|
+
else if (a === '--timeout') out.timeout = Number(args[++i]) * 1000;
|
|
72
|
+
else if (a === '--sender-agent-id') out.senderAgentId = args[++i];
|
|
73
|
+
else if (a === '--sender-token') out.senderToken = args[++i];
|
|
74
|
+
else if (a.startsWith('--')) throw new Error(`unknown argument: ${a}`);
|
|
75
|
+
else out._.push(a);
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function printHelp() {
|
|
81
|
+
console.log(`wtt-connect
|
|
82
|
+
|
|
83
|
+
Commands:
|
|
84
|
+
up <agenttype> <agent_id> <token> Bind profile, install user service, and start it
|
|
85
|
+
link <agenttype> <agent_id> <token>
|
|
86
|
+
Alias for up
|
|
87
|
+
list List configured profiles
|
|
88
|
+
status [profile|all] Show systemd service status for profiles
|
|
89
|
+
restart [profile|all] Restart one or all configured services
|
|
90
|
+
logs <profile> [--lines 100] Show service logs
|
|
91
|
+
down <profile> Stop/disable service; keep profile/state files
|
|
92
|
+
unlink <profile> Alias for down
|
|
93
|
+
setup [--claim-code] Register/write local .env and optionally print claim code
|
|
94
|
+
start [--profile name|--env-file file]
|
|
95
|
+
Start WTT-native connector daemon
|
|
96
|
+
doctor [--profile name|--env-file file]
|
|
97
|
+
Check local configuration and adapter binaries
|
|
98
|
+
smoke-task Create/run a WTT task and wait for connector result
|
|
99
|
+
smoke-chat Send P2P chat from smoke sender and wait for reply
|
|
100
|
+
claim-code Print claim code for current WTT_AGENT_ID/TOKEN
|
|
101
|
+
help Show this help
|
|
102
|
+
`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
function doctor(config) {
|
|
107
|
+
log('info', 'wtt-connect doctor', {
|
|
108
|
+
agentId: config.agentId || '(missing)',
|
|
109
|
+
token: config.token ? redact(config.token) : '(missing)',
|
|
110
|
+
adapter: config.adapter,
|
|
111
|
+
adapters: config.adapters.join(','),
|
|
112
|
+
envFile: config.envFile || '(default)',
|
|
113
|
+
workDir: config.workDir,
|
|
114
|
+
mode: config.mode,
|
|
115
|
+
allowYolo: config.allowYolo,
|
|
116
|
+
tts: config.tts.provider,
|
|
117
|
+
store: config.storeFile,
|
|
118
|
+
uploadArtifacts: config.uploadArtifacts,
|
|
119
|
+
});
|
|
120
|
+
const checks = [];
|
|
121
|
+
try { new PermissionBroker(config).validate(); checks.push(['permission policy', 'ok', true]); }
|
|
122
|
+
catch (err) { checks.push(['permission policy', err.message, false]); }
|
|
123
|
+
checks.push(['node', process.version, true]);
|
|
124
|
+
checks.push(['WTT_AGENT_ID', config.agentId ? 'ok' : 'missing', Boolean(config.agentId)]);
|
|
125
|
+
checks.push(['WTT_TOKEN', config.token ? 'ok' : 'missing', Boolean(config.token)]);
|
|
126
|
+
for (const adapter of config.adapters) {
|
|
127
|
+
const bin = resolveAdapterBin(config, adapter);
|
|
128
|
+
const found = spawnSync('which', [bin], { encoding: 'utf8' });
|
|
129
|
+
checks.push([`${adapter} binary`, found.status === 0 ? found.stdout.trim() : `not found: ${bin}`, found.status === 0]);
|
|
130
|
+
}
|
|
131
|
+
for (const [name, value, ok] of checks) console.log(`${ok ? '✓' : '✗'} ${name}: ${value}`);
|
|
132
|
+
if (checks.some(([, , ok]) => !ok)) process.exitCode = 1;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function resolveAdapterBin(config, adapter) {
|
|
136
|
+
const name = normalizeAdapterName(adapter);
|
|
137
|
+
if (name === 'codex') return config.codexBin;
|
|
138
|
+
if (name === 'claude-code') return config.claudeBin;
|
|
139
|
+
if (name === 'acp') return config.acpCommand || config.acpBin;
|
|
140
|
+
if (name === 'devin') return config.devinBin;
|
|
141
|
+
if (name === 'openclaw') return config.openclawBin;
|
|
142
|
+
return adapterBin(config, normalizeProfileName(name)) || name;
|
|
143
|
+
}
|
package/src/mime.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const MAP = new Map([
|
|
2
|
+
['.txt', 'text/plain'], ['.md', 'text/markdown'], ['.json', 'application/json'],
|
|
3
|
+
['.html', 'text/html'], ['.pdf', 'application/pdf'], ['.zip', 'application/zip'],
|
|
4
|
+
['.png', 'image/png'], ['.jpg', 'image/jpeg'], ['.jpeg', 'image/jpeg'],
|
|
5
|
+
['.gif', 'image/gif'], ['.webp', 'image/webp'], ['.mp3', 'audio/mpeg'],
|
|
6
|
+
['.wav', 'audio/wav'], ['.ogg', 'audio/ogg'], ['.webm', 'audio/webm'], ['.mp4', 'video/mp4'], ['.mov', 'video/quicktime'],
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
export function lookupMime(filePath) {
|
|
10
|
+
const ext = filePath.slice(filePath.lastIndexOf('.')).toLowerCase();
|
|
11
|
+
return MAP.get(ext) || 'text/plain';
|
|
12
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const MODE_TO_CODEX = {
|
|
2
|
+
suggest: [],
|
|
3
|
+
'auto-edit': ['--full-auto'],
|
|
4
|
+
'full-auto': ['--full-auto'],
|
|
5
|
+
yolo: ['--dangerously-bypass-approvals-and-sandbox'],
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export class PermissionBroker {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
validate() {
|
|
14
|
+
if (this.config.mode === 'yolo' && !this.config.allowYolo) {
|
|
15
|
+
throw new Error('WTT_CONNECT_MODE=yolo requires WTT_CONNECT_ALLOW_YOLO=1');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
codexArgs() {
|
|
20
|
+
this.validate();
|
|
21
|
+
return MODE_TO_CODEX[this.config.mode] || MODE_TO_CODEX['full-auto'];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe() {
|
|
25
|
+
if (this.config.mode === 'yolo') return 'yolo/dangerously-bypass-approvals-and-sandbox';
|
|
26
|
+
return this.config.mode;
|
|
27
|
+
}
|
|
28
|
+
}
|