secure-agents 0.7.0 → 0.7.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/dist/config.js CHANGED
@@ -18,7 +18,15 @@ export async function moveOldHome() {
18
18
  const running = await fetch(`http://127.0.0.1:${port}/ui`, { signal: AbortSignal.timeout(1500) }).then(() => true, () => false);
19
19
  if (running)
20
20
  throw new Error('The old Securee is still running. Close its window (or stop it), then run this again: your data moves to ~/.secure.');
21
- fs.renameSync(old, HOME);
21
+ try {
22
+ fs.renameSync(old, HOME);
23
+ }
24
+ catch (e) {
25
+ const code = e.code;
26
+ if (code !== 'EPERM' && code !== 'EBUSY' && code !== 'EACCES')
27
+ throw e;
28
+ throw new Error(`Could not move ${old}: something still uses it, usually Claude Code or Codex running the old Securee tools. Close Claude Code and Codex completely (or restart the computer), then run this again.`);
29
+ }
22
30
  return `Moved your data from ${old} to ${HOME}: same keys, pairings and passphrase.`;
23
31
  }
24
32
  export const DEFAULT_LIMITS = { messagesPerMinute: 60, maxPendingPerPeer: 20, maxItems: 2000, maxLoginFailures: 5, lockoutSeconds: 60, sessionHours: 12, pendingHours: 24, updateCheckHours: 6, updateCheckOnOpenMinutes: 60, minFreeDiskMB: 1024, precheckSeconds: 120, historyLogBytes: 16384, notifyGroupSeconds: 3, fileLimitMaxMB: 51200, wakeMinutes: 240 };
package/dist/ui.js CHANGED
@@ -69,9 +69,12 @@ button:focus-visible,input:focus-visible,select:focus-visible,summary:focus-visi
69
69
  .toasts{position:fixed;right:20px;bottom:20px;z-index:20;display:grid;gap:8px;max-width:min(420px,calc(100vw - 40px))}
70
70
  .toast{background:var(--inv);color:var(--inv-ink);border-radius:12px;padding:12px 16px;font-size:13.5px;box-shadow:0 12px 32px -8px rgba(0,0,0,.35);animation:rise .2s ease-out} .toast.err{background:var(--bad);color:#fff}
71
71
  @keyframes rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
72
- dialog{border:1px solid var(--line);border-radius:16px;padding:0;background:var(--surface);color:var(--ink);width:min(520px,calc(100vw - 32px));box-shadow:0 30px 80px -20px rgba(0,0,0,.45)}
72
+ dialog{border:1px solid var(--line);border-radius:16px;padding:0;max-height:calc(100dvh - 32px);overflow:auto;background:var(--surface);color:var(--ink);width:min(520px,calc(100vw - 32px));box-shadow:0 30px 80px -20px rgba(0,0,0,.45)}
73
73
  dialog::backdrop{background:rgba(0,0,0,.45);backdrop-filter:blur(3px)}
74
74
  .dlg{padding:24px;display:grid;gap:16px}
75
+ .flow{display:grid;grid-template-columns:repeat(5,1fr);gap:8px;counter-reset:step} .flow>div{border:1px solid var(--line);border-radius:10px;padding:10px;display:grid;gap:4px;align-content:start;position:relative} .flow>div::before{counter-increment:step;content:counter(step);font-size:11px;color:var(--ink2)} .flow b{font-size:13px} .flow span{font-size:12px;color:var(--ink2)}
76
+ .opts{display:grid;gap:8px;margin:0;padding:0;list-style:none} .opts li{display:grid;grid-template-columns:150px 1fr;gap:12px;font-size:13px} .opts li b{font-weight:600}
77
+ @media (max-width:640px){.flow{grid-template-columns:1fr}.opts li{grid-template-columns:1fr;gap:2px}}
75
78
  .notes{margin:0;padding-left:18px;display:grid;gap:6px;color:var(--ink2)}
76
79
  .progress{height:4px;border-radius:4px;background:var(--sunk);overflow:hidden} .progress i{display:block;height:100%;width:35%;background:var(--ink);border-radius:4px;animation:slide 1.2s ease-in-out infinite}
77
80
  @keyframes slide{0%{transform:translateX(-100%)}100%{transform:translateX(300%)}}
@@ -99,7 +102,7 @@ dialog::backdrop{background:rgba(0,0,0,.45);backdrop-filter:blur(3px)}
99
102
  <section class="page" data-page="brain" hidden><div class="head"><h2>Brain</h2><p class="muted">Decisions your agent saved, with why and sources. Shared ones can be searched by paired computers, even while this one is off.</p></div><div id="brain" class="page" style="padding:0"></div></section>
100
103
 
101
104
  <section class="page" data-page="settings" hidden><div class="head"><h2>Settings</h2><p class="muted">How your agent is started, updates, and your passkeys.</p></div>
102
- <div class="card"><div><h3>Wake your agent</h3><p class="muted small">Starts your agent by itself for a question or task you approved, so it answers without you opening it. Questions never change files.</p></div>
105
+ <div class="card"><div class="row"><div class="grow"><h3>Wake your agent</h3><p class="muted small">Starts your agent by itself for a question or task you approved, so it answers without you opening it. Questions never change files.</p></div><button class="ghost" id="wake-help">How this works</button></div>
103
106
  <p id="wake"></p>
104
107
  <div class="row"><select id="wake-preset" aria-label="Agent to wake"><option value="off">Off: I tell my agent myself</option><option value="claude">Claude Code</option><option value="codex">Codex</option><option value="custom">Other agent (type its command)</option></select>
105
108
  <input id="wake-cwd" class="field" placeholder="Folder where tasks may make changes, e.g. /Users/me/project" aria-label="Folder where tasks may make changes"></div>
@@ -116,6 +119,16 @@ dialog::backdrop{background:rgba(0,0,0,.45);backdrop-filter:blur(3px)}
116
119
  </div>
117
120
  </main>
118
121
  <div class="toasts" id="toasts" aria-live="polite"></div>
122
+ <dialog id="wake-dlg" style="width:min(760px,calc(100vw - 32px))"><div class="dlg"><div class="row"><div class="mark">${lock()}</div><div class="grow"><h3>How waking your agent works</h3><p class="muted small">What happens when another computer asks your agent something.</p></div></div>
123
+ <div class="flow"><div><b>Request arrives</b><span>A question or a task from a paired computer.</span></div><div><b>You approve</b><span>With your passkey. Automatic questions skip this unless Secure flags them.</span></div><div><b>Secure starts your agent</b><span>Claude Code or Codex, in the background, inside its sandbox.</span></div><div><b>It does the work</b><span>Questions only read. Tasks change files only inside your folder.</span></div><div><b>Result goes back</b><span>An answer with sources, or a signed task result.</span></div></div>
124
+ <p class="sub" style="margin:0">The settings</p>
125
+ <ul class="opts"><li><b>Off</b><span>Nothing starts by itself. Approved requests wait until you open your agent and say "check Secure".</span></li>
126
+ <li><b>Claude Code / Codex</b><span>Secure starts that agent for you. What it may read is set per computer (Computers, then Reach).</span></li>
127
+ <li><b>Folder</b><span>Where tasks may change files, including every folder inside it. Nothing outside it can be changed. Questions never change files.</span></li>
128
+ <li><b>Tasks may run commands</b><span>Lets a task run tests, builds and scripts. Off: it can only read and edit files.</span></li>
129
+ <li><b>Tasks may use the internet</b><span>Lets a task open web pages and call online services.</span></li>
130
+ <li><b>Stop a run after</b><span>The longest one run may take before Secure stops it.</span></li></ul>
131
+ <div class="row"><span class="grow"></span><button class="primary" id="wake-dlg-close">Got it</button></div></div></dialog>
119
132
  <dialog id="update-dlg"><div class="dlg"><div class="row"><div class="mark">${lock()}</div><div class="grow"><h3 id="dlg-title">Update</h3><p class="muted small" id="dlg-sub"></p></div></div>
120
133
  <div id="dlg-notes-wrap"><p class="sub" style="margin-bottom:8px">What's new</p><ul class="notes" id="dlg-notes"></ul></div>
121
134
  <p class="small"><a id="dlg-link" target="_blank" rel="noopener">See the exact code changes on npm</a></p>
@@ -136,8 +149,10 @@ $('out').onclick = () => api('/human/logout', {}).then(load);
136
149
  // Tabs
137
150
  let tab = 'inbox';
138
151
  try { tab = localStorage.getItem('secure-tab') || 'inbox'; } catch (e) {}
139
- function show(t) { tab = t; try { localStorage.setItem('secure-tab', t); } catch (e) {} for (const b of document.querySelectorAll('nav.tabs button')) b.setAttribute('aria-selected', String(b.dataset.tab === t)); for (const s of document.querySelectorAll('[data-page]')) s.hidden = s.dataset.page !== t; if (t === 'history') loadHistory(); }
152
+ function show(t) { tab = t; try { localStorage.setItem('secure-tab', t); } catch (e) {}
153
+ if (t === 'settings' && !app.hidden) { let seen = true; try { seen = !!localStorage.getItem('secure-wake-explained'); localStorage.setItem('secure-wake-explained', '1'); } catch (e) {} if (!seen) $('wake-dlg').showModal(); } for (const b of document.querySelectorAll('nav.tabs button')) b.setAttribute('aria-selected', String(b.dataset.tab === t)); for (const s of document.querySelectorAll('[data-page]')) s.hidden = s.dataset.page !== t; if (t === 'history') loadHistory(); }
140
154
  for (const b of document.querySelectorAll('nav.tabs button')) b.onclick = () => show(b.dataset.tab);
155
+ $('wake-help').onclick = () => $('wake-dlg').showModal(); $('wake-dlg-close').onclick = () => $('wake-dlg').close();
141
156
 
142
157
  // Passkeys: the server fixes exactly what will happen, then your touch confirms it.
143
158
  const b64 = (s) => Uint8Array.from(atob(s.replace(/-/g, '+').replace(/_/g, '/') + '==='.slice((s.length + 3) % 4)), c => c.charCodeAt(0));
@@ -303,7 +318,7 @@ function brainCards(list) {
303
318
  el('p', { textContent: d.decision }), d.why ? el('p', { className: 'muted small', textContent: 'Why: ' + d.why }) : null, (d.sources || []).length ? el('p', { className: 'mono muted', textContent: d.sources.join(' · ') }) : null));
304
319
  }
305
320
 
306
- let pendingSig = '', peersSig = '', brainSig = '', lastState = {}, announced = '';
321
+ let pendingSig = '', peersSig = '', brainSig = '', wakeSig = '', lastState = {}, announced = '';
307
322
  async function load(force) {
308
323
  const s = await api('/human/state');
309
324
  signin.hidden = s.error !== 'signin';
@@ -323,7 +338,9 @@ async function load(force) {
323
338
  if (s.wakeLast) { wl.textContent = 'Last run ' + new Date(s.wakeLast.at).toLocaleTimeString() + ': ' + s.wakeLast.message; wl.style.color = s.wakeLast.ok ? '' : 'var(--bad)'; }
324
339
  $('wake').textContent = s.wake ? 'Wakes ' + s.wake + '.' : 'Off. Approved requests wait until you tell your agent "check Secure".';
325
340
  if (document.activeElement !== $('wake-preset') && document.activeElement !== $('wake-cwd') && !$('wake-cwd').value && s.wakePreset) $('wake-preset').value = s.wakePreset;
326
- if (!lastState.formsFilled) { lastState.formsFilled = true; $('wake-min').value = s.wakeMinutes; $('wake-net').checked = s.wakeInternet; $('wake-cmds').checked = s.wakeCommands; }
341
+ // Refill only when the saved values change, so a box you just ticked isn't reset before you save.
342
+ const ws = JSON.stringify([s.wakeMinutes, s.wakeInternet, s.wakeCommands]);
343
+ if (ws !== wakeSig) { wakeSig = ws; $('wake-min').value = s.wakeMinutes; $('wake-net').checked = s.wakeInternet; $('wake-cmds').checked = s.wakeCommands; }
327
344
  $('fl-mb').max = s.fileLimitMaxMB; $('fl-now').textContent = 'Now ' + Math.round(s.maxFileBytes / 1048576) + ' MB';
328
345
  // Redraw only what changed, so a half-read card or a typed path isn't reset every refresh.
329
346
  // Questions your agent is still checking stay out of the Inbox; they show up only if they need you.
package/dist/waker.js CHANGED
@@ -29,8 +29,9 @@ export function describeWake(w) {
29
29
  if (w.preset === 'custom')
30
30
  return `"${w.line}" (not sandboxed by Secure, so "Shared folders" can't be enforced); tasks change files in ${w.cwd}`;
31
31
  const old = w.preset === 'claude' && !claudeCanRestrict(w.question.command);
32
+ const cmds = commandsAllowed(w) ? '; tasks may run commands' : '; tasks may not run commands';
32
33
  const net = w.internet ? (w.preset === 'codex' ? '; tasks may use the internet when reach is the whole computer' : '; tasks may use the internet') : '';
33
- return `${w.preset === 'codex' ? 'Codex' : 'Claude Code'}; tasks change files in ${w.cwd}${net}${old ? '. This Claude Code is too old to limit reading to shared folders: update it' : ''}`;
34
+ return `${w.preset === 'codex' ? 'Codex' : 'Claude Code'}; tasks change files in ${w.cwd}${cmds}${net}${old ? '. This Claude Code is too old to limit reading to shared folders: update it' : ''}`;
34
35
  }
35
36
  /** Older saved wakes kept the task flags in their arguments. */
36
37
  const commandsAllowed = (w) => w.allowCommands ?? w.task.args.some((a) => a === 'workspace-write' || a === 'Bash');
package/package.json CHANGED
@@ -1,16 +1,11 @@
1
1
  {
2
2
  "name": "secure-agents",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "Let AI agents on different computers and platforms talk to each other over a direct encrypted link, with both people's consent.",
5
5
  "changes": [
6
- "Renamed from Securee to Secure. The npm package is now secure-agents; your data moves from ~/.securee to ~/.secure on first start.",
7
- "Desktop notification and a count in the tab title when something waits for your approval.",
8
- "The asking agent is told when its request waits for the other person's approval, and when they approve it, so it can tell you in chat.",
9
- "Questions your agent is still checking no longer flash up in the Inbox.",
10
- "Clearer reasons when a file isn't found, and whoami now says its rules are what this computer allows others.",
11
- "Settings: set the largest file this computer sends or accepts. Paired computers are told, and files move up to the smaller of the two limits.",
12
- "Task conversations: the agent doing a task can ask the asker a question and carry on with the answer, and follow-up questions about a task resume the same agent session.",
13
- "Settings: choose how long your agent may run (default now 4 hours, was 30 minutes), and whether tasks may use the internet."
6
+ "Fix: ticking \"Tasks may run commands\" or \"Tasks may use the internet\" in Settings no longer un-ticks itself before you save.",
7
+ "Settings shows whether tasks may run commands.",
8
+ "Settings: a \"How this works\" pop-up for Wake your agent, shown the first time you open Settings."
14
9
  ],
15
10
  "homepage": "https://www.npmjs.com/package/secure-agents",
16
11
  "type": "module",