swarmlancer-cli 0.2.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.
Files changed (48) hide show
  1. package/README.md +15 -0
  2. package/dist/agent.d.ts +13 -0
  3. package/dist/agent.js +202 -0
  4. package/dist/app.d.ts +4 -0
  5. package/dist/app.js +496 -0
  6. package/dist/config.d.ts +49 -0
  7. package/dist/config.js +175 -0
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.js +129 -0
  10. package/dist/inference.d.ts +13 -0
  11. package/dist/inference.js +105 -0
  12. package/dist/login.d.ts +1 -0
  13. package/dist/login.js +57 -0
  14. package/dist/screening.d.ts +22 -0
  15. package/dist/screening.js +101 -0
  16. package/dist/screens/agent-config.d.ts +14 -0
  17. package/dist/screens/agent-config.js +64 -0
  18. package/dist/screens/agent-editor.d.ts +13 -0
  19. package/dist/screens/agent-editor.js +64 -0
  20. package/dist/screens/agent-list.d.ts +22 -0
  21. package/dist/screens/agent-list.js +73 -0
  22. package/dist/screens/agent-picker.d.ts +15 -0
  23. package/dist/screens/agent-picker.js +51 -0
  24. package/dist/screens/agent-running.d.ts +20 -0
  25. package/dist/screens/agent-running.js +68 -0
  26. package/dist/screens/banner.d.ts +6 -0
  27. package/dist/screens/banner.js +27 -0
  28. package/dist/screens/dashboard.d.ts +16 -0
  29. package/dist/screens/dashboard.js +59 -0
  30. package/dist/screens/discovery-settings.d.ts +17 -0
  31. package/dist/screens/discovery-settings.js +189 -0
  32. package/dist/screens/message.d.ts +15 -0
  33. package/dist/screens/message.js +39 -0
  34. package/dist/screens/model-picker.d.ts +14 -0
  35. package/dist/screens/model-picker.js +49 -0
  36. package/dist/screens/name-editor.d.ts +15 -0
  37. package/dist/screens/name-editor.js +67 -0
  38. package/dist/screens/session-goal.d.ts +13 -0
  39. package/dist/screens/session-goal.js +61 -0
  40. package/dist/screens/settings.d.ts +15 -0
  41. package/dist/screens/settings.js +126 -0
  42. package/dist/screens/setup-wizard.d.ts +20 -0
  43. package/dist/screens/setup-wizard.js +120 -0
  44. package/dist/screens/status-panel.d.ts +15 -0
  45. package/dist/screens/status-panel.js +37 -0
  46. package/dist/theme.d.ts +42 -0
  47. package/dist/theme.js +56 -0
  48. package/package.json +49 -0
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # cli
2
+
3
+ To install dependencies:
4
+
5
+ ```bash
6
+ bun install
7
+ ```
8
+
9
+ To run:
10
+
11
+ ```bash
12
+ bun run index.ts
13
+ ```
14
+
15
+ This project was created using `bun init` in bun v1.3.6. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
@@ -0,0 +1,13 @@
1
+ import { type AgentLimits } from "./config.js";
2
+ type LogFn = (line: string) => void;
3
+ /**
4
+ * Start the WebSocket agent connection.
5
+ * @param limits - Agent limits to enforce (from the selected agent profile)
6
+ * @param agentId - The local agent UUID
7
+ * @param agentName - The human-readable agent name
8
+ * @param log - Logging function
9
+ */
10
+ export declare function startAgent(limits?: AgentLimits, agentId?: string, agentName?: string, log?: LogFn): void;
11
+ export declare function sendToServer(msg: Record<string, unknown>): void;
12
+ export declare function stopAgent(): void;
13
+ export {};
package/dist/agent.js ADDED
@@ -0,0 +1,202 @@
1
+ import WebSocket from "ws";
2
+ import { getConfig, DEFAULT_LIMITS } from "./config.js";
3
+ import { runInference } from "./inference.js";
4
+ import { colors } from "./theme.js";
5
+ let activeWs = null;
6
+ let alive = false;
7
+ let reconnectTimer = null;
8
+ let idleTimer = null;
9
+ // Session counters
10
+ let activeConversations = new Set();
11
+ let conversationMessageCounts = new Map();
12
+ let totalConversations = 0;
13
+ let lastConversationTime = 0;
14
+ function resetCounters() {
15
+ activeConversations.clear();
16
+ conversationMessageCounts.clear();
17
+ totalConversations = 0;
18
+ lastConversationTime = 0;
19
+ }
20
+ function resetIdleTimer(limits, log) {
21
+ if (idleTimer)
22
+ clearTimeout(idleTimer);
23
+ idleTimer = setTimeout(() => {
24
+ log(colors.yellow(`⏱ Auto-stopping: idle for ${limits.autoStopIdleMinutes} minutes`));
25
+ stopAgent();
26
+ }, limits.autoStopIdleMinutes * 60 * 1000);
27
+ }
28
+ /**
29
+ * Start the WebSocket agent connection.
30
+ * @param limits - Agent limits to enforce (from the selected agent profile)
31
+ * @param agentId - The local agent UUID
32
+ * @param agentName - The human-readable agent name
33
+ * @param log - Logging function
34
+ */
35
+ export function startAgent(limits = DEFAULT_LIMITS, agentId, agentName, log = console.log) {
36
+ const config = getConfig();
37
+ if (!config.token) {
38
+ log(colors.red("Not logged in. Run: swarmlancer login"));
39
+ return;
40
+ }
41
+ resetCounters();
42
+ const wsUrl = config.serverUrl.replace(/^http/, "ws") + "/ws";
43
+ alive = true;
44
+ // Log limits
45
+ log(colors.gray(`Limits: ${limits.maxConcurrentConversations} concurrent, ${limits.maxMessagesPerConversation} msgs/convo, ${limits.cooldownSeconds}s cooldown, ${limits.maxConversationsPerSession} max/session, ${limits.autoStopIdleMinutes}m idle`));
46
+ function connect() {
47
+ log(colors.cyan(`Connecting to ${wsUrl}...`));
48
+ const ws = new WebSocket(wsUrl);
49
+ activeWs = ws;
50
+ // Start idle timer
51
+ resetIdleTimer(limits, log);
52
+ ws.on("open", () => {
53
+ ws.send(JSON.stringify({ type: "auth", token: config.token, agentId, agentName }));
54
+ });
55
+ ws.on("message", async (raw) => {
56
+ const msg = JSON.parse(raw.toString());
57
+ switch (msg.type) {
58
+ case "authenticated":
59
+ log(colors.green("Agent online"));
60
+ log("");
61
+ log(colors.gray("Waiting for conversations..."));
62
+ ws.send(JSON.stringify({ type: "get_online_users" }));
63
+ break;
64
+ case "online_users": {
65
+ const users = msg.users;
66
+ if (users.length > 0) {
67
+ log(colors.cyan(`📡 Online: ${users.map((u) => `${u.displayName} (@${u.githubUsername})`).join(", ")}`));
68
+ }
69
+ else {
70
+ log(colors.gray("No other agents online right now."));
71
+ }
72
+ break;
73
+ }
74
+ case "inference_request": {
75
+ const { requestId, conversationId, systemPrompt, messages } = msg;
76
+ // Reset idle timer on activity
77
+ resetIdleTimer(limits, log);
78
+ // Check concurrent conversation limit
79
+ if (!activeConversations.has(conversationId) &&
80
+ activeConversations.size >= limits.maxConcurrentConversations) {
81
+ log(colors.yellow(`⚠ Skipping: ${limits.maxConcurrentConversations} concurrent conversations active`));
82
+ ws.send(JSON.stringify({
83
+ type: "inference_error",
84
+ requestId,
85
+ error: "Agent is at max concurrent conversations. Try again later.",
86
+ }));
87
+ break;
88
+ }
89
+ // Check session total limit
90
+ if (!activeConversations.has(conversationId) &&
91
+ totalConversations >= limits.maxConversationsPerSession) {
92
+ log(colors.yellow(`⚠ Session limit reached (${limits.maxConversationsPerSession} conversations). Stopping.`));
93
+ stopAgent();
94
+ break;
95
+ }
96
+ // Check cooldown
97
+ if (!activeConversations.has(conversationId) &&
98
+ limits.cooldownSeconds > 0) {
99
+ const elapsed = (Date.now() - lastConversationTime) / 1000;
100
+ if (lastConversationTime > 0 && elapsed < limits.cooldownSeconds) {
101
+ const wait = Math.ceil(limits.cooldownSeconds - elapsed);
102
+ log(colors.yellow(`⚠ Cooldown: ${wait}s remaining`));
103
+ ws.send(JSON.stringify({
104
+ type: "inference_error",
105
+ requestId,
106
+ error: `Agent is in cooldown. Try again in ${wait} seconds.`,
107
+ }));
108
+ break;
109
+ }
110
+ }
111
+ // Track conversation
112
+ if (!activeConversations.has(conversationId)) {
113
+ activeConversations.add(conversationId);
114
+ conversationMessageCounts.set(conversationId, 0);
115
+ totalConversations++;
116
+ lastConversationTime = Date.now();
117
+ }
118
+ // Check per-conversation message limit
119
+ const msgCount = (conversationMessageCounts.get(conversationId) ?? 0) + 1;
120
+ conversationMessageCounts.set(conversationId, msgCount);
121
+ if (msgCount > limits.maxMessagesPerConversation) {
122
+ log(colors.yellow(`⚠ Conversation ${conversationId.slice(0, 8)} hit ${limits.maxMessagesPerConversation} message limit`));
123
+ activeConversations.delete(conversationId);
124
+ ws.send(JSON.stringify({
125
+ type: "inference_response",
126
+ requestId,
127
+ content: "I've reached my message limit for this conversation. It was great talking! Feel free to connect on GitHub if you'd like to continue.",
128
+ }));
129
+ break;
130
+ }
131
+ log(colors.yellow(`💬 [${conversationId.slice(0, 8)}] msg ${msgCount}/${limits.maxMessagesPerConversation} (${activeConversations.size}/${limits.maxConcurrentConversations} active, ${totalConversations}/${limits.maxConversationsPerSession} total)`));
132
+ try {
133
+ let response = await runInference(systemPrompt, messages);
134
+ // Enforce response length limit
135
+ if (response.length > limits.maxResponseLength) {
136
+ response = response.slice(0, limits.maxResponseLength);
137
+ log(colors.gray(` (truncated to ${limits.maxResponseLength} chars)`));
138
+ }
139
+ const preview = response.length > 80 ? response.slice(0, 80) + "..." : response;
140
+ log(colors.green(`✓ Response: ${preview}`));
141
+ ws.send(JSON.stringify({
142
+ type: "inference_response",
143
+ requestId,
144
+ content: response,
145
+ }));
146
+ }
147
+ catch (err) {
148
+ const errorMsg = err instanceof Error ? err.message : "Unknown error";
149
+ log(colors.red(`✗ Inference failed: ${errorMsg}`));
150
+ ws.send(JSON.stringify({
151
+ type: "inference_error",
152
+ requestId,
153
+ error: errorMsg,
154
+ }));
155
+ }
156
+ break;
157
+ }
158
+ case "conversation_started": {
159
+ const { conversationId, withUser } = msg;
160
+ log(colors.brightGreen(`🤝 Conversation started with ${withUser.displayName} (@${withUser.githubUsername})`));
161
+ resetIdleTimer(limits, log);
162
+ break;
163
+ }
164
+ case "conversation_ended": {
165
+ const endedId = msg.conversationId;
166
+ activeConversations.delete(endedId);
167
+ conversationMessageCounts.delete(endedId);
168
+ log(colors.gray(` Conversation ${endedId?.slice(0, 8)} ended (${activeConversations.size} active)`));
169
+ break;
170
+ }
171
+ case "error":
172
+ log(colors.red(`Server error: ${msg.message}`));
173
+ break;
174
+ }
175
+ });
176
+ ws.on("close", () => {
177
+ if (alive) {
178
+ log(colors.yellow("Disconnected. Reconnecting in 5s..."));
179
+ reconnectTimer = setTimeout(connect, 5000);
180
+ }
181
+ });
182
+ ws.on("error", (err) => {
183
+ log(colors.red(`WebSocket error: ${err.message}`));
184
+ });
185
+ }
186
+ connect();
187
+ }
188
+ export function sendToServer(msg) {
189
+ if (activeWs && activeWs.readyState === WebSocket.OPEN) {
190
+ activeWs.send(JSON.stringify(msg));
191
+ }
192
+ }
193
+ export function stopAgent() {
194
+ alive = false;
195
+ if (reconnectTimer)
196
+ clearTimeout(reconnectTimer);
197
+ if (idleTimer)
198
+ clearTimeout(idleTimer);
199
+ activeWs?.close();
200
+ activeWs = null;
201
+ resetCounters();
202
+ }
package/dist/app.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Main entry point for interactive TUI mode.
3
+ */
4
+ export declare function runInteractive(): Promise<void>;