heyatlas 1.2.2 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +292 -58
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -5496,7 +5496,7 @@ var DEFAULT_HOST = "agent.heyatlas.app";
5496
5496
  class AtlasTunnel {
5497
5497
  client = null;
5498
5498
  options;
5499
- agentId = "heyatlas-cli";
5499
+ agentId = "local";
5500
5500
  currentUserId = null;
5501
5501
  seenTaskIds = new Set;
5502
5502
  taskCallback = null;
@@ -52114,9 +52114,9 @@ _a153 = symbol153;
52114
52114
  // agents/acp-provider.ts
52115
52115
  var ACP_COMMANDS = {
52116
52116
  opencode: { command: "opencode", args: ["acp"] },
52117
- claude: { command: "claude-code-acp", args: [] },
52117
+ "claude-code": { command: "claude-code-acp", args: [] },
52118
52118
  goose: { command: "goose", args: ["acp"] },
52119
- gemini: { command: "gemini", args: ["--experimental-acp"] },
52119
+ "gemini-code": { command: "gemini", args: ["--experimental-acp"] },
52120
52120
  codex: { command: "npx", args: ["@zed-industries/codex-acp"] },
52121
52121
  kimi: { command: "kimi", args: ["--acp"] },
52122
52122
  vibe: { command: "vibe-acp", args: [] },
@@ -52199,17 +52199,259 @@ class ACPProviderAgent {
52199
52199
  }
52200
52200
  }
52201
52201
 
52202
+ // agents/config.ts
52203
+ function isHTTPAgent(agent) {
52204
+ return agent === "agent-smith-py";
52205
+ }
52206
+
52207
+ // agents/http-agent.ts
52208
+ import { spawn as spawn2 } from "child_process";
52209
+ var HTTP_AGENT_CONFIG = {
52210
+ "agent-smith-py": {
52211
+ command: "uv",
52212
+ args: ["run", "python", "main.py"],
52213
+ port: 3141,
52214
+ healthPath: "/health",
52215
+ taskPath: "/agents/agent-smith/text",
52216
+ streamPath: "/agents/agent-smith/stream",
52217
+ cwd: "desktop-sandbox/agent-smith-py"
52218
+ }
52219
+ };
52220
+
52221
+ class HTTPAgent {
52222
+ agentType;
52223
+ options;
52224
+ process = null;
52225
+ baseUrl;
52226
+ config;
52227
+ constructor(agentType, options = {}) {
52228
+ this.agentType = agentType;
52229
+ this.options = options;
52230
+ this.config = HTTP_AGENT_CONFIG[agentType];
52231
+ this.baseUrl = `http://localhost:${this.config.port}`;
52232
+ }
52233
+ get name() {
52234
+ return this.agentType;
52235
+ }
52236
+ get port() {
52237
+ return this.config.port;
52238
+ }
52239
+ async isAvailable() {
52240
+ try {
52241
+ const { promisify } = await import("node:util");
52242
+ const exec = promisify((await import("node:child_process")).exec);
52243
+ await exec("which uv || where uv");
52244
+ return true;
52245
+ } catch {
52246
+ return false;
52247
+ }
52248
+ }
52249
+ async start() {
52250
+ const env = {
52251
+ ...process.env,
52252
+ PYTHONUNBUFFERED: "1"
52253
+ };
52254
+ const cwd = this.config.cwd ? `${this.options.cwd || process.cwd()}/${this.config.cwd}` : this.options.cwd || process.cwd();
52255
+ this.process = spawn2(this.config.command, [...this.config.args, "--port", String(this.config.port)], {
52256
+ cwd,
52257
+ env,
52258
+ stdio: ["pipe", "pipe", "pipe"]
52259
+ });
52260
+ this.process.stdout?.on("data", (data) => {
52261
+ console.log(`[${this.agentType}] ${data.toString().trim()}`);
52262
+ });
52263
+ this.process.stderr?.on("data", (data) => {
52264
+ console.error(`[${this.agentType}] ${data.toString().trim()}`);
52265
+ });
52266
+ await this.waitForReady();
52267
+ }
52268
+ async waitForReady(maxAttempts = 30) {
52269
+ const delay2 = (ms) => new Promise((r) => setTimeout(r, ms));
52270
+ for (let i = 0;i < maxAttempts; i++) {
52271
+ try {
52272
+ const response = await fetch(`${this.baseUrl}${this.config.healthPath}`);
52273
+ if (response.ok) {
52274
+ return;
52275
+ }
52276
+ } catch {}
52277
+ await delay2(1000);
52278
+ }
52279
+ throw new Error(`Agent ${this.agentType} failed to start after ${maxAttempts}s`);
52280
+ }
52281
+ async executeTask(prompt, taskId) {
52282
+ const response = await fetch(`${this.baseUrl}${this.config.taskPath}`, {
52283
+ method: "POST",
52284
+ headers: { "Content-Type": "application/json" },
52285
+ body: JSON.stringify({ prompt, task_id: taskId })
52286
+ });
52287
+ if (!response.ok) {
52288
+ throw new Error(`Task failed: ${response.statusText}`);
52289
+ }
52290
+ const data = await response.json();
52291
+ return data;
52292
+ }
52293
+ async* executeTaskStream(prompt, taskId) {
52294
+ const response = await fetch(`${this.baseUrl}${this.config.streamPath}`, {
52295
+ method: "POST",
52296
+ headers: { "Content-Type": "application/json" },
52297
+ body: JSON.stringify({ prompt, task_id: taskId })
52298
+ });
52299
+ if (!response.ok) {
52300
+ throw new Error(`Stream failed: ${response.statusText}`);
52301
+ }
52302
+ const reader = response.body?.getReader();
52303
+ if (!reader) {
52304
+ throw new Error("No response body");
52305
+ }
52306
+ const decoder = new TextDecoder;
52307
+ let buffer = "";
52308
+ while (true) {
52309
+ const { done, value } = await reader.read();
52310
+ if (done)
52311
+ break;
52312
+ buffer += decoder.decode(value, { stream: true });
52313
+ const lines = buffer.split(`
52314
+ `);
52315
+ buffer = lines.pop() || "";
52316
+ for (const line of lines) {
52317
+ if (line.startsWith("data: ")) {
52318
+ try {
52319
+ const event = JSON.parse(line.slice(6));
52320
+ yield event;
52321
+ } catch {}
52322
+ }
52323
+ }
52324
+ }
52325
+ }
52326
+ stop() {
52327
+ if (this.process) {
52328
+ this.process.kill("SIGTERM");
52329
+ this.process = null;
52330
+ }
52331
+ }
52332
+ }
52333
+
52202
52334
  // commands/connect.ts
52203
52335
  async function connect(agentType, options = {}) {
52204
52336
  const credentials = await login();
52205
- if (!isACPAgent(agentType)) {
52206
- console.error(`Error: Agent '${agentType}' is not ACP-compatible`);
52207
- console.error(`Supported agents: opencode, claude, goose, gemini, codex, etc.`);
52337
+ if (isHTTPAgent(agentType)) {
52338
+ return connectHTTPAgent(agentType, credentials, options);
52339
+ }
52340
+ if (isACPAgent(agentType)) {
52341
+ return connectACPAgent(agentType, credentials, options);
52342
+ }
52343
+ console.error(`Error: Unknown agent type '${agentType}'`);
52344
+ process.exit(1);
52345
+ }
52346
+ async function connectHTTPAgent(agentType, credentials, options) {
52347
+ const agent = new HTTPAgent(agentType, { cwd: process.cwd() });
52348
+ const available = await agent.isAvailable();
52349
+ if (!available) {
52350
+ console.error(`Error: Python not found. Install Python 3.11+ to use ${agentType}`);
52351
+ process.exit(1);
52352
+ }
52353
+ console.log(`Agent: ${agentType} (HTTP mode)`);
52354
+ try {
52355
+ console.log("Starting agent server...");
52356
+ await agent.start();
52357
+ console.log(`Agent server running on port ${agent.port}`);
52358
+ } catch (error87) {
52359
+ console.error(`Failed to start agent: ${error87}`);
52208
52360
  process.exit(1);
52209
52361
  }
52210
- const agent = new ACPProviderAgent(agentType, {
52211
- cwd: process.cwd()
52362
+ const tunnel = new AtlasTunnel({
52363
+ host: process.env.ATLAS_AGENT_HOST || "agent.heyatlas.app",
52364
+ token: credentials.accessToken,
52365
+ interactive: true
52212
52366
  });
52367
+ tunnel.onNewTask(async (task) => {
52368
+ const prompt = task.description || "Hello";
52369
+ console.log(`Task: ${prompt.slice(0, 50)}...`);
52370
+ await tunnel.updateTask(task.id, { state: "in-progress" });
52371
+ await tunnel.appendContext(task.id, [
52372
+ {
52373
+ type: "message",
52374
+ timestamp: Date.now(),
52375
+ data: { role: "user", content: prompt }
52376
+ }
52377
+ ]);
52378
+ await tunnel.broadcastTaskEvent(task.id, {
52379
+ type: "workforce_event",
52380
+ timestamp: Date.now(),
52381
+ data: { event_type: "task_started", task_content: prompt }
52382
+ });
52383
+ try {
52384
+ let finalResult = "Task completed";
52385
+ let finalStatus = "completed";
52386
+ const parts = [];
52387
+ for await (const event of agent.executeTaskStream(prompt, task.id)) {
52388
+ await tunnel.broadcastTaskEvent(task.id, {
52389
+ type: "workforce_event",
52390
+ timestamp: Date.now(),
52391
+ data: event
52392
+ });
52393
+ if (event.event_type === "result") {
52394
+ finalResult = event.result || event.error || "Task completed";
52395
+ finalStatus = event.status || "completed";
52396
+ }
52397
+ if (event.event_type === "task_assigned") {
52398
+ console.log(` → Assigned to: ${event.worker_name?.slice(0, 40)}...`);
52399
+ } else if (event.event_type === "task_decomposed") {
52400
+ console.log(` → Decomposed into ${event.subtasks?.length || 0} subtasks`);
52401
+ }
52402
+ }
52403
+ await tunnel.appendContext(task.id, [
52404
+ {
52405
+ type: "ui_message",
52406
+ timestamp: Date.now(),
52407
+ data: {
52408
+ id: crypto.randomUUID(),
52409
+ role: "assistant",
52410
+ parts: [{ type: "text", text: finalResult }]
52411
+ }
52412
+ }
52413
+ ]);
52414
+ await tunnel.updateTask(task.id, {
52415
+ state: finalStatus === "completed" ? "completed" : "failed",
52416
+ result: finalResult
52417
+ });
52418
+ console.log(`Task ${finalStatus}`);
52419
+ } catch (error87) {
52420
+ console.error(`Task failed: ${error87}`);
52421
+ await tunnel.updateTask(task.id, {
52422
+ state: "failed",
52423
+ result: String(error87)
52424
+ });
52425
+ }
52426
+ });
52427
+ await tunnel.connect(credentials.userId, agentType);
52428
+ console.log("Tunnel established");
52429
+ const voiceUrl = `${process.env.HEYATLAS_API || "https://heyatlas.app"}/chat`;
52430
+ if (options.openBrowser !== false) {
52431
+ try {
52432
+ const { execSync } = await import("child_process");
52433
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? 'start ""' : "xdg-open";
52434
+ execSync(`${cmd} "${voiceUrl}"`, { stdio: "ignore" });
52435
+ } catch {}
52436
+ }
52437
+ console.log(`
52438
+ HeyAtlas connected to ${agentType}`);
52439
+ console.log(`Continue here: ${voiceUrl}`);
52440
+ console.log(`
52441
+ Press Ctrl+C to disconnect
52442
+ `);
52443
+ process.on("SIGINT", async () => {
52444
+ console.log(`
52445
+ Disconnecting...
52446
+ `);
52447
+ agent.stop();
52448
+ await tunnel.disconnect();
52449
+ process.exit(0);
52450
+ });
52451
+ await new Promise(() => {});
52452
+ }
52453
+ async function connectACPAgent(agentType, credentials, options) {
52454
+ const agent = new ACPProviderAgent(agentType, { cwd: process.cwd() });
52213
52455
  const available = await agent.isAvailable();
52214
52456
  if (!available) {
52215
52457
  const cmd = getACPCommand(agentType);
@@ -52220,7 +52462,7 @@ async function connect(agentType, options = {}) {
52220
52462
  console.log(`Agent: ${agentType} (via ACP AI Provider)`);
52221
52463
  try {
52222
52464
  await agent.init();
52223
- console.log(`ACP provider initialized`);
52465
+ console.log("ACP provider initialized");
52224
52466
  } catch (error87) {
52225
52467
  console.error(`Failed to initialize agent: ${error87}`);
52226
52468
  process.exit(1);
@@ -52269,49 +52511,56 @@ async function connect(agentType, options = {}) {
52269
52511
  text: "",
52270
52512
  state: "streaming"
52271
52513
  };
52272
- activeReasoningParts.set(chunk.id, reasoningPart);
52514
+ activeReasoningParts.set(chunk.id || "default", reasoningPart);
52273
52515
  parts.push(reasoningPart);
52274
52516
  break;
52275
52517
  }
52276
- case "reasoning-delta": {
52277
- const reasoningPart = activeReasoningParts.get(chunk.id);
52278
- if (reasoningPart && "text" in reasoningPart) {
52279
- reasoningPart.text += chunk.delta || "";
52518
+ case "reasoning-delta":
52519
+ case "reasoning": {
52520
+ const id = chunk.id || "default";
52521
+ let reasoningPart = activeReasoningParts.get(id);
52522
+ if (!reasoningPart) {
52523
+ reasoningPart = { type: "reasoning", text: "", state: "streaming" };
52524
+ activeReasoningParts.set(id, reasoningPart);
52525
+ parts.push(reasoningPart);
52280
52526
  }
52527
+ reasoningPart.text += chunk.delta || chunk.text || "";
52281
52528
  break;
52282
52529
  }
52283
52530
  case "reasoning-end": {
52284
- const reasoningPart = activeReasoningParts.get(chunk.id);
52531
+ const id = chunk.id || "default";
52532
+ const reasoningPart = activeReasoningParts.get(id);
52285
52533
  if (reasoningPart) {
52286
52534
  reasoningPart.state = "done";
52287
- activeReasoningParts.delete(chunk.id);
52535
+ activeReasoningParts.delete(id);
52288
52536
  }
52289
52537
  break;
52290
52538
  }
52291
- case "tool-input-available":
52539
+ case "tool-input-available": {
52540
+ const input = chunk.input;
52541
+ const realToolName = input?.toolName || chunk.toolName;
52542
+ const realArgs = input?.args || input || {};
52292
52543
  toolCalls.set(chunk.toolCallId, {
52293
52544
  type: "dynamic-tool",
52294
52545
  toolCallId: chunk.toolCallId,
52295
- toolName: chunk.toolName,
52546
+ toolName: realToolName,
52296
52547
  state: "input-available",
52297
- input: chunk.input
52548
+ input: realArgs
52298
52549
  });
52299
52550
  parts.push(toolCalls.get(chunk.toolCallId));
52300
52551
  break;
52301
- case "tool-output-available":
52552
+ }
52553
+ case "tool-output-available": {
52302
52554
  const existing = toolCalls.get(chunk.toolCallId);
52303
52555
  if (existing) {
52304
- const updated = {
52305
- ...existing,
52306
- state: "output-available",
52307
- output: chunk.output
52308
- };
52556
+ const updated = { ...existing, state: "output-available", output: chunk.output };
52309
52557
  toolCalls.set(chunk.toolCallId, updated);
52310
52558
  const idx = parts.findIndex((p) => p.type === "dynamic-tool" && p.toolCallId === chunk.toolCallId);
52311
52559
  if (idx >= 0)
52312
52560
  parts[idx] = updated;
52313
52561
  }
52314
52562
  break;
52563
+ }
52315
52564
  }
52316
52565
  }
52317
52566
  if (parts.length > 0) {
@@ -52319,29 +52568,19 @@ async function connect(agentType, options = {}) {
52319
52568
  {
52320
52569
  type: "ui_message",
52321
52570
  timestamp: Date.now(),
52322
- data: {
52323
- id: crypto.randomUUID(),
52324
- role: "assistant",
52325
- parts
52326
- }
52571
+ data: { id: crypto.randomUUID(), role: "assistant", parts }
52327
52572
  }
52328
52573
  ]);
52329
52574
  }
52330
- await tunnel.updateTask(task.id, {
52331
- state: "completed",
52332
- result: "end_turn"
52333
- });
52334
- console.log(`Task completed`);
52575
+ await tunnel.updateTask(task.id, { state: "completed", result: "end_turn" });
52576
+ console.log("Task completed");
52335
52577
  } catch (error87) {
52336
52578
  console.error(`Task failed: ${error87}`);
52337
- await tunnel.updateTask(task.id, {
52338
- state: "failed",
52339
- result: String(error87)
52340
- });
52579
+ await tunnel.updateTask(task.id, { state: "failed", result: String(error87) });
52341
52580
  }
52342
52581
  });
52343
52582
  await tunnel.connect(credentials.userId, agentType);
52344
- console.log(`Tunnel established`);
52583
+ console.log("Tunnel established");
52345
52584
  const voiceUrl = `${process.env.HEYATLAS_API || "https://heyatlas.app"}/chat`;
52346
52585
  if (options.openBrowser !== false) {
52347
52586
  try {
@@ -52386,10 +52625,7 @@ function buildPromptWithContext(task) {
52386
52625
  } else if (e.type === "message" && e.data) {
52387
52626
  const data = e.data;
52388
52627
  if (data.role && data.content) {
52389
- messages.push({
52390
- role: String(data.role),
52391
- content: String(data.content)
52392
- });
52628
+ messages.push({ role: String(data.role), content: String(data.content) });
52393
52629
  }
52394
52630
  } else if (e.role && e.content) {
52395
52631
  messages.push({ role: String(e.role), content: String(e.content) });
@@ -52421,11 +52657,11 @@ Please continue based on the above context.`;
52421
52657
  }
52422
52658
 
52423
52659
  // index.ts
52424
- var SUPPORTED_AGENTS = [
52660
+ var ACP_AGENTS = [
52425
52661
  "opencode",
52426
- "claude",
52662
+ "claude-code",
52427
52663
  "goose",
52428
- "gemini",
52664
+ "gemini-code",
52429
52665
  "codex",
52430
52666
  "kimi",
52431
52667
  "vibe",
@@ -52434,6 +52670,8 @@ var SUPPORTED_AGENTS = [
52434
52670
  "openhands",
52435
52671
  "cagent"
52436
52672
  ];
52673
+ var HTTP_AGENTS = ["agent-smith-py"];
52674
+ var SUPPORTED_AGENTS = [...ACP_AGENTS, ...HTTP_AGENTS];
52437
52675
  var { positionals, values } = parseArgs({
52438
52676
  args: process.argv.slice(2),
52439
52677
  options: {
@@ -52445,27 +52683,23 @@ var { positionals, values } = parseArgs({
52445
52683
  });
52446
52684
  function printHelp() {
52447
52685
  console.log(`
52448
- heyatlas - Tunnel local AI agents to the cloud via ACP
52686
+ heyatlas - Tunnel local AI agents to the cloud
52449
52687
 
52450
52688
  Usage:
52451
- heyatlas connect <agent> Connect agent to Atlas (via ACP protocol)
52689
+ heyatlas connect <agent> Connect agent to Atlas
52452
52690
 
52453
- ACP-Compatible Agents:
52454
- ${SUPPORTED_AGENTS.join(", ")}
52691
+ Supported Agents:
52692
+ ACP Agents: ${ACP_AGENTS.join(", ")}
52693
+ HTTP Agents: ${HTTP_AGENTS.join(", ")}
52455
52694
 
52456
52695
  Options:
52457
52696
  -h, --help Show this help message
52458
52697
  -v, --version Show version
52459
52698
  --no-browser Don't open browser automatically
52460
52699
 
52461
- Prerequisites:
52462
- - The agent must be installed and support ACP mode
52463
- - Agent-specific setup (API keys, etc.)
52464
-
52465
52700
  Examples:
52466
- heyatlas connect opencode Connect OpenCode via ACP
52467
- heyatlas connect goose Connect Goose via ACP
52468
- heyatlas connect claude Connect Claude Code via ACP
52701
+ heyatlas connect opencode Connect OpenCode via ACP
52702
+ heyatlas connect agent-smith-py Connect Agent Smith (CAMEL-AI workforce)
52469
52703
  `);
52470
52704
  }
52471
52705
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heyatlas",
3
- "version": "1.2.2",
3
+ "version": "1.3.3",
4
4
  "description": "Tunnel local AI agents to the cloud for voice-powered interactions",
5
5
  "author": "Bishwendu Kundu <bishwenduk029@gmail.com>",
6
6
  "license": "MIT",