openviber 0.4.3 → 0.5.0

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