augure 0.7.0 → 0.7.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/bin.js +314 -24
  2. package/package.json +11 -10
package/dist/bin.js CHANGED
@@ -223,7 +223,22 @@ var AppConfigSchema = z.object({
223
223
  password: z.string()
224
224
  })
225
225
  }).optional(),
226
- github: z.object({ token: z.string() }).optional()
226
+ github: z.object({ token: z.string() }).optional(),
227
+ browser: z.object({
228
+ provider: z.enum(["local", "browserbase"]),
229
+ browserbase: z.object({
230
+ apiKey: z.string().min(1),
231
+ projectId: z.string().optional()
232
+ }).optional(),
233
+ defaults: z.object({
234
+ timeout: z.number().int().positive().optional(),
235
+ headless: z.boolean().optional(),
236
+ viewport: z.object({
237
+ width: z.number().int().positive(),
238
+ height: z.number().int().positive()
239
+ }).optional()
240
+ }).optional()
241
+ }).optional()
227
242
  }),
228
243
  security: z.object({
229
244
  sandboxOnly: z.boolean(),
@@ -6345,6 +6360,248 @@ var githubTool = {
6345
6360
  }
6346
6361
  };
6347
6362
 
6363
+ // ../tools/dist/browser.js
6364
+ function createBrowserTool(manager) {
6365
+ return {
6366
+ name: "browser",
6367
+ description: "AI-powered browser automation. Open a session, then use natural language to interact with web pages. Actions: open (creates session), navigate, act (click/type/interact), extract (get structured data), observe (discover elements), screenshot, close. Use 'act' with natural language instructions instead of CSS selectors. Use 'extract' with an instruction describing what data to get. Always close sessions when done.",
6368
+ parameters: {
6369
+ type: "object",
6370
+ properties: {
6371
+ action: {
6372
+ type: "string",
6373
+ enum: ["open", "navigate", "act", "extract", "observe", "screenshot", "close"],
6374
+ description: "The browser action to perform"
6375
+ },
6376
+ session: {
6377
+ type: "string",
6378
+ description: "Session ID from 'open'. Required for all actions except 'open'."
6379
+ },
6380
+ url: {
6381
+ type: "string",
6382
+ description: "URL to navigate to. Used with 'open' and 'navigate'."
6383
+ },
6384
+ instruction: {
6385
+ type: "string",
6386
+ description: "Natural language instruction for act/extract/observe. Examples: 'click the search button', 'extract all product prices and titles', 'find the login form'."
6387
+ },
6388
+ schema: {
6389
+ type: "object",
6390
+ description: "JSON schema for structured extraction with 'extract'. Optional."
6391
+ },
6392
+ variables: {
6393
+ type: "object",
6394
+ description: "Variables for sensitive data in 'act'. Use %varName% in instruction. Example: instruction='type %password%', variables={password: 'secret'}"
6395
+ }
6396
+ },
6397
+ required: ["action"]
6398
+ },
6399
+ configCheck: (ctx) => {
6400
+ if (!ctx.config.tools?.browser) {
6401
+ return "Browser tool requires tools.browser config in augure.json5. Set provider to 'local' for Playwright or 'browserbase' for cloud.";
6402
+ }
6403
+ if (ctx.config.tools.browser.provider === "browserbase" && !ctx.config.tools.browser.browserbase?.apiKey) {
6404
+ return "Browserbase provider requires tools.browser.browserbase.apiKey";
6405
+ }
6406
+ return null;
6407
+ },
6408
+ execute: async (params, _ctx) => {
6409
+ const p = params;
6410
+ if (p.action !== "open" && !p.session) {
6411
+ return { success: false, output: "Missing 'session' \u2014 open a session first with action: 'open'" };
6412
+ }
6413
+ if (["act", "extract", "observe"].includes(p.action) && !p.instruction) {
6414
+ return { success: false, output: `Missing 'instruction' for action '${p.action}'` };
6415
+ }
6416
+ try {
6417
+ switch (p.action) {
6418
+ case "open": {
6419
+ const sessionId = await manager.open(p.url);
6420
+ return { success: true, output: `Session ${sessionId} opened.${p.url ? ` Navigated to ${p.url}` : ""}` };
6421
+ }
6422
+ case "navigate": {
6423
+ if (!p.url)
6424
+ return { success: false, output: "Missing 'url' for navigate" };
6425
+ await manager.navigate(p.session, p.url);
6426
+ return { success: true, output: `Navigated to ${p.url}` };
6427
+ }
6428
+ case "act": {
6429
+ const result = await manager.act(p.session, p.instruction, p.variables);
6430
+ return { success: result.success, output: result.message || "Action completed" };
6431
+ }
6432
+ case "extract": {
6433
+ const data = await manager.extract(p.session, p.instruction, p.schema);
6434
+ const output = typeof data === "string" ? data : JSON.stringify(data, null, 2);
6435
+ return { success: true, output };
6436
+ }
6437
+ case "observe": {
6438
+ const elements = await manager.observe(p.session, p.instruction);
6439
+ return {
6440
+ success: true,
6441
+ output: elements.length > 0 ? elements.map((e) => `- ${e.description} (${e.selector})`).join("\n") : "No matching elements found"
6442
+ };
6443
+ }
6444
+ case "screenshot": {
6445
+ const base64 = await manager.screenshot(p.session);
6446
+ return {
6447
+ success: true,
6448
+ output: "Screenshot captured",
6449
+ artifacts: [{ type: "image", name: "screenshot.png", content: base64 }]
6450
+ };
6451
+ }
6452
+ case "close": {
6453
+ await manager.close(p.session);
6454
+ return { success: true, output: `Session ${p.session} closed` };
6455
+ }
6456
+ default:
6457
+ return { success: false, output: `Unknown action: ${p.action}` };
6458
+ }
6459
+ } catch (err2) {
6460
+ return {
6461
+ success: false,
6462
+ output: `Browser error: ${err2 instanceof Error ? err2.message : String(err2)}`
6463
+ };
6464
+ }
6465
+ }
6466
+ };
6467
+ }
6468
+
6469
+ // ../browser/dist/session-manager.js
6470
+ import { Stagehand } from "@browserbasehq/stagehand";
6471
+
6472
+ // ../browser/dist/provider.js
6473
+ var PROVIDER_BASE_URLS = {
6474
+ openrouter: "https://openrouter.ai/api/v1",
6475
+ anthropic: "https://api.anthropic.com/v1",
6476
+ openai: "https://api.openai.com/v1"
6477
+ };
6478
+ function createStagehandConfig(config, llm) {
6479
+ const baseURL = PROVIDER_BASE_URLS[llm.provider];
6480
+ return {
6481
+ env: config.provider === "local" ? "LOCAL" : "BROWSERBASE",
6482
+ apiKey: config.browserbase?.apiKey,
6483
+ projectId: config.browserbase?.projectId,
6484
+ model: {
6485
+ modelName: llm.model,
6486
+ apiKey: llm.apiKey,
6487
+ ...baseURL ? { baseURL } : {}
6488
+ },
6489
+ localBrowserLaunchOptions: config.provider === "local" ? {
6490
+ headless: config.defaults?.headless ?? true,
6491
+ viewport: config.defaults?.viewport ?? { width: 1280, height: 720 }
6492
+ } : void 0,
6493
+ domSettleTimeout: (config.defaults?.timeout ?? 30) * 1e3,
6494
+ verbose: 0,
6495
+ disablePino: true
6496
+ };
6497
+ }
6498
+
6499
+ // ../browser/dist/session-manager.js
6500
+ var BrowserSessionManager = class {
6501
+ sessions = /* @__PURE__ */ new Map();
6502
+ config;
6503
+ llm;
6504
+ ttlMs;
6505
+ log;
6506
+ counter = 0;
6507
+ constructor(opts) {
6508
+ this.config = opts.config;
6509
+ this.llm = opts.llm;
6510
+ this.ttlMs = opts.ttlMs ?? 12e4;
6511
+ this.log = opts.logger ?? noopLogger;
6512
+ }
6513
+ async open(url) {
6514
+ const id = `s_${Date.now()}_${++this.counter}`;
6515
+ const stagehandConfig = createStagehandConfig(this.config, this.llm);
6516
+ const stagehand = new Stagehand(stagehandConfig);
6517
+ await stagehand.init();
6518
+ if (url) {
6519
+ const page = stagehand.context.activePage();
6520
+ if (!page)
6521
+ throw new Error(`No active page after init for session ${id}`);
6522
+ await page.goto(url, { waitUntil: "domcontentloaded" });
6523
+ }
6524
+ const timer = setTimeout(() => {
6525
+ this.log.warn(`Browser session ${id} expired (TTL ${this.ttlMs}ms)`);
6526
+ this.close(id).catch(() => {
6527
+ });
6528
+ }, this.ttlMs);
6529
+ this.sessions.set(id, { stagehand, timer });
6530
+ this.log.info(`Browser session ${id} opened`);
6531
+ return id;
6532
+ }
6533
+ async navigate(sessionId, url) {
6534
+ const entry = this.getSession(sessionId);
6535
+ this.resetTtl(sessionId, entry);
6536
+ const page = entry.stagehand.context.activePage();
6537
+ if (!page)
6538
+ throw new Error(`No active page for session ${sessionId}`);
6539
+ await page.goto(url, { waitUntil: "domcontentloaded" });
6540
+ }
6541
+ async act(sessionId, instruction, variables) {
6542
+ const entry = this.getSession(sessionId);
6543
+ this.resetTtl(sessionId, entry);
6544
+ const result = await entry.stagehand.act(instruction, variables ? { variables } : void 0);
6545
+ return { success: result.success, message: result.message ?? result.actionDescription ?? "" };
6546
+ }
6547
+ async extract(sessionId, instruction, schema) {
6548
+ const entry = this.getSession(sessionId);
6549
+ this.resetTtl(sessionId, entry);
6550
+ if (schema) {
6551
+ return entry.stagehand.extract(instruction, schema);
6552
+ }
6553
+ return entry.stagehand.extract(instruction);
6554
+ }
6555
+ async observe(sessionId, instruction) {
6556
+ const entry = this.getSession(sessionId);
6557
+ this.resetTtl(sessionId, entry);
6558
+ const actions2 = await entry.stagehand.observe(instruction);
6559
+ return actions2.map((a) => ({
6560
+ description: a.description ?? "",
6561
+ selector: a.selector ?? ""
6562
+ }));
6563
+ }
6564
+ async screenshot(sessionId) {
6565
+ const entry = this.getSession(sessionId);
6566
+ this.resetTtl(sessionId, entry);
6567
+ const page = entry.stagehand.context.activePage();
6568
+ if (!page)
6569
+ throw new Error(`No active page for session ${sessionId}`);
6570
+ const buffer = await page.screenshot();
6571
+ return Buffer.from(buffer).toString("base64");
6572
+ }
6573
+ async close(sessionId) {
6574
+ const entry = this.sessions.get(sessionId);
6575
+ if (!entry)
6576
+ return;
6577
+ clearTimeout(entry.timer);
6578
+ this.sessions.delete(sessionId);
6579
+ try {
6580
+ await entry.stagehand.close();
6581
+ } catch {
6582
+ }
6583
+ this.log.info(`Browser session ${sessionId} closed`);
6584
+ }
6585
+ async closeAll() {
6586
+ const ids = [...this.sessions.keys()];
6587
+ await Promise.all(ids.map((id) => this.close(id)));
6588
+ }
6589
+ getSession(sessionId) {
6590
+ const entry = this.sessions.get(sessionId);
6591
+ if (!entry)
6592
+ throw new Error(`Unknown or expired: no browser session ${sessionId}`);
6593
+ return entry;
6594
+ }
6595
+ resetTtl(sessionId, entry) {
6596
+ clearTimeout(entry.timer);
6597
+ entry.timer = setTimeout(() => {
6598
+ this.log.warn(`Browser session ${sessionId} expired (TTL ${this.ttlMs}ms)`);
6599
+ this.close(sessionId).catch(() => {
6600
+ });
6601
+ }, this.ttlMs);
6602
+ }
6603
+ };
6604
+
6348
6605
  // ../core/dist/main.js
6349
6606
  import Dockerode from "dockerode";
6350
6607
 
@@ -6846,15 +7103,25 @@ ${content}`;
6846
7103
  // ../scheduler/dist/cron.js
6847
7104
  import { createTask, validate } from "node-cron";
6848
7105
  var CronScheduler = class {
6849
- store;
6850
7106
  jobs = /* @__PURE__ */ new Map();
6851
7107
  tasks = /* @__PURE__ */ new Map();
6852
7108
  timers = /* @__PURE__ */ new Map();
6853
7109
  handlers = [];
6854
7110
  persistChain = Promise.resolve();
6855
7111
  running = false;
6856
- constructor(store) {
6857
- this.store = store;
7112
+ store;
7113
+ log;
7114
+ constructor(storeOrOpts) {
7115
+ if (storeOrOpts && "save" in storeOrOpts) {
7116
+ this.store = storeOrOpts;
7117
+ this.log = noopLogger;
7118
+ } else if (storeOrOpts) {
7119
+ const opts = storeOrOpts;
7120
+ this.store = opts.store;
7121
+ this.log = opts.logger ?? noopLogger;
7122
+ } else {
7123
+ this.log = noopLogger;
7124
+ }
6858
7125
  }
6859
7126
  onJobTrigger(handler2) {
6860
7127
  this.handlers.push(handler2);
@@ -6870,20 +7137,24 @@ var CronScheduler = class {
6870
7137
  throw new Error(`Invalid runAt date: ${job.runAt}`);
6871
7138
  }
6872
7139
  this.jobs.set(job.id, job);
6873
- console.log(`[scheduler] Added job ${job.id} (${job.cron ? `cron: ${job.cron}` : `runAt: ${job.runAt}`})`);
7140
+ this.log.info(`Added job ${job.id} (${job.cron ? `cron: ${job.cron}` : `runAt: ${job.runAt}`})`);
6874
7141
  if (job.enabled && job.cron) {
6875
7142
  const task = createTask(job.cron, () => {
6876
- console.log(`[scheduler] Cron fired for job ${job.id}`);
6877
- void this.executeHandlers(job);
7143
+ this.log.info(`Cron fired for job ${job.id}`);
7144
+ this.executeHandlers(job).catch((err2) => this.log.error(`Cron job ${job.id} handler failed:`, err2));
6878
7145
  });
6879
7146
  if (this.running) {
6880
7147
  task.start();
6881
- console.log(`[scheduler] Started cron task for ${job.id} immediately (scheduler already running)`);
7148
+ this.log.debug(`Started cron task for ${job.id} immediately (scheduler already running)`);
6882
7149
  }
6883
7150
  this.tasks.set(job.id, task);
6884
7151
  }
6885
- if (this.running && job.enabled && job.runAt && !job.cron) {
6886
- this.scheduleOneShot(job);
7152
+ if (job.enabled && job.runAt && !job.cron) {
7153
+ if (this.running) {
7154
+ this.scheduleOneShot(job);
7155
+ } else {
7156
+ this.log.warn(`Scheduler not running \u2014 one-shot job ${job.id} will be scheduled on start()`);
7157
+ }
6887
7158
  }
6888
7159
  this.persist();
6889
7160
  }
@@ -6899,7 +7170,7 @@ var CronScheduler = class {
6899
7170
  this.timers.delete(id);
6900
7171
  }
6901
7172
  this.jobs.delete(id);
6902
- console.log(`[scheduler] Removed job ${id}`);
7173
+ this.log.debug(`Removed job ${id}`);
6903
7174
  this.persist();
6904
7175
  }
6905
7176
  listJobs() {
@@ -6916,10 +7187,10 @@ var CronScheduler = class {
6916
7187
  if (!this.store)
6917
7188
  return;
6918
7189
  const jobs = await this.store.load();
6919
- console.log(`[scheduler] Loading ${jobs.length} persisted jobs`);
7190
+ this.log.info(`Loading ${jobs.length} persisted jobs`);
6920
7191
  for (const job of jobs) {
6921
7192
  if (job.runAt && Date.parse(job.runAt) <= Date.now()) {
6922
- console.log(`[scheduler] Skipping expired one-shot job ${job.id} (runAt: ${job.runAt})`);
7193
+ this.log.debug(`Skipping expired one-shot job ${job.id} (runAt: ${job.runAt})`);
6923
7194
  continue;
6924
7195
  }
6925
7196
  this.addJob(job);
@@ -6927,10 +7198,10 @@ var CronScheduler = class {
6927
7198
  }
6928
7199
  start() {
6929
7200
  this.running = true;
6930
- console.log(`[scheduler] Starting with ${this.tasks.size} cron tasks and ${this.handlers.length} handlers`);
7201
+ this.log.info(`Starting with ${this.tasks.size} cron tasks and ${this.handlers.length} handlers`);
6931
7202
  for (const [id, task] of this.tasks) {
6932
7203
  task.start();
6933
- console.log(`[scheduler] Started cron task: ${id}`);
7204
+ this.log.debug(`Started cron task: ${id}`);
6934
7205
  }
6935
7206
  for (const job of this.jobs.values()) {
6936
7207
  if (job.enabled && job.runAt && !job.cron) {
@@ -6939,6 +7210,7 @@ var CronScheduler = class {
6939
7210
  }
6940
7211
  }
6941
7212
  stop() {
7213
+ this.log.info("Scheduler stopped");
6942
7214
  this.running = false;
6943
7215
  for (const task of this.tasks.values()) {
6944
7216
  task.stop();
@@ -6951,16 +7223,14 @@ var CronScheduler = class {
6951
7223
  scheduleOneShot(job) {
6952
7224
  const delayMs = Date.parse(job.runAt) - Date.now();
6953
7225
  if (delayMs <= 0) {
6954
- console.log(`[scheduler] One-shot job ${job.id} already expired (delay: ${delayMs}ms), skipping`);
7226
+ this.log.warn(`One-shot job ${job.id} already expired (delay: ${delayMs}ms), skipping`);
6955
7227
  return;
6956
7228
  }
6957
- console.log(`[scheduler] Scheduled one-shot job ${job.id} in ${Math.round(delayMs / 1e3)}s (${job.runAt})`);
7229
+ this.log.info(`Scheduled one-shot job ${job.id} in ${Math.round(delayMs / 1e3)}s (${job.runAt})`);
6958
7230
  const timer = setTimeout(() => {
6959
- console.log(`[scheduler] One-shot job ${job.id} firing now`);
7231
+ this.log.info(`One-shot job ${job.id} firing now`);
6960
7232
  this.timers.delete(job.id);
6961
- void this.executeHandlers(job).then(() => {
6962
- this.removeJob(job.id);
6963
- });
7233
+ this.executeHandlers(job).then(() => this.removeJob(job.id)).catch((err2) => this.log.error(`One-shot job ${job.id} handler failed:`, err2));
6964
7234
  }, delayMs);
6965
7235
  this.timers.set(job.id, timer);
6966
7236
  }
@@ -6971,7 +7241,7 @@ var CronScheduler = class {
6971
7241
  this.persistChain = this.persistChain.then(() => this.store.save(jobs));
6972
7242
  }
6973
7243
  async executeHandlers(job) {
6974
- console.log(`[scheduler] Executing ${this.handlers.length} handlers for job ${job.id}`);
7244
+ this.log.debug(`Executing ${this.handlers.length} handlers for job ${job.id}`);
6975
7245
  for (const handler2 of this.handlers) {
6976
7246
  await handler2(job);
6977
7247
  }
@@ -8468,9 +8738,26 @@ async function startAgent(configPath, opts) {
8468
8738
  tools.register(sandboxExecTool);
8469
8739
  tools.register(opencodeTool);
8470
8740
  tools.register(githubTool);
8741
+ let browserManager;
8742
+ if (config.tools?.browser) {
8743
+ const browserLlm = config.llm.coding ?? config.llm.default;
8744
+ browserManager = new BrowserSessionManager({
8745
+ config: config.tools.browser,
8746
+ llm: {
8747
+ provider: browserLlm.provider ?? config.llm.default.provider,
8748
+ apiKey: browserLlm.apiKey ?? config.llm.default.apiKey,
8749
+ model: browserLlm.model ?? config.llm.default.model,
8750
+ maxTokens: browserLlm.maxTokens ?? config.llm.default.maxTokens
8751
+ },
8752
+ ttlMs: 12e4,
8753
+ logger: log.child("browser")
8754
+ });
8755
+ tools.register(createBrowserTool(browserManager));
8756
+ log.info("Browser tool registered", { provider: config.tools.browser.provider });
8757
+ }
8471
8758
  const jobStorePath = resolve(configPath, "..", "jobs.json");
8472
8759
  const jobStore = new JobStore(jobStorePath);
8473
- const scheduler = new CronScheduler(jobStore);
8760
+ const scheduler = new CronScheduler({ store: jobStore, logger: log.child("scheduler") });
8474
8761
  await scheduler.loadPersistedJobs();
8475
8762
  log.info(`Loaded ${scheduler.listJobs().length} persisted jobs`);
8476
8763
  for (const job of config.scheduler.jobs) {
@@ -8498,7 +8785,8 @@ async function startAgent(configPath, opts) {
8498
8785
  const skillRunner = new SkillRunner({
8499
8786
  pool,
8500
8787
  manager: skillManager,
8501
- defaults: config.sandbox.defaults
8788
+ defaults: config.sandbox.defaults,
8789
+ browserManager
8502
8790
  });
8503
8791
  const skillTester = new SkillTester({
8504
8792
  pool,
@@ -8773,6 +9061,8 @@ Run: \`npm update -g augure\``
8773
9061
  scheduler.stop();
8774
9062
  if (telegram)
8775
9063
  await telegram.stop();
9064
+ if (browserManager)
9065
+ await browserManager.closeAll();
8776
9066
  await pool.destroyAll();
8777
9067
  await audit.close();
8778
9068
  log.info("All containers destroyed");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "augure",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Augure — your proactive AI agent",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,20 +22,21 @@
22
22
  "gray-matter": "^4.0.3",
23
23
  "json5": "^2.2.3",
24
24
  "node-cron": "^4.2.1",
25
- "zod": "^4.3.6"
25
+ "zod": "^4.3.6",
26
+ "@browserbasehq/stagehand": "^3.0.0"
26
27
  },
27
28
  "devDependencies": {
28
29
  "@types/dockerode": "^4.0.1",
29
30
  "@types/node-cron": "^3.0.11",
30
31
  "tsup": "^8.5.1",
31
- "@augure/channels": "0.1.3",
32
- "@augure/core": "0.6.0",
33
- "@augure/sandbox": "0.1.2",
34
- "@augure/scheduler": "0.1.2",
35
- "@augure/memory": "0.0.5",
36
- "@augure/tools": "0.3.0",
37
- "@augure/types": "0.3.0",
38
- "@augure/skills": "0.1.3"
32
+ "@augure/channels": "0.1.4",
33
+ "@augure/core": "0.7.0",
34
+ "@augure/sandbox": "0.1.3",
35
+ "@augure/scheduler": "0.1.3",
36
+ "@augure/memory": "0.0.6",
37
+ "@augure/skills": "0.1.4",
38
+ "@augure/tools": "0.4.0",
39
+ "@augure/types": "0.4.0"
39
40
  },
40
41
  "keywords": [
41
42
  "ai",