heyatlas 1.9.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +81 -46
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -52209,19 +52209,14 @@ You coordinate the following specialized subagents — delegate to them via the
52209
52209
 
52210
52210
  - Always create a plan before executing
52211
52211
  - Update your plan as tasks progress
52212
- - Use absolute paths for all file operations
52212
+ - Use absolute paths for all file operations — save outputs under /home/user/output/
52213
52213
  - If you encounter auth barriers (logins, CAPTCHAs), ask the user to complete the manual step
52214
- - Provide a comprehensive summary when done
52215
-
52216
- ## Output Upload
52214
+ - **MANDATORY**: After ALL file-producing tasks, you MUST upload outputs to R2 before summarizing. This is not optional.
52217
52215
 
52218
- After completing a task that produces files, upload them to cloud storage using rclone:
52216
+ ## Output Upload (REQUIRED)
52219
52217
 
52220
- 1. Read \\\`/home/user/agents/task-meta.json\\\` to get the bucket name, userId, and taskId
52221
- 2. Upload all output files using: \\\`rclone copy <file-or-dir> r2:<bucket>/<userId>/<taskId>/\\\`
52222
- 3. Include the uploaded file paths in your summary
52218
+ You MUST upload every output file to cloud storage. Tasks are not complete without this step.
52223
52219
 
52224
- Example:
52225
52220
  \\\`\\\`\\\`bash
52226
52221
  # Read task metadata
52227
52222
  META=$(cat /home/user/agents/task-meta.json)
@@ -52230,12 +52225,14 @@ USER_ID=$(echo $META | jq -r '.userId')
52230
52225
  TASK_ID=$(echo $META | jq -r '.taskId')
52231
52226
  PUBLIC_URL=$(echo $META | jq -r '.publicUrl')
52232
52227
 
52233
- # Upload output files
52234
- rclone copy /home/user/output.docx r2:$BUCKET/$USER_ID/$TASK_ID/
52228
+ # Upload ALL output files
52229
+ rclone copy /home/user/output/ r2:$BUCKET/$USER_ID/$TASK_ID/
52230
+
52231
+ # Report public URLs for each file
52232
+ echo "File available at: $PUBLIC_URL/$USER_ID/$TASK_ID/<filename>"
52233
+ \\\`\\\`\\\`
52235
52234
 
52236
- # Share the public URL
52237
- echo "File available at: $PUBLIC_URL/$USER_ID/$TASK_ID/output.docx"
52238
- \\\`\\\`\\\``,
52235
+ Your final summary MUST include the public URL for every uploaded file.`,
52239
52236
  "smith-browser.md": `---
52240
52237
  description: Browser automation expert — navigates websites, fills forms, extracts data, and performs interactive web workflows
52241
52238
  mode: subagent
@@ -52355,7 +52352,7 @@ You are a Documentation Specialist responsible for creating, modifying, and mana
52355
52352
 
52356
52353
  - If no format is specified, create an HTML file
52357
52354
  - For data-heavy documents, generate charts using Python and embed them
52358
- - Use absolute paths for all file operations
52355
+ - Save ALL output files under /home/user/output/ (create directory if needed)
52359
52356
  - When complete, provide the file path and a summary
52360
52357
 
52361
52358
  ## Rules
@@ -52363,6 +52360,7 @@ You are a Documentation Specialist responsible for creating, modifying, and mana
52363
52360
  - Primary output should be files, not just text in response
52364
52361
  - Use bash tools for data processing, visualization, and file operations
52365
52362
  - For charts: write a Python script using plotly/matplotlib, execute it, save output as image
52363
+ - **ALWAYS save files to /home/user/output/** — this is required for upload to work
52366
52364
  - Provide a clear summary of work done and paths to created files`
52367
52365
  };
52368
52366
  var SMITH_OPENCODE_JSONC = `{
@@ -52662,7 +52660,7 @@ async function handleTask(task, agent, tunnel) {
52662
52660
  { type: "ui_message", timestamp: Date.now(), data: { id: crypto.randomUUID(), role: "assistant", parts } }
52663
52661
  ]);
52664
52662
  }
52665
- const outputs = extractOutputUrls(parts);
52663
+ const outputs = await uploadTaskOutputs(task.id);
52666
52664
  await tunnel.updateTask(task.id, {
52667
52665
  state: "completed",
52668
52666
  result: "end_turn",
@@ -52794,40 +52792,77 @@ Please continue based on the above context.`;
52794
52792
  }
52795
52793
  return { prompt, latestUserMessage };
52796
52794
  }
52797
- function extractOutputUrls(parts) {
52798
- const outputs = [];
52799
- const urlPattern = /https?:\/\/[^\s"'<>]+/g;
52800
- for (const part of parts) {
52801
- if (part.type === "text" && typeof part.text === "string") {
52802
- const matches = part.text.match(urlPattern);
52803
- if (!matches)
52804
- continue;
52805
- for (const url3 of matches) {
52806
- const filename = url3.split("/").pop() || "";
52807
- const ext = filename.includes(".") ? filename.split(".").pop()?.toLowerCase() : null;
52808
- if (!ext)
52795
+ async function uploadTaskOutputs(taskId) {
52796
+ const fs = await import("fs");
52797
+ const path = await import("path");
52798
+ const { execSync } = await import("child_process");
52799
+ const metaPath = "/home/user/agents/task-meta.json";
52800
+ if (!fs.existsSync(metaPath))
52801
+ return [];
52802
+ let meta3;
52803
+ try {
52804
+ meta3 = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
52805
+ } catch {
52806
+ return [];
52807
+ }
52808
+ if (!meta3.publicUrl || !meta3.bucket)
52809
+ return [];
52810
+ const outputFiles = [];
52811
+ const scanDirs = ["/home/user/output", "/home/user"];
52812
+ const skipDirs = new Set(["agents", ".opencode", ".heyatlas", ".config", ".npm", ".local", ".cache", "node_modules"]);
52813
+ const outputExts = new Set(["docx", "xlsx", "pptx", "pdf", "html", "csv", "json", "png", "jpg", "jpeg", "gif", "svg", "zip", "tar", "md", "txt"]);
52814
+ for (const dir of scanDirs) {
52815
+ if (!fs.existsSync(dir))
52816
+ continue;
52817
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
52818
+ for (const entry of entries) {
52819
+ if (entry.isDirectory() && dir === "/home/user") {
52820
+ if (skipDirs.has(entry.name) || entry.name.startsWith("."))
52809
52821
  continue;
52810
- const typeMap = {
52811
- pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
52812
- docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
52813
- xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
52814
- pdf: "application/pdf",
52815
- html: "text/html",
52816
- png: "image/png",
52817
- jpg: "image/jpeg",
52818
- jpeg: "image/jpeg",
52819
- csv: "text/csv",
52820
- json: "application/json"
52821
- };
52822
- outputs.push({
52823
- url: url3,
52824
- filename: decodeURIComponent(filename),
52825
- type: typeMap[ext] || undefined
52826
- });
52822
+ const subDir = path.join(dir, entry.name);
52823
+ const subEntries = fs.readdirSync(subDir, { withFileTypes: true });
52824
+ for (const sub of subEntries) {
52825
+ if (sub.isFile()) {
52826
+ const ext = sub.name.split(".").pop()?.toLowerCase();
52827
+ if (ext && outputExts.has(ext))
52828
+ outputFiles.push(path.join(subDir, sub.name));
52829
+ }
52830
+ }
52831
+ } else if (entry.isFile()) {
52832
+ const ext = entry.name.split(".").pop()?.toLowerCase();
52833
+ if (ext && outputExts.has(ext))
52834
+ outputFiles.push(path.join(dir, entry.name));
52827
52835
  }
52828
52836
  }
52829
52837
  }
52830
- return [...new Map(outputs.map((o) => [o.url, o])).values()];
52838
+ if (outputFiles.length === 0)
52839
+ return [];
52840
+ const r2Dest = `r2:${meta3.bucket}/${meta3.userId}/${meta3.taskId}`;
52841
+ const outputs = [];
52842
+ const typeMap = {
52843
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
52844
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
52845
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
52846
+ pdf: "application/pdf",
52847
+ html: "text/html",
52848
+ png: "image/png",
52849
+ jpg: "image/jpeg",
52850
+ jpeg: "image/jpeg",
52851
+ csv: "text/csv"
52852
+ };
52853
+ for (const filePath of outputFiles) {
52854
+ const filename = path.basename(filePath);
52855
+ try {
52856
+ execSync(`rclone copyto "${filePath}" "${r2Dest}/${filename}"`, { stdio: "pipe", timeout: 30000 });
52857
+ const ext = filename.split(".").pop()?.toLowerCase() || "";
52858
+ const publicUrl = `${meta3.publicUrl.replace(/\/$/, "")}/${meta3.userId}/${meta3.taskId}/${filename}`;
52859
+ outputs.push({ url: publicUrl, filename, type: typeMap[ext] });
52860
+ console.log(`Uploaded: ${filename}`);
52861
+ } catch (e) {
52862
+ console.error(`Failed to upload ${filename}:`, e instanceof Error ? e.message : e);
52863
+ }
52864
+ }
52865
+ return outputs;
52831
52866
  }
52832
52867
  function openBrowser(url3) {
52833
52868
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heyatlas",
3
- "version": "1.9.0",
3
+ "version": "1.9.1",
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",