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 +7 -9
- package/bin/agentcage.js +7 -5
- package/package.json +1 -1
- package/src/server.js +4 -1
- package/src/shortcut.js +2 -0
- package/src/ui.html +30 -1
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.
|
|
67
|
-
|
|
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
|
|
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: [
|
|
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
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 <name></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>
|