agent-office-cli 0.0.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.
- package/package.json +28 -0
- package/src/auth.js +178 -0
- package/src/core/config.js +13 -0
- package/src/core/index.js +36 -0
- package/src/core/providers/base.js +38 -0
- package/src/core/providers/claude-transcript.js +126 -0
- package/src/core/providers/claude.js +199 -0
- package/src/core/providers/codex-transcript.js +210 -0
- package/src/core/providers/codex.js +91 -0
- package/src/core/providers/generic.js +40 -0
- package/src/core/providers/index.js +17 -0
- package/src/core/session-contract.js +98 -0
- package/src/core/state.js +23 -0
- package/src/core/store/session-store.js +232 -0
- package/src/index.js +348 -0
- package/src/runtime/cli-helpers.js +90 -0
- package/src/runtime/ensure-node-pty.js +49 -0
- package/src/runtime/index.js +54 -0
- package/src/runtime/pty-manager.js +598 -0
- package/src/runtime/session-registry.js +74 -0
- package/src/runtime/tmux.js +152 -0
- package/src/server.js +208 -0
- package/src/tunnel.js +224 -0
- package/src/web/index.js +7 -0
- package/src/web/public/app.js +713 -0
- package/src/web/public/dashboard.html +245 -0
- package/src/web/public/index.html +84 -0
- package/src/web/public/login.css +833 -0
- package/src/web/public/login.html +28 -0
- package/src/web/public/office.html +22 -0
- package/src/web/public/register.html +316 -0
- package/src/web/public/styles.css +988 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>AgentOffice</title>
|
|
7
|
+
<link rel="stylesheet" href="/login.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body class="auth-page">
|
|
10
|
+
<div class="auth-shell">
|
|
11
|
+
<div class="auth-card">
|
|
12
|
+
<div class="auth-brand">
|
|
13
|
+
<div class="auth-brand-mark">
|
|
14
|
+
<svg viewBox="0 0 24 24"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>
|
|
15
|
+
</div>
|
|
16
|
+
<span class="auth-brand-name">AgentOffice</span>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<h1 class="auth-title">Redirecting</h1>
|
|
20
|
+
<p class="auth-subtitle">AgentOffice now uses the shared account login page.</p>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<script>
|
|
25
|
+
window.location.replace("/register.html");
|
|
26
|
+
</script>
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>AgentOffice</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=DM+Serif+Display&display=swap" rel="stylesheet" />
|
|
10
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/css/xterm.min.css" />
|
|
11
|
+
<link rel="stylesheet" href="styles.css" />
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div id="app"></div>
|
|
15
|
+
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.js"></script>
|
|
17
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0.10.0/lib/addon-fit.min.js"></script>
|
|
18
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-webgl@0.18.0/lib/addon-webgl.min.js"></script>
|
|
19
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-unicode11@0.8.0/lib/addon-unicode11.min.js"></script>
|
|
20
|
+
<script src="app.js"></script>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>AgentOffice — Sign Up / Login</title>
|
|
7
|
+
<link rel="stylesheet" href="/login.css" />
|
|
8
|
+
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" defer></script>
|
|
9
|
+
</head>
|
|
10
|
+
<body class="auth-page">
|
|
11
|
+
<div class="auth-shell">
|
|
12
|
+
<div class="auth-card">
|
|
13
|
+
<!-- Brand -->
|
|
14
|
+
<div class="auth-brand">
|
|
15
|
+
<div class="auth-brand-mark">
|
|
16
|
+
<svg viewBox="0 0 24 24"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>
|
|
17
|
+
</div>
|
|
18
|
+
<span class="auth-brand-name">AgentOffice</span>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<h1 class="auth-title" id="page-title">Welcome back</h1>
|
|
22
|
+
<p class="auth-subtitle" id="page-desc">Sign in to manage your API keys and office.</p>
|
|
23
|
+
|
|
24
|
+
<!-- ===== LOGIN ===== -->
|
|
25
|
+
<div class="auth-mode active" id="login-section">
|
|
26
|
+
<form class="auth-form" id="login-form">
|
|
27
|
+
<div class="auth-field">
|
|
28
|
+
<label class="auth-label" for="login-email">Email</label>
|
|
29
|
+
<input class="auth-input" id="login-email" type="email" placeholder="you@example.com" autocomplete="email" required />
|
|
30
|
+
</div>
|
|
31
|
+
<div class="auth-field">
|
|
32
|
+
<label class="auth-label" for="login-password">Password</label>
|
|
33
|
+
<input class="auth-input" id="login-password" type="password" placeholder="Your password" autocomplete="current-password" required />
|
|
34
|
+
</div>
|
|
35
|
+
<button class="auth-btn auth-btn-primary" type="submit" id="login-btn">Log In</button>
|
|
36
|
+
</form>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<!-- ===== REGISTER ===== -->
|
|
40
|
+
<div class="auth-mode" id="register-section">
|
|
41
|
+
<form class="auth-form" id="register-form">
|
|
42
|
+
<div class="auth-field">
|
|
43
|
+
<label class="auth-label" for="reg-email">Email</label>
|
|
44
|
+
<div class="auth-email-row">
|
|
45
|
+
<input class="auth-input" id="reg-email" type="email" placeholder="you@example.com" autocomplete="email" required />
|
|
46
|
+
<button type="button" class="auth-send-btn" id="send-code-btn">Send Code</button>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="auth-turnstile" id="turnstile-container"></div>
|
|
51
|
+
|
|
52
|
+
<div class="auth-code-row" id="code-row">
|
|
53
|
+
<input class="auth-code-input" id="code-input" type="text" inputmode="numeric" pattern="[0-9]{6}" maxlength="6" placeholder="6-digit code" autocomplete="one-time-code" required />
|
|
54
|
+
<button type="button" class="auth-resend-btn" id="resend-btn" disabled>Resend</button>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div class="auth-field">
|
|
58
|
+
<label class="auth-label" for="reg-password">Password</label>
|
|
59
|
+
<input class="auth-input" id="reg-password" type="password" placeholder="Choose a password" autocomplete="new-password" required />
|
|
60
|
+
</div>
|
|
61
|
+
<div class="auth-field">
|
|
62
|
+
<label class="auth-label" for="reg-name">Display Name</label>
|
|
63
|
+
<input class="auth-input" id="reg-name" type="text" placeholder="Optional" autocomplete="name" />
|
|
64
|
+
</div>
|
|
65
|
+
<button class="auth-btn auth-btn-primary" type="submit" id="register-btn">Create Account</button>
|
|
66
|
+
</form>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div id="auth-error" class="auth-error" hidden></div>
|
|
70
|
+
|
|
71
|
+
<div class="auth-toggle">
|
|
72
|
+
<span id="toggle-text">Don't have an account?</span>
|
|
73
|
+
<a id="toggle-link">Sign up</a>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<script>
|
|
79
|
+
(function () {
|
|
80
|
+
const API_BASE = window.AGENTOFFICE_API_BASE || "";
|
|
81
|
+
|
|
82
|
+
const errorBox = document.getElementById("auth-error");
|
|
83
|
+
const loginSection = document.getElementById("login-section");
|
|
84
|
+
const registerSection = document.getElementById("register-section");
|
|
85
|
+
const pageTitle = document.getElementById("page-title");
|
|
86
|
+
const pageDesc = document.getElementById("page-desc");
|
|
87
|
+
const toggleText = document.getElementById("toggle-text");
|
|
88
|
+
const toggleLink = document.getElementById("toggle-link");
|
|
89
|
+
const loginForm = document.getElementById("login-form");
|
|
90
|
+
const registerForm = document.getElementById("register-form");
|
|
91
|
+
const loginBtn = document.getElementById("login-btn");
|
|
92
|
+
const registerBtn = document.getElementById("register-btn");
|
|
93
|
+
const sendCodeBtn = document.getElementById("send-code-btn");
|
|
94
|
+
const codeRow = document.getElementById("code-row");
|
|
95
|
+
const codeInput = document.getElementById("code-input");
|
|
96
|
+
const resendBtn = document.getElementById("resend-btn");
|
|
97
|
+
const regEmail = document.getElementById("reg-email");
|
|
98
|
+
|
|
99
|
+
let isRegister = false;
|
|
100
|
+
let codeSent = false;
|
|
101
|
+
let countdownTimer = null;
|
|
102
|
+
let turnstileWidgetId = null;
|
|
103
|
+
let turnstileSiteKey = "";
|
|
104
|
+
|
|
105
|
+
// --- Mode toggle ---
|
|
106
|
+
toggleLink.addEventListener("click", (e) => {
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
isRegister = !isRegister;
|
|
109
|
+
errorBox.hidden = true;
|
|
110
|
+
if (isRegister) {
|
|
111
|
+
pageTitle.textContent = "Create account";
|
|
112
|
+
pageDesc.textContent = "Sign up to get your API key and connect your office.";
|
|
113
|
+
loginSection.classList.remove("active");
|
|
114
|
+
registerSection.classList.add("active");
|
|
115
|
+
toggleText.textContent = "Already have an account?";
|
|
116
|
+
toggleLink.textContent = "Log in";
|
|
117
|
+
} else {
|
|
118
|
+
pageTitle.textContent = "Welcome back";
|
|
119
|
+
pageDesc.textContent = "Sign in to manage your API keys and office.";
|
|
120
|
+
registerSection.classList.remove("active");
|
|
121
|
+
loginSection.classList.add("active");
|
|
122
|
+
toggleText.textContent = "Don't have an account?";
|
|
123
|
+
toggleLink.textContent = "Sign up";
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// --- Turnstile ---
|
|
128
|
+
async function initTurnstile() {
|
|
129
|
+
try {
|
|
130
|
+
const res = await fetch(`${API_BASE}/api/config/public`);
|
|
131
|
+
const data = await res.json();
|
|
132
|
+
turnstileSiteKey = data.turnstileSiteKey || "";
|
|
133
|
+
} catch { /* ignore */ }
|
|
134
|
+
|
|
135
|
+
if (turnstileSiteKey) {
|
|
136
|
+
const tryRender = () => {
|
|
137
|
+
if (typeof turnstile !== "undefined") renderTurnstile();
|
|
138
|
+
};
|
|
139
|
+
tryRender();
|
|
140
|
+
window.addEventListener("load", tryRender);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function renderTurnstile() {
|
|
145
|
+
const container = document.getElementById("turnstile-container");
|
|
146
|
+
if (turnstileWidgetId !== null) return;
|
|
147
|
+
turnstileWidgetId = turnstile.render(container, {
|
|
148
|
+
sitekey: turnstileSiteKey,
|
|
149
|
+
theme: "light"
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function getTurnstileToken() {
|
|
154
|
+
if (!turnstileSiteKey || typeof turnstile === "undefined") return "";
|
|
155
|
+
return turnstile.getResponse(turnstileWidgetId) || "";
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function resetTurnstile() {
|
|
159
|
+
if (turnstileWidgetId !== null && typeof turnstile !== "undefined") {
|
|
160
|
+
turnstile.reset(turnstileWidgetId);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
initTurnstile();
|
|
165
|
+
|
|
166
|
+
// --- Send code ---
|
|
167
|
+
sendCodeBtn.addEventListener("click", () => sendCode());
|
|
168
|
+
resendBtn.addEventListener("click", () => sendCode());
|
|
169
|
+
|
|
170
|
+
async function sendCode() {
|
|
171
|
+
const email = regEmail.value.trim();
|
|
172
|
+
if (!email) { showError("Please enter your email."); return; }
|
|
173
|
+
errorBox.hidden = true;
|
|
174
|
+
sendCodeBtn.disabled = true;
|
|
175
|
+
sendCodeBtn.textContent = "Sending...";
|
|
176
|
+
resendBtn.disabled = true;
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const res = await fetch(`${API_BASE}/api/auth/send-code`, {
|
|
180
|
+
method: "POST",
|
|
181
|
+
headers: { "Content-Type": "application/json" },
|
|
182
|
+
body: JSON.stringify({ email, turnstileToken: getTurnstileToken() })
|
|
183
|
+
});
|
|
184
|
+
const data = await res.json();
|
|
185
|
+
|
|
186
|
+
if (!res.ok) {
|
|
187
|
+
showError(data.error || "Failed to send code.");
|
|
188
|
+
resetTurnstile();
|
|
189
|
+
sendCodeBtn.disabled = false;
|
|
190
|
+
sendCodeBtn.textContent = "Send Code";
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
codeSent = true;
|
|
195
|
+
codeRow.classList.add("visible");
|
|
196
|
+
codeInput.focus();
|
|
197
|
+
startCountdown(60);
|
|
198
|
+
sendCodeBtn.textContent = "Sent";
|
|
199
|
+
sendCodeBtn.classList.add("sent");
|
|
200
|
+
} catch {
|
|
201
|
+
showError("Connection failed. Is the API server running?");
|
|
202
|
+
sendCodeBtn.disabled = false;
|
|
203
|
+
sendCodeBtn.textContent = "Send Code";
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function startCountdown(seconds) {
|
|
208
|
+
clearInterval(countdownTimer);
|
|
209
|
+
let remaining = seconds;
|
|
210
|
+
resendBtn.disabled = true;
|
|
211
|
+
resendBtn.textContent = remaining + "s";
|
|
212
|
+
countdownTimer = setInterval(() => {
|
|
213
|
+
remaining--;
|
|
214
|
+
if (remaining <= 0) {
|
|
215
|
+
clearInterval(countdownTimer);
|
|
216
|
+
resendBtn.disabled = false;
|
|
217
|
+
resendBtn.textContent = "Resend";
|
|
218
|
+
sendCodeBtn.disabled = false;
|
|
219
|
+
sendCodeBtn.textContent = "Send Code";
|
|
220
|
+
sendCodeBtn.classList.remove("sent");
|
|
221
|
+
} else {
|
|
222
|
+
resendBtn.textContent = remaining + "s";
|
|
223
|
+
}
|
|
224
|
+
}, 1000);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// --- Register ---
|
|
228
|
+
registerForm.addEventListener("submit", async (e) => {
|
|
229
|
+
e.preventDefault();
|
|
230
|
+
errorBox.hidden = true;
|
|
231
|
+
|
|
232
|
+
if (!codeSent) {
|
|
233
|
+
showError("Please send a verification code first.");
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const code = codeInput.value.trim();
|
|
238
|
+
if (code.length !== 6) {
|
|
239
|
+
showError("Please enter the 6-digit verification code.");
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
registerBtn.disabled = true;
|
|
244
|
+
registerBtn.textContent = "Creating account...";
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
const res = await fetch(`${API_BASE}/api/auth/register`, {
|
|
248
|
+
method: "POST",
|
|
249
|
+
headers: { "Content-Type": "application/json" },
|
|
250
|
+
body: JSON.stringify({
|
|
251
|
+
email: regEmail.value.trim(),
|
|
252
|
+
password: document.getElementById("reg-password").value,
|
|
253
|
+
displayName: document.getElementById("reg-name").value.trim(),
|
|
254
|
+
code
|
|
255
|
+
})
|
|
256
|
+
});
|
|
257
|
+
const data = await res.json();
|
|
258
|
+
|
|
259
|
+
if (res.ok && data.token) {
|
|
260
|
+
localStorage.setItem("agentoffice_jwt", data.token);
|
|
261
|
+
localStorage.setItem("agentoffice_user_id", data.userId);
|
|
262
|
+
window.location.href = "/";
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
showError(data.error || "Registration failed.");
|
|
267
|
+
} catch {
|
|
268
|
+
showError("Connection failed. Is the API server running?");
|
|
269
|
+
} finally {
|
|
270
|
+
registerBtn.disabled = false;
|
|
271
|
+
registerBtn.textContent = "Create Account";
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// --- Login ---
|
|
276
|
+
loginForm.addEventListener("submit", async (e) => {
|
|
277
|
+
e.preventDefault();
|
|
278
|
+
errorBox.hidden = true;
|
|
279
|
+
loginBtn.disabled = true;
|
|
280
|
+
loginBtn.textContent = "Signing in...";
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
const res = await fetch(`${API_BASE}/api/auth/login`, {
|
|
284
|
+
method: "POST",
|
|
285
|
+
headers: { "Content-Type": "application/json" },
|
|
286
|
+
body: JSON.stringify({
|
|
287
|
+
email: document.getElementById("login-email").value.trim(),
|
|
288
|
+
password: document.getElementById("login-password").value
|
|
289
|
+
})
|
|
290
|
+
});
|
|
291
|
+
const data = await res.json();
|
|
292
|
+
|
|
293
|
+
if (res.ok && data.token) {
|
|
294
|
+
localStorage.setItem("agentoffice_jwt", data.token);
|
|
295
|
+
localStorage.setItem("agentoffice_user_id", data.userId);
|
|
296
|
+
window.location.href = "/";
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
showError(data.error || "Login failed.");
|
|
301
|
+
} catch {
|
|
302
|
+
showError("Connection failed. Is the API server running?");
|
|
303
|
+
} finally {
|
|
304
|
+
loginBtn.disabled = false;
|
|
305
|
+
loginBtn.textContent = "Log In";
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
function showError(msg) {
|
|
310
|
+
errorBox.textContent = msg;
|
|
311
|
+
errorBox.hidden = false;
|
|
312
|
+
}
|
|
313
|
+
})();
|
|
314
|
+
</script>
|
|
315
|
+
</body>
|
|
316
|
+
</html>
|