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