@yeaft/webchat-agent 1.0.356 → 1.0.358
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/cli.js +40 -7
- package/local-run.js +45 -6
- package/local-runtime/server/handlers/agent-output.js +8 -1
- package/local-runtime/server/ws-agent.js +22 -2
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +43 -44
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/local-service.js +219 -0
- package/package.json +1 -1
- package/service/config.js +13 -0
- package/yeaft/models.js +11 -7
package/cli.js
CHANGED
|
@@ -64,13 +64,7 @@ if (command === 'doctor') {
|
|
|
64
64
|
} else if (command === 'llm') {
|
|
65
65
|
await handleLlmCommand(subArgs);
|
|
66
66
|
} else if (command === 'local') {
|
|
67
|
-
|
|
68
|
-
const { runLocal } = await import('./local-run.js');
|
|
69
|
-
await runLocal(subArgs);
|
|
70
|
-
} catch (error) {
|
|
71
|
-
console.error(`Local run failed: ${error.message}`);
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
67
|
+
await handleLocalCommand(subArgs);
|
|
74
68
|
} else if (command === 'upgrade') {
|
|
75
69
|
await upgrade(subArgs);
|
|
76
70
|
} else if (command === '--version' || command === '-v') {
|
|
@@ -84,6 +78,37 @@ if (command === 'doctor') {
|
|
|
84
78
|
parseAndStart(args);
|
|
85
79
|
}
|
|
86
80
|
|
|
81
|
+
/** Dispatch the local foreground and managed-service command family. */
|
|
82
|
+
export async function handleLocalCommand(subArgs, options = {}) {
|
|
83
|
+
const printHelpFn = options.printHelp || printHelp;
|
|
84
|
+
const warn = options.warn || console.warn;
|
|
85
|
+
const loadLocalService = options.loadLocalService || (() => import('./local-service.js'));
|
|
86
|
+
const loadLocalRun = options.loadLocalRun || (() => import('./local-run.js'));
|
|
87
|
+
try {
|
|
88
|
+
const localCommand = subArgs[0];
|
|
89
|
+
if (localCommand === '--help' || localCommand === '-h') {
|
|
90
|
+
printHelpFn();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
warnDeprecatedInstanceArg(subArgs, warn);
|
|
94
|
+
if (SERVICE_COMMANDS.includes(localCommand)) {
|
|
95
|
+
const { handleLocalServiceCommand } = await loadLocalService();
|
|
96
|
+
await handleLocalServiceCommand(localCommand, subArgs.slice(1));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const { runLocal } = await loadLocalRun();
|
|
100
|
+
await runLocal(subArgs);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
const message = `Local run failed: ${error.message}`;
|
|
103
|
+
if (options.onError) {
|
|
104
|
+
options.onError(message, error);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
console.error(message);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
87
112
|
function printHelp() {
|
|
88
113
|
console.log(`
|
|
89
114
|
${pkg.name} v${pkg.version}
|
|
@@ -91,6 +116,11 @@ function printHelp() {
|
|
|
91
116
|
Usage:
|
|
92
117
|
yeaft-agent [options] Run agent in foreground
|
|
93
118
|
yeaft-agent local [options] Run local Web UI, server, and agent
|
|
119
|
+
yeaft-agent local --background Run local mode in the background
|
|
120
|
+
yeaft-agent local install [options] Install local mode as a managed service
|
|
121
|
+
yeaft-agent local uninstall [options] Remove the local managed service
|
|
122
|
+
yeaft-agent local start|stop|restart|status|logs [options]
|
|
123
|
+
Control the local managed service
|
|
94
124
|
yeaft-agent install [options] Install as system service
|
|
95
125
|
yeaft-agent uninstall [options] Remove system service
|
|
96
126
|
yeaft-agent start [options] Start installed service
|
|
@@ -108,6 +138,7 @@ function printHelp() {
|
|
|
108
138
|
--server <url> WebSocket server URL (default: ws://localhost:3456)
|
|
109
139
|
--name <name> Agent name and instance id (default: computer name; invalid chars become -)
|
|
110
140
|
--port <port> Local server port (local command only; default: 6868)
|
|
141
|
+
--background, -d Detach local mode after spawning it (local command only)
|
|
111
142
|
--secret <secret> Agent secret for authentication
|
|
112
143
|
--work-dir <dir> Default working directory (default: cwd)
|
|
113
144
|
--yeaft-dir <dir> Yeaft data directory for this instance
|
|
@@ -124,6 +155,8 @@ function printHelp() {
|
|
|
124
155
|
Examples:
|
|
125
156
|
yeaft-agent local
|
|
126
157
|
yeaft-agent local --name my-worker --port 7000
|
|
158
|
+
yeaft-agent local --name my-worker --background
|
|
159
|
+
yeaft-agent local install --name my-worker --port 7000
|
|
127
160
|
yeaft-agent --server wss://your-server.com --name my-worker --secret xxx
|
|
128
161
|
yeaft-agent install --server wss://your-server.com --name my-worker --secret xxx
|
|
129
162
|
yeaft-agent install --server wss://your-server.com --name my-worker-2 --secret xxx
|
package/local-run.js
CHANGED
|
@@ -5,18 +5,23 @@ import { createServer } from 'net';
|
|
|
5
5
|
import { dirname, join } from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { WebSocket } from 'ws';
|
|
8
|
-
import {
|
|
8
|
+
import { resolveServiceInstanceId, resolveYeaftDir } from './service/config.js';
|
|
9
9
|
|
|
10
10
|
const DEFAULT_PORT = 6868;
|
|
11
11
|
const LOCAL_HOST = '127.0.0.1';
|
|
12
12
|
|
|
13
13
|
export function parseLocalArgs(args, env = process.env) {
|
|
14
|
-
const options = {
|
|
14
|
+
const options = {
|
|
15
|
+
name: resolveServiceInstanceId(args, env),
|
|
16
|
+
port: DEFAULT_PORT,
|
|
17
|
+
background: false,
|
|
18
|
+
yeaftDir: null,
|
|
19
|
+
};
|
|
15
20
|
for (let i = 0; i < args.length; i++) {
|
|
16
21
|
const arg = args[i];
|
|
17
22
|
const value = args[i + 1];
|
|
18
|
-
if (arg === '--name') {
|
|
19
|
-
if (!value || value.startsWith('-')) throw new Error(
|
|
23
|
+
if (arg === '--name' || arg === '--instance' || arg === '--yeaft-dir') {
|
|
24
|
+
if (!value || value.startsWith('-')) throw new Error(`${arg} requires a value`);
|
|
20
25
|
i++;
|
|
21
26
|
} else if (arg === '--port') {
|
|
22
27
|
if (!value || value.startsWith('-')) throw new Error('--port requires a value');
|
|
@@ -24,11 +29,13 @@ export function parseLocalArgs(args, env = process.env) {
|
|
|
24
29
|
options.port = Number(value);
|
|
25
30
|
if (options.port < 1 || options.port > 65535) throw new Error(`Invalid port: ${value}`);
|
|
26
31
|
i++;
|
|
32
|
+
} else if (arg === '--background' || arg === '-d') {
|
|
33
|
+
options.background = true;
|
|
27
34
|
} else {
|
|
28
35
|
throw new Error(`Unknown local option: ${arg}`);
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
|
-
|
|
38
|
+
options.yeaftDir = resolveYeaftDir(args, env, options.name);
|
|
32
39
|
return options;
|
|
33
40
|
}
|
|
34
41
|
|
|
@@ -127,8 +134,12 @@ function readAgentList(wsUrl) {
|
|
|
127
134
|
|
|
128
135
|
export async function runLocal(args, options = {}) {
|
|
129
136
|
const config = parseLocalArgs(args);
|
|
137
|
+
if (config.background && options.backgroundHandled !== true) {
|
|
138
|
+
return launchLocalInBackground(args, options);
|
|
139
|
+
}
|
|
130
140
|
const paths = options.paths || runtimePaths();
|
|
131
141
|
const dataDir = options.dataDir || join(homedir(), '.yeaft', 'server');
|
|
142
|
+
const yeaftDir = options.yeaftDir || config.yeaftDir;
|
|
132
143
|
const url = `http://${LOCAL_HOST}:${config.port}`;
|
|
133
144
|
const children = new Set();
|
|
134
145
|
const signalHandlers = new Map();
|
|
@@ -173,6 +184,7 @@ export async function runLocal(args, options = {}) {
|
|
|
173
184
|
SERVER_DATA_DIR: dataDir,
|
|
174
185
|
PERF_TRACE_DIR: join(dataDir, 'perf-traces'),
|
|
175
186
|
SKIP_AUTH: 'true',
|
|
187
|
+
YEAFT_LOCAL_RUN: 'true',
|
|
176
188
|
WEB_DIR: paths.webDir,
|
|
177
189
|
},
|
|
178
190
|
});
|
|
@@ -189,6 +201,7 @@ export async function runLocal(args, options = {}) {
|
|
|
189
201
|
AGENT_NAME: config.name,
|
|
190
202
|
YEAFT_AGENT_INSTANCE: config.name,
|
|
191
203
|
AGENT_SECRET: '',
|
|
204
|
+
YEAFT_DIR: yeaftDir,
|
|
192
205
|
YEAFT_LOCAL_RUN: 'true',
|
|
193
206
|
YEAFT_SKIP_STARTUP_INSTALLS: 'true',
|
|
194
207
|
},
|
|
@@ -204,7 +217,7 @@ export async function runLocal(args, options = {}) {
|
|
|
204
217
|
server.once('exit', fail('Local server'));
|
|
205
218
|
agent.once('exit', fail('Local agent'));
|
|
206
219
|
|
|
207
|
-
await waitForAgent(`ws://${LOCAL_HOST}:${config.port}`, config.name, agent);
|
|
220
|
+
await (options.waitForAgent || waitForAgent)(`ws://${LOCAL_HOST}:${config.port}`, config.name, agent);
|
|
208
221
|
|
|
209
222
|
console.log(`Yeaft local is available at ${url}`);
|
|
210
223
|
return { url, server, agent, stop };
|
|
@@ -213,3 +226,29 @@ export async function runLocal(args, options = {}) {
|
|
|
213
226
|
throw error;
|
|
214
227
|
}
|
|
215
228
|
}
|
|
229
|
+
|
|
230
|
+
function localDaemonArgs(args) {
|
|
231
|
+
return args.filter(arg => arg !== '--background' && arg !== '-d');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export async function launchLocalInBackground(args, options = {}) {
|
|
235
|
+
const config = parseLocalArgs(args);
|
|
236
|
+
const spawnProcess = options.spawn || spawn;
|
|
237
|
+
const cliPath = options.cliPath || join(dirname(fileURLToPath(import.meta.url)), 'cli.js');
|
|
238
|
+
const child = spawnProcess(process.execPath, [cliPath, 'local', ...localDaemonArgs(args)], {
|
|
239
|
+
detached: true,
|
|
240
|
+
stdio: 'ignore',
|
|
241
|
+
windowsHide: true,
|
|
242
|
+
env: {
|
|
243
|
+
...process.env,
|
|
244
|
+
YEAFT_LOCAL_RUN_BACKGROUND: 'true',
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
child.unref();
|
|
248
|
+
const url = `http://${LOCAL_HOST}:${config.port}`;
|
|
249
|
+
const result = { url, pid: child.pid, background: true };
|
|
250
|
+
if (options.quiet !== true) {
|
|
251
|
+
console.log(`Yeaft local is starting in the background at ${url} (PID ${child.pid}).`);
|
|
252
|
+
}
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
@@ -674,7 +674,14 @@ export async function handleAgentOutput(agentId, agent, msg) {
|
|
|
674
674
|
}
|
|
675
675
|
}
|
|
676
676
|
}
|
|
677
|
-
|
|
677
|
+
// Nested `session_list_updated` events carry the same authoritative
|
|
678
|
+
// snapshot as the top-level alias below. Re-project the server-owned
|
|
679
|
+
// catalog after reconciliation so the unified sidebar updates in both
|
|
680
|
+
// local no-auth and deployed runtimes. Roster changes are handled by
|
|
681
|
+
// their own metadata branch above.
|
|
682
|
+
if ((event?.type === 'session_list_updated' || catalogChanged) && agent.ownerId) {
|
|
683
|
+
await broadcastSessionCatalog(agent.ownerId);
|
|
684
|
+
}
|
|
678
685
|
break;
|
|
679
686
|
}
|
|
680
687
|
|
|
@@ -4,6 +4,7 @@ import { CONFIG } from './config.js';
|
|
|
4
4
|
import { verifyAgent } from './auth.js';
|
|
5
5
|
import { encodeKey } from './encryption.js';
|
|
6
6
|
import { agents, pendingAgentConnections } from './context.js';
|
|
7
|
+
import { userDb } from './database.js';
|
|
7
8
|
import {
|
|
8
9
|
parseMessage, broadcastAgentList, clearAgentDirCache
|
|
9
10
|
} from './ws-utils.js';
|
|
@@ -114,18 +115,37 @@ export function handleAgentConnection(ws, url) {
|
|
|
114
115
|
|
|
115
116
|
const capabilities = Array.isArray(msg.capabilities) ? msg.capabilities : urlCapabilities;
|
|
116
117
|
const agentVersion = msg.version || null;
|
|
118
|
+
// Local no-auth mode still has one durable browser owner. This makes
|
|
119
|
+
// the server-side Session catalog persistent without changing generic
|
|
120
|
+
// development-server behavior, which remains ownerless.
|
|
121
|
+
const localOwner = skipAgentAuth && process.env.YEAFT_LOCAL_RUN === 'true'
|
|
122
|
+
? userDb.getOrCreate('dev-user')
|
|
123
|
+
: null;
|
|
124
|
+
const ownerId = localOwner?.id || authResult.userId;
|
|
125
|
+
const ownerUsername = localOwner?.username || authResult.username;
|
|
117
126
|
// Authenticated Agents use an owner-scoped key. SKIP_AUTH preserves
|
|
118
127
|
// its historical unscoped id while still receiving version metadata.
|
|
119
128
|
resolvedAgentId = skipAgentAuth
|
|
120
129
|
? clientAgentId
|
|
121
|
-
: buildAgentMapKey(
|
|
130
|
+
: buildAgentMapKey(ownerId, pending.instanceId || pending.agentId || pending.agentName);
|
|
122
131
|
if (!claimAgentConnection(resolvedAgentId, connectionGeneration)) {
|
|
123
132
|
resolvedAgentId = null;
|
|
124
133
|
pruneAgentConnectionGenerations();
|
|
125
134
|
ws.close(1008, 'Superseded by a newer Agent connection');
|
|
126
135
|
return;
|
|
127
136
|
}
|
|
128
|
-
completeAgentRegistration(
|
|
137
|
+
completeAgentRegistration(
|
|
138
|
+
ws,
|
|
139
|
+
resolvedAgentId,
|
|
140
|
+
pending.agentName,
|
|
141
|
+
pending.workDir,
|
|
142
|
+
authResult.sessionKey,
|
|
143
|
+
capabilities,
|
|
144
|
+
ownerId,
|
|
145
|
+
ownerUsername,
|
|
146
|
+
agentVersion,
|
|
147
|
+
pending.instanceId || pending.agentId || pending.agentName,
|
|
148
|
+
);
|
|
129
149
|
pruneAgentConnectionGenerations();
|
|
130
150
|
}
|
|
131
151
|
} catch (e) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.358"}
|