@openagents-org/agent-launcher 0.2.93 → 0.2.94

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openagents-org/agent-launcher",
3
- "version": "0.2.93",
3
+ "version": "0.2.94",
4
4
  "description": "OpenAgents Launcher — install, configure, and run AI coding agents from your terminal",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/registry.json CHANGED
@@ -44,6 +44,8 @@
44
44
  "cli",
45
45
  "anthropic"
46
46
  ],
47
+ "featured": true,
48
+ "order": 1,
47
49
  "builtin": true,
48
50
  "install": {
49
51
  "binary": "claude",
@@ -107,6 +109,8 @@
107
109
  "openai",
108
110
  "cli"
109
111
  ],
112
+ "featured": true,
113
+ "order": 3,
110
114
  "builtin": true,
111
115
  "install": {
112
116
  "binary": "codex",
@@ -352,6 +356,8 @@
352
356
  "coding",
353
357
  "open-source"
354
358
  ],
359
+ "featured": true,
360
+ "order": 2,
355
361
  "builtin": true,
356
362
  "install": {
357
363
  "binary": "openclaw",
package/src/mcp-server.js CHANGED
@@ -104,11 +104,14 @@ function buildToolDefs(disabledModules) {
104
104
  tools.push(
105
105
  {
106
106
  name: 'workspace_browser_open',
107
- description: 'Open a new shared browser tab.',
107
+ description:
108
+ 'Open a new shared browser tab. Use context_name to open in a persistent browser context ' +
109
+ '(preserves cookies/login sessions). List contexts with workspace_browser_list_contexts.',
108
110
  inputSchema: {
109
111
  type: 'object',
110
112
  properties: {
111
113
  url: { type: 'string', description: 'URL to open (default: about:blank)' },
114
+ context_name: { type: 'string', description: 'Name of a persistent browser context (e.g. "Hackernews"). Preserves login cookies across sessions.' },
112
115
  },
113
116
  },
114
117
  },
@@ -187,6 +190,11 @@ function buildToolDefs(disabledModules) {
187
190
  required: ['tab_id'],
188
191
  },
189
192
  },
193
+ {
194
+ name: 'workspace_browser_list_contexts',
195
+ description: 'List available persistent browser contexts (saved login sessions).',
196
+ inputSchema: { type: 'object', properties: {} },
197
+ },
190
198
  );
191
199
  }
192
200
 
@@ -416,12 +424,28 @@ class McpServer {
416
424
  // ── Browser ──
417
425
 
418
426
  case 'workspace_browser_open': {
419
- const result = await this.ws.browserOpenTab(this.workspaceId, this.token, {
427
+ const opts = {
420
428
  url: args.url || 'about:blank',
421
429
  source: `openagents:${this.agentName}`,
422
- });
430
+ };
431
+ // Resolve context_name to context_id
432
+ if (args.context_name) {
433
+ const ctxData = await this.ws.browserListContexts(this.workspaceId, this.token);
434
+ const contexts = (ctxData && ctxData.contexts) || [];
435
+ const match = contexts.find(
436
+ (c) => c.name.toLowerCase() === args.context_name.toLowerCase(),
437
+ );
438
+ if (match) {
439
+ opts.context_id = match.id;
440
+ } else {
441
+ const names = contexts.map((c) => c.name).join(', ') || '(none)';
442
+ return text(`Context "${args.context_name}" not found. Available: ${names}`);
443
+ }
444
+ }
445
+ const result = await this.ws.browserOpenTab(this.workspaceId, this.token, opts);
423
446
  const tabId = result.tab_id || result.id || 'unknown';
424
- return text(`Browser tab opened: ${tabId} ${args.url || 'about:blank'}`);
447
+ const persistent = result.persistent ? ' (persistent)' : '';
448
+ return text(`Browser tab opened${persistent}: ${tabId} → ${args.url || 'about:blank'}`);
425
449
  }
426
450
 
427
451
  case 'workspace_browser_navigate': {
@@ -466,6 +490,16 @@ class McpServer {
466
490
  return text(`Browser tab closed: ${args.tab_id}`);
467
491
  }
468
492
 
493
+ case 'workspace_browser_list_contexts': {
494
+ const data = await this.ws.browserListContexts(this.workspaceId, this.token);
495
+ const contexts = (data && data.contexts) || [];
496
+ if (!contexts.length) return text('No persistent browser contexts.');
497
+ const lines = contexts.map((c) =>
498
+ `- ${c.name} (domain: ${c.domain || 'any'}, id: ${c.id})`
499
+ );
500
+ return text(lines.join('\n'));
501
+ }
502
+
469
503
  // ── Tunnel ──
470
504
 
471
505
  case 'tunnel_expose': {
package/src/registry.js CHANGED
@@ -7,6 +7,19 @@ const DEFAULT_REGISTRY_URL = 'https://endpoint.openagents.org/v1/agent-registry'
7
7
  const CACHE_FILE = 'agent_catalog.json';
8
8
  const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
9
9
 
10
+ /**
11
+ * Sort catalog: featured entries first (by order), then the rest alphabetically.
12
+ */
13
+ function _sortCatalog(catalog) {
14
+ return catalog.sort((a, b) => {
15
+ const af = a.featured ? 1 : 0;
16
+ const bf = b.featured ? 1 : 0;
17
+ if (af !== bf) return bf - af; // featured first
18
+ if (af && bf) return (a.order || 999) - (b.order || 999); // by order within featured
19
+ return (a.label || a.name).localeCompare(b.label || b.name); // alphabetical for the rest
20
+ });
21
+ }
22
+
10
23
  /**
11
24
  * Agent registry — fetches the catalog of available agent types.
12
25
  *
@@ -30,7 +43,7 @@ class Registry {
30
43
  // Try cache first (avoids network on every call)
31
44
  const cached = this._loadCache();
32
45
  if (cached) {
33
- this._catalog = this._mergeBundled(cached);
46
+ this._catalog = _sortCatalog(this._mergeBundled(cached));
34
47
  this._refreshInBackground();
35
48
  return this._catalog;
36
49
  }
@@ -38,12 +51,12 @@ class Registry {
38
51
  // No cache — try remote
39
52
  const remote = await this._fetchRemote();
40
53
  if (remote) {
41
- this._catalog = this._mergeBundled(remote);
54
+ this._catalog = _sortCatalog(this._mergeBundled(remote));
42
55
  return this._catalog;
43
56
  }
44
57
 
45
58
  // Fallback to bundled
46
- this._catalog = this._loadBundled();
59
+ this._catalog = _sortCatalog(this._loadBundled());
47
60
  return this._catalog;
48
61
  }
49
62
 
@@ -54,10 +67,10 @@ class Registry {
54
67
  if (this._catalog) return this._catalog;
55
68
  const cached = this._loadCache();
56
69
  if (cached) {
57
- this._catalog = this._mergeBundled(cached);
70
+ this._catalog = _sortCatalog(this._mergeBundled(cached));
58
71
  return this._catalog;
59
72
  }
60
- this._catalog = this._loadBundled();
73
+ this._catalog = _sortCatalog(this._loadBundled());
61
74
  return this._catalog;
62
75
  }
63
76
 
@@ -127,7 +140,7 @@ class Registry {
127
140
  */
128
141
  async refresh() {
129
142
  const remote = await this._fetchRemote();
130
- if (remote) this._catalog = this._mergeBundled(remote);
143
+ if (remote) this._catalog = _sortCatalog(this._mergeBundled(remote));
131
144
  return this._catalog || this.getCatalogSync();
132
145
  }
133
146
 
@@ -347,10 +347,10 @@ class WorkspaceClient {
347
347
  /**
348
348
  * Open a new browser tab via POST /v1/browser/tabs.
349
349
  */
350
- async browserOpenTab(workspaceId, token, { url = 'about:blank', source = 'human:user' } = {}) {
351
- const data = await this._post('/v1/browser/tabs', {
352
- url, network: workspaceId, source,
353
- }, this._wsHeaders(token));
350
+ async browserOpenTab(workspaceId, token, { url = 'about:blank', source = 'human:user', context_id } = {}) {
351
+ const body = { url, network: workspaceId, source };
352
+ if (context_id) body.context_id = context_id;
353
+ const data = await this._post('/v1/browser/tabs', body, this._wsHeaders(token));
354
354
  return data.data || data;
355
355
  }
356
356
 
@@ -412,6 +412,15 @@ class WorkspaceClient {
412
412
  return data.data || data;
413
413
  }
414
414
 
415
+ /**
416
+ * List persistent browser contexts via GET /v1/browser/contexts.
417
+ */
418
+ async browserListContexts(workspaceId, token) {
419
+ const params = new URLSearchParams({ network: workspaceId });
420
+ const data = await this._get(`/v1/browser/contexts?${params}`, this._wsHeaders(token));
421
+ return data.data || data;
422
+ }
423
+
415
424
  // ── Internal helpers ──
416
425
 
417
426
  /**