openviber 0.4.3 → 0.5.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 +24 -1
- package/dist/cli/index.cjs +2784 -170
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +2797 -183
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +415 -144
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +416 -145
- package/dist/index.js.map +1 -1
- package/package.json +9 -3
package/dist/cli/index.js
CHANGED
|
@@ -35,10 +35,145 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
// src/core/message.ts
|
|
38
|
-
var ConversationHistory;
|
|
38
|
+
var MessageQueue, ConversationHistory;
|
|
39
39
|
var init_message = __esm({
|
|
40
40
|
"src/core/message.ts"() {
|
|
41
41
|
"use strict";
|
|
42
|
+
MessageQueue = class {
|
|
43
|
+
queue = [];
|
|
44
|
+
current;
|
|
45
|
+
processing = false;
|
|
46
|
+
listeners = /* @__PURE__ */ new Set();
|
|
47
|
+
nextId = 1;
|
|
48
|
+
/**
|
|
49
|
+
* Add message to queue
|
|
50
|
+
*/
|
|
51
|
+
add(content, metadata) {
|
|
52
|
+
const message = {
|
|
53
|
+
id: `msg-${this.nextId++}`,
|
|
54
|
+
content,
|
|
55
|
+
status: "queued",
|
|
56
|
+
timestamp: Date.now(),
|
|
57
|
+
metadata
|
|
58
|
+
};
|
|
59
|
+
this.queue.push(message);
|
|
60
|
+
this.notify();
|
|
61
|
+
return message.id;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get next message from queue
|
|
65
|
+
*/
|
|
66
|
+
next() {
|
|
67
|
+
const message = this.queue.shift();
|
|
68
|
+
if (message) {
|
|
69
|
+
message.status = "processing";
|
|
70
|
+
this.current = message;
|
|
71
|
+
this.processing = true;
|
|
72
|
+
this.notify();
|
|
73
|
+
}
|
|
74
|
+
return message;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Mark current message as complete
|
|
78
|
+
*/
|
|
79
|
+
complete(messageId) {
|
|
80
|
+
if (this.current?.id === messageId) {
|
|
81
|
+
this.current.status = "completed";
|
|
82
|
+
this.current = void 0;
|
|
83
|
+
this.processing = false;
|
|
84
|
+
this.notify();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Mark current message as error
|
|
89
|
+
*/
|
|
90
|
+
error(messageId, error) {
|
|
91
|
+
if (this.current?.id === messageId) {
|
|
92
|
+
this.current.status = "error";
|
|
93
|
+
this.current.error = error;
|
|
94
|
+
this.current = void 0;
|
|
95
|
+
this.processing = false;
|
|
96
|
+
this.notify();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Remove message from queue
|
|
101
|
+
*/
|
|
102
|
+
remove(messageId) {
|
|
103
|
+
const index = this.queue.findIndex((m) => m.id === messageId);
|
|
104
|
+
if (index > -1) {
|
|
105
|
+
this.queue.splice(index, 1);
|
|
106
|
+
this.notify();
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Reorder queue
|
|
113
|
+
*/
|
|
114
|
+
reorder(messageId, newIndex) {
|
|
115
|
+
const currentIndex = this.queue.findIndex((m) => m.id === messageId);
|
|
116
|
+
if (currentIndex > -1 && newIndex >= 0 && newIndex < this.queue.length) {
|
|
117
|
+
const [message] = this.queue.splice(currentIndex, 1);
|
|
118
|
+
this.queue.splice(newIndex, 0, message);
|
|
119
|
+
this.notify();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Edit queued message
|
|
124
|
+
*/
|
|
125
|
+
edit(messageId, content) {
|
|
126
|
+
const message = this.queue.find((m) => m.id === messageId);
|
|
127
|
+
if (message && message.status === "queued") {
|
|
128
|
+
message.content = content;
|
|
129
|
+
message.edited = true;
|
|
130
|
+
this.notify();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Clear all queued messages
|
|
135
|
+
*/
|
|
136
|
+
clear() {
|
|
137
|
+
this.queue = [];
|
|
138
|
+
this.notify();
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get queue state
|
|
142
|
+
*/
|
|
143
|
+
getState() {
|
|
144
|
+
return {
|
|
145
|
+
current: this.current,
|
|
146
|
+
queue: [...this.queue],
|
|
147
|
+
processing: this.processing
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Subscribe to queue changes
|
|
152
|
+
*/
|
|
153
|
+
subscribe(listener) {
|
|
154
|
+
this.listeners.add(listener);
|
|
155
|
+
return () => this.listeners.delete(listener);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Check if queue is empty
|
|
159
|
+
*/
|
|
160
|
+
isEmpty() {
|
|
161
|
+
return this.queue.length === 0;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Check if processing
|
|
165
|
+
*/
|
|
166
|
+
isProcessing() {
|
|
167
|
+
return this.processing;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Notify listeners
|
|
171
|
+
*/
|
|
172
|
+
notify() {
|
|
173
|
+
const state = this.getState();
|
|
174
|
+
this.listeners.forEach((listener) => listener(state));
|
|
175
|
+
}
|
|
176
|
+
};
|
|
42
177
|
ConversationHistory = class {
|
|
43
178
|
messages = [];
|
|
44
179
|
add(message) {
|
|
@@ -4875,50 +5010,20 @@ var init_runtime = __esm({
|
|
|
4875
5010
|
});
|
|
4876
5011
|
|
|
4877
5012
|
// src/daemon/terminal.ts
|
|
4878
|
-
import { spawn, execSync } from "child_process";
|
|
5013
|
+
import { spawn, spawnSync, execSync } from "child_process";
|
|
4879
5014
|
import { EventEmitter } from "events";
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
"tmux list-sessions -F '#{session_name}|#{session_windows}|#{session_attached}' 2>/dev/null",
|
|
4884
|
-
{ encoding: "utf8", stdio: "pipe" }
|
|
4885
|
-
).trim();
|
|
4886
|
-
if (!out) return [];
|
|
4887
|
-
return out.split("\n").map((line) => {
|
|
4888
|
-
const [name, windows, attached] = line.split("|");
|
|
4889
|
-
return {
|
|
4890
|
-
name,
|
|
4891
|
-
windows: parseInt(windows, 10) || 0,
|
|
4892
|
-
attached: attached === "1"
|
|
4893
|
-
};
|
|
4894
|
-
});
|
|
4895
|
-
} catch {
|
|
4896
|
-
return [];
|
|
4897
|
-
}
|
|
5015
|
+
import { randomUUID } from "crypto";
|
|
5016
|
+
function sanitizeName(input) {
|
|
5017
|
+
return input.replace(SAFE_NAME_RE, "-");
|
|
4898
5018
|
}
|
|
4899
|
-
function
|
|
4900
|
-
|
|
4901
|
-
const
|
|
4902
|
-
|
|
4903
|
-
{ encoding: "utf8", stdio: "pipe" }
|
|
4904
|
-
).trim();
|
|
4905
|
-
if (!out) return [];
|
|
4906
|
-
return out.split("\n").map((line) => {
|
|
4907
|
-
const [session, window2, windowName, pane, command] = line.split("|");
|
|
4908
|
-
return {
|
|
4909
|
-
session,
|
|
4910
|
-
window: window2,
|
|
4911
|
-
windowName,
|
|
4912
|
-
pane,
|
|
4913
|
-
command,
|
|
4914
|
-
target: `${session}:${window2}.${pane}`
|
|
4915
|
-
};
|
|
4916
|
-
});
|
|
4917
|
-
} catch {
|
|
4918
|
-
return [];
|
|
5019
|
+
function resolveAppTarget(target, appHint) {
|
|
5020
|
+
if (target.includes("::")) {
|
|
5021
|
+
const [appId, ...rest] = target.split("::");
|
|
5022
|
+
return { appId, rawTarget: rest.join("::") };
|
|
4919
5023
|
}
|
|
5024
|
+
return { appId: appHint || "tmux", rawTarget: target };
|
|
4920
5025
|
}
|
|
4921
|
-
function
|
|
5026
|
+
function sendTmuxKeys(target, keys, pressEnter = false) {
|
|
4922
5027
|
try {
|
|
4923
5028
|
const args = ["send-keys", "-t", target, keys];
|
|
4924
5029
|
if (pressEnter) args.push("Enter");
|
|
@@ -4931,7 +5036,7 @@ function sendKeys(target, keys, pressEnter = false) {
|
|
|
4931
5036
|
return false;
|
|
4932
5037
|
}
|
|
4933
5038
|
}
|
|
4934
|
-
function
|
|
5039
|
+
function captureTmuxPane(target, lines = 500) {
|
|
4935
5040
|
const cmds = [
|
|
4936
5041
|
`tmux capture-pane -t '${target}' -pae -S -${lines}`,
|
|
4937
5042
|
`tmux capture-pane -t '${target}' -pe -S -${lines}`
|
|
@@ -4944,38 +5049,24 @@ function capturePane(target, lines = 500) {
|
|
|
4944
5049
|
}
|
|
4945
5050
|
return "";
|
|
4946
5051
|
}
|
|
4947
|
-
|
|
4948
|
-
try {
|
|
4949
|
-
execSync(`tmux resize-pane -t '${target}' -x ${cols} -y ${rows}`, {
|
|
4950
|
-
encoding: "utf8",
|
|
4951
|
-
stdio: "pipe"
|
|
4952
|
-
});
|
|
4953
|
-
return true;
|
|
4954
|
-
} catch {
|
|
4955
|
-
return false;
|
|
4956
|
-
}
|
|
4957
|
-
}
|
|
4958
|
-
var TerminalStream, TerminalManager;
|
|
5052
|
+
var SAFE_NAME_RE, TmuxTerminalStream, TmuxTerminalApp, ShellTerminalApp, TerminalManager;
|
|
4959
5053
|
var init_terminal = __esm({
|
|
4960
5054
|
"src/daemon/terminal.ts"() {
|
|
4961
5055
|
"use strict";
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
catProcess = null;
|
|
4965
|
-
pipePath;
|
|
4966
|
-
isAttached = false;
|
|
5056
|
+
SAFE_NAME_RE = /[^a-zA-Z0-9_.:-]/g;
|
|
5057
|
+
TmuxTerminalStream = class extends EventEmitter {
|
|
4967
5058
|
constructor(target) {
|
|
4968
5059
|
super();
|
|
4969
5060
|
this.target = target;
|
|
4970
5061
|
this.pipePath = `/tmp/viber-term-${target.replace(/[^a-zA-Z0-9]/g, "-")}-${Date.now()}`;
|
|
4971
5062
|
}
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
5063
|
+
catProcess = null;
|
|
5064
|
+
pipePath;
|
|
5065
|
+
isAttached = false;
|
|
4975
5066
|
async attach() {
|
|
4976
5067
|
if (this.isAttached) return true;
|
|
4977
5068
|
try {
|
|
4978
|
-
const history =
|
|
5069
|
+
const history = captureTmuxPane(this.target, 200);
|
|
4979
5070
|
if (history) {
|
|
4980
5071
|
this.emit("data", history);
|
|
4981
5072
|
}
|
|
@@ -5005,9 +5096,6 @@ var init_terminal = __esm({
|
|
|
5005
5096
|
return false;
|
|
5006
5097
|
}
|
|
5007
5098
|
}
|
|
5008
|
-
/**
|
|
5009
|
-
* Stop streaming
|
|
5010
|
-
*/
|
|
5011
5099
|
detach() {
|
|
5012
5100
|
if (!this.isAttached) return;
|
|
5013
5101
|
try {
|
|
@@ -5016,12 +5104,6 @@ var init_terminal = __esm({
|
|
|
5016
5104
|
}
|
|
5017
5105
|
this.cleanup();
|
|
5018
5106
|
}
|
|
5019
|
-
/**
|
|
5020
|
-
* Send input to the pane
|
|
5021
|
-
*/
|
|
5022
|
-
sendInput(keys) {
|
|
5023
|
-
return sendKeys(this.target, keys, false);
|
|
5024
|
-
}
|
|
5025
5107
|
cleanup() {
|
|
5026
5108
|
this.isAttached = false;
|
|
5027
5109
|
if (this.catProcess) {
|
|
@@ -5038,20 +5120,61 @@ var init_terminal = __esm({
|
|
|
5038
5120
|
return this.isAttached;
|
|
5039
5121
|
}
|
|
5040
5122
|
};
|
|
5041
|
-
|
|
5123
|
+
TmuxTerminalApp = class {
|
|
5124
|
+
id = "tmux";
|
|
5125
|
+
label = "tmux";
|
|
5042
5126
|
streams = /* @__PURE__ */ new Map();
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5127
|
+
isAvailable() {
|
|
5128
|
+
try {
|
|
5129
|
+
execSync("tmux -V", { stdio: "pipe" });
|
|
5130
|
+
return true;
|
|
5131
|
+
} catch {
|
|
5132
|
+
return false;
|
|
5133
|
+
}
|
|
5134
|
+
}
|
|
5135
|
+
listSessions() {
|
|
5136
|
+
try {
|
|
5137
|
+
const out = execSync(
|
|
5138
|
+
"tmux list-sessions -F '#{session_name}|#{session_windows}|#{session_attached}' 2>/dev/null",
|
|
5139
|
+
{ encoding: "utf8", stdio: "pipe" }
|
|
5140
|
+
).trim();
|
|
5141
|
+
if (!out) return [];
|
|
5142
|
+
return out.split("\n").map((line) => {
|
|
5143
|
+
const [name, windows, attached] = line.split("|");
|
|
5144
|
+
return {
|
|
5145
|
+
appId: this.id,
|
|
5146
|
+
name,
|
|
5147
|
+
windows: parseInt(windows, 10) || 0,
|
|
5148
|
+
attached: attached === "1"
|
|
5149
|
+
};
|
|
5150
|
+
});
|
|
5151
|
+
} catch {
|
|
5152
|
+
return [];
|
|
5153
|
+
}
|
|
5154
|
+
}
|
|
5155
|
+
listPanes() {
|
|
5156
|
+
try {
|
|
5157
|
+
const out = execSync(
|
|
5158
|
+
"tmux list-panes -a -F '#{session_name}|#{window_index}|#{window_name}|#{pane_index}|#{pane_current_command}' 2>/dev/null",
|
|
5159
|
+
{ encoding: "utf8", stdio: "pipe" }
|
|
5160
|
+
).trim();
|
|
5161
|
+
if (!out) return [];
|
|
5162
|
+
return out.split("\n").map((line) => {
|
|
5163
|
+
const [session, window2, windowName, pane, command] = line.split("|");
|
|
5164
|
+
return {
|
|
5165
|
+
appId: this.id,
|
|
5166
|
+
session,
|
|
5167
|
+
window: window2,
|
|
5168
|
+
windowName,
|
|
5169
|
+
pane,
|
|
5170
|
+
command,
|
|
5171
|
+
target: `${session}:${window2}.${pane}`
|
|
5172
|
+
};
|
|
5173
|
+
});
|
|
5174
|
+
} catch {
|
|
5175
|
+
return [];
|
|
5176
|
+
}
|
|
5051
5177
|
}
|
|
5052
|
-
/**
|
|
5053
|
-
* Attach to a pane and return the stream
|
|
5054
|
-
*/
|
|
5055
5178
|
async attach(target, onData, onClose) {
|
|
5056
5179
|
let stream = this.streams.get(target);
|
|
5057
5180
|
if (stream && stream.attached) {
|
|
@@ -5059,7 +5182,7 @@ var init_terminal = __esm({
|
|
|
5059
5182
|
stream.on("close", onClose);
|
|
5060
5183
|
return true;
|
|
5061
5184
|
}
|
|
5062
|
-
stream = new
|
|
5185
|
+
stream = new TmuxTerminalStream(target);
|
|
5063
5186
|
stream.on("data", onData);
|
|
5064
5187
|
stream.on("close", () => {
|
|
5065
5188
|
this.streams.delete(target);
|
|
@@ -5071,31 +5194,62 @@ var init_terminal = __esm({
|
|
|
5071
5194
|
}
|
|
5072
5195
|
return ok;
|
|
5073
5196
|
}
|
|
5074
|
-
/**
|
|
5075
|
-
* Detach from a pane
|
|
5076
|
-
*/
|
|
5077
5197
|
detach(target) {
|
|
5078
5198
|
const stream = this.streams.get(target);
|
|
5079
|
-
if (stream)
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
}
|
|
5199
|
+
if (!stream) return;
|
|
5200
|
+
stream.detach();
|
|
5201
|
+
this.streams.delete(target);
|
|
5083
5202
|
}
|
|
5084
|
-
/**
|
|
5085
|
-
* Send input to a pane
|
|
5086
|
-
*/
|
|
5087
5203
|
sendInput(target, keys) {
|
|
5088
|
-
return
|
|
5204
|
+
return sendTmuxKeys(target, keys);
|
|
5089
5205
|
}
|
|
5090
|
-
/**
|
|
5091
|
-
* Resize pane to match web terminal
|
|
5092
|
-
*/
|
|
5093
5206
|
resize(target, cols, rows) {
|
|
5094
|
-
|
|
5207
|
+
try {
|
|
5208
|
+
execSync(`tmux resize-pane -t '${target}' -x ${cols} -y ${rows}`, {
|
|
5209
|
+
encoding: "utf8",
|
|
5210
|
+
stdio: "pipe"
|
|
5211
|
+
});
|
|
5212
|
+
return true;
|
|
5213
|
+
} catch {
|
|
5214
|
+
return false;
|
|
5215
|
+
}
|
|
5216
|
+
}
|
|
5217
|
+
createSession(sessionName, windowName = "main", cwd) {
|
|
5218
|
+
const safeSession = sanitizeName(sessionName || "coding");
|
|
5219
|
+
const safeWindow = sanitizeName(windowName || "main");
|
|
5220
|
+
try {
|
|
5221
|
+
execSync(`tmux has-session -t '${safeSession}' 2>/dev/null`, { stdio: "pipe" });
|
|
5222
|
+
return { ok: true, appId: this.id, sessionName: safeSession, created: false };
|
|
5223
|
+
} catch {
|
|
5224
|
+
}
|
|
5225
|
+
const args = ["new-session", "-d", "-s", safeSession, "-n", safeWindow];
|
|
5226
|
+
if (cwd) {
|
|
5227
|
+
args.push("-c", cwd);
|
|
5228
|
+
}
|
|
5229
|
+
const result = spawnSync("tmux", args, {
|
|
5230
|
+
encoding: "utf8",
|
|
5231
|
+
stdio: "pipe"
|
|
5232
|
+
});
|
|
5233
|
+
if (result.error) {
|
|
5234
|
+
return {
|
|
5235
|
+
ok: false,
|
|
5236
|
+
appId: this.id,
|
|
5237
|
+
sessionName: safeSession,
|
|
5238
|
+
created: false,
|
|
5239
|
+
error: `Failed to start tmux: ${result.error.message}`
|
|
5240
|
+
};
|
|
5241
|
+
}
|
|
5242
|
+
if (result.status !== 0) {
|
|
5243
|
+
return {
|
|
5244
|
+
ok: false,
|
|
5245
|
+
appId: this.id,
|
|
5246
|
+
sessionName: safeSession,
|
|
5247
|
+
created: false,
|
|
5248
|
+
error: (result.stderr || result.stdout || "Failed to create tmux session").trim()
|
|
5249
|
+
};
|
|
5250
|
+
}
|
|
5251
|
+
return { ok: true, appId: this.id, sessionName: safeSession, created: true };
|
|
5095
5252
|
}
|
|
5096
|
-
/**
|
|
5097
|
-
* Detach all streams
|
|
5098
|
-
*/
|
|
5099
5253
|
detachAll() {
|
|
5100
5254
|
for (const stream of this.streams.values()) {
|
|
5101
5255
|
stream.detach();
|
|
@@ -5103,6 +5257,177 @@ var init_terminal = __esm({
|
|
|
5103
5257
|
this.streams.clear();
|
|
5104
5258
|
}
|
|
5105
5259
|
};
|
|
5260
|
+
ShellTerminalApp = class {
|
|
5261
|
+
id = "shell";
|
|
5262
|
+
label = "shell";
|
|
5263
|
+
sessions = /* @__PURE__ */ new Map();
|
|
5264
|
+
isAvailable() {
|
|
5265
|
+
return true;
|
|
5266
|
+
}
|
|
5267
|
+
listSessions() {
|
|
5268
|
+
return Array.from(this.sessions.values()).map((state) => ({
|
|
5269
|
+
appId: this.id,
|
|
5270
|
+
name: state.sessionName,
|
|
5271
|
+
windows: 1,
|
|
5272
|
+
attached: state.listeners.size > 0
|
|
5273
|
+
}));
|
|
5274
|
+
}
|
|
5275
|
+
listPanes() {
|
|
5276
|
+
return Array.from(this.sessions.entries()).map(([id, state]) => ({
|
|
5277
|
+
appId: this.id,
|
|
5278
|
+
session: state.sessionName,
|
|
5279
|
+
window: "0",
|
|
5280
|
+
windowName: state.windowName,
|
|
5281
|
+
pane: state.pane,
|
|
5282
|
+
command: process.env.SHELL || "sh",
|
|
5283
|
+
target: id
|
|
5284
|
+
}));
|
|
5285
|
+
}
|
|
5286
|
+
async attach(target, onData, onClose) {
|
|
5287
|
+
const state = this.sessions.get(target);
|
|
5288
|
+
if (!state) return false;
|
|
5289
|
+
state.listeners.add(onData);
|
|
5290
|
+
state.closeListeners.add(onClose);
|
|
5291
|
+
if (state.history.length > 0) {
|
|
5292
|
+
onData(state.history.join(""));
|
|
5293
|
+
}
|
|
5294
|
+
return true;
|
|
5295
|
+
}
|
|
5296
|
+
detach(target) {
|
|
5297
|
+
const state = this.sessions.get(target);
|
|
5298
|
+
if (!state) return;
|
|
5299
|
+
state.listeners.clear();
|
|
5300
|
+
state.closeListeners.clear();
|
|
5301
|
+
}
|
|
5302
|
+
sendInput(target, keys) {
|
|
5303
|
+
const state = this.sessions.get(target);
|
|
5304
|
+
if (!state || !state.proc.stdin?.writable) return false;
|
|
5305
|
+
state.proc.stdin.write(keys);
|
|
5306
|
+
return true;
|
|
5307
|
+
}
|
|
5308
|
+
resize(_target, _cols, _rows) {
|
|
5309
|
+
return true;
|
|
5310
|
+
}
|
|
5311
|
+
createSession(sessionName, windowName = "main", cwd) {
|
|
5312
|
+
const safeSession = sanitizeName(sessionName || `shell-${Date.now()}`);
|
|
5313
|
+
const shell = process.env.SHELL || "sh";
|
|
5314
|
+
const target = `${safeSession}:${randomUUID().slice(0, 8)}`;
|
|
5315
|
+
if (this.sessions.has(target)) {
|
|
5316
|
+
return { ok: true, appId: this.id, sessionName: safeSession, created: false };
|
|
5317
|
+
}
|
|
5318
|
+
const proc = spawn(shell, [], {
|
|
5319
|
+
cwd,
|
|
5320
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5321
|
+
env: process.env
|
|
5322
|
+
});
|
|
5323
|
+
if (!proc.pid) {
|
|
5324
|
+
return {
|
|
5325
|
+
ok: false,
|
|
5326
|
+
appId: this.id,
|
|
5327
|
+
sessionName: safeSession,
|
|
5328
|
+
created: false,
|
|
5329
|
+
error: "Failed to start shell process"
|
|
5330
|
+
};
|
|
5331
|
+
}
|
|
5332
|
+
const state = {
|
|
5333
|
+
proc,
|
|
5334
|
+
sessionName: safeSession,
|
|
5335
|
+
windowName,
|
|
5336
|
+
pane: "0",
|
|
5337
|
+
history: [],
|
|
5338
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
5339
|
+
closeListeners: /* @__PURE__ */ new Set()
|
|
5340
|
+
};
|
|
5341
|
+
const pushChunk = (chunk) => {
|
|
5342
|
+
const text = chunk.toString();
|
|
5343
|
+
state.history.push(text);
|
|
5344
|
+
if (state.history.length > 200) {
|
|
5345
|
+
state.history.shift();
|
|
5346
|
+
}
|
|
5347
|
+
for (const listener of state.listeners) {
|
|
5348
|
+
listener(text);
|
|
5349
|
+
}
|
|
5350
|
+
};
|
|
5351
|
+
proc.stdout?.on("data", pushChunk);
|
|
5352
|
+
proc.stderr?.on("data", pushChunk);
|
|
5353
|
+
proc.on("close", () => {
|
|
5354
|
+
for (const onClose of state.closeListeners) {
|
|
5355
|
+
onClose();
|
|
5356
|
+
}
|
|
5357
|
+
this.sessions.delete(target);
|
|
5358
|
+
});
|
|
5359
|
+
this.sessions.set(target, state);
|
|
5360
|
+
return { ok: true, appId: this.id, sessionName: safeSession, created: true };
|
|
5361
|
+
}
|
|
5362
|
+
detachAll() {
|
|
5363
|
+
for (const [id, state] of this.sessions.entries()) {
|
|
5364
|
+
state.proc.kill();
|
|
5365
|
+
this.sessions.delete(id);
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
};
|
|
5369
|
+
TerminalManager = class {
|
|
5370
|
+
apps = /* @__PURE__ */ new Map();
|
|
5371
|
+
constructor(adapters) {
|
|
5372
|
+
const defaultAdapters = adapters ?? [new TmuxTerminalApp(), new ShellTerminalApp()];
|
|
5373
|
+
for (const adapter of defaultAdapters) {
|
|
5374
|
+
this.apps.set(adapter.id, adapter);
|
|
5375
|
+
}
|
|
5376
|
+
}
|
|
5377
|
+
list() {
|
|
5378
|
+
const metadata = Array.from(this.apps.values()).map((app) => ({
|
|
5379
|
+
id: app.id,
|
|
5380
|
+
label: app.label,
|
|
5381
|
+
available: app.isAvailable()
|
|
5382
|
+
}));
|
|
5383
|
+
const sessions = [];
|
|
5384
|
+
const panes = [];
|
|
5385
|
+
for (const app of this.apps.values()) {
|
|
5386
|
+
if (!app.isAvailable()) continue;
|
|
5387
|
+
sessions.push(...app.listSessions());
|
|
5388
|
+
panes.push(...app.listPanes());
|
|
5389
|
+
}
|
|
5390
|
+
return { apps: metadata, sessions, panes };
|
|
5391
|
+
}
|
|
5392
|
+
async attach(target, onData, onClose, appHint) {
|
|
5393
|
+
const { appId, rawTarget } = resolveAppTarget(target, appHint);
|
|
5394
|
+
const app = this.apps.get(appId);
|
|
5395
|
+
if (!app || !app.isAvailable()) return false;
|
|
5396
|
+
return app.attach(rawTarget, onData, onClose);
|
|
5397
|
+
}
|
|
5398
|
+
detach(target, appHint) {
|
|
5399
|
+
const { appId, rawTarget } = resolveAppTarget(target, appHint);
|
|
5400
|
+
this.apps.get(appId)?.detach(rawTarget);
|
|
5401
|
+
}
|
|
5402
|
+
sendInput(target, keys, appHint) {
|
|
5403
|
+
const { appId, rawTarget } = resolveAppTarget(target, appHint);
|
|
5404
|
+
const app = this.apps.get(appId);
|
|
5405
|
+
return !!app && app.isAvailable() ? app.sendInput(rawTarget, keys) : false;
|
|
5406
|
+
}
|
|
5407
|
+
resize(target, cols, rows, appHint) {
|
|
5408
|
+
const { appId, rawTarget } = resolveAppTarget(target, appHint);
|
|
5409
|
+
const app = this.apps.get(appId);
|
|
5410
|
+
return !!app && app.isAvailable() ? app.resize(rawTarget, cols, rows) : false;
|
|
5411
|
+
}
|
|
5412
|
+
createSession(sessionName, windowName = "main", cwd, appId = "tmux") {
|
|
5413
|
+
const app = this.apps.get(appId);
|
|
5414
|
+
if (!app || !app.isAvailable()) {
|
|
5415
|
+
return {
|
|
5416
|
+
ok: false,
|
|
5417
|
+
appId,
|
|
5418
|
+
sessionName,
|
|
5419
|
+
created: false,
|
|
5420
|
+
error: `Terminal app '${appId}' is not available`
|
|
5421
|
+
};
|
|
5422
|
+
}
|
|
5423
|
+
return app.createSession(sessionName, windowName, cwd);
|
|
5424
|
+
}
|
|
5425
|
+
detachAll() {
|
|
5426
|
+
for (const app of this.apps.values()) {
|
|
5427
|
+
app.detachAll();
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
};
|
|
5106
5431
|
}
|
|
5107
5432
|
});
|
|
5108
5433
|
|
|
@@ -5267,16 +5592,16 @@ var init_controller = __esm({
|
|
|
5267
5592
|
this.handleTerminalList();
|
|
5268
5593
|
break;
|
|
5269
5594
|
case "terminal:attach":
|
|
5270
|
-
await this.handleTerminalAttach(message.target);
|
|
5595
|
+
await this.handleTerminalAttach(message.target, message.appId);
|
|
5271
5596
|
break;
|
|
5272
5597
|
case "terminal:detach":
|
|
5273
|
-
this.handleTerminalDetach(message.target);
|
|
5598
|
+
this.handleTerminalDetach(message.target, message.appId);
|
|
5274
5599
|
break;
|
|
5275
5600
|
case "terminal:input":
|
|
5276
|
-
this.handleTerminalInput(message.target, message.keys);
|
|
5601
|
+
this.handleTerminalInput(message.target, message.keys, message.appId);
|
|
5277
5602
|
break;
|
|
5278
5603
|
case "terminal:resize":
|
|
5279
|
-
this.handleTerminalResize(message.target, message.cols, message.rows);
|
|
5604
|
+
this.handleTerminalResize(message.target, message.cols, message.rows, message.appId);
|
|
5280
5605
|
break;
|
|
5281
5606
|
}
|
|
5282
5607
|
} catch (error) {
|
|
@@ -5306,7 +5631,87 @@ var init_controller = __esm({
|
|
|
5306
5631
|
},
|
|
5307
5632
|
messages
|
|
5308
5633
|
);
|
|
5309
|
-
|
|
5634
|
+
let finalText = "";
|
|
5635
|
+
let pendingDelta = "";
|
|
5636
|
+
let lastProgressAt = 0;
|
|
5637
|
+
const flushProgress = (force = false) => {
|
|
5638
|
+
if (!pendingDelta) return;
|
|
5639
|
+
const now = Date.now();
|
|
5640
|
+
if (!force && now - lastProgressAt < 250) return;
|
|
5641
|
+
this.send({
|
|
5642
|
+
type: "task:progress",
|
|
5643
|
+
taskId,
|
|
5644
|
+
event: {
|
|
5645
|
+
kind: "text-delta",
|
|
5646
|
+
delta: pendingDelta,
|
|
5647
|
+
totalLength: finalText.length,
|
|
5648
|
+
at: new Date(now).toISOString()
|
|
5649
|
+
}
|
|
5650
|
+
});
|
|
5651
|
+
pendingDelta = "";
|
|
5652
|
+
lastProgressAt = now;
|
|
5653
|
+
};
|
|
5654
|
+
this.send({
|
|
5655
|
+
type: "task:progress",
|
|
5656
|
+
taskId,
|
|
5657
|
+
event: {
|
|
5658
|
+
kind: "status",
|
|
5659
|
+
phase: "executing",
|
|
5660
|
+
message: "Agent execution started",
|
|
5661
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5662
|
+
}
|
|
5663
|
+
});
|
|
5664
|
+
for await (const part of streamResult.fullStream) {
|
|
5665
|
+
switch (part.type) {
|
|
5666
|
+
case "text-delta":
|
|
5667
|
+
if (part.text) {
|
|
5668
|
+
finalText += part.text;
|
|
5669
|
+
pendingDelta += part.text;
|
|
5670
|
+
flushProgress(false);
|
|
5671
|
+
}
|
|
5672
|
+
break;
|
|
5673
|
+
case "tool-call":
|
|
5674
|
+
this.send({
|
|
5675
|
+
type: "task:progress",
|
|
5676
|
+
taskId,
|
|
5677
|
+
event: {
|
|
5678
|
+
kind: "tool-call",
|
|
5679
|
+
toolName: part.toolName,
|
|
5680
|
+
toolCallId: part.toolCallId,
|
|
5681
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5682
|
+
}
|
|
5683
|
+
});
|
|
5684
|
+
break;
|
|
5685
|
+
case "tool-result":
|
|
5686
|
+
this.send({
|
|
5687
|
+
type: "task:progress",
|
|
5688
|
+
taskId,
|
|
5689
|
+
event: {
|
|
5690
|
+
kind: "tool-result",
|
|
5691
|
+
toolCallId: part.toolCallId,
|
|
5692
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5693
|
+
}
|
|
5694
|
+
});
|
|
5695
|
+
break;
|
|
5696
|
+
case "error":
|
|
5697
|
+
throw new Error(part.error?.message || "Task stream error");
|
|
5698
|
+
case "finish":
|
|
5699
|
+
flushProgress(true);
|
|
5700
|
+
this.send({
|
|
5701
|
+
type: "task:progress",
|
|
5702
|
+
taskId,
|
|
5703
|
+
event: {
|
|
5704
|
+
kind: "status",
|
|
5705
|
+
phase: "verifying",
|
|
5706
|
+
message: "Agent execution finished, preparing final output",
|
|
5707
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5708
|
+
}
|
|
5709
|
+
});
|
|
5710
|
+
break;
|
|
5711
|
+
default:
|
|
5712
|
+
break;
|
|
5713
|
+
}
|
|
5714
|
+
}
|
|
5310
5715
|
this.send({
|
|
5311
5716
|
type: "task:completed",
|
|
5312
5717
|
taskId,
|
|
@@ -5343,33 +5748,34 @@ var init_controller = __esm({
|
|
|
5343
5748
|
}
|
|
5344
5749
|
// ==================== Terminal Streaming ====================
|
|
5345
5750
|
handleTerminalList() {
|
|
5346
|
-
const { sessions, panes } = this.terminalManager.list();
|
|
5347
|
-
this.send({ type: "terminal:list", sessions, panes });
|
|
5751
|
+
const { apps, sessions, panes } = this.terminalManager.list();
|
|
5752
|
+
this.send({ type: "terminal:list", apps, sessions, panes });
|
|
5348
5753
|
}
|
|
5349
|
-
async handleTerminalAttach(target) {
|
|
5754
|
+
async handleTerminalAttach(target, appId) {
|
|
5350
5755
|
console.log(`[Viber] Attaching to terminal: ${target}`);
|
|
5351
5756
|
const ok = await this.terminalManager.attach(
|
|
5352
5757
|
target,
|
|
5353
5758
|
(data) => {
|
|
5354
|
-
this.send({ type: "terminal:output", target, data });
|
|
5759
|
+
this.send({ type: "terminal:output", target, appId, data });
|
|
5355
5760
|
},
|
|
5356
5761
|
() => {
|
|
5357
|
-
this.send({ type: "terminal:detached", target });
|
|
5358
|
-
}
|
|
5762
|
+
this.send({ type: "terminal:detached", target, appId });
|
|
5763
|
+
},
|
|
5764
|
+
appId
|
|
5359
5765
|
);
|
|
5360
|
-
this.send({ type: "terminal:attached", target, ok });
|
|
5766
|
+
this.send({ type: "terminal:attached", target, appId, ok });
|
|
5361
5767
|
}
|
|
5362
|
-
handleTerminalDetach(target) {
|
|
5768
|
+
handleTerminalDetach(target, appId) {
|
|
5363
5769
|
console.log(`[Viber] Detaching from terminal: ${target}`);
|
|
5364
|
-
this.terminalManager.detach(target);
|
|
5365
|
-
this.send({ type: "terminal:detached", target });
|
|
5770
|
+
this.terminalManager.detach(target, appId);
|
|
5771
|
+
this.send({ type: "terminal:detached", target, appId });
|
|
5366
5772
|
}
|
|
5367
|
-
handleTerminalInput(target, keys) {
|
|
5368
|
-
this.terminalManager.sendInput(target, keys);
|
|
5773
|
+
handleTerminalInput(target, keys, appId) {
|
|
5774
|
+
this.terminalManager.sendInput(target, keys, appId);
|
|
5369
5775
|
}
|
|
5370
|
-
handleTerminalResize(target, cols, rows) {
|
|
5371
|
-
const ok = this.terminalManager.resize(target, cols, rows);
|
|
5372
|
-
this.send({ type: "terminal:resized", target, ok });
|
|
5776
|
+
handleTerminalResize(target, cols, rows, appId) {
|
|
5777
|
+
const ok = this.terminalManager.resize(target, cols, rows, appId);
|
|
5778
|
+
this.send({ type: "terminal:resized", target, appId, ok });
|
|
5373
5779
|
}
|
|
5374
5780
|
// ==================== Communication ====================
|
|
5375
5781
|
send(message) {
|
|
@@ -5521,7 +5927,7 @@ var init_antigravity = __esm({
|
|
|
5521
5927
|
|
|
5522
5928
|
// src/skills/cursor-agent/index.ts
|
|
5523
5929
|
import { z as z9 } from "zod";
|
|
5524
|
-
import { execSync as execSync2, spawnSync } from "child_process";
|
|
5930
|
+
import { execSync as execSync2, spawnSync as spawnSync2 } from "child_process";
|
|
5525
5931
|
import * as path11 from "path";
|
|
5526
5932
|
function runInTmux(goal, cwd, waitSeconds) {
|
|
5527
5933
|
execSync2(
|
|
@@ -5532,17 +5938,17 @@ function runInTmux(goal, cwd, waitSeconds) {
|
|
|
5532
5938
|
}
|
|
5533
5939
|
);
|
|
5534
5940
|
const cdCmd = cwd.includes(" ") ? `cd "${cwd.replace(/"/g, '\\"')}"` : `cd ${cwd}`;
|
|
5535
|
-
|
|
5941
|
+
spawnSync2("tmux", ["send-keys", "-t", TMUX_SESSION, cdCmd, "Enter"], {
|
|
5536
5942
|
encoding: "utf8",
|
|
5537
5943
|
stdio: "pipe"
|
|
5538
5944
|
});
|
|
5539
5945
|
const sendArg = `agent -p ${JSON.stringify(goal)}`;
|
|
5540
|
-
|
|
5946
|
+
spawnSync2("tmux", ["send-keys", "-t", TMUX_SESSION, sendArg, "Enter"], {
|
|
5541
5947
|
encoding: "utf8",
|
|
5542
5948
|
stdio: "pipe"
|
|
5543
5949
|
});
|
|
5544
5950
|
execSync2("sleep 3", { encoding: "utf8", stdio: "pipe" });
|
|
5545
|
-
|
|
5951
|
+
spawnSync2("tmux", ["send-keys", "-t", TMUX_SESSION, "a"], {
|
|
5546
5952
|
encoding: "utf8",
|
|
5547
5953
|
stdio: "pipe"
|
|
5548
5954
|
});
|
|
@@ -5594,7 +6000,7 @@ var init_cursor_agent = __esm({
|
|
|
5594
6000
|
|
|
5595
6001
|
// src/skills/tmux/index.ts
|
|
5596
6002
|
import { z as z10 } from "zod";
|
|
5597
|
-
import { execSync as execSync3, spawnSync as
|
|
6003
|
+
import { execSync as execSync3, spawnSync as spawnSync3 } from "child_process";
|
|
5598
6004
|
import * as path12 from "path";
|
|
5599
6005
|
function safeTarget(t) {
|
|
5600
6006
|
return t.replace(SAFE_RE, "-");
|
|
@@ -5606,11 +6012,11 @@ function runInTmux2(sessionName, command, cwd, waitSeconds) {
|
|
|
5606
6012
|
{ encoding: "utf8", stdio: "pipe" }
|
|
5607
6013
|
);
|
|
5608
6014
|
const cdCmd = cwd.includes(" ") ? `cd "${cwd.replace(/"/g, '\\"')}"` : `cd ${cwd}`;
|
|
5609
|
-
|
|
6015
|
+
spawnSync3("tmux", ["send-keys", "-t", safeSession, cdCmd, "Enter"], {
|
|
5610
6016
|
encoding: "utf8",
|
|
5611
6017
|
stdio: "pipe"
|
|
5612
6018
|
});
|
|
5613
|
-
|
|
6019
|
+
spawnSync3("tmux", ["send-keys", "-t", safeSession, command, "Enter"], {
|
|
5614
6020
|
encoding: "utf8",
|
|
5615
6021
|
stdio: "pipe"
|
|
5616
6022
|
});
|
|
@@ -5698,9 +6104,9 @@ function getTools3() {
|
|
|
5698
6104
|
if (args.cwd) {
|
|
5699
6105
|
newWinArgs.push("-c", path12.resolve(args.cwd));
|
|
5700
6106
|
}
|
|
5701
|
-
|
|
6107
|
+
spawnSync3("tmux", newWinArgs, { encoding: "utf8", stdio: "pipe" });
|
|
5702
6108
|
if (args.command) {
|
|
5703
|
-
|
|
6109
|
+
spawnSync3("tmux", ["send-keys", "-t", session, args.command, "Enter"], {
|
|
5704
6110
|
encoding: "utf8",
|
|
5705
6111
|
stdio: "pipe"
|
|
5706
6112
|
});
|
|
@@ -5736,9 +6142,9 @@ function getTools3() {
|
|
|
5736
6142
|
if (args.cwd) {
|
|
5737
6143
|
splitArgs.push("-c", path12.resolve(args.cwd));
|
|
5738
6144
|
}
|
|
5739
|
-
|
|
6145
|
+
spawnSync3("tmux", splitArgs, { encoding: "utf8", stdio: "pipe" });
|
|
5740
6146
|
if (args.command) {
|
|
5741
|
-
|
|
6147
|
+
spawnSync3("tmux", ["send-keys", "-t", t, args.command, "Enter"], {
|
|
5742
6148
|
encoding: "utf8",
|
|
5743
6149
|
stdio: "pipe"
|
|
5744
6150
|
});
|
|
@@ -5767,7 +6173,7 @@ function getTools3() {
|
|
|
5767
6173
|
execute: async (args) => {
|
|
5768
6174
|
const t = safeTarget(args.target);
|
|
5769
6175
|
try {
|
|
5770
|
-
|
|
6176
|
+
spawnSync3("tmux", ["send-keys", "-t", t, args.keys, ...args.pressEnter !== false ? ["Enter"] : []], {
|
|
5771
6177
|
encoding: "utf8",
|
|
5772
6178
|
stdio: "pipe"
|
|
5773
6179
|
});
|
|
@@ -5951,49 +6357,74 @@ var init_local_server = __esm({
|
|
|
5951
6357
|
this.handleTerminalList(ws);
|
|
5952
6358
|
break;
|
|
5953
6359
|
case "terminal:attach":
|
|
5954
|
-
this.handleTerminalAttach(ws, msg.target);
|
|
6360
|
+
this.handleTerminalAttach(ws, msg.target, msg.appId);
|
|
5955
6361
|
break;
|
|
5956
6362
|
case "terminal:detach":
|
|
5957
|
-
this.handleTerminalDetach(ws, msg.target);
|
|
6363
|
+
this.handleTerminalDetach(ws, msg.target, msg.appId);
|
|
5958
6364
|
break;
|
|
5959
6365
|
case "terminal:input":
|
|
5960
|
-
this.handleTerminalInput(msg.target, msg.keys);
|
|
6366
|
+
this.handleTerminalInput(msg.target, msg.keys, msg.appId);
|
|
5961
6367
|
break;
|
|
5962
6368
|
case "terminal:resize":
|
|
5963
|
-
this.handleTerminalResize(ws, msg.target, msg.cols, msg.rows);
|
|
6369
|
+
this.handleTerminalResize(ws, msg.target, msg.cols, msg.rows, msg.appId);
|
|
6370
|
+
break;
|
|
6371
|
+
case "terminal:create-session":
|
|
6372
|
+
this.handleTerminalCreateSession(
|
|
6373
|
+
ws,
|
|
6374
|
+
msg.sessionName,
|
|
6375
|
+
msg.windowName,
|
|
6376
|
+
msg.cwd,
|
|
6377
|
+
msg.appId
|
|
6378
|
+
);
|
|
5964
6379
|
break;
|
|
5965
6380
|
default:
|
|
5966
6381
|
console.log(`[Viber] Unknown message type: ${msg.type}`);
|
|
5967
6382
|
}
|
|
5968
6383
|
}
|
|
5969
6384
|
handleTerminalList(ws) {
|
|
5970
|
-
const { sessions, panes } = this.terminalManager.list();
|
|
5971
|
-
this.send(ws, { type: "terminal:list", sessions, panes });
|
|
6385
|
+
const { apps, sessions, panes } = this.terminalManager.list();
|
|
6386
|
+
this.send(ws, { type: "terminal:list", apps, sessions, panes });
|
|
5972
6387
|
}
|
|
5973
|
-
async handleTerminalAttach(ws, target) {
|
|
6388
|
+
async handleTerminalAttach(ws, target, appId) {
|
|
5974
6389
|
console.log(`[Viber] Attaching to terminal: ${target}`);
|
|
5975
6390
|
const ok = await this.terminalManager.attach(
|
|
5976
6391
|
target,
|
|
5977
6392
|
(data) => {
|
|
5978
|
-
this.send(ws, { type: "terminal:output", target, data });
|
|
6393
|
+
this.send(ws, { type: "terminal:output", target, appId, data });
|
|
5979
6394
|
},
|
|
5980
6395
|
() => {
|
|
5981
|
-
this.send(ws, { type: "terminal:detached", target });
|
|
5982
|
-
}
|
|
6396
|
+
this.send(ws, { type: "terminal:detached", target, appId });
|
|
6397
|
+
},
|
|
6398
|
+
appId
|
|
5983
6399
|
);
|
|
5984
|
-
this.send(ws, { type: "terminal:attached", target, ok });
|
|
6400
|
+
this.send(ws, { type: "terminal:attached", target, appId, ok });
|
|
5985
6401
|
}
|
|
5986
|
-
handleTerminalDetach(ws, target) {
|
|
6402
|
+
handleTerminalDetach(ws, target, appId) {
|
|
5987
6403
|
console.log(`[Viber] Detaching from terminal: ${target}`);
|
|
5988
|
-
this.terminalManager.detach(target);
|
|
5989
|
-
this.send(ws, { type: "terminal:detached", target });
|
|
5990
|
-
}
|
|
5991
|
-
handleTerminalInput(target, keys) {
|
|
5992
|
-
this.terminalManager.sendInput(target, keys);
|
|
5993
|
-
}
|
|
5994
|
-
handleTerminalResize(ws, target, cols, rows) {
|
|
5995
|
-
const ok = this.terminalManager.resize(target, cols, rows);
|
|
5996
|
-
this.send(ws, { type: "terminal:resized", target, ok });
|
|
6404
|
+
this.terminalManager.detach(target, appId);
|
|
6405
|
+
this.send(ws, { type: "terminal:detached", target, appId });
|
|
6406
|
+
}
|
|
6407
|
+
handleTerminalInput(target, keys, appId) {
|
|
6408
|
+
this.terminalManager.sendInput(target, keys, appId);
|
|
6409
|
+
}
|
|
6410
|
+
handleTerminalResize(ws, target, cols, rows, appId) {
|
|
6411
|
+
const ok = this.terminalManager.resize(target, cols, rows, appId);
|
|
6412
|
+
this.send(ws, { type: "terminal:resized", target, appId, ok });
|
|
6413
|
+
}
|
|
6414
|
+
handleTerminalCreateSession(ws, sessionName, windowName, cwd, appId = "tmux") {
|
|
6415
|
+
const result = this.terminalManager.createSession(
|
|
6416
|
+
sessionName || "coding",
|
|
6417
|
+
windowName || "main",
|
|
6418
|
+
cwd,
|
|
6419
|
+
appId
|
|
6420
|
+
);
|
|
6421
|
+
this.send(ws, {
|
|
6422
|
+
type: "terminal:session-created",
|
|
6423
|
+
...result
|
|
6424
|
+
});
|
|
6425
|
+
if (result.ok) {
|
|
6426
|
+
this.handleTerminalList(ws);
|
|
6427
|
+
}
|
|
5997
6428
|
}
|
|
5998
6429
|
send(ws, msg) {
|
|
5999
6430
|
if (ws.readyState === WebSocket3.OPEN) {
|
|
@@ -6173,7 +6604,9 @@ var init_hub = __esm({
|
|
|
6173
6604
|
viberId: viber.id,
|
|
6174
6605
|
goal,
|
|
6175
6606
|
status: "pending",
|
|
6176
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
6607
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
6608
|
+
events: [],
|
|
6609
|
+
partialText: ""
|
|
6177
6610
|
};
|
|
6178
6611
|
this.tasks.set(taskId, task);
|
|
6179
6612
|
viber.ws.send(
|
|
@@ -6199,7 +6632,9 @@ var init_hub = __esm({
|
|
|
6199
6632
|
goal: t.goal,
|
|
6200
6633
|
status: t.status,
|
|
6201
6634
|
createdAt: t.createdAt.toISOString(),
|
|
6202
|
-
completedAt: t.completedAt?.toISOString()
|
|
6635
|
+
completedAt: t.completedAt?.toISOString(),
|
|
6636
|
+
eventCount: t.events.length,
|
|
6637
|
+
partialText: t.partialText
|
|
6203
6638
|
}));
|
|
6204
6639
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6205
6640
|
res.end(JSON.stringify({ tasks }));
|
|
@@ -6221,7 +6656,10 @@ var init_hub = __esm({
|
|
|
6221
6656
|
result: task.result,
|
|
6222
6657
|
error: task.error,
|
|
6223
6658
|
createdAt: task.createdAt.toISOString(),
|
|
6224
|
-
completedAt: task.completedAt?.toISOString()
|
|
6659
|
+
completedAt: task.completedAt?.toISOString(),
|
|
6660
|
+
events: task.events,
|
|
6661
|
+
eventCount: task.events.length,
|
|
6662
|
+
partialText: task.partialText
|
|
6225
6663
|
})
|
|
6226
6664
|
);
|
|
6227
6665
|
}
|
|
@@ -6275,6 +6713,7 @@ var init_hub = __esm({
|
|
|
6275
6713
|
this.handleTaskStarted(msg.taskId);
|
|
6276
6714
|
break;
|
|
6277
6715
|
case "task:progress":
|
|
6716
|
+
this.handleTaskProgress(msg.taskId, msg.event);
|
|
6278
6717
|
break;
|
|
6279
6718
|
case "task:completed":
|
|
6280
6719
|
this.handleTaskCompleted(msg.taskId, msg.result);
|
|
@@ -6320,9 +6759,27 @@ var init_hub = __esm({
|
|
|
6320
6759
|
task.status = "completed";
|
|
6321
6760
|
task.result = result;
|
|
6322
6761
|
task.completedAt = /* @__PURE__ */ new Date();
|
|
6762
|
+
if (typeof result?.text === "string") {
|
|
6763
|
+
task.partialText = result.text;
|
|
6764
|
+
}
|
|
6323
6765
|
console.log(`[Hub] Task completed: ${taskId}`);
|
|
6324
6766
|
}
|
|
6325
6767
|
}
|
|
6768
|
+
handleTaskProgress(taskId, event) {
|
|
6769
|
+
const task = this.tasks.get(taskId);
|
|
6770
|
+
if (!task) return;
|
|
6771
|
+
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
6772
|
+
task.events.push({ at, event });
|
|
6773
|
+
if (task.events.length > 500) {
|
|
6774
|
+
task.events.shift();
|
|
6775
|
+
}
|
|
6776
|
+
if (event?.kind === "text-delta" && typeof event?.delta === "string") {
|
|
6777
|
+
task.partialText = (task.partialText || "") + event.delta;
|
|
6778
|
+
if (task.partialText.length > 2e4) {
|
|
6779
|
+
task.partialText = task.partialText.slice(-2e4);
|
|
6780
|
+
}
|
|
6781
|
+
}
|
|
6782
|
+
}
|
|
6326
6783
|
handleTaskError(taskId, error) {
|
|
6327
6784
|
const task = this.tasks.get(taskId);
|
|
6328
6785
|
if (task) {
|
|
@@ -6344,30 +6801,2027 @@ var init_hub = __esm({
|
|
|
6344
6801
|
}
|
|
6345
6802
|
});
|
|
6346
6803
|
|
|
6347
|
-
// src/
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6804
|
+
// src/core/task.ts
|
|
6805
|
+
var Task;
|
|
6806
|
+
var init_task = __esm({
|
|
6807
|
+
"src/core/task.ts"() {
|
|
6808
|
+
"use strict";
|
|
6809
|
+
Task = class _Task {
|
|
6810
|
+
id;
|
|
6811
|
+
title;
|
|
6812
|
+
description;
|
|
6813
|
+
status;
|
|
6814
|
+
assignedTo;
|
|
6815
|
+
priority;
|
|
6816
|
+
estimatedTime;
|
|
6817
|
+
actualTime;
|
|
6818
|
+
dependencies;
|
|
6819
|
+
steps;
|
|
6820
|
+
tags;
|
|
6821
|
+
metadata;
|
|
6822
|
+
createdAt;
|
|
6823
|
+
updatedAt;
|
|
6824
|
+
startedAt;
|
|
6825
|
+
completedAt;
|
|
6826
|
+
error;
|
|
6827
|
+
constructor({
|
|
6828
|
+
id,
|
|
6829
|
+
title,
|
|
6830
|
+
description,
|
|
6831
|
+
status = "pending" /* PENDING */,
|
|
6832
|
+
assignedTo,
|
|
6833
|
+
priority = "medium",
|
|
6834
|
+
estimatedTime,
|
|
6835
|
+
dependencies = [],
|
|
6836
|
+
steps = [],
|
|
6837
|
+
tags = [],
|
|
6838
|
+
metadata = {}
|
|
6839
|
+
}) {
|
|
6840
|
+
this.id = id;
|
|
6841
|
+
this.title = title;
|
|
6842
|
+
this.description = description;
|
|
6843
|
+
this.status = status;
|
|
6844
|
+
this.assignedTo = assignedTo;
|
|
6845
|
+
this.priority = priority;
|
|
6846
|
+
this.estimatedTime = estimatedTime;
|
|
6847
|
+
this.dependencies = dependencies;
|
|
6848
|
+
this.steps = steps;
|
|
6849
|
+
this.tags = tags;
|
|
6850
|
+
this.metadata = metadata;
|
|
6851
|
+
this.createdAt = /* @__PURE__ */ new Date();
|
|
6852
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
6853
|
+
}
|
|
6854
|
+
start() {
|
|
6855
|
+
if (this.status !== "pending" /* PENDING */) {
|
|
6856
|
+
throw new Error(`Cannot start task in ${this.status} status`);
|
|
6857
|
+
}
|
|
6858
|
+
this.status = "running" /* RUNNING */;
|
|
6859
|
+
this.startedAt = /* @__PURE__ */ new Date();
|
|
6860
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
6861
|
+
}
|
|
6862
|
+
complete() {
|
|
6863
|
+
if (this.status !== "running" /* RUNNING */) {
|
|
6864
|
+
throw new Error(`Cannot complete task in ${this.status} status`);
|
|
6865
|
+
}
|
|
6866
|
+
this.status = "completed" /* COMPLETED */;
|
|
6867
|
+
this.completedAt = /* @__PURE__ */ new Date();
|
|
6868
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
6869
|
+
if (this.startedAt) {
|
|
6870
|
+
this.actualTime = this.calculateDuration(
|
|
6871
|
+
this.startedAt,
|
|
6872
|
+
this.completedAt
|
|
6873
|
+
);
|
|
6874
|
+
}
|
|
6875
|
+
}
|
|
6876
|
+
fail(error) {
|
|
6877
|
+
this.status = "failed" /* FAILED */;
|
|
6878
|
+
this.error = error;
|
|
6879
|
+
this.completedAt = /* @__PURE__ */ new Date();
|
|
6880
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
6881
|
+
}
|
|
6882
|
+
block(reason) {
|
|
6883
|
+
this.status = "blocked" /* BLOCKED */;
|
|
6884
|
+
this.error = reason;
|
|
6885
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
6886
|
+
}
|
|
6887
|
+
cancel() {
|
|
6888
|
+
this.status = "cancelled" /* CANCELLED */;
|
|
6889
|
+
this.completedAt = /* @__PURE__ */ new Date();
|
|
6890
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
6891
|
+
}
|
|
6892
|
+
isActionable(context) {
|
|
6893
|
+
return this.status === "pending" /* PENDING */ && !this.hasBlockingDependencies(context);
|
|
6894
|
+
}
|
|
6895
|
+
hasBlockingDependencies(context) {
|
|
6896
|
+
if (!context || !this.dependencies || this.dependencies.length === 0) {
|
|
6897
|
+
return false;
|
|
6898
|
+
}
|
|
6899
|
+
return this.dependencies.some((dep) => {
|
|
6900
|
+
if (dep.type === "optional") return false;
|
|
6901
|
+
const status = context.getTaskStatus(dep.taskId);
|
|
6902
|
+
return status !== "completed" /* COMPLETED */;
|
|
6903
|
+
});
|
|
6904
|
+
}
|
|
6905
|
+
calculateDuration(start, end) {
|
|
6906
|
+
const ms = end.getTime() - start.getTime();
|
|
6907
|
+
const seconds = Math.floor(ms / 1e3);
|
|
6908
|
+
const minutes = Math.floor(seconds / 60);
|
|
6909
|
+
const hours = Math.floor(minutes / 60);
|
|
6910
|
+
if (hours > 0) {
|
|
6911
|
+
return `${hours}h ${minutes % 60}m`;
|
|
6912
|
+
} else if (minutes > 0) {
|
|
6913
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
6914
|
+
} else {
|
|
6915
|
+
return `${seconds}s`;
|
|
6916
|
+
}
|
|
6917
|
+
}
|
|
6918
|
+
toJSON() {
|
|
6919
|
+
return {
|
|
6920
|
+
id: this.id,
|
|
6921
|
+
title: this.title,
|
|
6922
|
+
description: this.description,
|
|
6923
|
+
status: this.status,
|
|
6924
|
+
assignedTo: this.assignedTo,
|
|
6925
|
+
priority: this.priority,
|
|
6926
|
+
estimatedTime: this.estimatedTime,
|
|
6927
|
+
actualTime: this.actualTime,
|
|
6928
|
+
dependencies: this.dependencies,
|
|
6929
|
+
steps: this.steps,
|
|
6930
|
+
tags: this.tags,
|
|
6931
|
+
metadata: this.metadata,
|
|
6932
|
+
createdAt: this.createdAt.toISOString(),
|
|
6933
|
+
updatedAt: this.updatedAt.toISOString(),
|
|
6934
|
+
startedAt: this.startedAt?.toISOString(),
|
|
6935
|
+
completedAt: this.completedAt?.toISOString(),
|
|
6936
|
+
error: this.error
|
|
6937
|
+
};
|
|
6938
|
+
}
|
|
6939
|
+
static fromJSON(data) {
|
|
6940
|
+
const task = new _Task({
|
|
6941
|
+
id: data.id,
|
|
6942
|
+
title: data.title,
|
|
6943
|
+
description: data.description,
|
|
6944
|
+
status: data.status,
|
|
6945
|
+
assignedTo: data.assignedTo,
|
|
6946
|
+
priority: data.priority,
|
|
6947
|
+
estimatedTime: data.estimatedTime,
|
|
6948
|
+
dependencies: data.dependencies,
|
|
6949
|
+
steps: data.steps,
|
|
6950
|
+
tags: data.tags,
|
|
6951
|
+
metadata: data.metadata
|
|
6952
|
+
});
|
|
6953
|
+
task.createdAt = new Date(data.createdAt);
|
|
6954
|
+
task.updatedAt = new Date(data.updatedAt);
|
|
6955
|
+
task.actualTime = data.actualTime;
|
|
6956
|
+
task.error = data.error;
|
|
6957
|
+
if (data.startedAt) {
|
|
6958
|
+
task.startedAt = new Date(data.startedAt);
|
|
6959
|
+
}
|
|
6960
|
+
if (data.completedAt) {
|
|
6961
|
+
task.completedAt = new Date(data.completedAt);
|
|
6962
|
+
}
|
|
6963
|
+
return task;
|
|
6964
|
+
}
|
|
6965
|
+
};
|
|
6966
|
+
}
|
|
6967
|
+
});
|
|
6968
|
+
|
|
6969
|
+
// src/core/plan.ts
|
|
6970
|
+
var Plan;
|
|
6971
|
+
var init_plan = __esm({
|
|
6972
|
+
"src/core/plan.ts"() {
|
|
6973
|
+
"use strict";
|
|
6974
|
+
init_task();
|
|
6975
|
+
Plan = class _Plan {
|
|
6976
|
+
tasks;
|
|
6977
|
+
goal;
|
|
6978
|
+
createdAt;
|
|
6979
|
+
updatedAt;
|
|
6980
|
+
constructor({ tasks = [], goal }) {
|
|
6981
|
+
this.tasks = tasks;
|
|
6982
|
+
this.goal = goal;
|
|
6983
|
+
this.createdAt = /* @__PURE__ */ new Date();
|
|
6984
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
6985
|
+
}
|
|
6986
|
+
addTask(task) {
|
|
6987
|
+
this.tasks.push(task);
|
|
6988
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
6989
|
+
}
|
|
6990
|
+
removeTask(taskId) {
|
|
6991
|
+
const index = this.tasks.findIndex((t) => t.id === taskId);
|
|
6992
|
+
if (index >= 0) {
|
|
6993
|
+
this.tasks.splice(index, 1);
|
|
6994
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
6995
|
+
return true;
|
|
6996
|
+
}
|
|
6997
|
+
return false;
|
|
6998
|
+
}
|
|
6999
|
+
getTaskById(taskId) {
|
|
7000
|
+
return this.tasks.find((t) => t.id === taskId);
|
|
7001
|
+
}
|
|
7002
|
+
updateTaskStatus(taskId, status) {
|
|
7003
|
+
const task = this.getTaskById(taskId);
|
|
7004
|
+
if (task) {
|
|
7005
|
+
task.status = status;
|
|
7006
|
+
task.updatedAt = /* @__PURE__ */ new Date();
|
|
7007
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
7008
|
+
return true;
|
|
7009
|
+
}
|
|
7010
|
+
return false;
|
|
7011
|
+
}
|
|
7012
|
+
getNextActionableTask() {
|
|
7013
|
+
return this.tasks.find(
|
|
7014
|
+
(task) => task.isActionable({
|
|
7015
|
+
getTaskStatus: (id) => this.getTaskById(id)?.status
|
|
7016
|
+
})
|
|
7017
|
+
);
|
|
7018
|
+
}
|
|
7019
|
+
getAllActionableTasks(maxTasks) {
|
|
7020
|
+
const actionableTasks = this.tasks.filter(
|
|
7021
|
+
(task) => task.isActionable({
|
|
7022
|
+
getTaskStatus: (id) => this.getTaskById(id)?.status
|
|
7023
|
+
})
|
|
7024
|
+
);
|
|
7025
|
+
return maxTasks ? actionableTasks.slice(0, maxTasks) : actionableTasks;
|
|
7026
|
+
}
|
|
7027
|
+
getTasksByStatus(status) {
|
|
7028
|
+
return this.tasks.filter((task) => task.status === status);
|
|
7029
|
+
}
|
|
7030
|
+
getTasksByAssignee(assignee) {
|
|
7031
|
+
return this.tasks.filter((task) => task.assignedTo === assignee);
|
|
7032
|
+
}
|
|
7033
|
+
isComplete() {
|
|
7034
|
+
return this.tasks.every(
|
|
7035
|
+
(task) => task.status === "completed" /* COMPLETED */ || task.status === "cancelled" /* CANCELLED */
|
|
7036
|
+
);
|
|
7037
|
+
}
|
|
7038
|
+
hasFailedTasks() {
|
|
7039
|
+
return this.tasks.some((task) => task.status === "failed" /* FAILED */);
|
|
7040
|
+
}
|
|
7041
|
+
hasBlockedTasks() {
|
|
7042
|
+
return this.tasks.some((task) => task.status === "blocked" /* BLOCKED */);
|
|
7043
|
+
}
|
|
7044
|
+
getProgressSummary() {
|
|
7045
|
+
const summary = {
|
|
7046
|
+
totalTasks: this.tasks.length,
|
|
7047
|
+
completedTasks: 0,
|
|
7048
|
+
runningTasks: 0,
|
|
7049
|
+
pendingTasks: 0,
|
|
7050
|
+
failedTasks: 0,
|
|
7051
|
+
blockedTasks: 0,
|
|
7052
|
+
progressPercentage: 0
|
|
7053
|
+
};
|
|
7054
|
+
for (const task of this.tasks) {
|
|
7055
|
+
switch (task.status) {
|
|
7056
|
+
case "completed" /* COMPLETED */:
|
|
7057
|
+
summary.completedTasks++;
|
|
7058
|
+
break;
|
|
7059
|
+
case "running" /* RUNNING */:
|
|
7060
|
+
summary.runningTasks++;
|
|
7061
|
+
break;
|
|
7062
|
+
case "pending" /* PENDING */:
|
|
7063
|
+
summary.pendingTasks++;
|
|
7064
|
+
break;
|
|
7065
|
+
case "failed" /* FAILED */:
|
|
7066
|
+
summary.failedTasks++;
|
|
7067
|
+
break;
|
|
7068
|
+
case "blocked" /* BLOCKED */:
|
|
7069
|
+
summary.blockedTasks++;
|
|
7070
|
+
break;
|
|
7071
|
+
}
|
|
7072
|
+
}
|
|
7073
|
+
if (summary.totalTasks > 0) {
|
|
7074
|
+
summary.progressPercentage = summary.completedTasks / summary.totalTasks * 100;
|
|
7075
|
+
}
|
|
7076
|
+
return summary;
|
|
7077
|
+
}
|
|
7078
|
+
reorderTasks(fromIndex, toIndex) {
|
|
7079
|
+
if (fromIndex < 0 || fromIndex >= this.tasks.length || toIndex < 0 || toIndex >= this.tasks.length) {
|
|
7080
|
+
throw new Error("Invalid task indices");
|
|
7081
|
+
}
|
|
7082
|
+
const [task] = this.tasks.splice(fromIndex, 1);
|
|
7083
|
+
this.tasks.splice(toIndex, 0, task);
|
|
7084
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
7085
|
+
}
|
|
7086
|
+
toJSON() {
|
|
7087
|
+
return {
|
|
7088
|
+
tasks: this.tasks.map((task) => task.toJSON()),
|
|
7089
|
+
goal: this.goal,
|
|
7090
|
+
createdAt: this.createdAt.toISOString(),
|
|
7091
|
+
updatedAt: this.updatedAt.toISOString()
|
|
7092
|
+
};
|
|
7093
|
+
}
|
|
7094
|
+
static fromJSON(data) {
|
|
7095
|
+
const plan = new _Plan({
|
|
7096
|
+
goal: data.goal,
|
|
7097
|
+
tasks: data.tasks.map((taskData) => Task.fromJSON(taskData))
|
|
7098
|
+
});
|
|
7099
|
+
plan.createdAt = new Date(data.createdAt);
|
|
7100
|
+
plan.updatedAt = new Date(data.updatedAt);
|
|
7101
|
+
return plan;
|
|
7102
|
+
}
|
|
7103
|
+
};
|
|
7104
|
+
}
|
|
7105
|
+
});
|
|
7106
|
+
|
|
7107
|
+
// src/core/collaboration.ts
|
|
7108
|
+
var AgentCollaborationManager, ParallelExecutionEngine, CollaborativePlanner;
|
|
7109
|
+
var init_collaboration = __esm({
|
|
7110
|
+
"src/core/collaboration.ts"() {
|
|
7111
|
+
"use strict";
|
|
7112
|
+
AgentCollaborationManager = class {
|
|
7113
|
+
space;
|
|
7114
|
+
messageQueue;
|
|
7115
|
+
// Agent ID -> messages
|
|
7116
|
+
sharedContext;
|
|
7117
|
+
listeners;
|
|
7118
|
+
constructor(space) {
|
|
7119
|
+
this.space = space;
|
|
7120
|
+
this.messageQueue = /* @__PURE__ */ new Map();
|
|
7121
|
+
this.sharedContext = {
|
|
7122
|
+
spaceId: space.spaceId,
|
|
7123
|
+
data: {},
|
|
7124
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
7125
|
+
updatedBy: "system"
|
|
7126
|
+
};
|
|
7127
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
7128
|
+
}
|
|
7129
|
+
/**
|
|
7130
|
+
* Send a message from one agent to another
|
|
7131
|
+
*/
|
|
7132
|
+
sendMessage(from, to, content, metadata) {
|
|
7133
|
+
const message = {
|
|
7134
|
+
from,
|
|
7135
|
+
to,
|
|
7136
|
+
content,
|
|
7137
|
+
metadata,
|
|
7138
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
7139
|
+
};
|
|
7140
|
+
if (to === "broadcast") {
|
|
7141
|
+
for (const agentId of this.space.agents.keys()) {
|
|
7142
|
+
if (agentId !== from) {
|
|
7143
|
+
this.queueMessage(agentId, message);
|
|
7144
|
+
}
|
|
7145
|
+
}
|
|
7146
|
+
} else {
|
|
7147
|
+
this.queueMessage(to, message);
|
|
7148
|
+
}
|
|
7149
|
+
this.notifyListeners(to, message);
|
|
7150
|
+
if (to !== "broadcast") {
|
|
7151
|
+
this.notifyListeners("broadcast", message);
|
|
7152
|
+
}
|
|
7153
|
+
}
|
|
7154
|
+
/**
|
|
7155
|
+
* Queue a message for an agent
|
|
7156
|
+
*/
|
|
7157
|
+
queueMessage(agentId, message) {
|
|
7158
|
+
if (!this.messageQueue.has(agentId)) {
|
|
7159
|
+
this.messageQueue.set(agentId, []);
|
|
7160
|
+
}
|
|
7161
|
+
this.messageQueue.get(agentId).push(message);
|
|
7162
|
+
}
|
|
7163
|
+
/**
|
|
7164
|
+
* Get pending messages for an agent
|
|
7165
|
+
*/
|
|
7166
|
+
getMessages(agentId) {
|
|
7167
|
+
const messages = this.messageQueue.get(agentId) || [];
|
|
7168
|
+
this.messageQueue.set(agentId, []);
|
|
7169
|
+
return messages;
|
|
7170
|
+
}
|
|
7171
|
+
/**
|
|
7172
|
+
* Subscribe to messages for an agent
|
|
7173
|
+
*/
|
|
7174
|
+
subscribe(agentId, callback) {
|
|
7175
|
+
if (!this.listeners.has(agentId)) {
|
|
7176
|
+
this.listeners.set(agentId, /* @__PURE__ */ new Set());
|
|
7177
|
+
}
|
|
7178
|
+
this.listeners.get(agentId).add(callback);
|
|
7179
|
+
return () => {
|
|
7180
|
+
const callbacks = this.listeners.get(agentId);
|
|
7181
|
+
if (callbacks) {
|
|
7182
|
+
callbacks.delete(callback);
|
|
7183
|
+
if (callbacks.size === 0) {
|
|
7184
|
+
this.listeners.delete(agentId);
|
|
7185
|
+
}
|
|
7186
|
+
}
|
|
7187
|
+
};
|
|
7188
|
+
}
|
|
7189
|
+
/**
|
|
7190
|
+
* Notify listeners of a new message
|
|
7191
|
+
*/
|
|
7192
|
+
notifyListeners(agentId, message) {
|
|
7193
|
+
const callbacks = this.listeners.get(agentId);
|
|
7194
|
+
if (callbacks) {
|
|
7195
|
+
callbacks.forEach((cb) => {
|
|
7196
|
+
try {
|
|
7197
|
+
cb(message);
|
|
7198
|
+
} catch (error) {
|
|
7199
|
+
console.error(`[AgentCollaborationManager] Listener error:`, error);
|
|
7200
|
+
}
|
|
7201
|
+
});
|
|
7202
|
+
}
|
|
7203
|
+
}
|
|
7204
|
+
/**
|
|
7205
|
+
* Update shared context
|
|
7206
|
+
*/
|
|
7207
|
+
updateContext(agentId, updates) {
|
|
7208
|
+
this.sharedContext.data = {
|
|
7209
|
+
...this.sharedContext.data,
|
|
7210
|
+
...updates
|
|
7211
|
+
};
|
|
7212
|
+
this.sharedContext.updatedAt = /* @__PURE__ */ new Date();
|
|
7213
|
+
this.sharedContext.updatedBy = agentId;
|
|
7214
|
+
}
|
|
7215
|
+
/**
|
|
7216
|
+
* Get shared context
|
|
7217
|
+
*/
|
|
7218
|
+
getContext() {
|
|
7219
|
+
return { ...this.sharedContext };
|
|
7220
|
+
}
|
|
7221
|
+
/**
|
|
7222
|
+
* Get a specific value from shared context
|
|
7223
|
+
*/
|
|
7224
|
+
getContextValue(key) {
|
|
7225
|
+
return this.sharedContext.data[key];
|
|
7226
|
+
}
|
|
7227
|
+
};
|
|
7228
|
+
ParallelExecutionEngine = class {
|
|
7229
|
+
space;
|
|
7230
|
+
maxConcurrency;
|
|
7231
|
+
activeTasks;
|
|
7232
|
+
constructor(space, maxConcurrency = 3) {
|
|
7233
|
+
this.space = space;
|
|
7234
|
+
this.maxConcurrency = maxConcurrency;
|
|
7235
|
+
this.activeTasks = /* @__PURE__ */ new Map();
|
|
7236
|
+
}
|
|
7237
|
+
/**
|
|
7238
|
+
* Execute multiple tasks in parallel
|
|
7239
|
+
*/
|
|
7240
|
+
async executeParallel(tasks) {
|
|
7241
|
+
const sortedTasks = [...tasks].sort(
|
|
7242
|
+
(a, b) => (b.priority || 0) - (a.priority || 0)
|
|
7243
|
+
);
|
|
7244
|
+
const results = [];
|
|
7245
|
+
const executing = [];
|
|
7246
|
+
for (const task of sortedTasks) {
|
|
7247
|
+
if (executing.length >= this.maxConcurrency) {
|
|
7248
|
+
const completed = await Promise.race(executing);
|
|
7249
|
+
const index = executing.findIndex(
|
|
7250
|
+
(p) => p === Promise.resolve(completed)
|
|
7251
|
+
);
|
|
7252
|
+
if (index >= 0) {
|
|
7253
|
+
executing.splice(index, 1);
|
|
7254
|
+
}
|
|
7255
|
+
results.push(completed);
|
|
7256
|
+
}
|
|
7257
|
+
const promise = this.executeTask(task);
|
|
7258
|
+
executing.push(promise);
|
|
7259
|
+
this.activeTasks.set(task.id, promise);
|
|
7260
|
+
}
|
|
7261
|
+
const remaining = await Promise.allSettled(executing);
|
|
7262
|
+
for (const result of remaining) {
|
|
7263
|
+
if (result.status === "fulfilled") {
|
|
7264
|
+
results.push(result.value);
|
|
7265
|
+
} else {
|
|
7266
|
+
console.error("[ParallelExecutionEngine] Task failed:", result.reason);
|
|
7267
|
+
}
|
|
7268
|
+
}
|
|
7269
|
+
this.activeTasks.clear();
|
|
7270
|
+
return results;
|
|
7271
|
+
}
|
|
7272
|
+
/**
|
|
7273
|
+
* Execute a single task
|
|
7274
|
+
*/
|
|
7275
|
+
async executeTask(task) {
|
|
7276
|
+
const startTime = Date.now();
|
|
7277
|
+
try {
|
|
7278
|
+
const agent = this.space.getAgent(task.agentId);
|
|
7279
|
+
if (!agent) {
|
|
7280
|
+
throw new Error(`Agent ${task.agentId} not found`);
|
|
7281
|
+
}
|
|
7282
|
+
const result = await agent.generateText({
|
|
7283
|
+
messages: task.messages,
|
|
7284
|
+
system: task.system,
|
|
7285
|
+
spaceId: this.space.spaceId,
|
|
7286
|
+
metadata: {
|
|
7287
|
+
...task.metadata,
|
|
7288
|
+
parallelTaskId: task.id
|
|
7289
|
+
}
|
|
7290
|
+
});
|
|
7291
|
+
const duration = Date.now() - startTime;
|
|
7292
|
+
return {
|
|
7293
|
+
taskId: task.id,
|
|
7294
|
+
agentId: task.agentId,
|
|
7295
|
+
result: {
|
|
7296
|
+
text: result.text || "",
|
|
7297
|
+
toolCalls: result.toolCalls,
|
|
7298
|
+
reasoning: result.reasoning,
|
|
7299
|
+
metadata: result.metadata
|
|
7300
|
+
},
|
|
7301
|
+
duration
|
|
7302
|
+
};
|
|
7303
|
+
} catch (error) {
|
|
7304
|
+
const duration = Date.now() - startTime;
|
|
7305
|
+
return {
|
|
7306
|
+
taskId: task.id,
|
|
7307
|
+
agentId: task.agentId,
|
|
7308
|
+
result: {
|
|
7309
|
+
text: "",
|
|
7310
|
+
metadata: { error: String(error) }
|
|
7311
|
+
},
|
|
7312
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
7313
|
+
duration
|
|
7314
|
+
};
|
|
7315
|
+
}
|
|
7316
|
+
}
|
|
7317
|
+
/**
|
|
7318
|
+
* Cancel a running task
|
|
7319
|
+
*/
|
|
7320
|
+
cancelTask(taskId) {
|
|
7321
|
+
const task = this.activeTasks.get(taskId);
|
|
7322
|
+
if (task) {
|
|
7323
|
+
this.activeTasks.delete(taskId);
|
|
7324
|
+
}
|
|
7325
|
+
}
|
|
7326
|
+
/**
|
|
7327
|
+
* Get active task count
|
|
7328
|
+
*/
|
|
7329
|
+
getActiveTaskCount() {
|
|
7330
|
+
return this.activeTasks.size;
|
|
7331
|
+
}
|
|
7332
|
+
};
|
|
7333
|
+
CollaborativePlanner = class {
|
|
7334
|
+
space;
|
|
7335
|
+
collaborationManager;
|
|
7336
|
+
constructor(space, collaborationManager) {
|
|
7337
|
+
this.space = space;
|
|
7338
|
+
this.collaborationManager = collaborationManager;
|
|
7339
|
+
}
|
|
7340
|
+
/**
|
|
7341
|
+
* Create a collaborative plan with multiple agents
|
|
7342
|
+
*/
|
|
7343
|
+
async createCollaborativePlan(goal, agentIds) {
|
|
7344
|
+
const context = this.collaborationManager.getContext();
|
|
7345
|
+
const planningTasks = agentIds.map((agentId, index) => ({
|
|
7346
|
+
id: `plan-${agentId}-${Date.now()}`,
|
|
7347
|
+
agentId,
|
|
7348
|
+
messages: [
|
|
7349
|
+
{
|
|
7350
|
+
role: "user",
|
|
7351
|
+
content: `We need to create a collaborative plan for: ${goal}
|
|
7352
|
+
|
|
7353
|
+
Shared context: ${JSON.stringify(context.data, null, 2)}
|
|
7354
|
+
|
|
7355
|
+
You are one of ${agentIds.length} agents working together. Propose your part of the plan and identify dependencies on other agents.`
|
|
7356
|
+
}
|
|
7357
|
+
],
|
|
7358
|
+
priority: agentIds.length - index,
|
|
7359
|
+
// First agent gets highest priority
|
|
7360
|
+
metadata: {
|
|
7361
|
+
planningSession: true,
|
|
7362
|
+
goal,
|
|
7363
|
+
otherAgents: agentIds.filter((id) => id !== agentId)
|
|
7364
|
+
}
|
|
7365
|
+
}));
|
|
7366
|
+
const executionEngine = new ParallelExecutionEngine(this.space);
|
|
7367
|
+
const results = await executionEngine.executeParallel(planningTasks);
|
|
7368
|
+
const agentPlans = /* @__PURE__ */ new Map();
|
|
7369
|
+
for (const result of results) {
|
|
7370
|
+
if (!result.error) {
|
|
7371
|
+
agentPlans.set(result.agentId, result.result);
|
|
7372
|
+
}
|
|
7373
|
+
}
|
|
7374
|
+
const plan = this.mergePlans(agentPlans, goal);
|
|
7375
|
+
const agentAssignments = this.assignTasks(plan, agentIds);
|
|
7376
|
+
return { plan, agentAssignments };
|
|
7377
|
+
}
|
|
7378
|
+
/**
|
|
7379
|
+
* Merge multiple agent plans into one
|
|
7380
|
+
*/
|
|
7381
|
+
mergePlans(agentPlans, goal) {
|
|
7382
|
+
const tasks = [];
|
|
7383
|
+
let taskId = 1;
|
|
7384
|
+
for (const [agentId, plan] of agentPlans.entries()) {
|
|
7385
|
+
if (plan.text) {
|
|
7386
|
+
const lines = plan.text.split("\n").filter(
|
|
7387
|
+
(line) => line.trim().startsWith("-") || line.trim().match(/^\d+\./)
|
|
7388
|
+
);
|
|
7389
|
+
for (const line of lines) {
|
|
7390
|
+
tasks.push({
|
|
7391
|
+
id: `task-${taskId++}`,
|
|
7392
|
+
description: line.replace(/^[-•\d.]+\s*/, "").trim(),
|
|
7393
|
+
assignedAgent: agentId,
|
|
7394
|
+
status: "pending"
|
|
7395
|
+
});
|
|
7396
|
+
}
|
|
7397
|
+
}
|
|
7398
|
+
}
|
|
7399
|
+
return {
|
|
7400
|
+
goal,
|
|
7401
|
+
tasks,
|
|
7402
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7403
|
+
};
|
|
7404
|
+
}
|
|
7405
|
+
/**
|
|
7406
|
+
* Assign tasks to agents
|
|
7407
|
+
*/
|
|
7408
|
+
assignTasks(plan, agentIds) {
|
|
7409
|
+
const assignments = /* @__PURE__ */ new Map();
|
|
7410
|
+
for (const agentId of agentIds) {
|
|
7411
|
+
assignments.set(agentId, []);
|
|
7412
|
+
}
|
|
7413
|
+
for (const task of plan.tasks) {
|
|
7414
|
+
const agentId = task.assignedAgent || agentIds[0];
|
|
7415
|
+
const tasks = assignments.get(agentId) || [];
|
|
7416
|
+
tasks.push(task.id);
|
|
7417
|
+
assignments.set(agentId, tasks);
|
|
7418
|
+
}
|
|
7419
|
+
return assignments;
|
|
7420
|
+
}
|
|
7421
|
+
};
|
|
7422
|
+
}
|
|
7423
|
+
});
|
|
7424
|
+
|
|
7425
|
+
// src/core/space.ts
|
|
7426
|
+
var space_exports2 = {};
|
|
7427
|
+
__export(space_exports2, {
|
|
7428
|
+
Space: () => Space,
|
|
7429
|
+
startSpace: () => startSpace
|
|
7430
|
+
});
|
|
7431
|
+
async function startSpace({
|
|
7432
|
+
goal,
|
|
7433
|
+
spaceId,
|
|
7434
|
+
userId,
|
|
7435
|
+
spaceRoot,
|
|
7436
|
+
name,
|
|
7437
|
+
model
|
|
7438
|
+
}) {
|
|
7439
|
+
const id = spaceId || `proj_${Date.now().toString(36)}`;
|
|
7440
|
+
const spaceConfig = {
|
|
7441
|
+
name: name || goal.slice(0, 50),
|
|
7442
|
+
autoSave: true,
|
|
7443
|
+
checkpointInterval: 300
|
|
7444
|
+
};
|
|
7445
|
+
console.log(`[Space] Creating space - agents will be loaded on demand`);
|
|
7446
|
+
const storage = await SpaceStorageFactory.create(id);
|
|
7447
|
+
const agents = /* @__PURE__ */ new Map();
|
|
7448
|
+
console.log(`[Space] Space initialized (agents loaded on demand)`);
|
|
7449
|
+
const messageQueue = new MessageQueue();
|
|
7450
|
+
const history = new ConversationHistory();
|
|
7451
|
+
const space = new Space({
|
|
7452
|
+
spaceId: id,
|
|
7453
|
+
userId,
|
|
7454
|
+
config: spaceConfig,
|
|
7455
|
+
history,
|
|
7456
|
+
messageQueue,
|
|
7457
|
+
agents,
|
|
7458
|
+
storage,
|
|
7459
|
+
goal,
|
|
7460
|
+
name: name || spaceConfig.name
|
|
7461
|
+
});
|
|
7462
|
+
const viberAgentConfig = {
|
|
7463
|
+
name: "Viber",
|
|
7464
|
+
description: "I manage this space and coordinate all work.",
|
|
7465
|
+
provider: "openrouter",
|
|
7466
|
+
model: model || "deepseek/deepseek-chat",
|
|
7467
|
+
temperature: 0.7,
|
|
7468
|
+
promptFile: "",
|
|
7469
|
+
// ViberAgent doesn't use prompt files
|
|
7470
|
+
skills: ["tmux", "cursor-agent"]
|
|
7471
|
+
};
|
|
7472
|
+
const viberAgent = new ViberAgent(viberAgentConfig, space, {
|
|
7473
|
+
model,
|
|
7474
|
+
spaceId: id
|
|
7475
|
+
});
|
|
7476
|
+
space.viberAgent = viberAgent;
|
|
7477
|
+
await space.persistState();
|
|
7478
|
+
return space;
|
|
7479
|
+
}
|
|
7480
|
+
var Space;
|
|
7481
|
+
var init_space2 = __esm({
|
|
7482
|
+
"src/core/space.ts"() {
|
|
7483
|
+
"use strict";
|
|
7484
|
+
init_task();
|
|
7485
|
+
init_viber_agent();
|
|
7486
|
+
init_space();
|
|
7487
|
+
init_message();
|
|
7488
|
+
init_collaboration();
|
|
7489
|
+
Space = class {
|
|
7490
|
+
spaceId;
|
|
7491
|
+
userId;
|
|
7492
|
+
// User ID of space owner
|
|
7493
|
+
config;
|
|
7494
|
+
history;
|
|
7495
|
+
// Legacy: primary task history
|
|
7496
|
+
tasks;
|
|
7497
|
+
// NEW: Multiple tasks
|
|
7498
|
+
messageQueue;
|
|
7499
|
+
agents;
|
|
7500
|
+
storage;
|
|
7501
|
+
goal;
|
|
7502
|
+
name;
|
|
7503
|
+
viberAgent;
|
|
7504
|
+
createdAt;
|
|
7505
|
+
updatedAt;
|
|
7506
|
+
plan;
|
|
7507
|
+
artifacts;
|
|
7508
|
+
collaborationManager;
|
|
7509
|
+
parallelEngine;
|
|
7510
|
+
collaborativePlanner;
|
|
7511
|
+
constructor({
|
|
7512
|
+
spaceId,
|
|
7513
|
+
userId,
|
|
7514
|
+
config: config2,
|
|
7515
|
+
history,
|
|
7516
|
+
messageQueue,
|
|
7517
|
+
agents,
|
|
7518
|
+
storage,
|
|
7519
|
+
goal,
|
|
7520
|
+
name,
|
|
7521
|
+
viberAgent
|
|
7522
|
+
}) {
|
|
7523
|
+
this.spaceId = spaceId;
|
|
7524
|
+
this.userId = userId;
|
|
7525
|
+
this.config = config2;
|
|
7526
|
+
this.history = history;
|
|
7527
|
+
this.tasks = /* @__PURE__ */ new Map();
|
|
7528
|
+
this.messageQueue = messageQueue;
|
|
7529
|
+
this.agents = agents;
|
|
7530
|
+
this.storage = storage;
|
|
7531
|
+
this.goal = goal;
|
|
7532
|
+
this.name = name || `Space ${spaceId}`;
|
|
7533
|
+
this.viberAgent = viberAgent;
|
|
7534
|
+
this.createdAt = /* @__PURE__ */ new Date();
|
|
7535
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
7536
|
+
this.collaborationManager = new AgentCollaborationManager(this);
|
|
7537
|
+
this.parallelEngine = new ParallelExecutionEngine(this);
|
|
7538
|
+
this.collaborativePlanner = new CollaborativePlanner(
|
|
7539
|
+
this,
|
|
7540
|
+
this.collaborationManager
|
|
7541
|
+
);
|
|
7542
|
+
}
|
|
7543
|
+
/**
|
|
7544
|
+
* Get or create a task within this space
|
|
7545
|
+
*/
|
|
7546
|
+
getOrCreateTask(taskId, title) {
|
|
7547
|
+
if (!this.tasks.has(taskId)) {
|
|
7548
|
+
const task = {
|
|
7549
|
+
id: taskId,
|
|
7550
|
+
spaceId: this.spaceId,
|
|
7551
|
+
title: title || `Task ${taskId}`,
|
|
7552
|
+
history: new ConversationHistory(),
|
|
7553
|
+
artifactIds: [],
|
|
7554
|
+
status: "active",
|
|
7555
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
7556
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
7557
|
+
};
|
|
7558
|
+
this.tasks.set(taskId, task);
|
|
7559
|
+
console.log(`[Space] Created task ${taskId} in space ${this.spaceId}`);
|
|
7560
|
+
}
|
|
7561
|
+
return this.tasks.get(taskId);
|
|
7562
|
+
}
|
|
7563
|
+
/**
|
|
7564
|
+
* Get task by ID
|
|
7565
|
+
*/
|
|
7566
|
+
getTask(taskId) {
|
|
7567
|
+
return this.tasks.get(taskId);
|
|
7568
|
+
}
|
|
7569
|
+
/**
|
|
7570
|
+
* Get all tasks in this space
|
|
7571
|
+
*/
|
|
7572
|
+
getAllTasks() {
|
|
7573
|
+
return Array.from(this.tasks.values());
|
|
7574
|
+
}
|
|
7575
|
+
/**
|
|
7576
|
+
* Update space task status (for conversation tasks, not Plan tasks)
|
|
7577
|
+
*/
|
|
7578
|
+
updateSpaceTaskStatus(taskId, status) {
|
|
7579
|
+
const task = this.tasks.get(taskId);
|
|
7580
|
+
if (task) {
|
|
7581
|
+
task.status = status;
|
|
7582
|
+
task.updatedAt = /* @__PURE__ */ new Date();
|
|
7583
|
+
return true;
|
|
7584
|
+
}
|
|
7585
|
+
return false;
|
|
7586
|
+
}
|
|
7587
|
+
getAgent(name) {
|
|
7588
|
+
return this.agents.get(name);
|
|
7589
|
+
}
|
|
7590
|
+
registerAgent(name, agent) {
|
|
7591
|
+
this.agents.set(name, agent);
|
|
7592
|
+
console.log(`[Space] Registered agent: ${name} - ${agent.description}`);
|
|
7593
|
+
}
|
|
7594
|
+
complete() {
|
|
7595
|
+
console.log(`Space ${this.spaceId} completed`);
|
|
7596
|
+
}
|
|
7597
|
+
getContext() {
|
|
7598
|
+
const context = {
|
|
7599
|
+
spaceId: this.spaceId,
|
|
7600
|
+
goal: this.goal,
|
|
7601
|
+
storagePath: this.storage.getSpacePath(),
|
|
7602
|
+
agents: Array.from(this.agents.keys()),
|
|
7603
|
+
historyLength: this.history.messages.length,
|
|
7604
|
+
createdAt: this.createdAt.toISOString()
|
|
7605
|
+
};
|
|
7606
|
+
if (this.plan) {
|
|
7607
|
+
context.plan = {
|
|
7608
|
+
goal: this.goal,
|
|
7609
|
+
totalTasks: this.plan.tasks.length,
|
|
7610
|
+
progress: this.plan.getProgressSummary()
|
|
7611
|
+
};
|
|
7612
|
+
}
|
|
7613
|
+
return context;
|
|
7614
|
+
}
|
|
7615
|
+
async createPlan(plan) {
|
|
7616
|
+
this.plan = plan;
|
|
7617
|
+
await this.persistState();
|
|
7618
|
+
console.log(
|
|
7619
|
+
`Created plan for space ${this.spaceId} with ${plan.tasks.length} tasks`
|
|
7620
|
+
);
|
|
7621
|
+
}
|
|
7622
|
+
async updatePlan(plan) {
|
|
7623
|
+
this.plan = plan;
|
|
7624
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
7625
|
+
await this.persistState();
|
|
7626
|
+
console.log(`Updated plan for space ${this.spaceId}`);
|
|
7627
|
+
}
|
|
7628
|
+
async setName(name) {
|
|
7629
|
+
this.name = name;
|
|
7630
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
7631
|
+
await this.persistState();
|
|
7632
|
+
console.log(`Updated space ${this.spaceId} name to: ${name}`);
|
|
7633
|
+
}
|
|
7634
|
+
async getNextTask() {
|
|
7635
|
+
if (!this.plan) {
|
|
7636
|
+
return void 0;
|
|
7637
|
+
}
|
|
7638
|
+
return this.plan.getNextActionableTask();
|
|
7639
|
+
}
|
|
7640
|
+
async getParallelTasks(maxTasks = 3) {
|
|
7641
|
+
if (!this.plan) {
|
|
7642
|
+
return [];
|
|
7643
|
+
}
|
|
7644
|
+
return this.plan.getAllActionableTasks(maxTasks);
|
|
7645
|
+
}
|
|
7646
|
+
async updateTaskStatus(taskId, status) {
|
|
7647
|
+
if (!this.plan) {
|
|
7648
|
+
return false;
|
|
7649
|
+
}
|
|
7650
|
+
const success = this.plan.updateTaskStatus(taskId, status);
|
|
7651
|
+
if (success) {
|
|
7652
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
7653
|
+
await this.persistState();
|
|
7654
|
+
console.log(`Updated task ${taskId} status to ${status}`);
|
|
7655
|
+
}
|
|
7656
|
+
return success;
|
|
7657
|
+
}
|
|
7658
|
+
async assignTask(taskId, agentName) {
|
|
7659
|
+
if (!this.plan) {
|
|
7660
|
+
return false;
|
|
7661
|
+
}
|
|
7662
|
+
const task = this.plan.getTaskById(taskId);
|
|
7663
|
+
if (!task) {
|
|
7664
|
+
return false;
|
|
7665
|
+
}
|
|
7666
|
+
if (!this.agents.has(agentName)) {
|
|
7667
|
+
console.error(`Agent '${agentName}' not found in space team`);
|
|
7668
|
+
return false;
|
|
7669
|
+
}
|
|
7670
|
+
task.assignedTo = agentName;
|
|
7671
|
+
this.updatedAt = /* @__PURE__ */ new Date();
|
|
7672
|
+
await this.persistState();
|
|
7673
|
+
console.log(`Assigned task ${taskId} to agent ${agentName}`);
|
|
7674
|
+
return true;
|
|
7675
|
+
}
|
|
7676
|
+
isPlanComplete() {
|
|
7677
|
+
if (!this.plan) {
|
|
7678
|
+
return false;
|
|
7679
|
+
}
|
|
7680
|
+
return this.plan.isComplete();
|
|
7681
|
+
}
|
|
7682
|
+
hasFailedTasks() {
|
|
7683
|
+
if (!this.plan) {
|
|
7684
|
+
return false;
|
|
7685
|
+
}
|
|
7686
|
+
return this.plan.hasFailedTasks();
|
|
7687
|
+
}
|
|
7688
|
+
async persistState() {
|
|
7689
|
+
console.log("[Space] State persistence handled by database adapter");
|
|
7690
|
+
}
|
|
7691
|
+
async loadState() {
|
|
7692
|
+
try {
|
|
7693
|
+
console.log("[Space] State loading handled by database adapter");
|
|
7694
|
+
return false;
|
|
7695
|
+
} catch (error) {
|
|
7696
|
+
console.error("Failed to load space state:", error);
|
|
7697
|
+
}
|
|
7698
|
+
return false;
|
|
7699
|
+
}
|
|
7700
|
+
async loadPlan() {
|
|
7701
|
+
if (await this.loadState()) {
|
|
7702
|
+
return this.plan;
|
|
7703
|
+
}
|
|
7704
|
+
return void 0;
|
|
7705
|
+
}
|
|
7706
|
+
getState() {
|
|
7707
|
+
const state = {
|
|
7708
|
+
spaceId: this.spaceId,
|
|
7709
|
+
name: this.name,
|
|
7710
|
+
goal: this.goal,
|
|
7711
|
+
createdAt: this.createdAt.toISOString(),
|
|
7712
|
+
updatedAt: this.updatedAt.toISOString(),
|
|
7713
|
+
teamSize: this.agents.size
|
|
7714
|
+
};
|
|
7715
|
+
if (this.plan) {
|
|
7716
|
+
const taskStats = {
|
|
7717
|
+
total: this.plan.tasks.length,
|
|
7718
|
+
completed: this.plan.tasks.filter(
|
|
7719
|
+
(t) => t.status === "completed" /* COMPLETED */
|
|
7720
|
+
).length,
|
|
7721
|
+
running: this.plan.tasks.filter((t) => t.status === "running" /* RUNNING */).length,
|
|
7722
|
+
pending: this.plan.tasks.filter((t) => t.status === "pending" /* PENDING */).length,
|
|
7723
|
+
failed: this.plan.tasks.filter((t) => t.status === "failed" /* FAILED */).length
|
|
7724
|
+
};
|
|
7725
|
+
state.tasks = taskStats;
|
|
7726
|
+
state.progressPercentage = taskStats.total > 0 ? taskStats.completed / taskStats.total * 100 : 0;
|
|
7727
|
+
}
|
|
7728
|
+
return state;
|
|
7729
|
+
}
|
|
7730
|
+
};
|
|
7731
|
+
}
|
|
7732
|
+
});
|
|
7733
|
+
|
|
7734
|
+
// src/core/viber-agent.ts
|
|
7735
|
+
import { generateText as generateText2, Output } from "ai";
|
|
7736
|
+
import { z as z11 } from "zod";
|
|
7737
|
+
var ViberAgent;
|
|
7738
|
+
var init_viber_agent = __esm({
|
|
7739
|
+
"src/core/viber-agent.ts"() {
|
|
7740
|
+
"use strict";
|
|
7741
|
+
init_agent();
|
|
7742
|
+
init_factory();
|
|
7743
|
+
init_plan();
|
|
7744
|
+
init_task();
|
|
7745
|
+
ViberAgent = class extends Agent {
|
|
7746
|
+
space;
|
|
7747
|
+
spaceId;
|
|
7748
|
+
abortController;
|
|
7749
|
+
singleAgentId;
|
|
7750
|
+
// If set, bypass planning
|
|
7751
|
+
constructor(config2, space, options) {
|
|
7752
|
+
const xConfig = {
|
|
7753
|
+
...config2,
|
|
7754
|
+
name: "X",
|
|
7755
|
+
description: `I am X, the conversational representative for this space. I manage all aspects of the space and coordinate with other agents to achieve our goals.`
|
|
7756
|
+
};
|
|
7757
|
+
super(xConfig);
|
|
7758
|
+
this.space = space;
|
|
7759
|
+
this.spaceId = space.spaceId;
|
|
7760
|
+
this.singleAgentId = options?.singleAgentId;
|
|
7761
|
+
}
|
|
7762
|
+
/**
|
|
7763
|
+
* Getter for space (needed for external access)
|
|
7764
|
+
*/
|
|
7765
|
+
getSpace() {
|
|
7766
|
+
return this.space;
|
|
7767
|
+
}
|
|
7768
|
+
/**
|
|
7769
|
+
* Override getSystemPrompt to include plan and artifacts context
|
|
7770
|
+
*/
|
|
7771
|
+
getSystemPrompt(context) {
|
|
7772
|
+
const basePrompt = super.getSystemPrompt(context);
|
|
7773
|
+
return basePrompt + this.getPlanContext() + this.getArtifactsContext();
|
|
7774
|
+
}
|
|
7775
|
+
/**
|
|
7776
|
+
* Generate text - uses new AI SDK-style signature
|
|
7777
|
+
*/
|
|
7778
|
+
async generateText(options) {
|
|
7779
|
+
return super.generateText({
|
|
7780
|
+
...options,
|
|
7781
|
+
spaceId: this.space.spaceId,
|
|
7782
|
+
metadata: {
|
|
7783
|
+
spaceName: this.space.name,
|
|
7784
|
+
spaceGoal: this.space.goal,
|
|
7785
|
+
...options.metadata
|
|
7786
|
+
}
|
|
7787
|
+
});
|
|
7788
|
+
}
|
|
7789
|
+
/**
|
|
7790
|
+
* ViberAgent streamText - Orchestration Layer
|
|
7791
|
+
* Responsibilities: History management, agent delegation, persistence
|
|
7792
|
+
*/
|
|
7793
|
+
async streamText(options) {
|
|
7794
|
+
const {
|
|
7795
|
+
messages,
|
|
7796
|
+
system: systemMessage,
|
|
7797
|
+
spaceId,
|
|
7798
|
+
metadata = {},
|
|
7799
|
+
...restOptions
|
|
7800
|
+
} = options;
|
|
7801
|
+
console.log("[ViberAgent] Orchestration layer: starting streamText");
|
|
7802
|
+
const mode = metadata?.mode;
|
|
7803
|
+
if (!mode || mode !== "agent") {
|
|
7804
|
+
throw new Error("ViberAgent only supports 'agent' mode");
|
|
7805
|
+
}
|
|
7806
|
+
await this.updateSpaceHistory(messages, metadata);
|
|
7807
|
+
const streamResult = await this.handleAgentMode(
|
|
7808
|
+
messages,
|
|
7809
|
+
systemMessage,
|
|
7810
|
+
spaceId,
|
|
7811
|
+
metadata,
|
|
7812
|
+
restOptions
|
|
7813
|
+
);
|
|
7814
|
+
if (spaceId) {
|
|
7815
|
+
this.handleMessagePersistence(streamResult, messages, spaceId, metadata);
|
|
7816
|
+
}
|
|
7817
|
+
return streamResult;
|
|
7818
|
+
}
|
|
7819
|
+
/**
|
|
7820
|
+
* Agent Mode Handler - Direct delegation with performance optimization
|
|
7821
|
+
* Supports both single agent and parallel execution
|
|
7822
|
+
*/
|
|
7823
|
+
async handleAgentMode(messages, systemMessage, spaceId, metadata, restOptions) {
|
|
7824
|
+
const parallelAgents = metadata.parallelAgents;
|
|
7825
|
+
if (parallelAgents && parallelAgents.length > 1) {
|
|
7826
|
+
return this.handleParallelExecution(
|
|
7827
|
+
parallelAgents,
|
|
7828
|
+
messages,
|
|
7829
|
+
systemMessage,
|
|
7830
|
+
spaceId,
|
|
7831
|
+
metadata,
|
|
7832
|
+
restOptions
|
|
7833
|
+
);
|
|
7834
|
+
}
|
|
7835
|
+
const targetAgent = metadata.requestedAgent;
|
|
7836
|
+
if (!targetAgent) {
|
|
7837
|
+
throw new Error("Agent mode requires requestedAgent in metadata");
|
|
7838
|
+
}
|
|
7839
|
+
console.log(
|
|
7840
|
+
`[ViberAgent] Agent mode: direct delegation to '${targetAgent}'`
|
|
7841
|
+
);
|
|
7842
|
+
let agent = this.space.getAgent(targetAgent);
|
|
7843
|
+
if (!agent) {
|
|
7844
|
+
console.log(`[ViberAgent] Loading agent '${targetAgent}' on demand`);
|
|
7845
|
+
const dataAdapter = getServerDataAdapter();
|
|
7846
|
+
const agentConfig = await dataAdapter.getAgent(targetAgent);
|
|
7847
|
+
if (!agentConfig) {
|
|
7848
|
+
throw new Error(`Agent '${targetAgent}' not found`);
|
|
7849
|
+
}
|
|
7850
|
+
agent = new Agent(agentConfig);
|
|
7851
|
+
this.space.registerAgent(targetAgent, agent);
|
|
7852
|
+
}
|
|
7853
|
+
const taskId = metadata?.taskId || metadata?.conversationId || "default";
|
|
7854
|
+
const task = this.space.getOrCreateTask(taskId);
|
|
7855
|
+
const fullHistory = task.history.getMessages();
|
|
7856
|
+
const messagesForAgent = fullHistory.length > 0 ? this.optimizeContextForAgent(fullHistory) : this.optimizeContextForAgent(messages);
|
|
7857
|
+
console.log(
|
|
7858
|
+
`[ViberAgent] Agent mode: using ${messagesForAgent.length} messages from history`
|
|
7859
|
+
);
|
|
7860
|
+
return await agent.streamText({
|
|
7861
|
+
messages: messagesForAgent,
|
|
7862
|
+
system: systemMessage,
|
|
7863
|
+
spaceId,
|
|
7864
|
+
metadata: {
|
|
7865
|
+
...metadata,
|
|
7866
|
+
delegationType: "direct",
|
|
7867
|
+
userId: this.space.userId
|
|
7868
|
+
// Pass space owner ID for tracking
|
|
7869
|
+
},
|
|
7870
|
+
...restOptions
|
|
7871
|
+
});
|
|
7872
|
+
}
|
|
7873
|
+
/**
|
|
7874
|
+
* Handle parallel execution of multiple agents
|
|
7875
|
+
*/
|
|
7876
|
+
async handleParallelExecution(agentIds, messages, systemMessage, spaceId, metadata, restOptions) {
|
|
7877
|
+
console.log(`[ViberAgent] Parallel execution: ${agentIds.length} agents`);
|
|
7878
|
+
const dataAdapter = getServerDataAdapter();
|
|
7879
|
+
for (const agentId of agentIds) {
|
|
7880
|
+
if (!this.space.getAgent(agentId)) {
|
|
7881
|
+
const agentConfig = await dataAdapter.getAgent(agentId);
|
|
7882
|
+
if (!agentConfig) {
|
|
7883
|
+
throw new Error(`Agent '${agentId}' not found`);
|
|
7884
|
+
}
|
|
7885
|
+
const agent = new Agent(agentConfig);
|
|
7886
|
+
this.space.registerAgent(agentId, agent);
|
|
7887
|
+
}
|
|
7888
|
+
}
|
|
7889
|
+
if (!this.space.parallelEngine) {
|
|
7890
|
+
throw new Error("Parallel execution engine not initialized");
|
|
7891
|
+
}
|
|
7892
|
+
const tasks = agentIds.map((agentId, index) => ({
|
|
7893
|
+
id: `parallel-${agentId}-${Date.now()}-${index}`,
|
|
7894
|
+
agentId,
|
|
7895
|
+
messages: this.optimizeContextForAgent(messages),
|
|
7896
|
+
system: systemMessage,
|
|
7897
|
+
metadata: {
|
|
7898
|
+
...metadata,
|
|
7899
|
+
delegationType: "parallel",
|
|
7900
|
+
userId: this.space.userId,
|
|
7901
|
+
parallelIndex: index
|
|
7902
|
+
},
|
|
7903
|
+
priority: agentIds.length - index
|
|
7904
|
+
// First agent gets highest priority
|
|
7905
|
+
}));
|
|
7906
|
+
const results = await this.space.parallelEngine.executeParallel(tasks);
|
|
7907
|
+
return {
|
|
7908
|
+
text: results.map((r) => `[${r.agentId}]: ${r.result.text}`).join("\n\n"),
|
|
7909
|
+
toolCalls: results.flatMap((r) => r.result.toolCalls || []),
|
|
7910
|
+
metadata: {
|
|
7911
|
+
...metadata,
|
|
7912
|
+
parallelResults: results,
|
|
7913
|
+
agentCount: agentIds.length
|
|
7914
|
+
}
|
|
7915
|
+
};
|
|
7916
|
+
}
|
|
7917
|
+
/**
|
|
7918
|
+
* Update space history with new messages
|
|
7919
|
+
* Now supports per-task history
|
|
7920
|
+
*/
|
|
7921
|
+
async updateSpaceHistory(messages, metadata) {
|
|
7922
|
+
const taskId = metadata?.taskId || metadata?.conversationId || "default";
|
|
7923
|
+
const task = this.space.getOrCreateTask(taskId);
|
|
7924
|
+
const existingMessages = task.history.getMessages();
|
|
7925
|
+
const newMessages = messages.length > existingMessages.length ? messages.slice(existingMessages.length) : messages;
|
|
7926
|
+
if (newMessages.length > 0) {
|
|
7927
|
+
for (const msg of newMessages) {
|
|
7928
|
+
const formattedMsg = {
|
|
7929
|
+
...msg,
|
|
7930
|
+
content: typeof msg.content === "string" ? msg.content : Array.isArray(msg.content) ? msg.content : [{ type: "text", text: msg.content }]
|
|
7931
|
+
};
|
|
7932
|
+
task.history.add(formattedMsg);
|
|
7933
|
+
}
|
|
7934
|
+
console.log(
|
|
7935
|
+
`[ViberAgent] Updated task ${taskId} history with ${newMessages.length} new messages`
|
|
7936
|
+
);
|
|
7937
|
+
}
|
|
7938
|
+
}
|
|
7939
|
+
/**
|
|
7940
|
+
* Optimize message context for single-agent performance
|
|
7941
|
+
*/
|
|
7942
|
+
optimizeContextForAgent(messages) {
|
|
7943
|
+
return messages.slice(-4);
|
|
7944
|
+
}
|
|
7945
|
+
/**
|
|
7946
|
+
* Extract user prompt from messages for orchestration
|
|
7947
|
+
*/
|
|
7948
|
+
extractPromptFromMessages(messages) {
|
|
7949
|
+
const lastMessage = messages[messages.length - 1];
|
|
7950
|
+
if (lastMessage?.role === "user") {
|
|
7951
|
+
const content = lastMessage.content;
|
|
7952
|
+
if (typeof content === "string") {
|
|
7953
|
+
return content;
|
|
7954
|
+
} else if (Array.isArray(content)) {
|
|
7955
|
+
return content.filter((part) => part.type === "text" && part.text).map((part) => part.text).join(" ");
|
|
7956
|
+
}
|
|
7957
|
+
}
|
|
7958
|
+
return "";
|
|
7959
|
+
}
|
|
7960
|
+
/**
|
|
7961
|
+
* Handle message persistence after streaming completes
|
|
7962
|
+
*/
|
|
7963
|
+
handleMessagePersistence(streamResult, messages, spaceId, metadata) {
|
|
7964
|
+
(async () => {
|
|
7965
|
+
try {
|
|
7966
|
+
let finalText = "";
|
|
7967
|
+
const toolInvocations = [];
|
|
7968
|
+
for await (const part of streamResult.fullStream) {
|
|
7969
|
+
switch (part.type) {
|
|
7970
|
+
case "text-delta":
|
|
7971
|
+
if (part.text) {
|
|
7972
|
+
finalText += part.text;
|
|
7973
|
+
}
|
|
7974
|
+
break;
|
|
7975
|
+
case "tool-call":
|
|
7976
|
+
toolInvocations.push({
|
|
7977
|
+
toolCallId: part.toolCallId,
|
|
7978
|
+
toolName: part.toolName,
|
|
7979
|
+
args: part.args
|
|
7980
|
+
});
|
|
7981
|
+
break;
|
|
7982
|
+
case "tool-result":
|
|
7983
|
+
const toolCall = toolInvocations.find(
|
|
7984
|
+
(t) => t.toolCallId === part.toolCallId
|
|
7985
|
+
);
|
|
7986
|
+
if (toolCall) {
|
|
7987
|
+
toolCall.result = part.result;
|
|
7988
|
+
}
|
|
7989
|
+
break;
|
|
7990
|
+
case "finish":
|
|
7991
|
+
break;
|
|
7992
|
+
case "error":
|
|
7993
|
+
console.error("[ViberAgent] Stream error:", part.error);
|
|
7994
|
+
return;
|
|
7995
|
+
}
|
|
7996
|
+
}
|
|
7997
|
+
const assistantMessage = {
|
|
7998
|
+
role: "assistant",
|
|
7999
|
+
content: [{ type: "text", text: finalText }],
|
|
8000
|
+
id: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
8001
|
+
metadata: {
|
|
8002
|
+
agentName: this.singleAgentId || "team",
|
|
8003
|
+
spaceId,
|
|
8004
|
+
timestamp: Date.now(),
|
|
8005
|
+
...metadata
|
|
8006
|
+
},
|
|
8007
|
+
...toolInvocations.length > 0 && { toolInvocations }
|
|
8008
|
+
};
|
|
8009
|
+
const taskId = metadata?.taskId || metadata?.conversationId || "default";
|
|
8010
|
+
const task = this.space.getOrCreateTask(taskId);
|
|
8011
|
+
task.history.add(assistantMessage);
|
|
8012
|
+
} catch (error) {
|
|
8013
|
+
console.error("[ViberAgent] Failed to persist messages:", error);
|
|
8014
|
+
}
|
|
8015
|
+
})();
|
|
8016
|
+
}
|
|
8017
|
+
/**
|
|
8018
|
+
* Save space to storage
|
|
8019
|
+
*/
|
|
8020
|
+
async saveSpace() {
|
|
8021
|
+
try {
|
|
8022
|
+
console.log("[ViberAgent] Space persistence handled by database adapter");
|
|
8023
|
+
} catch (error) {
|
|
8024
|
+
console.error("[ViberAgent] Error saving space:", error);
|
|
8025
|
+
}
|
|
8026
|
+
}
|
|
8027
|
+
/**
|
|
8028
|
+
* Stop current operation
|
|
8029
|
+
*/
|
|
8030
|
+
stop() {
|
|
8031
|
+
this.space.messageQueue.clear();
|
|
8032
|
+
if (this.abortController) {
|
|
8033
|
+
this.abortController.abort();
|
|
8034
|
+
}
|
|
8035
|
+
}
|
|
8036
|
+
/**
|
|
8037
|
+
* Add message to queue (soft interrupt)
|
|
8038
|
+
*/
|
|
8039
|
+
addMessage(message, metadata) {
|
|
8040
|
+
return this.space.messageQueue.add(message, metadata);
|
|
8041
|
+
}
|
|
8042
|
+
/**
|
|
8043
|
+
* Enrich context with space information
|
|
8044
|
+
*/
|
|
8045
|
+
enrichContext(context) {
|
|
8046
|
+
return {
|
|
8047
|
+
spaceId: this.space.spaceId,
|
|
8048
|
+
conversationHistory: this.space.history,
|
|
8049
|
+
metadata: {
|
|
8050
|
+
spaceName: this.space.name,
|
|
8051
|
+
spaceGoal: this.space.goal,
|
|
8052
|
+
...context?.metadata
|
|
8053
|
+
},
|
|
8054
|
+
...context
|
|
8055
|
+
};
|
|
8056
|
+
}
|
|
8057
|
+
/**
|
|
8058
|
+
* Create or update the space plan
|
|
8059
|
+
*/
|
|
8060
|
+
async createPlan(goal) {
|
|
8061
|
+
const planGoal = goal || this.space.goal;
|
|
8062
|
+
const planSchema = z11.object({
|
|
8063
|
+
tasks: z11.array(
|
|
8064
|
+
z11.object({
|
|
8065
|
+
id: z11.string(),
|
|
8066
|
+
title: z11.string(),
|
|
8067
|
+
description: z11.string(),
|
|
8068
|
+
assignedTo: z11.string().optional(),
|
|
8069
|
+
priority: z11.enum(["low", "medium", "high"]).default("medium"),
|
|
8070
|
+
estimatedTime: z11.string().optional(),
|
|
8071
|
+
dependencies: z11.array(
|
|
8072
|
+
z11.object({
|
|
8073
|
+
taskId: z11.string(),
|
|
8074
|
+
type: z11.enum(["required", "optional"])
|
|
8075
|
+
})
|
|
8076
|
+
).default([]),
|
|
8077
|
+
tags: z11.array(z11.string()).default([])
|
|
8078
|
+
})
|
|
8079
|
+
)
|
|
8080
|
+
});
|
|
8081
|
+
const result = await generateText2({
|
|
8082
|
+
model: this.getModel(),
|
|
8083
|
+
system: this.getSystemPrompt() + "\n\nCreate a detailed plan to achieve the goal.",
|
|
8084
|
+
prompt: `Goal: ${planGoal}
|
|
8085
|
+
|
|
8086
|
+
Available agents: ${Array.from(
|
|
8087
|
+
this.space.agents.keys()
|
|
8088
|
+
).join(", ")}`,
|
|
8089
|
+
output: Output.object({ schema: planSchema })
|
|
8090
|
+
});
|
|
8091
|
+
const planData = result.output;
|
|
8092
|
+
const tasks = planData.tasks.map(
|
|
8093
|
+
(taskData) => new Task({
|
|
8094
|
+
...taskData,
|
|
8095
|
+
status: "pending" /* PENDING */
|
|
8096
|
+
})
|
|
8097
|
+
);
|
|
8098
|
+
const plan = new Plan({
|
|
8099
|
+
goal: planGoal,
|
|
8100
|
+
tasks
|
|
8101
|
+
});
|
|
8102
|
+
await this.space.createPlan(plan);
|
|
8103
|
+
return plan;
|
|
8104
|
+
}
|
|
8105
|
+
/**
|
|
8106
|
+
* Adapt the plan based on new information or user feedback
|
|
8107
|
+
*/
|
|
8108
|
+
async adaptPlan(feedback) {
|
|
8109
|
+
if (!this.space.plan) {
|
|
8110
|
+
return this.createPlan(feedback);
|
|
8111
|
+
}
|
|
8112
|
+
const currentPlan = this.space.plan;
|
|
8113
|
+
const progress = currentPlan.getProgressSummary();
|
|
8114
|
+
const adaptSchema = z11.object({
|
|
8115
|
+
preserveTasks: z11.array(z11.string()).describe("IDs of tasks to keep unchanged"),
|
|
8116
|
+
modifyTasks: z11.array(
|
|
8117
|
+
z11.object({
|
|
8118
|
+
id: z11.string(),
|
|
8119
|
+
changes: z11.object({
|
|
8120
|
+
title: z11.string().optional(),
|
|
8121
|
+
description: z11.string().optional(),
|
|
8122
|
+
priority: z11.enum(["low", "medium", "high"]).optional(),
|
|
8123
|
+
assignedTo: z11.string().optional()
|
|
8124
|
+
})
|
|
8125
|
+
})
|
|
8126
|
+
).describe("Tasks to modify"),
|
|
8127
|
+
removeTasks: z11.array(z11.string()).describe("IDs of tasks to remove"),
|
|
8128
|
+
addTasks: z11.array(
|
|
8129
|
+
z11.object({
|
|
8130
|
+
id: z11.string(),
|
|
8131
|
+
title: z11.string(),
|
|
8132
|
+
description: z11.string(),
|
|
8133
|
+
assignedTo: z11.string().optional(),
|
|
8134
|
+
priority: z11.enum(["low", "medium", "high"]).default("medium"),
|
|
8135
|
+
dependencies: z11.array(
|
|
8136
|
+
z11.object({
|
|
8137
|
+
taskId: z11.string(),
|
|
8138
|
+
type: z11.enum(["required", "optional"])
|
|
8139
|
+
})
|
|
8140
|
+
).default([]),
|
|
8141
|
+
tags: z11.array(z11.string()).default([])
|
|
8142
|
+
})
|
|
8143
|
+
).describe("New tasks to add"),
|
|
8144
|
+
reasoning: z11.string().describe("Explanation of the plan changes")
|
|
8145
|
+
});
|
|
8146
|
+
const prompt = `
|
|
8147
|
+
Current Plan Progress:
|
|
8148
|
+
- Total tasks: ${progress.totalTasks}
|
|
8149
|
+
- Completed: ${progress.completedTasks}
|
|
8150
|
+
- In Progress: ${progress.runningTasks}
|
|
8151
|
+
- Pending: ${progress.pendingTasks}
|
|
8152
|
+
|
|
8153
|
+
Current Tasks:
|
|
8154
|
+
${currentPlan.tasks.map((t) => `- [${t.id}] ${t.title} (${t.status})`).join("\n")}
|
|
8155
|
+
|
|
8156
|
+
User Feedback: ${feedback}
|
|
8157
|
+
|
|
8158
|
+
Analyze the current plan and adapt it based on the user's feedback.
|
|
8159
|
+
Keep completed tasks unless explicitly asked to redo them.
|
|
8160
|
+
Preserve tasks that are still relevant.
|
|
8161
|
+
Modify, remove, or add tasks as needed to better achieve the goal.
|
|
8162
|
+
`;
|
|
8163
|
+
const result = await generateText2({
|
|
8164
|
+
model: this.getModel(),
|
|
8165
|
+
system: this.getSystemPrompt() + "\n\nAdapt the existing plan based on user feedback.",
|
|
8166
|
+
prompt,
|
|
8167
|
+
output: Output.object({ schema: adaptSchema })
|
|
8168
|
+
});
|
|
8169
|
+
const adaptData = result.output;
|
|
8170
|
+
const adaptedTasks = [];
|
|
8171
|
+
for (const taskId of adaptData.preserveTasks) {
|
|
8172
|
+
const task = currentPlan.tasks.find((t) => t.id === taskId);
|
|
8173
|
+
if (task) {
|
|
8174
|
+
adaptedTasks.push(task);
|
|
8175
|
+
}
|
|
8176
|
+
}
|
|
8177
|
+
for (const modification of adaptData.modifyTasks) {
|
|
8178
|
+
const task = currentPlan.tasks.find((t) => t.id === modification.id);
|
|
8179
|
+
if (task) {
|
|
8180
|
+
if (modification.changes.title) task.title = modification.changes.title;
|
|
8181
|
+
if (modification.changes.description)
|
|
8182
|
+
task.description = modification.changes.description;
|
|
8183
|
+
if (modification.changes.priority)
|
|
8184
|
+
task.priority = modification.changes.priority;
|
|
8185
|
+
if (modification.changes.assignedTo)
|
|
8186
|
+
task.assignedTo = modification.changes.assignedTo;
|
|
8187
|
+
adaptedTasks.push(task);
|
|
8188
|
+
}
|
|
8189
|
+
}
|
|
8190
|
+
for (const newTaskData of adaptData.addTasks) {
|
|
8191
|
+
const newTask = new Task({
|
|
8192
|
+
...newTaskData,
|
|
8193
|
+
status: "pending" /* PENDING */
|
|
8194
|
+
});
|
|
8195
|
+
adaptedTasks.push(newTask);
|
|
8196
|
+
}
|
|
8197
|
+
const adaptedPlan = new Plan({
|
|
8198
|
+
goal: currentPlan.goal,
|
|
8199
|
+
tasks: adaptedTasks
|
|
8200
|
+
});
|
|
8201
|
+
await this.space.createPlan(adaptedPlan);
|
|
8202
|
+
console.log("[Plan Adaptation]", adaptData.reasoning);
|
|
8203
|
+
return adaptedPlan;
|
|
8204
|
+
}
|
|
8205
|
+
/**
|
|
8206
|
+
* Get plan context for system prompt
|
|
8207
|
+
*/
|
|
8208
|
+
getPlanContext() {
|
|
8209
|
+
if (!this.space.plan) {
|
|
8210
|
+
return "\n\nNo active plan for this space yet.";
|
|
8211
|
+
}
|
|
8212
|
+
const summary = this.space.plan.getProgressSummary();
|
|
8213
|
+
return `
|
|
8214
|
+
|
|
8215
|
+
Current Plan Status:
|
|
8216
|
+
- Total tasks: ${summary.totalTasks}
|
|
8217
|
+
- Completed: ${summary.completedTasks}
|
|
8218
|
+
- Running: ${summary.runningTasks}
|
|
8219
|
+
- Pending: ${summary.pendingTasks}
|
|
8220
|
+
- Failed: ${summary.failedTasks}
|
|
8221
|
+
- Progress: ${summary.progressPercentage.toFixed(1)}%
|
|
8222
|
+
`;
|
|
8223
|
+
}
|
|
8224
|
+
/**
|
|
8225
|
+
* Get artifacts context for system prompt
|
|
8226
|
+
*/
|
|
8227
|
+
getArtifactsContext() {
|
|
8228
|
+
if (!this.space.artifacts || this.space.artifacts.length === 0) {
|
|
8229
|
+
return "";
|
|
8230
|
+
}
|
|
8231
|
+
const artifactsList = this.space.artifacts.map((a) => `- ${a.title || a.path} (${a.artifactType || "document"})`).join("\n");
|
|
8232
|
+
return `
|
|
8233
|
+
|
|
8234
|
+
Available Artifacts:
|
|
8235
|
+
${artifactsList}
|
|
8236
|
+
|
|
8237
|
+
These artifacts are pre-loaded in the space and can be referenced in your responses.
|
|
8238
|
+
`;
|
|
8239
|
+
}
|
|
8240
|
+
/**
|
|
8241
|
+
* Get ViberAgent summary
|
|
8242
|
+
*/
|
|
8243
|
+
getSummary() {
|
|
8244
|
+
const base = super.getSummary();
|
|
8245
|
+
return {
|
|
8246
|
+
...base,
|
|
8247
|
+
spaceId: this.space.spaceId,
|
|
8248
|
+
spaceName: this.space.name,
|
|
8249
|
+
spaceGoal: this.space.goal,
|
|
8250
|
+
planStatus: this.space.plan?.getProgressSummary()
|
|
8251
|
+
};
|
|
8252
|
+
}
|
|
8253
|
+
/**
|
|
8254
|
+
* Static factory to start a new space
|
|
8255
|
+
*/
|
|
8256
|
+
static async start(goal, options = {}) {
|
|
8257
|
+
const { spaceId, model, singleAgentId } = options;
|
|
8258
|
+
const id = spaceId || `proj_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
8259
|
+
const { startSpace: startSpace2 } = await Promise.resolve().then(() => (init_space2(), space_exports2));
|
|
8260
|
+
const space = await startSpace2({
|
|
8261
|
+
spaceId: id,
|
|
8262
|
+
goal,
|
|
8263
|
+
name: goal.slice(0, 50),
|
|
8264
|
+
model
|
|
8265
|
+
});
|
|
8266
|
+
if (!space.viberAgent) {
|
|
8267
|
+
throw new Error("Failed to initialize ViberAgent");
|
|
8268
|
+
}
|
|
8269
|
+
if (singleAgentId) {
|
|
8270
|
+
space.viberAgent.singleAgentId = singleAgentId;
|
|
8271
|
+
}
|
|
8272
|
+
return space.viberAgent;
|
|
8273
|
+
}
|
|
8274
|
+
/**
|
|
8275
|
+
* Static factory to resume an existing space
|
|
8276
|
+
*/
|
|
8277
|
+
static async resume(spaceId, options = {}) {
|
|
8278
|
+
const { model } = options;
|
|
8279
|
+
const { SpaceStorageFactory: SpaceStorageFactory2 } = await Promise.resolve().then(() => (init_space(), space_exports));
|
|
8280
|
+
const { startSpace: startSpace2 } = await Promise.resolve().then(() => (init_space2(), space_exports2));
|
|
8281
|
+
const exists = await SpaceStorageFactory2.exists(spaceId);
|
|
8282
|
+
if (!exists) {
|
|
8283
|
+
throw new Error(`Space ${spaceId} not found`);
|
|
8284
|
+
}
|
|
8285
|
+
const storage = await SpaceStorageFactory2.create(spaceId);
|
|
8286
|
+
const spaceData = await storage.readJSON("space.json");
|
|
8287
|
+
if (!spaceData) {
|
|
8288
|
+
throw new Error(`Failed to load space ${spaceId}`);
|
|
8289
|
+
}
|
|
8290
|
+
const space = await startSpace2({
|
|
8291
|
+
spaceId,
|
|
8292
|
+
goal: spaceData.goal,
|
|
8293
|
+
name: spaceData.name,
|
|
8294
|
+
model: model || spaceData.model
|
|
8295
|
+
});
|
|
8296
|
+
if (!space.viberAgent) {
|
|
8297
|
+
throw new Error("Failed to initialize ViberAgent");
|
|
8298
|
+
}
|
|
8299
|
+
const agentId = options.singleAgentId || spaceData.singleAgentId;
|
|
8300
|
+
if (agentId) {
|
|
8301
|
+
space.viberAgent.singleAgentId = agentId;
|
|
8302
|
+
}
|
|
8303
|
+
const messages = await storage.readJSON("messages.json");
|
|
8304
|
+
if (messages && Array.isArray(messages)) {
|
|
8305
|
+
space.history.clear();
|
|
8306
|
+
for (const msg of messages) {
|
|
8307
|
+
const normalizedMsg = { ...msg };
|
|
8308
|
+
if (typeof msg.content === "string") {
|
|
8309
|
+
normalizedMsg.content = [{ type: "text", text: msg.content }];
|
|
8310
|
+
}
|
|
8311
|
+
space.history.add(normalizedMsg);
|
|
8312
|
+
}
|
|
8313
|
+
}
|
|
8314
|
+
return space.viberAgent;
|
|
8315
|
+
}
|
|
8316
|
+
};
|
|
8317
|
+
}
|
|
8318
|
+
});
|
|
8319
|
+
|
|
8320
|
+
// src/channels/manager.ts
|
|
8321
|
+
var manager_exports = {};
|
|
8322
|
+
__export(manager_exports, {
|
|
8323
|
+
ChannelManager: () => ChannelManager,
|
|
8324
|
+
channelManager: () => channelManager
|
|
8325
|
+
});
|
|
8326
|
+
import { EventEmitter as EventEmitter3 } from "events";
|
|
8327
|
+
var ChannelManager, channelManager;
|
|
8328
|
+
var init_manager = __esm({
|
|
8329
|
+
"src/channels/manager.ts"() {
|
|
8330
|
+
"use strict";
|
|
8331
|
+
init_viber_agent();
|
|
8332
|
+
ChannelManager = class extends EventEmitter3 {
|
|
8333
|
+
channels = /* @__PURE__ */ new Map();
|
|
8334
|
+
conversations = /* @__PURE__ */ new Map();
|
|
8335
|
+
/**
|
|
8336
|
+
* Register a channel
|
|
8337
|
+
*/
|
|
8338
|
+
register(channel) {
|
|
8339
|
+
if (this.channels.has(channel.id)) {
|
|
8340
|
+
console.warn(`[ChannelManager] Channel ${channel.id} already registered`);
|
|
8341
|
+
return;
|
|
8342
|
+
}
|
|
8343
|
+
this.channels.set(channel.id, channel);
|
|
8344
|
+
console.log(`[ChannelManager] Registered channel: ${channel.id}`);
|
|
8345
|
+
}
|
|
8346
|
+
/**
|
|
8347
|
+
* Unregister a channel
|
|
8348
|
+
*/
|
|
8349
|
+
unregister(id) {
|
|
8350
|
+
this.channels.delete(id);
|
|
8351
|
+
console.log(`[ChannelManager] Unregistered channel: ${id}`);
|
|
8352
|
+
}
|
|
8353
|
+
/**
|
|
8354
|
+
* Get a channel by ID
|
|
8355
|
+
*/
|
|
8356
|
+
getChannel(id) {
|
|
8357
|
+
return this.channels.get(id);
|
|
8358
|
+
}
|
|
8359
|
+
/**
|
|
8360
|
+
* Start all registered channels
|
|
8361
|
+
*/
|
|
8362
|
+
async startAll() {
|
|
8363
|
+
for (const [id, channel] of this.channels) {
|
|
8364
|
+
try {
|
|
8365
|
+
await channel.start();
|
|
8366
|
+
console.log(`[ChannelManager] Started channel: ${id}`);
|
|
8367
|
+
} catch (error) {
|
|
8368
|
+
console.error(`[ChannelManager] Failed to start channel ${id}:`, error);
|
|
8369
|
+
}
|
|
8370
|
+
}
|
|
8371
|
+
}
|
|
8372
|
+
/**
|
|
8373
|
+
* Stop all registered channels
|
|
8374
|
+
*/
|
|
8375
|
+
async stopAll() {
|
|
8376
|
+
for (const [id, channel] of this.channels) {
|
|
8377
|
+
try {
|
|
8378
|
+
await channel.stop();
|
|
8379
|
+
console.log(`[ChannelManager] Stopped channel: ${id}`);
|
|
8380
|
+
} catch (error) {
|
|
8381
|
+
console.error(`[ChannelManager] Failed to stop channel ${id}:`, error);
|
|
8382
|
+
}
|
|
8383
|
+
}
|
|
8384
|
+
}
|
|
8385
|
+
/**
|
|
8386
|
+
* Route an inbound message to ViberAgent
|
|
8387
|
+
*/
|
|
8388
|
+
async routeMessage(message) {
|
|
8389
|
+
const { source, conversationId, content, userId } = message;
|
|
8390
|
+
console.log(
|
|
8391
|
+
`[ChannelManager] Routing message from ${source}: ${content.substring(0, 50)}...`
|
|
8392
|
+
);
|
|
8393
|
+
let conversation = this.conversations.get(conversationId);
|
|
8394
|
+
if (!conversation) {
|
|
8395
|
+
const agent = await ViberAgent.start(content, {
|
|
8396
|
+
// Use conversation ID as space ID for isolation
|
|
8397
|
+
spaceId: conversationId
|
|
8398
|
+
});
|
|
8399
|
+
conversation = {
|
|
8400
|
+
channelId: source,
|
|
8401
|
+
agent,
|
|
8402
|
+
startedAt: /* @__PURE__ */ new Date()
|
|
8403
|
+
};
|
|
8404
|
+
this.conversations.set(conversationId, conversation);
|
|
8405
|
+
}
|
|
8406
|
+
try {
|
|
8407
|
+
const result = await conversation.agent.streamText({
|
|
8408
|
+
messages: [{ role: "user", content }],
|
|
8409
|
+
metadata: { userId, source }
|
|
8410
|
+
});
|
|
8411
|
+
const channel = this.channels.get(source);
|
|
8412
|
+
if (!channel) {
|
|
8413
|
+
console.error(`[ChannelManager] Channel not found: ${source}`);
|
|
8414
|
+
return;
|
|
8415
|
+
}
|
|
8416
|
+
for await (const chunk of result.fullStream) {
|
|
8417
|
+
await channel.stream(conversationId, this.mapToStreamEvent(chunk));
|
|
8418
|
+
}
|
|
8419
|
+
await channel.stream(conversationId, {
|
|
8420
|
+
type: "done",
|
|
8421
|
+
agentId: conversation.agent.spaceId
|
|
8422
|
+
});
|
|
8423
|
+
} catch (error) {
|
|
8424
|
+
console.error(`[ChannelManager] Error processing message:`, error);
|
|
8425
|
+
const channel = this.channels.get(source);
|
|
8426
|
+
if (channel) {
|
|
8427
|
+
await channel.stream(conversationId, {
|
|
8428
|
+
type: "error",
|
|
8429
|
+
error: error.message,
|
|
8430
|
+
agentId: conversation?.agent?.spaceId || "unknown"
|
|
8431
|
+
});
|
|
8432
|
+
}
|
|
8433
|
+
}
|
|
8434
|
+
}
|
|
8435
|
+
/**
|
|
8436
|
+
* Handle interrupt signal
|
|
8437
|
+
*/
|
|
8438
|
+
async handleInterrupt(signal) {
|
|
8439
|
+
const conversation = this.conversations.get(signal.conversationId);
|
|
8440
|
+
if (conversation) {
|
|
8441
|
+
conversation.agent.stop();
|
|
8442
|
+
this.conversations.delete(signal.conversationId);
|
|
8443
|
+
console.log(
|
|
8444
|
+
`[ChannelManager] Interrupted conversation: ${signal.conversationId}`
|
|
8445
|
+
);
|
|
8446
|
+
}
|
|
8447
|
+
}
|
|
8448
|
+
/**
|
|
8449
|
+
* Map AI SDK stream chunk to AgentStreamEvent
|
|
8450
|
+
*/
|
|
8451
|
+
mapToStreamEvent(chunk) {
|
|
8452
|
+
if (chunk.type === "text-delta") {
|
|
8453
|
+
return {
|
|
8454
|
+
type: "text-delta",
|
|
8455
|
+
content: chunk.textDelta,
|
|
8456
|
+
agentId: "viber"
|
|
8457
|
+
};
|
|
8458
|
+
}
|
|
8459
|
+
if (chunk.type === "tool-call") {
|
|
8460
|
+
return {
|
|
8461
|
+
type: "tool-call",
|
|
8462
|
+
tool: chunk.toolName,
|
|
8463
|
+
args: chunk.args,
|
|
8464
|
+
agentId: "viber"
|
|
8465
|
+
};
|
|
8466
|
+
}
|
|
8467
|
+
if (chunk.type === "tool-result") {
|
|
8468
|
+
return {
|
|
8469
|
+
type: "tool-result",
|
|
8470
|
+
tool: chunk.toolName,
|
|
8471
|
+
result: chunk.result,
|
|
8472
|
+
agentId: "viber"
|
|
8473
|
+
};
|
|
8474
|
+
}
|
|
8475
|
+
return {
|
|
8476
|
+
type: "state-change",
|
|
8477
|
+
state: chunk.type,
|
|
8478
|
+
agentId: "viber"
|
|
8479
|
+
};
|
|
8480
|
+
}
|
|
8481
|
+
};
|
|
8482
|
+
channelManager = new ChannelManager();
|
|
8483
|
+
}
|
|
8484
|
+
});
|
|
8485
|
+
|
|
8486
|
+
// src/channels/dingtalk.ts
|
|
8487
|
+
var dingtalk_exports = {};
|
|
8488
|
+
__export(dingtalk_exports, {
|
|
8489
|
+
DingTalkChannel: () => DingTalkChannel
|
|
8490
|
+
});
|
|
8491
|
+
import crypto from "crypto";
|
|
8492
|
+
var DingTalkChannel;
|
|
8493
|
+
var init_dingtalk = __esm({
|
|
8494
|
+
"src/channels/dingtalk.ts"() {
|
|
8495
|
+
"use strict";
|
|
8496
|
+
DingTalkChannel = class {
|
|
8497
|
+
id = "dingtalk";
|
|
8498
|
+
type = "webhook";
|
|
8499
|
+
config;
|
|
8500
|
+
sessionWebhooks = /* @__PURE__ */ new Map();
|
|
8501
|
+
responseBuffers = /* @__PURE__ */ new Map();
|
|
8502
|
+
constructor(config2) {
|
|
8503
|
+
this.config = config2;
|
|
8504
|
+
}
|
|
8505
|
+
async start() {
|
|
8506
|
+
console.log("[DingTalk] Channel started");
|
|
8507
|
+
}
|
|
8508
|
+
async stop() {
|
|
8509
|
+
this.sessionWebhooks.clear();
|
|
8510
|
+
this.responseBuffers.clear();
|
|
8511
|
+
console.log("[DingTalk] Channel stopped");
|
|
8512
|
+
}
|
|
8513
|
+
/**
|
|
8514
|
+
* Verify webhook signature
|
|
8515
|
+
*/
|
|
8516
|
+
verifySignature(timestamp, sign) {
|
|
8517
|
+
const stringToSign = timestamp + "\n" + this.config.appSecret;
|
|
8518
|
+
const hmac = crypto.createHmac("sha256", this.config.appSecret);
|
|
8519
|
+
hmac.update(stringToSign);
|
|
8520
|
+
const expectedSign = hmac.digest("base64");
|
|
8521
|
+
return sign === expectedSign;
|
|
8522
|
+
}
|
|
8523
|
+
/**
|
|
8524
|
+
* Parse webhook payload to InboundMessage
|
|
8525
|
+
*/
|
|
8526
|
+
parseWebhook(payload) {
|
|
8527
|
+
this.sessionWebhooks.set(payload.conversationId, payload.sessionWebhook);
|
|
8528
|
+
return {
|
|
8529
|
+
id: payload.msgId,
|
|
8530
|
+
source: this.id,
|
|
8531
|
+
userId: payload.senderId,
|
|
8532
|
+
conversationId: payload.conversationId,
|
|
8533
|
+
content: payload.text?.content || "",
|
|
8534
|
+
metadata: {
|
|
8535
|
+
senderNick: payload.senderNick,
|
|
8536
|
+
conversationType: payload.conversationType,
|
|
8537
|
+
conversationTitle: payload.conversationTitle,
|
|
8538
|
+
robotCode: payload.robotCode
|
|
8539
|
+
}
|
|
8540
|
+
};
|
|
8541
|
+
}
|
|
8542
|
+
async handleMessage(message) {
|
|
8543
|
+
this.responseBuffers.set(message.conversationId, "");
|
|
8544
|
+
}
|
|
8545
|
+
async stream(conversationId, event) {
|
|
8546
|
+
const sessionWebhook = this.sessionWebhooks.get(conversationId);
|
|
8547
|
+
if (!sessionWebhook) {
|
|
8548
|
+
console.error(
|
|
8549
|
+
`[DingTalk] No session webhook for conversation: ${conversationId}`
|
|
8550
|
+
);
|
|
8551
|
+
return;
|
|
8552
|
+
}
|
|
8553
|
+
if (event.type === "text-delta") {
|
|
8554
|
+
const current = this.responseBuffers.get(conversationId) || "";
|
|
8555
|
+
this.responseBuffers.set(conversationId, current + event.content);
|
|
8556
|
+
} else if (event.type === "done") {
|
|
8557
|
+
const text = this.responseBuffers.get(conversationId) || "";
|
|
8558
|
+
await this.sendMessage(sessionWebhook, {
|
|
8559
|
+
msgtype: "markdown",
|
|
8560
|
+
markdown: {
|
|
8561
|
+
title: "Reply",
|
|
8562
|
+
text
|
|
8563
|
+
}
|
|
8564
|
+
});
|
|
8565
|
+
this.responseBuffers.delete(conversationId);
|
|
8566
|
+
} else if (event.type === "error") {
|
|
8567
|
+
await this.sendMessage(sessionWebhook, {
|
|
8568
|
+
msgtype: "text",
|
|
8569
|
+
text: { content: `Error: ${event.error}` }
|
|
8570
|
+
});
|
|
8571
|
+
this.responseBuffers.delete(conversationId);
|
|
8572
|
+
}
|
|
8573
|
+
}
|
|
8574
|
+
/**
|
|
8575
|
+
* Send message via DingTalk session webhook
|
|
8576
|
+
*/
|
|
8577
|
+
async sendMessage(webhookUrl, message) {
|
|
8578
|
+
try {
|
|
8579
|
+
const response = await fetch(webhookUrl, {
|
|
8580
|
+
method: "POST",
|
|
8581
|
+
headers: { "Content-Type": "application/json" },
|
|
8582
|
+
body: JSON.stringify(message)
|
|
8583
|
+
});
|
|
8584
|
+
if (!response.ok) {
|
|
8585
|
+
const error = await response.text();
|
|
8586
|
+
console.error("[DingTalk] Send message failed:", error);
|
|
8587
|
+
}
|
|
8588
|
+
} catch (error) {
|
|
8589
|
+
console.error("[DingTalk] Send message error:", error);
|
|
8590
|
+
}
|
|
8591
|
+
}
|
|
8592
|
+
};
|
|
8593
|
+
}
|
|
8594
|
+
});
|
|
8595
|
+
|
|
8596
|
+
// src/channels/wecom.ts
|
|
8597
|
+
var wecom_exports = {};
|
|
8598
|
+
__export(wecom_exports, {
|
|
8599
|
+
WeComChannel: () => WeComChannel
|
|
8600
|
+
});
|
|
8601
|
+
import crypto2 from "crypto";
|
|
8602
|
+
var WeComCrypto, WeComChannel;
|
|
8603
|
+
var init_wecom = __esm({
|
|
8604
|
+
"src/channels/wecom.ts"() {
|
|
8605
|
+
"use strict";
|
|
8606
|
+
WeComCrypto = class {
|
|
8607
|
+
token;
|
|
8608
|
+
aesKey;
|
|
8609
|
+
corpId;
|
|
8610
|
+
constructor(token, encodingAESKey, corpId) {
|
|
8611
|
+
this.token = token;
|
|
8612
|
+
this.corpId = corpId;
|
|
8613
|
+
this.aesKey = Buffer.from(encodingAESKey + "=", "base64");
|
|
8614
|
+
}
|
|
8615
|
+
/**
|
|
8616
|
+
* Verify callback URL signature
|
|
8617
|
+
*/
|
|
8618
|
+
verifySignature(signature, timestamp, nonce, echostr) {
|
|
8619
|
+
const sortedParams = [this.token, timestamp, nonce, echostr || ""].filter(Boolean).sort().join("");
|
|
8620
|
+
const hash = crypto2.createHash("sha1").update(sortedParams).digest("hex");
|
|
8621
|
+
return hash === signature;
|
|
8622
|
+
}
|
|
8623
|
+
/**
|
|
8624
|
+
* Decrypt message
|
|
8625
|
+
*/
|
|
8626
|
+
decrypt(encrypted) {
|
|
8627
|
+
const decipher = crypto2.createDecipheriv(
|
|
8628
|
+
"aes-256-cbc",
|
|
8629
|
+
this.aesKey,
|
|
8630
|
+
this.aesKey.subarray(0, 16)
|
|
8631
|
+
);
|
|
8632
|
+
decipher.setAutoPadding(false);
|
|
8633
|
+
let decrypted = Buffer.concat([
|
|
8634
|
+
decipher.update(Buffer.from(encrypted, "base64")),
|
|
8635
|
+
decipher.final()
|
|
8636
|
+
]);
|
|
8637
|
+
const padLen = decrypted[decrypted.length - 1];
|
|
8638
|
+
decrypted = decrypted.subarray(0, decrypted.length - padLen);
|
|
8639
|
+
const msgLen = decrypted.readUInt32BE(16);
|
|
8640
|
+
const content = decrypted.subarray(20, 20 + msgLen).toString("utf-8");
|
|
8641
|
+
return content;
|
|
8642
|
+
}
|
|
8643
|
+
/**
|
|
8644
|
+
* Encrypt message for reply
|
|
8645
|
+
*/
|
|
8646
|
+
encrypt(message) {
|
|
8647
|
+
const random = crypto2.randomBytes(16);
|
|
8648
|
+
const msgBuffer = Buffer.from(message, "utf-8");
|
|
8649
|
+
const msgLen = Buffer.alloc(4);
|
|
8650
|
+
msgLen.writeUInt32BE(msgBuffer.length);
|
|
8651
|
+
const corpIdBuffer = Buffer.from(this.corpId, "utf-8");
|
|
8652
|
+
const plaintext = Buffer.concat([
|
|
8653
|
+
random,
|
|
8654
|
+
msgLen,
|
|
8655
|
+
msgBuffer,
|
|
8656
|
+
corpIdBuffer
|
|
8657
|
+
]);
|
|
8658
|
+
const blockSize = 32;
|
|
8659
|
+
const padLen = blockSize - plaintext.length % blockSize;
|
|
8660
|
+
const padding = Buffer.alloc(padLen, padLen);
|
|
8661
|
+
const padded = Buffer.concat([plaintext, padding]);
|
|
8662
|
+
const cipher = crypto2.createCipheriv(
|
|
8663
|
+
"aes-256-cbc",
|
|
8664
|
+
this.aesKey,
|
|
8665
|
+
this.aesKey.subarray(0, 16)
|
|
8666
|
+
);
|
|
8667
|
+
cipher.setAutoPadding(false);
|
|
8668
|
+
const encrypted = Buffer.concat([cipher.update(padded), cipher.final()]);
|
|
8669
|
+
return encrypted.toString("base64");
|
|
8670
|
+
}
|
|
8671
|
+
};
|
|
8672
|
+
WeComChannel = class {
|
|
8673
|
+
id = "wecom";
|
|
8674
|
+
type = "webhook";
|
|
8675
|
+
config;
|
|
8676
|
+
crypto;
|
|
8677
|
+
accessToken = null;
|
|
8678
|
+
tokenExpiry = 0;
|
|
8679
|
+
responseBuffers = /* @__PURE__ */ new Map();
|
|
8680
|
+
constructor(config2) {
|
|
8681
|
+
this.config = config2;
|
|
8682
|
+
this.crypto = new WeComCrypto(config2.token, config2.aesKey, config2.corpId);
|
|
8683
|
+
}
|
|
8684
|
+
async start() {
|
|
8685
|
+
await this.refreshAccessToken();
|
|
8686
|
+
console.log("[WeCom] Channel started");
|
|
8687
|
+
}
|
|
8688
|
+
async stop() {
|
|
8689
|
+
this.accessToken = null;
|
|
8690
|
+
this.responseBuffers.clear();
|
|
8691
|
+
console.log("[WeCom] Channel stopped");
|
|
8692
|
+
}
|
|
8693
|
+
/**
|
|
8694
|
+
* Verify URL callback (for WeCom verification)
|
|
8695
|
+
*/
|
|
8696
|
+
verifyUrl(signature, timestamp, nonce, echostr) {
|
|
8697
|
+
if (this.crypto.verifySignature(signature, timestamp, nonce, echostr)) {
|
|
8698
|
+
return this.crypto.decrypt(echostr);
|
|
8699
|
+
}
|
|
8700
|
+
return null;
|
|
8701
|
+
}
|
|
8702
|
+
/**
|
|
8703
|
+
* Parse XML webhook payload
|
|
8704
|
+
*/
|
|
8705
|
+
parseWebhook(xmlContent, encrypted) {
|
|
8706
|
+
const decrypted = this.crypto.decrypt(encrypted);
|
|
8707
|
+
const getTag = (xml, tag) => {
|
|
8708
|
+
const match = xml.match(new RegExp(`<${tag}><\\!\\[CDATA\\[(.+?)\\]\\]></${tag}>`));
|
|
8709
|
+
return match ? match[1] : "";
|
|
8710
|
+
};
|
|
8711
|
+
const fromUser = getTag(decrypted, "FromUserName");
|
|
8712
|
+
const content = getTag(decrypted, "Content");
|
|
8713
|
+
const msgId = getTag(decrypted, "MsgId");
|
|
8714
|
+
if (!content) return null;
|
|
8715
|
+
return {
|
|
8716
|
+
id: msgId || crypto2.randomUUID(),
|
|
8717
|
+
source: this.id,
|
|
8718
|
+
userId: fromUser,
|
|
8719
|
+
conversationId: fromUser,
|
|
8720
|
+
// Use user ID as conversation ID
|
|
8721
|
+
content,
|
|
8722
|
+
metadata: {
|
|
8723
|
+
corpId: this.config.corpId,
|
|
8724
|
+
agentId: this.config.agentId
|
|
8725
|
+
}
|
|
8726
|
+
};
|
|
8727
|
+
}
|
|
8728
|
+
async handleMessage(message) {
|
|
8729
|
+
this.responseBuffers.set(message.conversationId, "");
|
|
8730
|
+
}
|
|
8731
|
+
async stream(conversationId, event) {
|
|
8732
|
+
if (event.type === "text-delta") {
|
|
8733
|
+
const current = this.responseBuffers.get(conversationId) || "";
|
|
8734
|
+
this.responseBuffers.set(conversationId, current + event.content);
|
|
8735
|
+
} else if (event.type === "done") {
|
|
8736
|
+
const text = this.responseBuffers.get(conversationId) || "";
|
|
8737
|
+
await this.sendMessage(conversationId, text);
|
|
8738
|
+
this.responseBuffers.delete(conversationId);
|
|
8739
|
+
} else if (event.type === "error") {
|
|
8740
|
+
await this.sendMessage(conversationId, `Error: ${event.error}`);
|
|
8741
|
+
this.responseBuffers.delete(conversationId);
|
|
8742
|
+
}
|
|
8743
|
+
}
|
|
8744
|
+
/**
|
|
8745
|
+
* Send message via WeCom API
|
|
8746
|
+
*/
|
|
8747
|
+
async sendMessage(userId, content) {
|
|
8748
|
+
const token = await this.getAccessToken();
|
|
8749
|
+
const url = `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${token}`;
|
|
8750
|
+
try {
|
|
8751
|
+
const response = await fetch(url, {
|
|
8752
|
+
method: "POST",
|
|
8753
|
+
headers: { "Content-Type": "application/json" },
|
|
8754
|
+
body: JSON.stringify({
|
|
8755
|
+
touser: userId,
|
|
8756
|
+
msgtype: "markdown",
|
|
8757
|
+
agentid: this.config.agentId,
|
|
8758
|
+
markdown: { content }
|
|
8759
|
+
})
|
|
8760
|
+
});
|
|
8761
|
+
const result = await response.json();
|
|
8762
|
+
if (result.errcode !== 0) {
|
|
8763
|
+
console.error("[WeCom] Send message failed:", result);
|
|
8764
|
+
}
|
|
8765
|
+
} catch (error) {
|
|
8766
|
+
console.error("[WeCom] Send message error:", error);
|
|
8767
|
+
}
|
|
8768
|
+
}
|
|
8769
|
+
/**
|
|
8770
|
+
* Get access token (with caching)
|
|
8771
|
+
*/
|
|
8772
|
+
async getAccessToken() {
|
|
8773
|
+
if (this.accessToken && Date.now() < this.tokenExpiry) {
|
|
8774
|
+
return this.accessToken;
|
|
8775
|
+
}
|
|
8776
|
+
await this.refreshAccessToken();
|
|
8777
|
+
return this.accessToken;
|
|
8778
|
+
}
|
|
8779
|
+
/**
|
|
8780
|
+
* Refresh access token from WeCom API
|
|
8781
|
+
*/
|
|
8782
|
+
async refreshAccessToken() {
|
|
8783
|
+
const url = `https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${this.config.corpId}&corpsecret=${this.config.secret}`;
|
|
8784
|
+
try {
|
|
8785
|
+
const response = await fetch(url);
|
|
8786
|
+
const result = await response.json();
|
|
8787
|
+
if (result.errcode === 0) {
|
|
8788
|
+
this.accessToken = result.access_token;
|
|
8789
|
+
this.tokenExpiry = Date.now() + (result.expires_in - 300) * 1e3;
|
|
8790
|
+
} else {
|
|
8791
|
+
console.error("[WeCom] Get token failed:", result);
|
|
8792
|
+
}
|
|
8793
|
+
} catch (error) {
|
|
8794
|
+
console.error("[WeCom] Get token error:", error);
|
|
8795
|
+
}
|
|
8796
|
+
}
|
|
8797
|
+
};
|
|
8798
|
+
}
|
|
8799
|
+
});
|
|
8800
|
+
|
|
8801
|
+
// src/cli/index.ts
|
|
8802
|
+
import "dotenv/config";
|
|
8803
|
+
import { program } from "commander";
|
|
8804
|
+
import * as os2 from "os";
|
|
8805
|
+
import * as fs7 from "fs/promises";
|
|
8806
|
+
import * as path13 from "path";
|
|
8807
|
+
var VERSION = "1.0.0";
|
|
8808
|
+
program.name("openviber").description("OpenViber - Workspace-first assistant runtime (vibers on your machines)").version(VERSION);
|
|
8809
|
+
program.command("start").description(
|
|
8810
|
+
"Start viber with all apps (local mode, or connect to server with --server)"
|
|
8811
|
+
).option("-s, --server <url>", "Command center URL (enables connected mode)").option("-t, --token <token>", "Authentication token (or set VIBER_TOKEN)").option("-n, --name <name>", "Viber name", `${os2.hostname()}-viber`).option("--desktop", "Enable desktop control (UI-TARS)").option("--disable-app <apps...>", "Disable specific apps (comma-separated)").option("--no-apps", "Disable all apps").option("--reconnect-interval <ms>", "Reconnect interval in ms", "5000").option("--heartbeat-interval <ms>", "Heartbeat interval in ms", "30000").action(async (options) => {
|
|
8812
|
+
const { JobScheduler: JobScheduler2 } = await Promise.resolve().then(() => (init_scheduler(), scheduler_exports));
|
|
8813
|
+
const { ViberController: ViberController2 } = await Promise.resolve().then(() => (init_controller(), controller_exports));
|
|
8814
|
+
const { EventEmitter: EventEmitter4 } = await import("events");
|
|
8815
|
+
await Promise.resolve().then(() => (init_skills(), skills_exports));
|
|
8816
|
+
const viberId = await getViberId();
|
|
8817
|
+
const token = options.token || process.env.VIBER_TOKEN;
|
|
8818
|
+
const connectToServer = options.server && token;
|
|
8819
|
+
if (options.server && !token) {
|
|
8820
|
+
console.error(
|
|
8821
|
+
"Error: Authentication token required when using --server."
|
|
8822
|
+
);
|
|
8823
|
+
console.error(
|
|
8824
|
+
"Use --token <token> or set VIBER_TOKEN environment variable."
|
|
6371
8825
|
);
|
|
6372
8826
|
console.error("\nTo get a token, run: viber login");
|
|
6373
8827
|
process.exit(1);
|
|
@@ -6533,6 +8987,166 @@ Viber Status
|
|
|
6533
8987
|
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
6534
8988
|
`);
|
|
6535
8989
|
});
|
|
8990
|
+
program.command("onboard").description("Initialize OpenViber configuration (first-time setup)").action(async () => {
|
|
8991
|
+
const configDir = path13.join(os2.homedir(), ".openviber");
|
|
8992
|
+
const agentsDir = path13.join(configDir, "agents");
|
|
8993
|
+
const jobsDir = path13.join(configDir, "jobs");
|
|
8994
|
+
const spaceDir = path13.join(configDir, "space");
|
|
8995
|
+
console.log(`
|
|
8996
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
8997
|
+
\u2551 OPENVIBER SETUP \u2551
|
|
8998
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
8999
|
+
`);
|
|
9000
|
+
console.log("Creating directories...");
|
|
9001
|
+
await fs7.mkdir(configDir, { recursive: true });
|
|
9002
|
+
await fs7.mkdir(agentsDir, { recursive: true });
|
|
9003
|
+
await fs7.mkdir(jobsDir, { recursive: true });
|
|
9004
|
+
await fs7.mkdir(spaceDir, { recursive: true });
|
|
9005
|
+
console.log(` \u2713 ${configDir}`);
|
|
9006
|
+
console.log(` \u2713 ${agentsDir}`);
|
|
9007
|
+
console.log(` \u2713 ${jobsDir}`);
|
|
9008
|
+
console.log(` \u2713 ${spaceDir}`);
|
|
9009
|
+
const defaultAgentPath = path13.join(agentsDir, "default.yaml");
|
|
9010
|
+
try {
|
|
9011
|
+
await fs7.access(defaultAgentPath);
|
|
9012
|
+
console.log(`
|
|
9013
|
+
\u23ED agents/default.yaml already exists, skipping`);
|
|
9014
|
+
} catch {
|
|
9015
|
+
const defaultAgent = `# Default Viber Agent Configuration
|
|
9016
|
+
name: default
|
|
9017
|
+
description: General-purpose assistant
|
|
9018
|
+
|
|
9019
|
+
# LLM Provider (openrouter recommended for multi-model access)
|
|
9020
|
+
provider: openrouter
|
|
9021
|
+
model: anthropic/claude-sonnet-4-20250514
|
|
9022
|
+
|
|
9023
|
+
# System prompt
|
|
9024
|
+
systemPrompt: |
|
|
9025
|
+
You are a helpful AI assistant running on the user's local machine.
|
|
9026
|
+
You have access to files, terminal, and browser tools.
|
|
9027
|
+
Be concise and helpful.
|
|
9028
|
+
|
|
9029
|
+
# Tools available to this agent
|
|
9030
|
+
tools:
|
|
9031
|
+
- file
|
|
9032
|
+
- terminal
|
|
9033
|
+
- browser
|
|
9034
|
+
|
|
9035
|
+
# Working mode: "always-ask" | "agent-decides" | "always-execute"
|
|
9036
|
+
workingMode: agent-decides
|
|
9037
|
+
`;
|
|
9038
|
+
await fs7.writeFile(defaultAgentPath, defaultAgent);
|
|
9039
|
+
console.log(`
|
|
9040
|
+
\u2713 Created agents/default.yaml`);
|
|
9041
|
+
}
|
|
9042
|
+
const taskPath = path13.join(spaceDir, "task.md");
|
|
9043
|
+
try {
|
|
9044
|
+
await fs7.access(taskPath);
|
|
9045
|
+
} catch {
|
|
9046
|
+
await fs7.writeFile(taskPath, "# Current Task\n\nNo active task.\n");
|
|
9047
|
+
console.log(` \u2713 Created space/task.md`);
|
|
9048
|
+
}
|
|
9049
|
+
const memoryPath = path13.join(spaceDir, "MEMORY.md");
|
|
9050
|
+
try {
|
|
9051
|
+
await fs7.access(memoryPath);
|
|
9052
|
+
} catch {
|
|
9053
|
+
await fs7.writeFile(memoryPath, "# Memory\n\nLong-term notes and context.\n");
|
|
9054
|
+
console.log(` \u2713 Created space/MEMORY.md`);
|
|
9055
|
+
}
|
|
9056
|
+
const viberId = await getViberId();
|
|
9057
|
+
console.log(`
|
|
9058
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
9059
|
+
Setup complete!
|
|
9060
|
+
|
|
9061
|
+
Your viber ID: ${viberId}
|
|
9062
|
+
Config directory: ${configDir}
|
|
9063
|
+
|
|
9064
|
+
Next steps:
|
|
9065
|
+
1. Set your API key:
|
|
9066
|
+
export OPENROUTER_API_KEY="sk-or-v1-xxx"
|
|
9067
|
+
|
|
9068
|
+
2. Start your viber:
|
|
9069
|
+
openviber start
|
|
9070
|
+
|
|
9071
|
+
3. Or run a quick task:
|
|
9072
|
+
openviber run "Hello, what can you do?"
|
|
9073
|
+
|
|
9074
|
+
Get an API key at: https://openrouter.ai/keys
|
|
9075
|
+
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
9076
|
+
`);
|
|
9077
|
+
});
|
|
9078
|
+
program.command("gateway").description("Start the enterprise channel gateway (DingTalk, WeCom, etc.)").option("-p, --port <port>", "Gateway port", "6009").action(async (options) => {
|
|
9079
|
+
const { channelManager: channelManager2 } = await Promise.resolve().then(() => (init_manager(), manager_exports));
|
|
9080
|
+
const { DingTalkChannel: DingTalkChannel2 } = await Promise.resolve().then(() => (init_dingtalk(), dingtalk_exports));
|
|
9081
|
+
const { WeComChannel: WeComChannel2 } = await Promise.resolve().then(() => (init_wecom(), wecom_exports));
|
|
9082
|
+
console.log(`
|
|
9083
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
9084
|
+
\u2551 GATEWAY STARTING \u2551
|
|
9085
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
9086
|
+
`);
|
|
9087
|
+
const channels = [];
|
|
9088
|
+
if (process.env.DINGTALK_APP_KEY && process.env.DINGTALK_APP_SECRET) {
|
|
9089
|
+
channelManager2.register(
|
|
9090
|
+
new DingTalkChannel2({
|
|
9091
|
+
enabled: true,
|
|
9092
|
+
appKey: process.env.DINGTALK_APP_KEY,
|
|
9093
|
+
appSecret: process.env.DINGTALK_APP_SECRET,
|
|
9094
|
+
robotCode: process.env.DINGTALK_ROBOT_CODE
|
|
9095
|
+
})
|
|
9096
|
+
);
|
|
9097
|
+
channels.push("DingTalk");
|
|
9098
|
+
}
|
|
9099
|
+
if (process.env.WECOM_CORP_ID && process.env.WECOM_AGENT_SECRET && process.env.WECOM_TOKEN && process.env.WECOM_ENCODING_AES_KEY) {
|
|
9100
|
+
channelManager2.register(
|
|
9101
|
+
new WeComChannel2({
|
|
9102
|
+
enabled: true,
|
|
9103
|
+
corpId: process.env.WECOM_CORP_ID,
|
|
9104
|
+
agentId: process.env.WECOM_AGENT_ID || "0",
|
|
9105
|
+
secret: process.env.WECOM_AGENT_SECRET,
|
|
9106
|
+
token: process.env.WECOM_TOKEN,
|
|
9107
|
+
aesKey: process.env.WECOM_ENCODING_AES_KEY
|
|
9108
|
+
})
|
|
9109
|
+
);
|
|
9110
|
+
channels.push("WeCom");
|
|
9111
|
+
}
|
|
9112
|
+
if (channels.length === 0) {
|
|
9113
|
+
console.log(`
|
|
9114
|
+
No channels configured. Set environment variables to enable channels:
|
|
9115
|
+
|
|
9116
|
+
DingTalk:
|
|
9117
|
+
DINGTALK_APP_KEY, DINGTALK_APP_SECRET, DINGTALK_ROBOT_CODE
|
|
9118
|
+
|
|
9119
|
+
WeCom:
|
|
9120
|
+
WECOM_CORP_ID, WECOM_AGENT_ID, WECOM_AGENT_SECRET
|
|
9121
|
+
WECOM_TOKEN, WECOM_ENCODING_AES_KEY (optional)
|
|
9122
|
+
|
|
9123
|
+
Run 'openviber gateway' again after setting environment variables.
|
|
9124
|
+
`);
|
|
9125
|
+
process.exit(1);
|
|
9126
|
+
}
|
|
9127
|
+
await channelManager2.startAll();
|
|
9128
|
+
process.on("SIGINT", async () => {
|
|
9129
|
+
console.log("\n[Gateway] Shutting down...");
|
|
9130
|
+
await channelManager2.stopAll();
|
|
9131
|
+
process.exit(0);
|
|
9132
|
+
});
|
|
9133
|
+
process.on("SIGTERM", async () => {
|
|
9134
|
+
console.log("\n[Gateway] Shutting down...");
|
|
9135
|
+
await channelManager2.stopAll();
|
|
9136
|
+
process.exit(0);
|
|
9137
|
+
});
|
|
9138
|
+
console.log(`
|
|
9139
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
9140
|
+
\u2551 GATEWAY RUNNING \u2551
|
|
9141
|
+
\u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563
|
|
9142
|
+
\u2551 Channels: ${channels.join(", ").padEnd(41)}\u2551
|
|
9143
|
+
\u2551 Status: \u25CF Ready \u2551
|
|
9144
|
+
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
9145
|
+
|
|
9146
|
+
Listening for messages from enterprise channels...
|
|
9147
|
+
Press Ctrl+C to stop.
|
|
9148
|
+
`);
|
|
9149
|
+
});
|
|
6536
9150
|
async function getViberId() {
|
|
6537
9151
|
const configDir = path13.join(os2.homedir(), ".openviber");
|
|
6538
9152
|
const idFile = path13.join(configDir, "viber-id");
|