u-foo 1.2.12 → 1.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/chat/index.js CHANGED
@@ -34,7 +34,6 @@ const { createInputListenerController } = require("./inputListenerController");
34
34
  const { createDaemonMessageRouter } = require("./daemonMessageRouter");
35
35
  const { createChatLogController } = require("./chatLogController");
36
36
  const { createPasteController } = require("./pasteController");
37
- const { createCronScheduler } = require("./cronScheduler");
38
37
  const { createAgentViewController } = require("./agentViewController");
39
38
  const { createSettingsController } = require("./settingsController");
40
39
  const { createChatLayout } = require("./layout");
@@ -87,12 +86,7 @@ async function runChat(projectRoot) {
87
86
  let agentProvider = config.agentProvider;
88
87
  let assistantEngine = normalizeAssistantEngine(config.assistantEngine);
89
88
  let autoResume = config.autoResume !== false;
90
- let cronScheduler = {
91
- addTask: () => null,
92
- listTasks: () => [],
93
- stopTask: () => false,
94
- stopAll: () => 0,
95
- };
89
+ let cronTasks = [];
96
90
 
97
91
  // Dynamic input height settings
98
92
  // Layout: topLine(1) + content + bottomLine(1) + dashboard(1)
@@ -319,7 +313,6 @@ async function runChat(projectRoot) {
319
313
  if (daemonCoordinator) {
320
314
  daemonCoordinator.markExit();
321
315
  }
322
- cronScheduler.stopAll();
323
316
  exitAgentView();
324
317
  if (screen && screen.program && typeof screen.program.decrst === "function") {
325
318
  screen.program.decrst(2004);
@@ -724,21 +717,6 @@ async function runChat(projectRoot) {
724
717
  daemonCoordinator.send(req);
725
718
  }
726
719
 
727
- cronScheduler = createCronScheduler({
728
- dispatch: ({ taskId, target, message }) => {
729
- send({
730
- type: IPC_REQUEST_TYPES.BUS_SEND,
731
- target,
732
- message,
733
- });
734
- queueStatusLine(`cron:${taskId} -> ${target}`);
735
- },
736
- onChange: () => {
737
- renderDashboard();
738
- screen.render();
739
- },
740
- });
741
-
742
720
  function updatePromptBox() {
743
721
  if (targetAgent) {
744
722
  const label = getAgentLabel(targetAgent);
@@ -907,7 +885,7 @@ async function runChat(projectRoot) {
907
885
  selectedProviderIndex,
908
886
  selectedAssistantIndex,
909
887
  selectedResumeIndex,
910
- cronTasks: cronScheduler.listTasks(),
888
+ cronTasks,
911
889
  providerOptions,
912
890
  assistantOptions,
913
891
  resumeOptions,
@@ -923,6 +901,7 @@ async function runChat(projectRoot) {
923
901
  reportPendingTotal = Number.isFinite(status?.reports?.pending_total)
924
902
  ? status.reports.pending_total
925
903
  : 0;
904
+ cronTasks = Array.isArray(status?.cron?.tasks) ? status.cron.tasks : [];
926
905
  const metaList = Array.isArray(status.active_meta) ? status.active_meta : [];
927
906
  let fallbackMap = null;
928
907
  if (metaList.length === 0 && activeAgents.length > 0) {
@@ -1018,7 +997,7 @@ async function runChat(projectRoot) {
1018
997
  agentProvider: { get: () => agentProvider },
1019
998
  assistantEngine: { get: () => assistantEngine },
1020
999
  autoResume: { get: () => autoResume },
1021
- cronTasks: { get: () => cronScheduler.listTasks() },
1000
+ cronTasks: { get: () => cronTasks },
1022
1001
  providerOptions: { get: () => providerOptions },
1023
1002
  assistantOptions: { get: () => assistantOptions },
1024
1003
  resumeOptions: { get: () => resumeOptions },
@@ -1225,10 +1204,12 @@ async function runChat(projectRoot) {
1225
1204
  restartDaemon,
1226
1205
  send,
1227
1206
  requestStatus,
1228
- createCronTask: ({ intervalMs, targets, prompt }) =>
1229
- cronScheduler.addTask({ intervalMs, targets, prompt }),
1230
- listCronTasks: () => cronScheduler.listTasks(),
1231
- stopCronTask: (id) => cronScheduler.stopTask(id),
1207
+ requestCron: (payload = {}) => {
1208
+ send({
1209
+ type: IPC_REQUEST_TYPES.CRON,
1210
+ ...payload,
1211
+ });
1212
+ },
1232
1213
  activateAgent: async (target) => {
1233
1214
  const activator = new AgentActivator(projectRoot);
1234
1215
  await activator.activate(target);
@@ -65,7 +65,7 @@ async function runOnlineToken(subscriber, opts = {}) {
65
65
  const { generateToken, setToken, defaultTokensPath } = require("../online/tokens");
66
66
  const filePath = opts.file || defaultTokensPath();
67
67
  const token = generateToken();
68
- const entry = setToken(filePath, subscriber, token, opts.server || "", {
68
+ const entry = setToken(filePath, subscriber, token, opts.server || "https://online.ufoo.dev", {
69
69
  nickname: opts.nickname || "",
70
70
  });
71
71
  console.log(JSON.stringify({
@@ -79,7 +79,7 @@ async function runOnlineToken(subscriber, opts = {}) {
79
79
  }
80
80
 
81
81
  async function runOnlineRoom(action, opts = {}, onlineAuthHeaders) {
82
- const base = opts.server || "http://127.0.0.1:8787";
82
+ const base = opts.server || "https://online.ufoo.dev";
83
83
  const endpoint = `${base.replace(/\/$/, "")}/ufoo/online/rooms`;
84
84
  const authHeaders = buildAuthHeaders(onlineAuthHeaders, {
85
85
  authToken: opts.authToken,
@@ -116,7 +116,7 @@ async function runOnlineRoom(action, opts = {}, onlineAuthHeaders) {
116
116
  }
117
117
 
118
118
  async function runOnlineChannel(action, opts = {}, onlineAuthHeaders) {
119
- const base = opts.server || "http://127.0.0.1:8787";
119
+ const base = opts.server || "https://online.ufoo.dev";
120
120
  const endpoint = `${base.replace(/\/$/, "")}/ufoo/online/channels`;
121
121
  const authHeaders = buildAuthHeaders(onlineAuthHeaders, {
122
122
  authToken: opts.authToken,
@@ -160,7 +160,7 @@ async function runOnlineConnect(opts = {}) {
160
160
  projectRoot: opts.projectRoot || process.cwd(),
161
161
  nickname: opts.nickname,
162
162
  subscriberId: opts.subscriber || "",
163
- url: opts.url || "ws://127.0.0.1:8787/ufoo/online",
163
+ url: opts.url || "wss://online.ufoo.dev/ufoo/online",
164
164
  token: opts.token || "",
165
165
  tokenHash: opts.tokenHash || "",
166
166
  tokenFile: opts.tokenFile || "",
@@ -220,12 +220,12 @@ async function runOnlineCommand(subcmd, payload = {}, options = {}) {
220
220
  case "token":
221
221
  return runOnlineToken(payload.subscriber, {
222
222
  nickname: opts.nickname || "",
223
- server: opts.server || "",
223
+ server: opts.server || "https://online.ufoo.dev",
224
224
  file: opts.file || "",
225
225
  });
226
226
  case "room":
227
227
  return runOnlineRoom(payload.action, {
228
- server: opts.server || "http://127.0.0.1:8787",
228
+ server: opts.server || "https://online.ufoo.dev",
229
229
  authToken: opts.authToken || "",
230
230
  tokenFile: opts.tokenFile || "",
231
231
  subscriber: opts.subscriber || "",
@@ -236,7 +236,7 @@ async function runOnlineCommand(subcmd, payload = {}, options = {}) {
236
236
  }, onlineAuthHeaders);
237
237
  case "channel":
238
238
  return runOnlineChannel(payload.action, {
239
- server: opts.server || "http://127.0.0.1:8787",
239
+ server: opts.server || "https://online.ufoo.dev",
240
240
  authToken: opts.authToken || "",
241
241
  tokenFile: opts.tokenFile || "",
242
242
  subscriber: opts.subscriber || "",
@@ -297,14 +297,14 @@ async function runOnlineCommand(subcmd, payload = {}, options = {}) {
297
297
  const subscriber = argv[1];
298
298
  return runOnlineToken(subscriber, {
299
299
  nickname: getFallbackOpt(argv, "--nickname"),
300
- server: getFallbackOpt(argv, "--server"),
300
+ server: getFallbackOpt(argv, "--server") || "https://online.ufoo.dev",
301
301
  file: getFallbackOpt(argv, "--file"),
302
302
  });
303
303
  }
304
304
  case "room": {
305
305
  const action = argv[1] || "";
306
306
  return runOnlineRoom(action, {
307
- server: getFallbackOpt(argv, "--server") || "http://127.0.0.1:8787",
307
+ server: getFallbackOpt(argv, "--server") || "https://online.ufoo.dev",
308
308
  authToken: getFallbackOpt(argv, "--auth-token"),
309
309
  tokenFile: getFallbackOpt(argv, "--token-file"),
310
310
  subscriber: getFallbackOpt(argv, "--subscriber"),
@@ -318,7 +318,7 @@ async function runOnlineCommand(subcmd, payload = {}, options = {}) {
318
318
  const action = argv[1] || "";
319
319
  const defaultChannelType = options.defaultChannelType || "public";
320
320
  return runOnlineChannel(action, {
321
- server: getFallbackOpt(argv, "--server") || "http://127.0.0.1:8787",
321
+ server: getFallbackOpt(argv, "--server") || "https://online.ufoo.dev",
322
322
  authToken: getFallbackOpt(argv, "--auth-token"),
323
323
  tokenFile: getFallbackOpt(argv, "--token-file"),
324
324
  subscriber: getFallbackOpt(argv, "--subscriber"),
@@ -339,7 +339,7 @@ async function runOnlineCommand(subcmd, payload = {}, options = {}) {
339
339
  projectRoot: options.projectRoot || process.cwd(),
340
340
  nickname: getFallbackOpt(argv, "--nickname"),
341
341
  subscriber: getFallbackOpt(argv, "--subscriber"),
342
- url: getFallbackOpt(argv, "--url") || "ws://127.0.0.1:8787/ufoo/online",
342
+ url: getFallbackOpt(argv, "--url") || "wss://online.ufoo.dev/ufoo/online",
343
343
  token: getFallbackOpt(argv, "--token"),
344
344
  tokenHash: getFallbackOpt(argv, "--token-hash"),
345
345
  tokenFile: getFallbackOpt(argv, "--token-file"),
package/src/cli.js CHANGED
@@ -247,6 +247,45 @@ async function runCli(argv) {
247
247
  const repoRoot = getPackageRoot();
248
248
  run(process.execPath, [path.join(repoRoot, "bin", "ufoo.js"), "chat"]);
249
249
  });
250
+ program
251
+ .command("launch")
252
+ .description("Launch an agent (ucode, uclaude, ucodex)")
253
+ .argument("<agent>", "Agent type: ucode|uclaude|ucodex|claude|codex")
254
+ .argument("[nickname]", "Optional nickname for the agent")
255
+ .action(async (agent, nickname) => {
256
+ try {
257
+ const projectRoot = process.cwd();
258
+ await ensureDaemonRunning(projectRoot);
259
+
260
+ // Normalize agent type
261
+ const agentLower = agent.toLowerCase();
262
+ let normalizedAgent = "";
263
+ if (agentLower === "ucode" || agentLower === "ufoo-code" || agentLower === "ufoo") {
264
+ normalizedAgent = "ucode";
265
+ } else if (agentLower === "uclaude" || agentLower === "claude-code" || agentLower === "claude") {
266
+ normalizedAgent = "claude";
267
+ } else if (agentLower === "ucodex" || agentLower === "codex" || agentLower === "openai") {
268
+ normalizedAgent = "codex";
269
+ } else {
270
+ console.error(`Unknown agent type: ${agent}`);
271
+ console.error("Valid types: ucode, uclaude, ucodex, claude, codex");
272
+ process.exitCode = 1;
273
+ return;
274
+ }
275
+
276
+ const resp = await sendDaemonRequest(projectRoot, {
277
+ type: "launch_agent",
278
+ agent: normalizedAgent,
279
+ nickname: nickname || "",
280
+ count: 1,
281
+ });
282
+ const reply = resp?.data?.reply || `Launching ${normalizedAgent} agent...`;
283
+ console.log(reply);
284
+ } catch (err) {
285
+ console.error(err.message || String(err));
286
+ process.exitCode = 1;
287
+ }
288
+ });
250
289
  program
251
290
  .command("resume")
252
291
  .description("Resume agent sessions (optional nickname)")
@@ -550,7 +589,7 @@ async function runCli(argv) {
550
589
  .description("Generate and store a ufoo-online token")
551
590
  .argument("<subscriber>", "Subscriber ID (e.g., claude-code:abc123)")
552
591
  .option("--nickname <name>", "Nickname for this agent")
553
- .option("--server <url>", "Online server URL")
592
+ .option("--server <url>", "Online server URL", "https://online.ufoo.dev")
554
593
  .option("--file <path>", "Tokens file path")
555
594
  .action(async (subscriber, opts) => {
556
595
  try {
@@ -569,7 +608,7 @@ async function runCli(argv) {
569
608
  .command("room")
570
609
  .description("Manage online rooms (HTTP)")
571
610
  .argument("<action>", "create|list")
572
- .option("--server <url>", "Online server base URL (http://host:port)")
611
+ .option("--server <url>", "Online server base URL (default: https://online.ufoo.dev)")
573
612
  .option("--auth-token <token>", "Bearer token for HTTP auth (token or token_hash)")
574
613
  .option("--token-file <path>", "Token file path for auth lookup")
575
614
  .option("--subscriber <id>", "Subscriber ID to resolve token")
@@ -594,7 +633,7 @@ async function runCli(argv) {
594
633
  .command("channel")
595
634
  .description("Manage online channels (HTTP)")
596
635
  .argument("<action>", "create|list")
597
- .option("--server <url>", "Online server base URL (http://host:port)")
636
+ .option("--server <url>", "Online server base URL (default: https://online.ufoo.dev)")
598
637
  .option("--auth-token <token>", "Bearer token for HTTP auth (token or token_hash)")
599
638
  .option("--token-file <path>", "Token file path for auth lookup")
600
639
  .option("--subscriber <id>", "Subscriber ID to resolve token")
@@ -618,7 +657,7 @@ async function runCli(argv) {
618
657
  .command("connect")
619
658
  .description("Connect to ufoo-online relay (long-running)")
620
659
  .requiredOption("--nickname <name>", "Agent nickname")
621
- .option("--url <url>", "WebSocket URL", "ws://127.0.0.1:8787/ufoo/online")
660
+ .option("--url <url>", "WebSocket URL", "wss://online.ufoo.dev/ufoo/online")
622
661
  .option("--subscriber <id>", "Subscriber ID (auto-generated if omitted)")
623
662
  .option("--token <tok>", "Auth token")
624
663
  .option("--token-hash <hash>", "Auth token hash")
package/src/code/tui.js CHANGED
@@ -960,39 +960,63 @@ function runUcodeTui({
960
960
  autoBusError = "";
961
961
  return;
962
962
  }
963
- const ubusResult = await runUbusCommand(state, {
964
- workspaceRoot,
965
- subscriberId: autoBusSubscriberId,
966
- onMessageReceived: (msg) => {
967
- // Display the incoming message immediately
968
- const { extractAgentNickname } = require("./agent");
969
- const nickname = extractAgentNickname(msg.from) || msg.from;
970
- logText(`${nickname}: ${msg.task}`);
971
- },
963
+
964
+ // Set pending state for autoBus tasks
965
+ const abortController = new AbortController();
966
+ pendingTask = {
967
+ abortController,
968
+ startedAt: Date.now(),
969
+ };
970
+ updateStatus("Processing bus messages...", "thinking", {
971
+ showTimer: true,
972
+ startedAt: pendingTask.startedAt,
972
973
  });
973
- if (!ubusResult.ok) {
974
- const nextError = String(ubusResult.error || "ubus failed");
975
- if (nextError !== autoBusError) {
976
- autoBusError = nextError;
977
- logText(`Error: ${nextError}`);
978
- }
979
- return;
980
- }
981
- autoBusError = "";
982
- if (ubusResult.handled > 0) {
983
- // Display only the replies (tasks were already shown via onMessageReceived)
984
- if (ubusResult.messageExchanges && ubusResult.messageExchanges.length > 0) {
985
- const { extractAgentNickname } = require("./agent");
986
- for (const exchange of ubusResult.messageExchanges) {
987
- const nickname = extractAgentNickname(exchange.from) || exchange.from;
988
- // Only show the reply since task was already displayed
989
- logText(`@${nickname} ${exchange.reply}`);
974
+
975
+ try {
976
+ const ubusResult = await runUbusCommand(state, {
977
+ workspaceRoot,
978
+ subscriberId: autoBusSubscriberId,
979
+ onMessageReceived: (msg) => {
980
+ // Display the incoming message immediately
981
+ const { extractAgentNickname } = require("./agent");
982
+ const nickname = extractAgentNickname(msg.from) || msg.from;
983
+ logText(`${nickname}: ${msg.task}`);
984
+ // Update status to show we're working on this specific task
985
+ updateStatus("Working on task...", "thinking", {
986
+ showTimer: true,
987
+ startedAt: pendingTask.startedAt,
988
+ });
989
+ },
990
+ });
991
+
992
+ if (!ubusResult.ok) {
993
+ const nextError = String(ubusResult.error || "ubus failed");
994
+ if (nextError !== autoBusError) {
995
+ autoBusError = nextError;
996
+ logText(`Error: ${nextError}`);
990
997
  }
998
+ return;
991
999
  }
992
- const persisted = persistSessionState(state);
993
- if (!persisted || persisted.ok === false) {
994
- logText(`Error: failed to persist session ${state.sessionId}: ${(persisted && persisted.error) || "unknown error"}`);
1000
+ autoBusError = "";
1001
+ if (ubusResult.handled > 0) {
1002
+ // Display only the replies (tasks were already shown via onMessageReceived)
1003
+ if (ubusResult.messageExchanges && ubusResult.messageExchanges.length > 0) {
1004
+ const { extractAgentNickname } = require("./agent");
1005
+ for (const exchange of ubusResult.messageExchanges) {
1006
+ const nickname = extractAgentNickname(exchange.from) || exchange.from;
1007
+ // Only show the reply since task was already displayed
1008
+ logText(`@${nickname} ${exchange.reply}`);
1009
+ }
1010
+ }
1011
+ const persisted = persistSessionState(state);
1012
+ if (!persisted || persisted.ok === false) {
1013
+ logText(`Error: failed to persist session ${state.sessionId}: ${(persisted && persisted.error) || "unknown error"}`);
1014
+ }
995
1015
  }
1016
+ } finally {
1017
+ // Clear pending state
1018
+ pendingTask = null;
1019
+ updateStatus("", "none");
996
1020
  }
997
1021
  };
998
1022