@shardflux/cli 0.14.4 → 0.15.0
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/CHANGELOG.md +66 -0
- package/README.md +73 -11
- package/dist/account-args.js +28 -7
- package/dist/account-commands.js +140 -11
- package/dist/acp-catalog.js +2 -2
- package/dist/acp-login.d.ts +20 -0
- package/dist/acp-login.js +122 -0
- package/dist/acp.js +36 -13
- package/dist/agent-commands.js +58 -6
- package/dist/agent-git-commands.js +2 -1
- package/dist/agent-reachability.js +1 -1
- package/dist/agent-target-commands.js +26 -1
- package/dist/agent-targets.d.ts +7 -0
- package/dist/agent-targets.js +44 -32
- package/dist/agent-terminal.d.ts +24 -2
- package/dist/agent-terminal.js +82 -4
- package/dist/args.d.ts +3 -1
- package/dist/args.js +21 -9
- package/dist/browser-login.d.ts +35 -0
- package/dist/browser-login.js +145 -0
- package/dist/claude-cloud-login.js +2 -2
- package/dist/codex-cloud.d.ts +49 -0
- package/dist/codex-cloud.js +157 -0
- package/dist/command-center-actions.js +7 -0
- package/dist/command-center-renderer.d.ts +3 -0
- package/dist/command-center-renderer.js +42 -38
- package/dist/command-center.js +65 -11
- package/dist/conversation-terminal.js +19 -0
- package/dist/conversations.d.ts +1 -1
- package/dist/conversations.js +21 -8
- package/dist/credential-prompts.d.ts +4 -2
- package/dist/credential-prompts.js +38 -16
- package/dist/device-bridge.d.ts +13 -0
- package/dist/device-bridge.js +14 -4
- package/dist/device-helper.js +2 -1
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +15 -2
- package/dist/handlers.js +2 -1
- package/dist/initial-prompt.d.ts +26 -0
- package/dist/initial-prompt.js +196 -0
- package/dist/legal.d.ts +36 -0
- package/dist/legal.js +44 -0
- package/dist/main.js +6 -2
- package/dist/menubar.d.ts +8 -0
- package/dist/menubar.js +9 -1
- package/dist/onboarding-app/AGENTS.md +19 -0
- package/dist/onboarding-app/CLAUDE.md +19 -0
- package/dist/onboarding-app/README.md +12 -0
- package/dist/onboarding-app/dev.mjs +18 -0
- package/dist/onboarding-app/index.html +25 -0
- package/dist/onboarding-app/package.json +12 -0
- package/dist/onboarding-app/server.mjs +41 -0
- package/dist/onboarding-sample.d.ts +3 -0
- package/dist/onboarding-sample.js +29 -0
- package/dist/onboarding.d.ts +258 -0
- package/dist/onboarding.js +371 -0
- package/dist/terminal-brand.d.ts +5 -0
- package/dist/terminal-brand.js +18 -0
- package/package.json +3 -3
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Daylight
|
|
2
|
+
|
|
3
|
+
A small task list: `server.mjs` (a Node.js server with a JSON API) and `index.html`. No dependencies.
|
|
4
|
+
|
|
5
|
+
## Run it
|
|
6
|
+
|
|
7
|
+
- `npm run dev` serves it on port 3000 and restarts on save. It watches files with inotifywait from inotify-tools, a
|
|
8
|
+
system package: install it with `sudo apt-get install -y inotify-tools` if it is missing.
|
|
9
|
+
- Open `http://localhost:3000` in the browser on your desktop (`DISPLAY=:1 setsid -f chromium http://localhost:3000`)
|
|
10
|
+
and use the computer tool to see and click it.
|
|
11
|
+
|
|
12
|
+
## Test it
|
|
13
|
+
|
|
14
|
+
Use it like a person: add a task, tick it off, and look at each view (All, Active, Completed). Leave the page open.
|
|
15
|
+
|
|
16
|
+
## Report
|
|
17
|
+
|
|
18
|
+
Reply with one short paragraph: what you started, what you clicked, and what works and what does not. Change code only
|
|
19
|
+
if you are asked to.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Daylight
|
|
2
|
+
|
|
3
|
+
A small task list: `server.mjs` (a Node.js server with a JSON API) and `index.html`. No dependencies.
|
|
4
|
+
|
|
5
|
+
## Run it
|
|
6
|
+
|
|
7
|
+
- `npm run dev` serves it on port 3000 and restarts on save. It watches files with inotifywait from inotify-tools, a
|
|
8
|
+
system package: install it with `sudo apt-get install -y inotify-tools` if it is missing.
|
|
9
|
+
- Open `http://localhost:3000` in the browser on your desktop (`DISPLAY=:1 setsid -f chromium http://localhost:3000`)
|
|
10
|
+
and use the computer tool to see and click it.
|
|
11
|
+
|
|
12
|
+
## Test it
|
|
13
|
+
|
|
14
|
+
Use it like a person: add a task, tick it off, and look at each view (All, Active, Completed). Leave the page open.
|
|
15
|
+
|
|
16
|
+
## Report
|
|
17
|
+
|
|
18
|
+
Reply with one short paragraph: what you started, what you clicked, and what works and what does not. Change code only
|
|
19
|
+
if you are asked to.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Daylight
|
|
2
|
+
|
|
3
|
+
A small full-stack task list: a Node.js server with a JSON API and one HTML page. No dependencies.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm run dev # serves on :3000 and reloads on save; needs inotify-tools (sudo apt-get install -y inotify-tools)
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Open `http://localhost:3000`. Tasks live in memory while the server runs. Set `PORT` to use another port.
|
|
10
|
+
|
|
11
|
+
Shardflux uses Daylight for its first-run demo, "Daylight (sample)": an agent installs what it needs, starts it and
|
|
12
|
+
tests it in the browser on its own computer. `CLAUDE.md` and `AGENTS.md` tell Claude Code and Codex how.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Dev server: runs server.mjs and restarts it when a source file is saved. Watches with inotifywait (inotify-tools).
|
|
2
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
3
|
+
|
|
4
|
+
if (spawnSync('inotifywait', ['--help'], { stdio: 'ignore' }).error) {
|
|
5
|
+
console.error('npm run dev watches files with inotifywait. Install it with: sudo apt-get install -y inotify-tools');
|
|
6
|
+
process.exit(1);
|
|
7
|
+
}
|
|
8
|
+
let server;
|
|
9
|
+
const start = () => { server = spawn(process.execPath, ['server.mjs'], { stdio: 'inherit' }); };
|
|
10
|
+
const restart = () => { console.log('Saved. Restarting Daylight…'); server.once('exit', start); server.kill(); };
|
|
11
|
+
start();
|
|
12
|
+
const watcher = spawn('inotifywait', ['-m', '-q', '-e', 'close_write,moved_to', '--format', '%f', '.'], { stdio: ['ignore', 'pipe', 'inherit'] });
|
|
13
|
+
let timer;
|
|
14
|
+
watcher.stdout.on('data', chunk => {
|
|
15
|
+
if (!/\.(mjs|html)\b/.test(String(chunk))) return;
|
|
16
|
+
clearTimeout(timer); timer = setTimeout(restart, 150);
|
|
17
|
+
});
|
|
18
|
+
for (const signal of ['SIGINT', 'SIGTERM']) process.on(signal, () => { watcher.kill(); server.kill(); process.exit(0); });
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<meta charset="utf-8">
|
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
5
|
+
<title>Daylight · One good day</title>
|
|
6
|
+
<style>
|
|
7
|
+
:root{color-scheme:light;--paper:#f5f2ea;--ink:#282b27;--muted:#747970;--line:#deded3;--green:#3f614e;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",sans-serif}*{box-sizing:border-box}body{margin:0;background:var(--paper);color:var(--ink)}main{max-width:660px;margin:12vh auto;padding:24px}header{display:flex;align-items:center;justify-content:space-between}.wordmark{font-size:15px;font-weight:600;letter-spacing:-.2px}.dot{width:28px;height:28px;border:1px solid var(--green);border-radius:50%;display:inline-block;margin-right:12px;vertical-align:middle}.edition{font-size:12px;color:var(--muted)}h1{font-size:48px;line-height:1.05;font-weight:650;letter-spacing:-1.8px;margin:52px 0 14px}p{color:var(--muted);line-height:1.6}form{display:flex;gap:10px;margin:34px 0 32px}input[type=text]{flex:1;min-width:0;border:1px solid var(--line);border-radius:8px;padding:14px;background:#fffdf8;color:var(--ink);font:inherit}button{cursor:pointer;font:inherit;border:0;border-radius:7px;padding:11px 16px;background:var(--green);color:white}nav{display:flex;gap:6px;border-bottom:1px solid var(--line);padding-bottom:14px}nav button{background:transparent;color:var(--muted);font-size:13px;padding:8px 13px}nav button[aria-pressed=true]{background:#e1e7dc;color:var(--green)}ul{list-style:none;margin:0;padding:0;min-height:170px}li{display:flex;align-items:center;gap:14px;padding:21px 2px;border-bottom:1px solid var(--line)}li input{accent-color:var(--green);width:20px;height:20px;cursor:pointer}li label{flex:1}li.done label{text-decoration:line-through;color:var(--muted)}footer{display:flex;justify-content:space-between;font-size:12px;color:var(--muted);margin-top:24px}.empty{padding:35px 0;color:var(--muted)}#error{color:#a13b32;min-height:18px;font-size:13px}button:focus-visible,input:focus-visible{outline:2px solid var(--green);outline-offset:3px}@media(max-width:600px){main{margin:5vh auto}h1{font-size:44px}.edition{display:none}}
|
|
8
|
+
</style>
|
|
9
|
+
<main>
|
|
10
|
+
<header><span class="wordmark"><span class="dot"></span>Daylight</span><span class="edition">A little room to get things done.</span></header>
|
|
11
|
+
<h1>One thing at a time.</h1>
|
|
12
|
+
<p>A short list. A clear head. Make room for a good day.</p>
|
|
13
|
+
<form id="add"><input type="text" id="title" aria-label="New task" placeholder="What would you like to do?" maxlength="100" required><button>Add task</button></form>
|
|
14
|
+
<nav aria-label="Task filters"><button type="button" data-view="all" aria-pressed="true">All</button><button type="button" data-view="active" aria-pressed="false">Active</button><button type="button" data-view="done" aria-pressed="false">Completed</button></nav>
|
|
15
|
+
<ul id="tasks" aria-live="polite"></ul><div id="error" role="alert"></div>
|
|
16
|
+
<footer><span id="remaining"></span><span>Small steps count.</span></footer>
|
|
17
|
+
</main>
|
|
18
|
+
<script type="module">
|
|
19
|
+
let view='all';const list=document.querySelector('#tasks'),error=document.querySelector('#error');
|
|
20
|
+
async function api(path,options){const response=await fetch(path,options);if(!response.ok)throw Error(await response.text());return response.json()}
|
|
21
|
+
async function render(){try{const data=await api('/api/tasks?view='+view);list.replaceChildren();for(const task of data.tasks){const row=document.createElement('li');row.classList.toggle('done',task.done);const check=document.createElement('input');check.type='checkbox';check.checked=task.done;check.id='task-'+task.id;check.addEventListener('change',async()=>{try{await api('/api/tasks/'+task.id,{method:'PATCH'});await render()}catch(e){error.textContent=e.message}});const label=document.createElement('label');label.htmlFor=check.id;label.textContent=task.title;row.append(check,label);list.append(row)}if(!data.tasks.length){const empty=document.createElement('li');empty.className='empty';empty.textContent='Nothing here. Enjoy the space.';list.append(empty)}document.querySelector('#remaining').textContent=data.remaining+' left to do';error.textContent=''}catch(e){error.textContent=e.message}}
|
|
22
|
+
document.querySelector('#add').addEventListener('submit',async e=>{e.preventDefault();try{await api('/api/tasks',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({title:document.querySelector('#title').value})});document.querySelector('#title').value='';await render()}catch(e){error.textContent=e.message}});
|
|
23
|
+
for(const button of document.querySelectorAll('[data-view]'))button.addEventListener('click',()=>{view=button.dataset.view;for(const other of document.querySelectorAll('[data-view]'))other.setAttribute('aria-pressed',String(other===button));document.title=view==='all'?'Daylight · One good day':'Daylight · '+button.textContent;render()});render();
|
|
24
|
+
</script>
|
|
25
|
+
</html>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const tasks = [
|
|
6
|
+
{ id: 1, title: 'Sketch the idea', done: true },
|
|
7
|
+
{ id: 2, title: 'Build something useful', done: false },
|
|
8
|
+
{ id: 3, title: 'Take a walk', done: false },
|
|
9
|
+
];
|
|
10
|
+
let nextID = 4;
|
|
11
|
+
const server = createServer(async (req, res) => {
|
|
12
|
+
try {
|
|
13
|
+
const url = new URL(req.url, 'http://localhost');
|
|
14
|
+
if (url.pathname === '/api/tasks' && req.method === 'GET') {
|
|
15
|
+
const view = url.searchParams.get('view') || 'all';
|
|
16
|
+
const visible = view === 'active' ? tasks.filter(t => !t.done) : view === 'done' ? tasks.filter(t => !t.done) : tasks;
|
|
17
|
+
res.setHeader('Content-Type', 'application/json');
|
|
18
|
+
return res.end(JSON.stringify({ tasks: visible, remaining: tasks.filter(t => !t.done).length }));
|
|
19
|
+
}
|
|
20
|
+
if (url.pathname === '/api/tasks' && req.method === 'POST') {
|
|
21
|
+
let body = ''; for await (const part of req) { body += part; if (body.length > 4096) throw Error('Too much text'); }
|
|
22
|
+
const title = String(JSON.parse(body).title || '').trim();
|
|
23
|
+
if (!title || title.length > 100) { res.writeHead(400); return res.end('Use 1–100 characters.'); }
|
|
24
|
+
tasks.push({ id: nextID++, title, done: false }); res.writeHead(201); return res.end('{}');
|
|
25
|
+
}
|
|
26
|
+
const match = url.pathname.match(/^\/api\/tasks\/(\d+)$/);
|
|
27
|
+
if (match && req.method === 'PATCH') {
|
|
28
|
+
const task = tasks.find(t => t.id === Number(match[1]));
|
|
29
|
+
if (!task) { res.writeHead(404); return res.end('Task not found'); }
|
|
30
|
+
task.done = !task.done; return res.end('{}');
|
|
31
|
+
}
|
|
32
|
+
if (url.pathname === '/' && req.method === 'GET') {
|
|
33
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
34
|
+
return res.end(await readFile(new URL('./index.html', import.meta.url)));
|
|
35
|
+
}
|
|
36
|
+
res.writeHead(404); res.end('Not found');
|
|
37
|
+
} catch { res.writeHead(400); res.end('Could not read that request.'); }
|
|
38
|
+
});
|
|
39
|
+
const port = Number(process.env.PORT || 3000);
|
|
40
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) server.listen(port, '0.0.0.0', () => console.log(`Daylight is running on http://0.0.0.0:${port}`));
|
|
41
|
+
export { server };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { access, cp, mkdir, mkdtemp } from 'node:fs/promises';
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const execute = promisify(execFile);
|
|
7
|
+
export async function sampleSource() {
|
|
8
|
+
for (const url of [new URL('./onboarding-app/', import.meta.url), new URL('../../../examples/onboarding-app/', import.meta.url)]) {
|
|
9
|
+
try {
|
|
10
|
+
await access(new URL('server.mjs', url));
|
|
11
|
+
return fileURLToPath(url);
|
|
12
|
+
}
|
|
13
|
+
catch { /* source tree or published bundle */ }
|
|
14
|
+
}
|
|
15
|
+
throw new Error('The onboarding app is missing. Reinstall the Shardflux CLI.');
|
|
16
|
+
}
|
|
17
|
+
/** A private, fresh real Git project; never writes into the user's selected repository. */
|
|
18
|
+
export async function prepareOnboardingSample(directory) {
|
|
19
|
+
const root = join(directory, 'onboarding-projects');
|
|
20
|
+
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
21
|
+
// The folder's own name is what the launcher lists: "Daylight (sample)".
|
|
22
|
+
const path = join(await mkdtemp(join(root, 'sample-')), 'Daylight (sample)');
|
|
23
|
+
await cp(await sampleSource(), path, { recursive: true });
|
|
24
|
+
const git = (args) => execute('git', args, { cwd: path, timeout: 10000, env: { ...process.env, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_NOSYSTEM: '1' } });
|
|
25
|
+
await git(['init', '-q', '-b', 'main']);
|
|
26
|
+
await git(['add', '.']);
|
|
27
|
+
await git(['-c', 'user.name=Shardflux', '-c', 'user.email=first-run@shardflux.dev', '-c', 'commit.gpgsign=false', 'commit', '-qm', 'Start Daylight onboarding app']);
|
|
28
|
+
return path;
|
|
29
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import type { Ctx } from './commands.js';
|
|
2
|
+
import type { Values } from './args.js';
|
|
3
|
+
import type { CodexConnectorView, CodexHealth } from './codex-cloud.js';
|
|
4
|
+
/**
|
|
5
|
+
* The first-run demo's task: it shows the agent's own computer at work. The user copies it and pastes it into the
|
|
6
|
+
* launcher themselves; Daylight's own CLAUDE.md and AGENTS.md say how to run it and test it in the desktop browser.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DEMO_PROMPT = "Start Daylight and test it in the browser, then tell me what works.";
|
|
9
|
+
export type AgentApp = 'claude' | 'codex';
|
|
10
|
+
/**
|
|
11
|
+
* Where Claude Code and Codex install their commands in a home folder. Apps opened from the Finder (the menu bar app,
|
|
12
|
+
* and this CLI run by it) get a minimal PATH; the menu bar app adds Homebrew's and /usr/local/bin, and these cover the
|
|
13
|
+
* rest (Claude Code's installer uses ~/.local/bin).
|
|
14
|
+
*/
|
|
15
|
+
export declare function installDirs(home: string): string[];
|
|
16
|
+
/** Claude Code or Codex is on this Mac: its home folder (~/.claude, ~/.codex) or its command on PATH or in an install folder. */
|
|
17
|
+
export declare function foundApps(env: NodeJS.ProcessEnv): Promise<AgentApp[]>;
|
|
18
|
+
/** --apps claude,codex (repeatable or comma separated); unknown names are refused. */
|
|
19
|
+
export declare function parseApps(value: unknown): AgentApp[] | undefined;
|
|
20
|
+
type CredentialRow = {
|
|
21
|
+
app: string;
|
|
22
|
+
status?: string;
|
|
23
|
+
scope?: string;
|
|
24
|
+
access?: {
|
|
25
|
+
verdict: 'usable' | 'refused' | 'unknown';
|
|
26
|
+
checked_at: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
type Action = {
|
|
30
|
+
title: string;
|
|
31
|
+
command?: string[];
|
|
32
|
+
retry?: boolean;
|
|
33
|
+
};
|
|
34
|
+
export interface Connection {
|
|
35
|
+
id: 'account' | AgentApp | 't3';
|
|
36
|
+
/**
|
|
37
|
+
* verified: checked live just now. action: the person does `action`. pending: it is getting there on its own.
|
|
38
|
+
* unknown: ask again. info: nothing to do here yet (T3 Code: after a project is picked, or its provider's own Sign in).
|
|
39
|
+
*/
|
|
40
|
+
state: 'verified' | 'action' | 'pending' | 'unknown' | 'info';
|
|
41
|
+
/** For scripts: the command that connects it later (T3 Code over SSH, once there is a project). */
|
|
42
|
+
next?: string[];
|
|
43
|
+
fact: string;
|
|
44
|
+
action?: Action;
|
|
45
|
+
picked?: boolean;
|
|
46
|
+
found?: boolean;
|
|
47
|
+
/** T3 Code only: optional (never needed to continue), its app (for the icon) and how it connects. */
|
|
48
|
+
optional?: boolean;
|
|
49
|
+
app_path?: string;
|
|
50
|
+
mode?: 'provider' | 'ssh';
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* T3 Code, when it is installed: an ACP-capable build (0.0.46+, nightly) has Shardflux as a provider, which every
|
|
54
|
+
* `shard` run writes into T3's settings, so it is connected with nothing to sign in; stable 0.0.45 connects a project
|
|
55
|
+
* over SSH (`shard agents new --in t3`), and says which environment to add. Null when T3 Code is not installed.
|
|
56
|
+
*/
|
|
57
|
+
export declare function t3Connection(ctx: Ctx, signedIn: boolean, appDirs?: string[], platform?: NodeJS.Platform): Promise<Connection | null>;
|
|
58
|
+
/** Claude: the account login is ready and Claude itself accepts it right now. */
|
|
59
|
+
export declare function claudeConnection(row: CredentialRow | undefined): Omit<Connection, 'id' | 'picked' | 'found'>;
|
|
60
|
+
/** Codex: this Mac's Codex login is copied and the account's Codex remote device (one executor) is connected. */
|
|
61
|
+
export declare function codexConnection(local: boolean, synced: boolean, connector: CodexConnectorView | null, health: CodexHealth): Omit<Connection, 'id' | 'picked' | 'found'>;
|
|
62
|
+
/**
|
|
63
|
+
* The onboarding report, for an agent setting itself up from chat and for the menu bar app. It verifies every
|
|
64
|
+
* connection live: the Shardflux account, Claude (Claude itself accepts the login) and Codex (the login is copied and
|
|
65
|
+
* the account's Codex remote device is connected), for the apps picked (--apps, else saved, else found on this Mac).
|
|
66
|
+
* With --connect it also does what needs no sign-in: copies the Codex login and enables the Codex remote device.
|
|
67
|
+
*/
|
|
68
|
+
export declare function onboardingReport(ctx: Ctx, values: Values): Promise<{
|
|
69
|
+
version: number;
|
|
70
|
+
api_url: string;
|
|
71
|
+
apps: AgentApp[];
|
|
72
|
+
found_apps: AgentApp[];
|
|
73
|
+
connections: Connection[];
|
|
74
|
+
ready: boolean;
|
|
75
|
+
account_error: string | null;
|
|
76
|
+
demo: {
|
|
77
|
+
prompt: string;
|
|
78
|
+
project: string;
|
|
79
|
+
prepare: string[];
|
|
80
|
+
};
|
|
81
|
+
activity: {
|
|
82
|
+
state: "running" | "failed" | "suspended" | "deleted" | "creating" | "starting" | "suspending" | "resuming" | "forking" | "stopping" | "deleting";
|
|
83
|
+
workspace: {
|
|
84
|
+
id: string;
|
|
85
|
+
key: string;
|
|
86
|
+
};
|
|
87
|
+
conversation: {
|
|
88
|
+
id: string;
|
|
89
|
+
state: "archived" | "working" | "needs_you" | "unresolved" | "done";
|
|
90
|
+
summary: string;
|
|
91
|
+
} | null;
|
|
92
|
+
commands: never[];
|
|
93
|
+
processes: never[];
|
|
94
|
+
ports: never[];
|
|
95
|
+
installs: never[];
|
|
96
|
+
screen: null;
|
|
97
|
+
browser: null;
|
|
98
|
+
changes: {
|
|
99
|
+
files: number;
|
|
100
|
+
commits: number;
|
|
101
|
+
};
|
|
102
|
+
} | {
|
|
103
|
+
changes: {
|
|
104
|
+
files: number;
|
|
105
|
+
commits: number;
|
|
106
|
+
};
|
|
107
|
+
observed_at: string;
|
|
108
|
+
screen: {
|
|
109
|
+
width: number;
|
|
110
|
+
height: number;
|
|
111
|
+
} | null;
|
|
112
|
+
browser: BrowserWindow | null;
|
|
113
|
+
state: string;
|
|
114
|
+
workspace: {
|
|
115
|
+
id: string;
|
|
116
|
+
key: string;
|
|
117
|
+
};
|
|
118
|
+
conversation: {
|
|
119
|
+
id: string;
|
|
120
|
+
state: "archived" | "working" | "needs_you" | "unresolved" | "done";
|
|
121
|
+
summary: string;
|
|
122
|
+
} | null;
|
|
123
|
+
commands: string[];
|
|
124
|
+
processes: {
|
|
125
|
+
command: string;
|
|
126
|
+
started_at: string | null;
|
|
127
|
+
}[];
|
|
128
|
+
ports: number[];
|
|
129
|
+
installs: {
|
|
130
|
+
packages: string[];
|
|
131
|
+
sudo: boolean;
|
|
132
|
+
}[];
|
|
133
|
+
} | null;
|
|
134
|
+
steps: ({
|
|
135
|
+
id: string;
|
|
136
|
+
title: string;
|
|
137
|
+
status: string;
|
|
138
|
+
command: string[];
|
|
139
|
+
action?: undefined;
|
|
140
|
+
} | {
|
|
141
|
+
id: string;
|
|
142
|
+
title: string;
|
|
143
|
+
status: string;
|
|
144
|
+
command?: undefined;
|
|
145
|
+
action?: undefined;
|
|
146
|
+
} | {
|
|
147
|
+
id: string;
|
|
148
|
+
title: string;
|
|
149
|
+
status: string;
|
|
150
|
+
action: string;
|
|
151
|
+
command?: undefined;
|
|
152
|
+
})[];
|
|
153
|
+
}>;
|
|
154
|
+
/** The report as text: one line per connection, then what to do next. */
|
|
155
|
+
export declare function onboardingText(report: Awaited<ReturnType<typeof onboardingReport>>): string;
|
|
156
|
+
interface ProcessRow {
|
|
157
|
+
pid: number;
|
|
158
|
+
comm: string;
|
|
159
|
+
cmdline?: string[];
|
|
160
|
+
started_at?: string | null;
|
|
161
|
+
}
|
|
162
|
+
/** Node names its main thread "MainThread" and npm retitles itself "npm run dev": the program is the first argument. */
|
|
163
|
+
export declare function activeCommands(processes: ProcessRow[]): string[];
|
|
164
|
+
/** Commands and listening ports of a running VM. Never wakes a parked computer. */
|
|
165
|
+
export declare function liveActivity(ctx: Ctx, ref: string): Promise<{
|
|
166
|
+
state: "running" | "failed" | "suspended" | "deleted" | "creating" | "starting" | "suspending" | "resuming" | "forking" | "stopping" | "deleting";
|
|
167
|
+
workspace: {
|
|
168
|
+
id: string;
|
|
169
|
+
key: string;
|
|
170
|
+
};
|
|
171
|
+
conversation: {
|
|
172
|
+
id: string;
|
|
173
|
+
state: "archived" | "working" | "needs_you" | "unresolved" | "done";
|
|
174
|
+
summary: string;
|
|
175
|
+
} | null;
|
|
176
|
+
commands: never[];
|
|
177
|
+
processes: never[];
|
|
178
|
+
ports: never[];
|
|
179
|
+
installs: never[];
|
|
180
|
+
screen: null;
|
|
181
|
+
browser: null;
|
|
182
|
+
changes: {
|
|
183
|
+
files: number;
|
|
184
|
+
commits: number;
|
|
185
|
+
};
|
|
186
|
+
} | {
|
|
187
|
+
changes: {
|
|
188
|
+
files: number;
|
|
189
|
+
commits: number;
|
|
190
|
+
};
|
|
191
|
+
observed_at: string;
|
|
192
|
+
screen: {
|
|
193
|
+
width: number;
|
|
194
|
+
height: number;
|
|
195
|
+
} | null;
|
|
196
|
+
browser: BrowserWindow | null;
|
|
197
|
+
state: string;
|
|
198
|
+
workspace: {
|
|
199
|
+
id: string;
|
|
200
|
+
key: string;
|
|
201
|
+
};
|
|
202
|
+
conversation: {
|
|
203
|
+
id: string;
|
|
204
|
+
state: "archived" | "working" | "needs_you" | "unresolved" | "done";
|
|
205
|
+
summary: string;
|
|
206
|
+
} | null;
|
|
207
|
+
commands: string[];
|
|
208
|
+
processes: {
|
|
209
|
+
command: string;
|
|
210
|
+
started_at: string | null;
|
|
211
|
+
}[];
|
|
212
|
+
ports: number[];
|
|
213
|
+
installs: {
|
|
214
|
+
packages: string[];
|
|
215
|
+
sudo: boolean;
|
|
216
|
+
}[];
|
|
217
|
+
}>;
|
|
218
|
+
/** The ranked commands with when each started: a dev server that restarts after a save starts again. */
|
|
219
|
+
export declare function activeProcesses(processes: ProcessRow[]): Array<{
|
|
220
|
+
command: string;
|
|
221
|
+
started_at: string | null;
|
|
222
|
+
}>;
|
|
223
|
+
/**
|
|
224
|
+
* What was installed with sudo since the workspace was created, from apt's own history: the packages each
|
|
225
|
+
* `apt-get install` asked for (not their automatic dependencies). Short installs finish between two process samples;
|
|
226
|
+
* the history keeps them. Entries without Requested-By ran as root, like the image build.
|
|
227
|
+
*/
|
|
228
|
+
export declare function aptInstalls(history: string, since?: string): Array<{
|
|
229
|
+
packages: string[];
|
|
230
|
+
sudo: boolean;
|
|
231
|
+
}>;
|
|
232
|
+
export declare function gitChanges(output: string): {
|
|
233
|
+
files: number;
|
|
234
|
+
commits: number;
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* The desktop's size and its largest visible browser window, so a live view can show the page the agent is testing
|
|
238
|
+
* rather than the whole screen with its panel and clock. Null fields when the desktop or the browser is not open.
|
|
239
|
+
*/
|
|
240
|
+
type BrowserWindow = {
|
|
241
|
+
x: number;
|
|
242
|
+
y: number;
|
|
243
|
+
width: number;
|
|
244
|
+
height: number;
|
|
245
|
+
title: string;
|
|
246
|
+
};
|
|
247
|
+
export declare function desktopWindows(output: string): {
|
|
248
|
+
screen: {
|
|
249
|
+
width: number;
|
|
250
|
+
height: number;
|
|
251
|
+
} | null;
|
|
252
|
+
browser: BrowserWindow | null;
|
|
253
|
+
};
|
|
254
|
+
/** "/usr/local/bin/node server.mjs" reads as "node server.mjs"; long arguments are cut. */
|
|
255
|
+
export declare function commandLine(argv: string[]): string;
|
|
256
|
+
/** Listening TCP ports from `ss -ltnp` or /proc/net/tcp{,6}, without the harness's or the desktop viewer's own ports. */
|
|
257
|
+
export declare function listeningPorts(output: string): number[];
|
|
258
|
+
export {};
|