agentcage 0.1.8 → 0.1.9

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/README.md CHANGED
@@ -23,11 +23,6 @@ You need **Docker** (Docker Desktop on Windows/macOS, Docker Engine on Linux), *
23
23
  npm install -g agentcage # also updates to the latest version
24
24
  ```
25
25
 
26
- ```bash
27
- agentcage doctor # checks all the prerequisites.
28
- ```
29
-
30
-
31
26
 
32
27
  ## Quickstart
33
28
 
@@ -41,17 +36,19 @@ Run this in terminal:
41
36
 
42
37
  ```bash
43
38
  agentcage doctor # checks all the prerequisites.
44
- agentcage # Starts the container-creation dialogue
39
+ agentcage # Starts the CLI: UI or container-creation dialogue
45
40
  ```
46
41
 
47
- Follow the steps
48
42
 
49
43
  ## The agentcage window (UI)
44
+ Agentcage UI gives you a graphical way to see and manage your devcontainers
50
45
 
51
46
  ```bash
52
47
  agentcage ui # opens the agentcage window
53
48
  ```
54
49
 
50
+ Running `agentcage` without a command also offers it as the first choice in the menu.
51
+
55
52
  A local web UI with the same features as the terminal dialog:
56
53
  - **Containers:** each container's status, and Start, Stop, Open in IDE, Add to OpenCode, OpenCode providers (gear button: add model provider API keys), Rebuild and Remove.
57
54
  - **New container:** a dialog to create one from a folder, a git URL, or a repository picked from **GitHub** or **Azure Repos**.
@@ -63,8 +60,9 @@ A local web UI with the same features as the terminal dialog:
63
60
  The window opens as an app window (Edge or Chrome `--app`), or as a normal browser tab if neither is installed.
64
61
 
65
62
  **Desktop icon:** the first `agentcage ui` offers to add an agentcage icon to the desktop and Start Menu (Windows) or app
66
- menu (Linux). Clicking it starts the UI without a console window. `agentcage ui --shortcut` adds the icon later, and
67
- `--no-shortcut` removes it.
63
+ menu (Linux). Clicking it starts the UI without a console window. The window also offers it when it opens (until you add
64
+ it or pick "Don't show this again"), and you can always add it under **Settings → Desktop icon** or with
65
+ `agentcage ui --shortcut`; `--no-shortcut` removes it.
68
66
 
69
67
  **How it runs:** the UI is served by a small server on `127.0.0.1` only, protected by a random session token. It stops a
70
68
  few seconds after you close the window, unless a container is still being created. `--no-open` only prints the URL.
package/bin/agentcage.js CHANGED
@@ -12,7 +12,7 @@ process.removeAllListeners('warning'); // hide node:sqlite ExperimentalWarning
12
12
 
13
13
  const HELP = `agentcage: isolated dev containers for AI coding agents
14
14
 
15
- agentcage interactive menu (create or manage containers)
15
+ agentcage menu: open the agentcage window, or create / manage containers in the terminal
16
16
  agentcage ui open the agentcage window (containers, create dialog, GitHub / Azure Repos, help)
17
17
  [--shortcut] add a desktop / menu icon [--no-shortcut] remove it [--no-open] only print the URL
18
18
  agentcage new [folder|git-url] create a container [--name x] [--tools opencode,claude-code,python,gh,pnpm]
@@ -123,14 +123,16 @@ const commands = {
123
123
  },
124
124
  adopt() { const b = adoptContainer(arg); console.log(`Adopted '${b.name}' (${b.container}).`); },
125
125
 
126
- // No command: menu
126
+ // No command: menu. The window (UI) comes first; the rest is the terminal flow.
127
127
  async menu() {
128
128
  const containers = listContainers();
129
- if (!containers.length) return newWizard();
130
129
  p.intro('agentcage');
131
- // '-settings' can't clash with a container name (names start with a letter or digit)
132
- const pick = await ask(p.select({ message: 'What do you want to do?', options: [{ value: '', label: 'Create a new container' },
130
+ // '-ui' and '-settings' can't clash with a container name (names start with a letter or digit)
131
+ const pick = await ask(p.select({ message: 'What do you want to do?', options: [
132
+ { value: '-ui', label: 'Open the agentcage window', hint: 'containers, create dialog, GitHub / Azure Repos' },
133
+ { value: '', label: 'Create a new container (here in the terminal)' },
133
134
  ...containers.map(b => ({ value: b.name, label: b.name, hint: state(b) })), { value: '-settings', label: 'Settings' }] }));
135
+ if (pick === '-ui') return commands.ui();
134
136
  if (pick === '-settings') return settingsWizard();
135
137
  if (!pick) return newWizard();
136
138
  const action = await ask(p.select({ message: pick, options: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentcage",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Isolated dev containers for AI coding agents (OpenCode, Claude Code), connected to OpenCode Desktop",
5
5
  "type": "module",
6
6
  "bin": {
package/src/server.js CHANGED
@@ -12,7 +12,7 @@ import { isGitUrl, localRemote, parseRemote } from './git.js';
12
12
  import { connectDesktop, desktopRunning, desktopRunningAsync, desktopServers, detectDesktop, latestVersion, serverName, setProviderKey, setupProject } from './opencode.js';
13
13
  import { run } from './run.js';
14
14
  import { CHOICES, DEFAULTS, LABELS, loadSettings, saveSettings } from './settings.js';
15
- import { openWindow, pickFolder } from './shortcut.js';
15
+ import { createShortcuts, hasShortcuts, openWindow, pickFolder } from './shortcut.js';
16
16
  import * as conn from './connections.js';
17
17
 
18
18
  const PAGE = new URL('./ui.html', import.meta.url), LOCK = join(ROOT, 'ui.json');
@@ -138,6 +138,9 @@ export async function serve({ open = true } = {}) {
138
138
  ['DELETE', /^\/api\/connections\/(github|azure)$/, ([, id]) => conn.removePat(id)],
139
139
  ['GET', /^\/api\/repos\/(github|azure)$/, ([, id], q) => conn.repos(id, q.get('org'))],
140
140
  ['POST', /^\/api\/browse$/, async (_, __, body) => ({ path: await pickFolder(body.start?.trim()) })],
141
+ ['GET', /^\/api\/shortcut$/, () => ({ supported: process.platform !== 'darwin', exists: hasShortcuts(), hideTip: !!loadSettings().hideShortcutTip })],
142
+ ['POST', /^\/api\/shortcut$/, () => ({ files: createShortcuts() })],
143
+ ['POST', /^\/api\/shortcut\/hide-tip$/, () => saveSettings({ ...loadSettings(), hideShortcutTip: true })],
141
144
  ];
142
145
 
143
146
  const server = createServer(async (req, res) => {
package/src/shortcut.js CHANGED
@@ -29,6 +29,7 @@ const PS = `$w = New-Object -ComObject WScript.Shell
29
29
  foreach ($d in [Environment]::GetFolderPath('Desktop'), [Environment]::GetFolderPath('Programs')) {
30
30
  $f = Join-Path $d 'agentcage.lnk'
31
31
  if ($env:AC_REMOVE) { if (Test-Path $f) { Remove-Item $f; $f }; continue }
32
+ if ($env:AC_CHECK) { if (Test-Path $f) { $f }; break } # the desktop icon only
32
33
  $s = $w.CreateShortcut($f); $s.TargetPath = $env:AC_NODE; $s.Arguments = '"' + $env:AC_SCRIPT + '" ui --detach'
33
34
  $s.IconLocation = $env:AC_ICO; $s.WindowStyle = 7; $s.Description = 'agentcage: dev containers for AI coding agents'; $s.Save(); $f }`;
34
35
  const powershell = env => spawnSync('powershell', ['-NoProfile', '-NonInteractive', '-Command', PS], { encoding: 'utf8', env: { ...process.env, ...env }, windowsHide: true })
@@ -53,6 +54,7 @@ export function createShortcuts() {
53
54
  });
54
55
  }
55
56
  export const removeShortcuts = () => (WIN ? powershell({ AC_REMOVE: '1' }) : linuxFiles().filter(existsSync).map(f => (rmSync(f), f)));
57
+ export const hasShortcuts = () => (WIN ? powershell({ AC_CHECK: '1' }) : linuxFiles().filter(existsSync)).length > 0;
56
58
 
57
59
  // Edge / Chrome / Chromium, for an app window (own window and taskbar icon, no tabs or address bar)
58
60
  function appBrowser() {
package/src/ui.html CHANGED
@@ -15,6 +15,8 @@
15
15
  header { display: flex; align-items: center; gap: 12px; padding: 10px 20px; background: var(--brand); color: var(--brand-text); }
16
16
  header img { width: 28px; height: 28px; border-radius: 6px; }
17
17
  header b { font-size: 16px; margin-right: 16px; }
18
+ .tip { position: fixed; right: 20px; top: 62px; width: 350px; max-width: calc(100vw - 40px); background: var(--panel); color: var(--text); border: 1px solid var(--line); border-radius: 9px; padding: 12px 30px 12px 14px; font-size: 13px; line-height: 1.45; box-shadow: 0 6px 20px rgba(0,0,0,.25); z-index: 5; }
19
+ .tip .x { position: absolute; top: 4px; right: 4px; border: 0; background: none; padding: 2px 6px; color: var(--muted); }
18
20
  nav button { background: none; border: 0; color: inherit; opacity: .75; padding: 6px 10px; border-radius: 6px; font: inherit; cursor: pointer; }
19
21
  nav button.on, nav button:hover { opacity: 1; background: rgba(255,255,255,.14); }
20
22
  main { max-width: 1320px; margin: 0 auto; padding: 20px; }
@@ -65,6 +67,9 @@
65
67
  </style>
66
68
  </head>
67
69
  <body>
70
+ <div id="tip" class="tip hidden" role="dialog" aria-label="Desktop icon"><button type="button" class="x" aria-label="Close">×</button>
71
+ <b>Open agentcage from your desktop</b><div style="margin-top:4px">Add an agentcage icon to your <span class="sc-places"></span>, so you can open this window without a terminal.</div>
72
+ <div class="row" style="margin-top:10px"><button class="primary add">Add desktop icon</button><button class="never">Don't show this again</button></div></div>
68
73
  <header>
69
74
  <img src="/icon.svg" alt=""><b>agentcage</b>
70
75
  <nav class="row">
@@ -123,6 +128,9 @@
123
128
  <p class="muted">Apply to containers created afterwards. Stored in <code>~/.agentcage/settings.json</code>, shared with <code>agentcage settings</code>.</p>
124
129
  <div class="card" style="max-width:560px"><div id="settings-form"></div>
125
130
  <div class="row" style="margin-top:14px"><button class="primary" id="settings-save">Save</button><span id="settings-msg" class="muted small"></span></div></div>
131
+ <div class="card hidden" id="sc-card" style="max-width:560px;margin-top:14px"><h3>Desktop icon</h3>
132
+ <p class="muted" style="margin:0">An agentcage icon on your <span class="sc-places"></span> opens this window without a terminal. <span id="sc-status"></span></p>
133
+ <div class="row"><button class="primary" id="sc-add">Add desktop icon</button></div></div>
126
134
  </section>
127
135
 
128
136
  <section id="help" class="hidden">
@@ -163,6 +171,13 @@
163
171
  <details><summary>IDEs</summary>
164
172
  <p><b>Open in IDE</b> attaches Cursor, VS Code or Antigravity to the container in the project folder. Use the IDE for editing, diffs
165
173
  and git. Its built-in AI agent runs outside the container's isolation, so run agents through OpenCode or Claude Code.</p></details>
174
+ <details><summary>Desktop icon</summary>
175
+ <ul><li>Go to <b>Settings</b> and click <b>Add desktop icon</b> at the bottom of the page. The icon is added to your desktop and to the
176
+ Start Menu (Windows) or app menu (Linux). New users are also offered it when the window opens.</li>
177
+ <li>The icon opens this window without a terminal. It reuses agentcage if it is already running.</li>
178
+ <li>In a terminal: <code>agentcage ui --shortcut</code> adds the icon, <code>agentcage ui --no-shortcut</code> removes it.</li>
179
+ <li>If you update or move Node.js, click <b>Add desktop icon</b> again so the icon points to the new location.</li>
180
+ <li>Not available on macOS yet: run <code>agentcage ui</code> there.</li></ul></details>
166
181
  <details><summary>Command line and troubleshooting</summary>
167
182
  <ul><li>Everything here also works in a terminal: <code>agentcage</code> (menu), <code>agentcage list</code>, <code>agentcage new</code>,
168
183
  <code>agentcage shell &lt;name&gt;</code>, <code>agentcage help</code>.</li>
@@ -435,13 +450,27 @@ $('#settings-save').onclick = run(async () => {
435
450
  STATE.settings = (await api('/api/state')).settings; $('#settings-msg').textContent = 'Saved.';
436
451
  });
437
452
 
453
+ // Desktop icon: a button at the bottom of Settings, and a popover when the window opens while there is no desktop icon
454
+ // (until the user adds one or picks "Don't show this again")
455
+ async function shortcuts() {
456
+ const sc = await api('/api/shortcut').catch(() => ({})), tip = $('#tip');
457
+ if (!sc.supported) return;
458
+ $$('.sc-places').forEach(e => (e.textContent = META.platform === 'win32' ? 'desktop and Start Menu' : 'desktop and app menu'));
459
+ const added = exists => { $('#sc-status').textContent = exists ? 'You already have one; adding it again recreates it.' : ''; $('#sc-add').textContent = exists ? 'Recreate desktop icon' : 'Add desktop icon'; };
460
+ added(sc.exists); $('#sc-card').classList.remove('hidden');
461
+ const add = run(async () => { tip.classList.add('hidden'); await api('/api/shortcut', { method: 'POST' }); added(true); toast('Added the agentcage icon.'); });
462
+ $('#sc-add').onclick = tip.querySelector('.add').onclick = add;
463
+ tip.querySelector('.x').onclick = () => tip.classList.add('hidden');
464
+ tip.querySelector('.never').onclick = run(async () => { tip.classList.add('hidden'); await api('/api/shortcut/hide-tip', { method: 'POST' }); });
465
+ tip.classList.toggle('hidden', sc.exists || sc.hideTip);
466
+ }
438
467
  // Tell the server this window is open (it stops a few seconds after the window closes); reconnect if it drops
439
468
  async function alive() { try { const r = await fetch('/api/alive', { headers: { 'x-agentcage-token': TOKEN } }); await r.text(); } catch {} setTimeout(alive, 2000); }
440
469
 
441
470
  (async () => {
442
471
  if (!TOKEN) return $('#banners').replaceChildren(h('div', { class: 'banner bad' }, 'Open agentcage with the agentcage icon or by running: agentcage ui'));
443
472
  try { META = await api('/api/meta'); } catch (e) { return $('#banners').replaceChildren(h('div', { class: 'banner bad' }, `${e.message}`)); }
444
- alive(); refresh();
473
+ alive(); refresh(); shortcuts();
445
474
  })();
446
475
  </script>
447
476
  </body>