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.
@@ -0,0 +1,245 @@
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 — Dashboard</title>
7
+ <link rel="stylesheet" href="/login.css" />
8
+ </head>
9
+ <body class="dash-page">
10
+ <div class="dash-shell">
11
+ <div class="dash-card">
12
+ <!-- Header -->
13
+ <div class="dash-header">
14
+ <div class="dash-brand">
15
+ <div class="dash-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="dash-brand-name">AgentOffice</span>
19
+ </div>
20
+ <div class="dash-header-actions">
21
+ <a class="dash-nav-link" href="/">Office</a>
22
+ <button class="dash-logout" id="logout-btn">Log out</button>
23
+ </div>
24
+ </div>
25
+
26
+ <h1 class="dash-title">Dashboard</h1>
27
+ <div class="dash-user" id="user-info">Loading...</div>
28
+
29
+ <!-- API Keys -->
30
+ <div class="dash-section">
31
+ <h2 class="dash-section-title">API Keys</h2>
32
+ <div id="new-key-area"></div>
33
+ <div id="keys-list"><p class="dash-empty">Loading...</p></div>
34
+ <button class="dash-create-btn" id="create-key-btn">+ Create New Key</button>
35
+ </div>
36
+
37
+ <!-- Connect Office -->
38
+ <div class="dash-section">
39
+ <h2 class="dash-section-title">Connect Your Office</h2>
40
+ <div class="dash-command" id="command-box">
41
+ <code id="startup-command">Create an API key first</code>
42
+ </div>
43
+ <p class="dash-helper">Run this command on your local machine to connect your office to the cloud.</p>
44
+ <a href="#" class="dash-office-link offline" id="office-link" style="margin-top:16px">View Office (offline)</a>
45
+ </div>
46
+ </div>
47
+ </div>
48
+
49
+ <script>
50
+ (function () {
51
+ const API_BASE = window.AGENTOFFICE_API_BASE || "";
52
+ const token = localStorage.getItem("agentoffice_jwt");
53
+ const userId = localStorage.getItem("agentoffice_user_id");
54
+ const relayUrl = window.location.origin;
55
+
56
+ if (!token || !userId) {
57
+ window.location.href = "/";
58
+ return;
59
+ }
60
+
61
+ let currentKeys = [];
62
+ let latestNewKey = null;
63
+
64
+ async function apiFetch(path, options = {}) {
65
+ const res = await fetch(`${API_BASE}${path}`, {
66
+ headers: {
67
+ "Content-Type": "application/json",
68
+ "Authorization": "Bearer " + token
69
+ },
70
+ ...options
71
+ });
72
+ if (res.status === 401) {
73
+ localStorage.removeItem("agentoffice_jwt");
74
+ localStorage.removeItem("agentoffice_user_id");
75
+ window.location.href = "/";
76
+ throw new Error("unauthorized");
77
+ }
78
+ if (!res.ok) throw new Error(await res.text());
79
+ return res.json();
80
+ }
81
+
82
+ async function loadUser() {
83
+ try {
84
+ const user = await apiFetch("/api/auth/me");
85
+ document.getElementById("user-info").innerHTML =
86
+ "<strong>" + esc(user.displayName || "User") + "</strong> " + esc(user.email);
87
+ } catch {
88
+ document.getElementById("user-info").textContent = "Failed to load user info";
89
+ }
90
+ }
91
+
92
+ async function loadKeys() {
93
+ try {
94
+ const data = await apiFetch("/api/keys");
95
+ currentKeys = data.keys || [];
96
+ renderKeys();
97
+ } catch {
98
+ document.getElementById("keys-list").innerHTML = '<p class="dash-empty">Failed to load keys</p>';
99
+ }
100
+ }
101
+
102
+ function renderKeys() {
103
+ const container = document.getElementById("keys-list");
104
+ if (currentKeys.length === 0) {
105
+ container.innerHTML = '<p class="dash-empty">No API keys yet. Create one to get started.</p>';
106
+ updateCommand(null);
107
+ return;
108
+ }
109
+
110
+ container.innerHTML = currentKeys.map(function (key) {
111
+ return '<div class="dash-key-item">' +
112
+ '<span class="dash-key-prefix">' + esc(key.keyPrefix) + '...</span>' +
113
+ '<span class="dash-key-label">' + esc(key.label || "") + '</span>' +
114
+ '<span class="dash-key-date">' + (key.createdAt ? key.createdAt.split(" ")[0] : "") + '</span>' +
115
+ '<button class="dash-key-delete" data-key-id="' + esc(key.id) + '">Delete</button>' +
116
+ '</div>';
117
+ }).join("");
118
+
119
+ container.querySelectorAll(".dash-key-delete").forEach(function (btn) {
120
+ btn.addEventListener("click", async function () {
121
+ if (!confirm("Delete this API key? Any offices using it will disconnect.")) return;
122
+ btn.disabled = true;
123
+ try {
124
+ await apiFetch("/api/keys/" + btn.dataset.keyId, { method: "DELETE" });
125
+ await loadKeys();
126
+ } catch {
127
+ btn.disabled = false;
128
+ }
129
+ });
130
+ });
131
+
132
+ if (latestNewKey) {
133
+ updateCommand(latestNewKey);
134
+ } else {
135
+ updateCommand(null);
136
+ var cmd = document.getElementById("startup-command");
137
+ cmd.textContent = "ato start --key " + currentKeys[0].keyPrefix + "... --relay " + relayUrl;
138
+ }
139
+ }
140
+
141
+ function updateCommand(fullKey) {
142
+ var cmd = document.getElementById("startup-command");
143
+ var box = document.getElementById("command-box");
144
+ if (!fullKey) {
145
+ if (currentKeys.length === 0) {
146
+ cmd.textContent = "Create an API key first";
147
+ var old = box.querySelector(".dash-copy-btn");
148
+ if (old) old.remove();
149
+ }
150
+ return;
151
+ }
152
+ var command = "ato start --key " + fullKey + " --relay " + relayUrl;
153
+ cmd.textContent = command;
154
+
155
+ if (!box.querySelector(".dash-copy-btn")) {
156
+ var copyBtn = document.createElement("button");
157
+ copyBtn.className = "dash-copy-btn";
158
+ copyBtn.textContent = "Copy";
159
+ copyBtn.addEventListener("click", function () {
160
+ navigator.clipboard.writeText(command).then(function () {
161
+ copyBtn.textContent = "Copied!";
162
+ setTimeout(function () { copyBtn.textContent = "Copy"; }, 2000);
163
+ });
164
+ });
165
+ box.appendChild(copyBtn);
166
+ }
167
+ }
168
+
169
+ document.getElementById("create-key-btn").addEventListener("click", async function () {
170
+ var btn = document.getElementById("create-key-btn");
171
+ btn.disabled = true;
172
+ btn.textContent = "Creating...";
173
+ try {
174
+ var result = await apiFetch("/api/keys", {
175
+ method: "POST",
176
+ body: JSON.stringify({ label: "office" })
177
+ });
178
+ latestNewKey = result.key;
179
+
180
+ document.getElementById("new-key-area").innerHTML =
181
+ '<div class="dash-new-key">' +
182
+ '<p>New key created \u2014 copy it now, it won\'t be shown again!</p>' +
183
+ '<div class="dash-new-key-value">' +
184
+ '<code>' + esc(result.key) + '</code>' +
185
+ '<button class="dash-copy-btn" id="copy-new-key">Copy Key</button>' +
186
+ '</div>' +
187
+ '</div>';
188
+
189
+ document.getElementById("copy-new-key").addEventListener("click", function () {
190
+ navigator.clipboard.writeText(result.key).then(function () {
191
+ var el = document.getElementById("copy-new-key");
192
+ if (el) { el.textContent = "Copied!"; setTimeout(function () { if (el) el.textContent = "Copy Key"; }, 2000); }
193
+ });
194
+ });
195
+
196
+ await loadKeys();
197
+ } catch (err) {
198
+ alert("Failed to create key: " + err.message);
199
+ } finally {
200
+ btn.disabled = false;
201
+ btn.textContent = "+ Create New Key";
202
+ }
203
+ });
204
+
205
+ async function checkOfficeStatus() {
206
+ var link = document.getElementById("office-link");
207
+ try {
208
+ var res = await fetch(API_BASE + "/api/users/" + encodeURIComponent(userId) + "/status");
209
+ if (res.ok) {
210
+ var status = await res.json();
211
+ if (status.online) {
212
+ link.href = "/tunnel/" + encodeURIComponent(userId) + "/office.html";
213
+ link.textContent = "View Office";
214
+ link.classList.remove("offline");
215
+ return;
216
+ }
217
+ }
218
+ } catch {}
219
+ link.textContent = "View Office (offline)";
220
+ link.classList.add("offline");
221
+ link.href = "#";
222
+ }
223
+
224
+ document.getElementById("logout-btn").addEventListener("click", function () {
225
+ localStorage.removeItem("agentoffice_jwt");
226
+ localStorage.removeItem("agentoffice_user_id");
227
+ window.location.href = "/";
228
+ });
229
+
230
+ function esc(value) {
231
+ return String(value || "")
232
+ .replace(/&/g, "&amp;")
233
+ .replace(/</g, "&lt;")
234
+ .replace(/>/g, "&gt;")
235
+ .replace(/"/g, "&quot;");
236
+ }
237
+
238
+ loadUser();
239
+ loadKeys();
240
+ checkOfficeStatus();
241
+ setInterval(checkOfficeStatus, 15000);
242
+ })();
243
+ </script>
244
+ </body>
245
+ </html>
@@ -0,0 +1,84 @@
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">Checking session</h1>
20
+ <p class="auth-subtitle" id="status-copy">Verifying your account and opening your office.</p>
21
+ <div id="bootstrap-error" class="auth-error" hidden></div>
22
+ </div>
23
+ </div>
24
+
25
+ <script>
26
+ (function () {
27
+ const token = localStorage.getItem("agentoffice_jwt");
28
+ const fallbackPath = "/register.html";
29
+ const statusCopy = document.getElementById("status-copy");
30
+ const errorBox = document.getElementById("bootstrap-error");
31
+
32
+ if (!token) {
33
+ window.location.replace(fallbackPath);
34
+ return;
35
+ }
36
+
37
+ boot().catch(function (error) {
38
+ localStorage.removeItem("agentoffice_jwt");
39
+ localStorage.removeItem("agentoffice_user_id");
40
+ showError(error && error.message ? error.message : "Unable to verify your account right now.");
41
+ setTimeout(function () {
42
+ window.location.replace(fallbackPath);
43
+ }, 1200);
44
+ });
45
+
46
+ async function boot() {
47
+ const me = await apiFetch("/api/auth/me", token);
48
+ const userId = me.id || me.userId;
49
+ if (!userId) {
50
+ throw new Error("Missing user information.");
51
+ }
52
+
53
+ localStorage.setItem("agentoffice_user_id", userId);
54
+ statusCopy.textContent = "Account verified. Opening office...";
55
+ window.location.replace("/tunnel/" + encodeURIComponent(userId) + "/office.html");
56
+ }
57
+
58
+ async function apiFetch(path, bearerToken) {
59
+ const response = await fetch(path, {
60
+ headers: {
61
+ Authorization: "Bearer " + bearerToken,
62
+ Accept: "application/json"
63
+ }
64
+ });
65
+
66
+ if (response.status === 401) {
67
+ throw new Error("Session expired.");
68
+ }
69
+
70
+ if (!response.ok) {
71
+ throw new Error("Authentication failed.");
72
+ }
73
+
74
+ return response.json();
75
+ }
76
+
77
+ function showError(message) {
78
+ errorBox.textContent = message;
79
+ errorBox.hidden = false;
80
+ }
81
+ })();
82
+ </script>
83
+ </body>
84
+ </html>