@bill10/agent-007 0.6.2
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/LICENSE +21 -0
- package/README.md +222 -0
- package/VERSION +1 -0
- package/bin/adduser.js +69 -0
- package/bin/agent-007.js +88 -0
- package/lib/cron.js +189 -0
- package/lib/helpers.js +541 -0
- package/lib/jobs.js +965 -0
- package/package.json +63 -0
- package/public/app.js +650 -0
- package/public/assets/characters/LICENSE +21 -0
- package/public/assets/characters/char_0.png +0 -0
- package/public/assets/characters/char_1.png +0 -0
- package/public/assets/characters/char_2.png +0 -0
- package/public/assets/characters/char_3.png +0 -0
- package/public/assets/characters/char_4.png +0 -0
- package/public/assets/characters/char_5.png +0 -0
- package/public/assets/furniture/bookshelf.png +0 -0
- package/public/assets/furniture/cactus.png +0 -0
- package/public/assets/furniture/chair_back.png +0 -0
- package/public/assets/furniture/chair_front.png +0 -0
- package/public/assets/furniture/chair_side.png +0 -0
- package/public/assets/furniture/coffee.png +0 -0
- package/public/assets/furniture/coffee_table.png +0 -0
- package/public/assets/furniture/desk.png +0 -0
- package/public/assets/furniture/desk2.png +0 -0
- package/public/assets/furniture/plant_2.png +0 -0
- package/public/assets/furniture/sofa_front.png +0 -0
- package/public/assets/furniture/sofa_side.png +0 -0
- package/public/assets/furniture/table_front.png +0 -0
- package/public/index.html +245 -0
- package/public/modules/auth.js +83 -0
- package/public/modules/explorer.js +760 -0
- package/public/modules/jobs.js +971 -0
- package/public/modules/office.js +2154 -0
- package/public/modules/paths.js +20 -0
- package/public/modules/shortcuts.js +54 -0
- package/public/modules/state.js +75 -0
- package/public/modules/terminal.js +651 -0
- package/public/modules/voice.js +393 -0
- package/public/modules/ws.js +56 -0
- package/public/style.css +1843 -0
- package/server/agent-mcp-bridge.js +45 -0
- package/server/agent-mcp.js +184 -0
- package/server/agent-transcripts.js +195 -0
- package/server/approvals.js +155 -0
- package/server/auth.js +162 -0
- package/server/billion.js +176 -0
- package/server/claude-trust.js +66 -0
- package/server/command-path.js +102 -0
- package/server/config.js +184 -0
- package/server/direct-run.js +33 -0
- package/server/git.js +630 -0
- package/server/http.js +276 -0
- package/server/jobs.js +2044 -0
- package/server/mcp.js +596 -0
- package/server/messages.js +319 -0
- package/server/permission-hook.js +47 -0
- package/server/pty.js +360 -0
- package/server/state.js +104 -0
- package/server/ws.js +583 -0
- package/server.js +306 -0
- package/templates/billion/COMPANY.md +14 -0
- package/templates/billion/STATE.md +17 -0
- package/templates/billion/charter.md +232 -0
- package/templates/billion/owner.md +11 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Path shapes, for the directory browser.
|
|
2
|
+
//
|
|
3
|
+
// The server answers with paths in its own platform's shape, so the browser
|
|
4
|
+
// cannot assume POSIX: an absolute path is `/home/me` on Linux and macOS but
|
|
5
|
+
// `C:\Users\me` or `\\server\share` on Windows. Treating those Windows forms as
|
|
6
|
+
// relative is what made the repo picker discard a typed path there.
|
|
7
|
+
|
|
8
|
+
/** True for an absolute POSIX path, a drive-letter path, or a UNC share. */
|
|
9
|
+
export function isAbsolutePath(p) {
|
|
10
|
+
return !!p && (p.startsWith('/') || /^[a-zA-Z]:[\\/]/.test(p) || p.startsWith('\\\\'));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Append a child to a directory path, keeping the separator that path already
|
|
15
|
+
* uses and not doubling the one a root already ends with.
|
|
16
|
+
*/
|
|
17
|
+
export function joinBrowsePath(dir, name) {
|
|
18
|
+
const sep = !dir.startsWith('/') && dir.includes('\\') ? '\\' : '/';
|
|
19
|
+
return /[\\/]$/.test(dir) ? dir + name : dir + sep + name;
|
|
20
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Keyboard shortcuts
|
|
2
|
+
import { agents } from './state.js';
|
|
3
|
+
import { switchToSession } from './terminal.js';
|
|
4
|
+
import { toggleExplorer } from './explorer.js';
|
|
5
|
+
import { toggleVoice } from './voice.js';
|
|
6
|
+
|
|
7
|
+
// Single source of truth for Cmd+<key> global shortcuts. terminal.js uses the
|
|
8
|
+
// same predicate so xterm passes exactly the chords the document handler acts
|
|
9
|
+
// on — an asymmetric filter would create dead keystrokes (blocked by xterm,
|
|
10
|
+
// ignored here). Add a new shortcut to the key list and the handler below,
|
|
11
|
+
// nowhere else. Shift chords are excluded naturally: e.key is 'E', not 'e'.
|
|
12
|
+
export const GLOBAL_SHORTCUT_KEYS = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', 'n', 'd'];
|
|
13
|
+
|
|
14
|
+
export function isGlobalShortcut(e) {
|
|
15
|
+
return e.metaKey && !e.ctrlKey && !e.altKey && GLOBAL_SHORTCUT_KEYS.includes(e.key);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function setupShortcuts() {
|
|
19
|
+
document.addEventListener('keydown', (e) => {
|
|
20
|
+
if (!isGlobalShortcut(e) || e.repeat) return;
|
|
21
|
+
|
|
22
|
+
// Cmd+1..9: switch to agent N
|
|
23
|
+
if (e.key >= '1' && e.key <= '9') {
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
const idx = parseInt(e.key) - 1;
|
|
26
|
+
const sessionIds = [...agents.keys()];
|
|
27
|
+
if (idx < sessionIds.length) {
|
|
28
|
+
switchToSession(sessionIds[idx]);
|
|
29
|
+
}
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Cmd+E: toggle explorer (disabled below 900px)
|
|
34
|
+
if (e.key === 'e') {
|
|
35
|
+
e.preventDefault();
|
|
36
|
+
if (window.innerWidth > 900) toggleExplorer();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Cmd+N: open spawn form
|
|
41
|
+
if (e.key === 'n') {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
document.getElementById('btn-new-agent').click();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Cmd+D: toggle voice input
|
|
48
|
+
if (e.key === 'd') {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
toggleVoice();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Shared state store — all modules import from here
|
|
2
|
+
export const agents = new Map(); // sessionId -> agent object
|
|
3
|
+
export const repos = new Map(); // repoPath -> { slug, exists, agents: Set }
|
|
4
|
+
export const orphans = new Map(); // orphanId -> orphan data
|
|
5
|
+
export let activeSessionId = null;
|
|
6
|
+
|
|
7
|
+
export function setActiveSession(id) {
|
|
8
|
+
activeSessionId = id;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// The job board shares the terminal panel with the agent tabs. activeSessionId
|
|
12
|
+
// is deliberately left untouched while the board is showing, so dismissing the
|
|
13
|
+
// board returns you to the terminal you were on.
|
|
14
|
+
export let boardActive = false;
|
|
15
|
+
export function setBoardActive(on) { boardActive = !!on; }
|
|
16
|
+
|
|
17
|
+
export const jobs = new Map(); // jobId -> job record from the server
|
|
18
|
+
export let boardSettings = { running: false, maxPerRepo: 2, intervalMs: 300000, permissionMode: 'auto' };
|
|
19
|
+
export function setBoardSettings(s) { boardSettings = { ...boardSettings, ...s }; }
|
|
20
|
+
|
|
21
|
+
// --- Viewer identity & ownership (phase 2) ---
|
|
22
|
+
export let selfUserId = null;
|
|
23
|
+
export let authEnabled = false;
|
|
24
|
+
export let serverPlatform = ''; // process.platform of the server, from the welcome message
|
|
25
|
+
|
|
26
|
+
// Whether this server runs Billion (server/billion.js). The explorer keeps its
|
|
27
|
+
// row pinned even while it is stopped, so there is always a way to start it.
|
|
28
|
+
export let billionEnabled = false;
|
|
29
|
+
export function setBillionEnabled(on) { billionEnabled = !!on; }
|
|
30
|
+
|
|
31
|
+
// Billion's tab first, then the rest in their own order.
|
|
32
|
+
export function billionFirst(entries) {
|
|
33
|
+
return [...entries].sort(([, a], [, b]) => Number(!!b.isBillion) - Number(!!a.isBillion));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function setSelf(userId, enabled, platform) {
|
|
37
|
+
selfUserId = userId;
|
|
38
|
+
authEnabled = !!enabled;
|
|
39
|
+
if (platform) serverPlatform = platform;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// The shell preset offered in the spawn form follows the server's OS —
|
|
43
|
+
// commands run there, not in the browser.
|
|
44
|
+
export function shellPreset() {
|
|
45
|
+
return serverPlatform === 'win32'
|
|
46
|
+
? { label: 'PowerShell', cmd: 'powershell.exe' }
|
|
47
|
+
: { label: 'Bash', cmd: 'bash' };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// True if the current viewer may control this agent: always in single-player
|
|
51
|
+
// (auth off) or for unowned agents; otherwise only the owner. Server enforces
|
|
52
|
+
// this regardless — the client guard just avoids a broken "type → nothing" feel.
|
|
53
|
+
export function canControlAgent(agent) {
|
|
54
|
+
if (!authEnabled) return true;
|
|
55
|
+
if (!agent || !agent.ownerId) return true;
|
|
56
|
+
return agent.ownerId === selfUserId;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Which panel a phone shows (body[data-view], see .mobile-nav in style.css).
|
|
60
|
+
// Set unconditionally: desktop CSS ignores it, so no width check is needed.
|
|
61
|
+
export function setView(view) {
|
|
62
|
+
document.body.dataset.view = view;
|
|
63
|
+
for (const b of document.querySelectorAll('.mobile-nav button')) b.setAttribute('aria-current', b.dataset.view === view);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function stateColor(state) {
|
|
67
|
+
switch (state) {
|
|
68
|
+
case 'WORKING': return 'var(--state-working)';
|
|
69
|
+
case 'WAITING': return 'var(--state-waiting)';
|
|
70
|
+
case 'MESSAGE': return 'var(--state-message)';
|
|
71
|
+
case 'IDLE': return 'var(--state-idle)';
|
|
72
|
+
case 'DISCONNECTED': return 'var(--state-disconnected)';
|
|
73
|
+
default: return 'var(--state-idle)';
|
|
74
|
+
}
|
|
75
|
+
}
|