fauna-api 0.0.0-stage → 1.0.676
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/brands.d.ts +2 -0
- package/dist/brands.js +2 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +121 -0
- package/dist/src/ant/chat.d.ts +130 -0
- package/dist/src/ant/chat.js +467 -0
- package/dist/src/ant/format.d.ts +40 -0
- package/dist/src/ant/format.js +143 -0
- package/dist/src/ant/graph.d.ts +130 -0
- package/dist/src/ant/graph.js +449 -0
- package/dist/src/ant/index.d.ts +16 -0
- package/dist/src/ant/index.js +61 -0
- package/dist/src/ant/persona.d.ts +151 -0
- package/dist/src/ant/persona.js +136 -0
- package/dist/src/ant/timeline.d.ts +283 -0
- package/dist/src/ant/timeline.js +1025 -0
- package/dist/src/authentification.d.ts +45 -0
- package/dist/src/authentification.js +194 -0
- package/dist/src/billing.d.ts +135 -0
- package/dist/src/billing.js +128 -0
- package/dist/src/calendar.d.ts +83 -0
- package/dist/src/calendar.js +133 -0
- package/dist/src/chats.d.ts +113 -0
- package/dist/src/chats.js +100 -0
- package/dist/src/cicada/contacts.d.ts +31 -0
- package/dist/src/cicada/contacts.js +28 -0
- package/dist/src/cicada/conversations.d.ts +73 -0
- package/dist/src/cicada/conversations.js +236 -0
- package/dist/src/cicada/inboxes.d.ts +48 -0
- package/dist/src/cicada/inboxes.js +82 -0
- package/dist/src/cicada/index.d.ts +16 -0
- package/dist/src/cicada/index.js +61 -0
- package/dist/src/cicada/phone.d.ts +48 -0
- package/dist/src/cicada/phone.js +306 -0
- package/dist/src/drive.d.ts +80 -0
- package/dist/src/drive.js +472 -0
- package/dist/src/extensions/index.d.ts +4 -0
- package/dist/src/extensions/index.js +5 -0
- package/dist/src/extensions/web/index.d.ts +6 -0
- package/dist/src/extensions/web/index.js +7 -0
- package/dist/src/extensions/web/session.d.ts +106 -0
- package/dist/src/extensions/web/session.js +469 -0
- package/dist/src/extensions/web/tools.d.ts +13 -0
- package/dist/src/extensions/web/tools.js +72 -0
- package/dist/src/friends.d.ts +39 -0
- package/dist/src/friends.js +119 -0
- package/dist/src/groups.d.ts +82 -0
- package/dist/src/groups.js +269 -0
- package/dist/src/inbox.d.ts +11 -0
- package/dist/src/inbox.js +35 -0
- package/dist/src/integrations.d.ts +52 -0
- package/dist/src/integrations.js +205 -0
- package/dist/src/magpie/explorer.d.ts +14 -0
- package/dist/src/magpie/explorer.js +36 -0
- package/dist/src/magpie/index.d.ts +16 -0
- package/dist/src/magpie/index.js +61 -0
- package/dist/src/magpie/portfolio.d.ts +52 -0
- package/dist/src/magpie/portfolio.js +179 -0
- package/dist/src/magpie/quotes.d.ts +35 -0
- package/dist/src/magpie/quotes.js +47 -0
- package/dist/src/magpie/room.d.ts +61 -0
- package/dist/src/magpie/room.js +238 -0
- package/dist/src/maps.d.ts +159 -0
- package/dist/src/maps.js +171 -0
- package/dist/src/marmot/browser.d.ts +139 -0
- package/dist/src/marmot/browser.js +389 -0
- package/dist/src/marmot/index.d.ts +20 -0
- package/dist/src/marmot/index.js +65 -0
- package/dist/src/marmot/media.d.ts +56 -0
- package/dist/src/marmot/media.js +265 -0
- package/dist/src/marmot/research.d.ts +337 -0
- package/dist/src/marmot/research.js +1138 -0
- package/dist/src/marmot/scraper.d.ts +59 -0
- package/dist/src/marmot/scraper.js +94 -0
- package/dist/src/marmot/web.d.ts +402 -0
- package/dist/src/marmot/web.js +2586 -0
- package/dist/src/marmot/workspace.d.ts +81 -0
- package/dist/src/marmot/workspace.js +263 -0
- package/dist/src/parrot/agent.d.ts +53 -0
- package/dist/src/parrot/agent.js +93 -0
- package/dist/src/parrot/chat.d.ts +84 -0
- package/dist/src/parrot/chat.js +237 -0
- package/dist/src/parrot/format.d.ts +128 -0
- package/dist/src/parrot/format.js +639 -0
- package/dist/src/parrot/generative.d.ts +261 -0
- package/dist/src/parrot/generative.js +185 -0
- package/dist/src/parrot/index.d.ts +22 -0
- package/dist/src/parrot/index.js +67 -0
- package/dist/src/parrot/room.d.ts +225 -0
- package/dist/src/parrot/room.js +466 -0
- package/dist/src/parrot/routing.d.ts +90 -0
- package/dist/src/parrot/routing.js +78 -0
- package/dist/src/parrot/share.d.ts +126 -0
- package/dist/src/parrot/share.js +261 -0
- package/dist/src/parrot/socket.d.ts +41 -0
- package/dist/src/parrot/socket.js +478 -0
- package/dist/src/payments.d.ts +73 -0
- package/dist/src/payments.js +107 -0
- package/dist/src/private/cards.css +75 -0
- package/dist/src/rooms.d.ts +76 -0
- package/dist/src/rooms.js +168 -0
- package/dist/src/shared/schema.d.ts +24 -0
- package/dist/src/shared/schema.js +1 -0
- package/dist/src/shared/server.d.ts +65 -0
- package/dist/src/shared/server.js +302 -0
- package/dist/src/termite/environment.d.ts +110 -0
- package/dist/src/termite/environment.js +263 -0
- package/dist/src/termite/index.d.ts +14 -0
- package/dist/src/termite/index.js +59 -0
- package/dist/src/termite/os.d.ts +326 -0
- package/dist/src/termite/os.js +572 -0
- package/dist/src/termite/versioning.d.ts +94 -0
- package/dist/src/termite/versioning.js +217 -0
- package/dist/src/tickets.d.ts +43 -0
- package/dist/src/tickets.js +138 -0
- package/dist/src/travel.d.ts +85 -0
- package/dist/src/travel.js +44 -0
- package/dist/src/ui/authentification.d.ts +24 -0
- package/dist/src/ui/authentification.js +177 -0
- package/dist/src/ui/brands.css +11 -0
- package/dist/src/ui/brands.d.ts +7 -0
- package/dist/src/ui/brands.js +14 -0
- package/dist/src/ui/browser.d.ts +74 -0
- package/dist/src/ui/browser.js +470 -0
- package/dist/src/ui/chat.d.ts +183 -0
- package/dist/src/ui/chat.js +2379 -0
- package/dist/src/ui/chatPanel.d.ts +109 -0
- package/dist/src/ui/chatPanel.js +768 -0
- package/dist/src/ui/chatSequence.d.ts +36 -0
- package/dist/src/ui/chatSequence.js +42 -0
- package/dist/src/ui/crown.d.ts +19 -0
- package/dist/src/ui/crown.js +199 -0
- package/dist/src/ui/cursor.d.ts +73 -0
- package/dist/src/ui/cursor.js +175 -0
- package/dist/src/ui/device.d.ts +33 -0
- package/dist/src/ui/device.js +49 -0
- package/dist/src/ui/drive.d.ts +69 -0
- package/dist/src/ui/drive.js +1243 -0
- package/dist/src/ui/generative.d.ts +39 -0
- package/dist/src/ui/generative.js +313 -0
- package/dist/src/ui/hover.d.ts +6 -0
- package/dist/src/ui/hover.js +210 -0
- package/dist/src/ui/index.d.ts +42 -0
- package/dist/src/ui/index.js +87 -0
- package/dist/src/ui/integrations.d.ts +13 -0
- package/dist/src/ui/integrations.js +175 -0
- package/dist/src/ui/maps.d.ts +87 -0
- package/dist/src/ui/maps.js +547 -0
- package/dist/src/ui/roster.d.ts +32 -0
- package/dist/src/ui/roster.js +163 -0
- package/dist/src/ui/talk.d.ts +12 -0
- package/dist/src/ui/talk.js +560 -0
- package/dist/src/ui/tickets.d.ts +13 -0
- package/dist/src/ui/tickets.js +966 -0
- package/dist/src/ui/toast.d.ts +7 -0
- package/dist/src/ui/toast.js +258 -0
- package/dist/src/ui/tokenText.d.ts +18 -0
- package/dist/src/ui/tokenText.js +96 -0
- package/dist/src/ui/tokenized.d.ts +42 -0
- package/dist/src/ui/tokenized.js +622 -0
- package/dist/src/ui/ui.css +4763 -0
- package/dist/src/ui/view.d.ts +23 -0
- package/dist/src/ui/view.js +584 -0
- package/dist/src/view.d.ts +113 -0
- package/dist/src/view.js +386 -0
- package/dist/src/webhooks.d.ts +66 -0
- package/dist/src/webhooks.js +78 -0
- package/dist/ui.d.ts +1 -0
- package/dist/ui.js +1 -0
- package/package.json +89 -6
- package/README.md +0 -3
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A live machine: one running boot of a Repository. It's a PageRoom whose
|
|
3
|
+
* page is the machine's desktop, one HTML page that is the whole OS, so it
|
|
4
|
+
* streams like any browser room. Window tools, file tools, exec, and stop ride
|
|
5
|
+
* the same socket as `machine-call`.
|
|
6
|
+
*/
|
|
7
|
+
import { PageRoom, parrotTools as browserParrotTools } from "../marmot/browser.js";
|
|
8
|
+
import { Presence, connect as connectServer } from "../shared/server.js";
|
|
9
|
+
import { Termite } from "./index.js";
|
|
10
|
+
export const Window = {
|
|
11
|
+
fromJSON(json) {
|
|
12
|
+
const kind = json.kind === 'web' || json.kind === 'files' || json.kind === 'preview' ? json.kind : 'app';
|
|
13
|
+
return {
|
|
14
|
+
id: Number(json.id),
|
|
15
|
+
kind,
|
|
16
|
+
app: String(json.app ?? kind),
|
|
17
|
+
title: String(json.title ?? ""),
|
|
18
|
+
x: Number(json.x ?? 0),
|
|
19
|
+
y: Number(json.y ?? 0),
|
|
20
|
+
width: Number(json.width ?? 0),
|
|
21
|
+
height: Number(json.height ?? 0),
|
|
22
|
+
focused: Boolean(json.focused),
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
export const FilesState = {
|
|
27
|
+
fromJSON(json) {
|
|
28
|
+
return { path: String(json?.path ?? ""), view: json?.view === 'cozy' ? 'cozy' : 'list' };
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
/** The smallest and largest desktop a machine draws. The largest is the guest's X screen. */
|
|
32
|
+
export const VIEWPORT = {
|
|
33
|
+
min: { width: 640, height: 400 },
|
|
34
|
+
max: { width: 2560, height: 1600 },
|
|
35
|
+
};
|
|
36
|
+
/** A desktop size inside `VIEWPORT`, in whole pixels. */
|
|
37
|
+
export function clampViewport(size) {
|
|
38
|
+
return {
|
|
39
|
+
width: Math.round(Math.min(VIEWPORT.max.width, Math.max(VIEWPORT.min.width, size.width))),
|
|
40
|
+
height: Math.round(Math.min(VIEWPORT.max.height, Math.max(VIEWPORT.min.height, size.height))),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const DESKTOP_TOOLS = ["move", "click", "drag", "scroll", "type"];
|
|
44
|
+
const DESKTOP_KINDS = new Set(DESKTOP_TOOLS);
|
|
45
|
+
const CALL_MS = 60000;
|
|
46
|
+
const EXEC_MS = 120000;
|
|
47
|
+
const STOP_MS = 10 * 60000;
|
|
48
|
+
/**
|
|
49
|
+
* Every machine tool for a model: desktop input, windows, files, and exec.
|
|
50
|
+
* `invoke` runs a tool; `exec` runs a shell command and returns its result as text.
|
|
51
|
+
*/
|
|
52
|
+
export function parrotTools(invoke, exec) {
|
|
53
|
+
const tools = [...browserParrotTools(new Set(DESKTOP_TOOLS), (tool) => invoke(tool))];
|
|
54
|
+
const add = (name, description, properties, required, build) => {
|
|
55
|
+
tools.push({
|
|
56
|
+
function: { name, description, parameters: { type: "OBJECT", properties, ...(required.length ? { required } : {}) } },
|
|
57
|
+
evaluate: async (params) => invoke(build(params ?? {})),
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
const num = (value) => value == null ? undefined : Number(value);
|
|
61
|
+
add("windows", "List every open desktop window with its id, kind, app, title, position, size, and focus.", {}, [], () => ({ kind: 'windows' }));
|
|
62
|
+
add("focus", "Bring a window to the front and give it keyboard focus.", {
|
|
63
|
+
window: { type: "NUMBER", description: "Window id" },
|
|
64
|
+
}, ["window"], (p) => ({ kind: 'focus', window: Number(p.window) }));
|
|
65
|
+
add("place", "Move a window so its top left corner is at desktop coordinates.", {
|
|
66
|
+
window: { type: "NUMBER", description: "Window id" },
|
|
67
|
+
x: { type: "NUMBER", description: "Desktop X pixel" },
|
|
68
|
+
y: { type: "NUMBER", description: "Desktop Y pixel" },
|
|
69
|
+
}, ["window", "x", "y"], (p) => ({ kind: 'place', window: Number(p.window), x: Number(p.x), y: Number(p.y) }));
|
|
70
|
+
add("resize", "Resize a window. Apps redraw at the new size.", {
|
|
71
|
+
window: { type: "NUMBER", description: "Window id" },
|
|
72
|
+
width: { type: "NUMBER", description: "Width in pixels" },
|
|
73
|
+
height: { type: "NUMBER", description: "Height in pixels" },
|
|
74
|
+
}, ["window", "width", "height"], (p) => ({ kind: 'resize', window: Number(p.window), width: Number(p.width), height: Number(p.height) }));
|
|
75
|
+
add("close", "Close a window.", {
|
|
76
|
+
window: { type: "NUMBER", description: "Window id" },
|
|
77
|
+
}, ["window"], (p) => ({ kind: 'close', window: Number(p.window) }));
|
|
78
|
+
add("reload", "Reload a Web window after its page's files change, so it shows them. Previews follow their file on their own, and reload too.", {
|
|
79
|
+
window: { type: "NUMBER", description: "Window id" },
|
|
80
|
+
}, ["window"], (p) => ({ kind: 'reload', window: Number(p.window) }));
|
|
81
|
+
add("open", "Open a window on the desktop: web opens a Web window at url (http or https; serve local pages over HTTP first, since file:// pages don't load); files opens a Files window at a folder; file previews a file (images, PDFs, video, audio, text); terminal opens a terminal, running command in view; when it ends the window stays open at a prompt, so people can read the result and you can type more. A preview follows its file as it changes.", {
|
|
82
|
+
app: { type: "STRING", enum: ["web", "files", "file", "terminal"], description: "web, files, file or terminal" },
|
|
83
|
+
url: { type: "STRING", description: "http or https address for a web window" },
|
|
84
|
+
path: { type: "STRING", description: "Folder for files, or the file to preview, relative to /repository" },
|
|
85
|
+
command: { type: "STRING", description: "Command for a terminal to run in view" },
|
|
86
|
+
}, ["app"], (p) => ({
|
|
87
|
+
kind: 'open',
|
|
88
|
+
app: p.app === 'files' || p.app === 'file' || p.app === 'terminal' ? p.app : 'web',
|
|
89
|
+
...(p.url != null ? { url: String(p.url) } : {}),
|
|
90
|
+
...(p.path != null ? { path: String(p.path) } : {}),
|
|
91
|
+
...(p.command != null ? { command: String(p.command) } : {}),
|
|
92
|
+
}));
|
|
93
|
+
add("key", "Press a key chord, like ctrl and s together. Focuses the window first when given. ctrl r or f5 on a Web window reloads it.", {
|
|
94
|
+
keys: { type: "ARRAY", items: { type: "STRING" }, description: "Keys held together, e.g. [\"ctrl\", \"s\"]" },
|
|
95
|
+
window: { type: "NUMBER", description: "Window id to focus first" },
|
|
96
|
+
}, ["keys"], (p) => ({ kind: 'key', keys: (Array.isArray(p.keys) ? p.keys : []).map(String), ...(p.window != null ? { window: Number(p.window) } : {}) }));
|
|
97
|
+
add("list", "List files under a folder of /repository. Folders end in a slash. With pattern (like **/*.ts) it matches recursively.", {
|
|
98
|
+
path: { type: "STRING", description: "Folder relative to /repository. Defaults to the root." },
|
|
99
|
+
pattern: { type: "STRING", description: "Glob to match recursively" },
|
|
100
|
+
}, [], (p) => ({ kind: 'list', ...(p.path != null ? { path: String(p.path) } : {}), ...(p.pattern != null ? { pattern: String(p.pattern) } : {}) }));
|
|
101
|
+
add("grep", "Search file contents in /repository with ripgrep. Output is matching files by default, or lines with context, or counts.", {
|
|
102
|
+
pattern: { type: "STRING", description: "Regular expression" },
|
|
103
|
+
path: { type: "STRING", description: "Folder or file to search" },
|
|
104
|
+
glob: { type: "STRING", description: "Only files matching this glob" },
|
|
105
|
+
ignoreCase: { type: "BOOLEAN", description: "Case insensitive" },
|
|
106
|
+
context: { type: "NUMBER", description: "Lines of context around each match" },
|
|
107
|
+
output: { type: "STRING", enum: ["files", "lines", "count"], description: "files, lines, or count" },
|
|
108
|
+
max: { type: "NUMBER", description: "Result cap, default 50" },
|
|
109
|
+
}, ["pattern"], (p) => ({
|
|
110
|
+
kind: 'grep',
|
|
111
|
+
pattern: String(p.pattern ?? ""),
|
|
112
|
+
...(p.path != null ? { path: String(p.path) } : {}),
|
|
113
|
+
...(p.glob != null ? { glob: String(p.glob) } : {}),
|
|
114
|
+
...(p.ignoreCase != null ? { ignoreCase: Boolean(p.ignoreCase) } : {}),
|
|
115
|
+
...(p.context != null ? { context: Number(p.context) } : {}),
|
|
116
|
+
...(p.output != null ? { output: p.output } : {}),
|
|
117
|
+
...(p.max != null ? { max: Number(p.max) } : {}),
|
|
118
|
+
}));
|
|
119
|
+
add("read", "Read numbered lines of a file: from line minus before to line plus after. Defaults to the first 100 lines.", {
|
|
120
|
+
path: { type: "STRING", description: "File relative to /repository" },
|
|
121
|
+
line: { type: "NUMBER", description: "Line to center on, default 1" },
|
|
122
|
+
before: { type: "NUMBER", description: "Lines before, default 0" },
|
|
123
|
+
after: { type: "NUMBER", description: "Lines after, default 100" },
|
|
124
|
+
}, ["path"], (p) => ({ kind: 'read', path: String(p.path ?? ""), line: num(p.line), before: num(p.before), after: num(p.after) }));
|
|
125
|
+
add("write", "Create or overwrite a file, creating missing folders.", {
|
|
126
|
+
path: { type: "STRING", description: "File relative to /repository" },
|
|
127
|
+
text: { type: "STRING", description: "Full file contents" },
|
|
128
|
+
}, ["path", "text"], (p) => ({ kind: 'write', path: String(p.path ?? ""), text: String(p.text ?? "") }));
|
|
129
|
+
add("replace", "Replace an exact string in a file. old must match exactly once unless all is true. Prefer this for edits.", {
|
|
130
|
+
path: { type: "STRING", description: "File relative to /repository" },
|
|
131
|
+
old: { type: "STRING", description: "Exact text to find" },
|
|
132
|
+
new: { type: "STRING", description: "Replacement text" },
|
|
133
|
+
all: { type: "BOOLEAN", description: "Replace every match" },
|
|
134
|
+
}, ["path", "old", "new"], (p) => ({ kind: 'replace', path: String(p.path ?? ""), old: String(p.old ?? ""), new: String(p.new ?? ""), ...(p.all != null ? { all: Boolean(p.all) } : {}) }));
|
|
135
|
+
add("insert", "Insert text after a line number. 0 inserts at the top. Read the file first.", {
|
|
136
|
+
path: { type: "STRING", description: "File relative to /repository" },
|
|
137
|
+
after: { type: "NUMBER", description: "Line to insert after" },
|
|
138
|
+
text: { type: "STRING", description: "Text to insert" },
|
|
139
|
+
}, ["path", "after", "text"], (p) => ({ kind: 'insert', path: String(p.path ?? ""), after: Number(p.after), text: String(p.text ?? "") }));
|
|
140
|
+
add("remove", "Delete lines start through end, inclusive. Read the file first.", {
|
|
141
|
+
path: { type: "STRING", description: "File relative to /repository" },
|
|
142
|
+
start: { type: "NUMBER", description: "First line" },
|
|
143
|
+
end: { type: "NUMBER", description: "Last line" },
|
|
144
|
+
}, ["path", "start", "end"], (p) => ({ kind: 'remove', path: String(p.path ?? ""), start: Number(p.start), end: Number(p.end) }));
|
|
145
|
+
add("processes", "List running processes with their CPU right now, memory, running time, command and listening ports, plus the machine's CPU load, memory and free disk. Default: the 25 busiest by CPU.", {
|
|
146
|
+
sort: { type: "STRING", enum: ["cpu", "memory"], description: "cpu or memory, default cpu" },
|
|
147
|
+
limit: { type: "NUMBER", description: "How many to list, default 25" },
|
|
148
|
+
filter: { type: "STRING", description: "Only commands containing this text" },
|
|
149
|
+
}, [], (p) => ({
|
|
150
|
+
kind: 'processes',
|
|
151
|
+
...(p.sort === 'memory' || p.sort === 'cpu' ? { sort: p.sort } : {}),
|
|
152
|
+
...(p.limit != null ? { limit: Number(p.limit) } : {}),
|
|
153
|
+
...(p.filter != null ? { filter: String(p.filter) } : {}),
|
|
154
|
+
}));
|
|
155
|
+
add("kill", "Stop one of your processes. TERM asks it to exit, KILL forces it. The machine's own processes, including the desktop's browser, can't be stopped.", {
|
|
156
|
+
pid: { type: "NUMBER", description: "Process id from processes" },
|
|
157
|
+
signal: { type: "STRING", enum: ["TERM", "KILL"], description: "TERM (default) or KILL" },
|
|
158
|
+
}, ["pid"], (p) => ({ kind: 'kill', pid: Number(p.pid), ...(p.signal === 'KILL' || p.signal === 'TERM' ? { signal: p.signal } : {}) }));
|
|
159
|
+
tools.push({
|
|
160
|
+
function: {
|
|
161
|
+
name: "exec",
|
|
162
|
+
description: "Run a Bash command as the agent user in /repository. Each call is a new shell: cd, exported variables and activated environments don't carry over, and there's no input or terminal. Output comes back when the command exits, and nothing shows on screen; to let people watch a command run, open a terminal with it instead. Start servers and GUI apps in the background with & (or nohup ... &) so they keep running; a timeout stops everything the command started. GUI apps open as desktop windows.",
|
|
163
|
+
parameters: {
|
|
164
|
+
type: "OBJECT",
|
|
165
|
+
properties: {
|
|
166
|
+
command: { type: "STRING", description: "Bash command" },
|
|
167
|
+
timeoutMs: { type: "NUMBER", description: "Timeout in ms, default 120000; pass more for installs and builds" },
|
|
168
|
+
},
|
|
169
|
+
required: ["command"],
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
evaluate: async (params) => exec(String(params?.command ?? ""), num(params?.timeoutMs)),
|
|
173
|
+
});
|
|
174
|
+
return tools;
|
|
175
|
+
}
|
|
176
|
+
/** `use_desktop`'s result when the person chose not to make a repository for it. */
|
|
177
|
+
export const DESKTOP_DECLINED = "They chose not to use a computer for this. Do it without one if you can.";
|
|
178
|
+
const DESKTOP_CAPTURE = "Everything it captures is attached to the chat for people to see, and comes back to you too. Set video to record the whole job as a captioned video, for when someone asks to see it done. Slower and costlier than the other tools; use it only when the screen matters.";
|
|
179
|
+
/**
|
|
180
|
+
* The `use_desktop` tool for a Parrot chat: a desktop agent does a job on the
|
|
181
|
+
* repository's machine. Without a repository it also takes `name`, what to call
|
|
182
|
+
* the repository the person is asked to make for it.
|
|
183
|
+
*/
|
|
184
|
+
export function desktopTool(repository) {
|
|
185
|
+
const properties = {
|
|
186
|
+
goal: { type: "STRING", description: "What the desktop agent should do, in full, with what to capture" },
|
|
187
|
+
video: { type: "BOOLEAN", description: "Record the whole job as a captioned video" },
|
|
188
|
+
};
|
|
189
|
+
if (repository) {
|
|
190
|
+
return {
|
|
191
|
+
name: "use_desktop",
|
|
192
|
+
description: `Do something on the repository's desktop that needs a screen, like using a website. A desktop agent does it and reports back. ${DESKTOP_CAPTURE}`,
|
|
193
|
+
parameters: { type: "OBJECT", properties, required: ["goal"] },
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
name: "use_desktop",
|
|
198
|
+
description: `Do something on a computer that needs a screen, like using a website. This chat has no computer yet, so the person is asked first to make a repository for it: a Linux machine with a desktop. If they agree, a desktop agent does it and reports back; if they don't, do that request without a computer. ${DESKTOP_CAPTURE}`,
|
|
199
|
+
parameters: {
|
|
200
|
+
type: "OBJECT",
|
|
201
|
+
properties: {
|
|
202
|
+
...properties,
|
|
203
|
+
name: { type: "STRING", description: "What to call the repository this makes: two to four words about the task, like eBay Switch search" },
|
|
204
|
+
},
|
|
205
|
+
required: ["goal", "name"],
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/** Termite's current prices. Anyone can read them, signed in or not. */
|
|
210
|
+
export async function pricing() {
|
|
211
|
+
const response = await fetch(`${Termite.getURL()}/termite/pricing`, { method: "GET" });
|
|
212
|
+
if (!response.ok)
|
|
213
|
+
throw new Error(await response.text());
|
|
214
|
+
const json = await response.json();
|
|
215
|
+
return { machinePerHour: Number(json.machinePerHour), storagePerGbMonth: Number(json.storagePerGbMonth) };
|
|
216
|
+
}
|
|
217
|
+
export const Wake = {
|
|
218
|
+
fromJSON(json) {
|
|
219
|
+
return {
|
|
220
|
+
id: String(json.id),
|
|
221
|
+
reason: json.reason === 'crashed' || json.reason === 'skipped' ? json.reason : 'called',
|
|
222
|
+
prompt: String(json.prompt ?? ''),
|
|
223
|
+
started: new Date(json.started),
|
|
224
|
+
finished: json.finished ? new Date(json.finished) : null,
|
|
225
|
+
ok: Boolean(json.ok),
|
|
226
|
+
answer: String(json.answer ?? ''),
|
|
227
|
+
};
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
/** The times a repository woke its agent, newest first. */
|
|
231
|
+
export async function wakes(authorization, repository, query, opts) {
|
|
232
|
+
const params = new URLSearchParams({ cursor: String(query.cursor), max: String(query.max) });
|
|
233
|
+
const response = await fetch(`${Termite.getURL()}/termite/repositories/${repository}/wakes?${params.toString()}`, {
|
|
234
|
+
method: "GET",
|
|
235
|
+
headers: {
|
|
236
|
+
Authorization: "email" in authorization ? `Token ${authorization.token}` : `ApiKey ${authorization.id}`,
|
|
237
|
+
...(opts?.group ? { Group: opts.group.id } : {}),
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
if (!response.ok)
|
|
241
|
+
throw new Error(await response.text());
|
|
242
|
+
return (await response.json()).map(Wake.fromJSON);
|
|
243
|
+
}
|
|
244
|
+
export class MachineRoom extends PageRoom {
|
|
245
|
+
constructor(ws, authorization, id, hooks, state) {
|
|
246
|
+
super(ws, authorization, id, hooks, {
|
|
247
|
+
bootstrap: state?.bootstrap ?? undefined,
|
|
248
|
+
cursor: state?.cursor ?? undefined,
|
|
249
|
+
enabledTools: DESKTOP_TOOLS,
|
|
250
|
+
control: state?.control,
|
|
251
|
+
}, `${Termite.getURL()}/termite/machine/live`);
|
|
252
|
+
/** The machine id, the number `connect` takes. */
|
|
253
|
+
Object.defineProperty(this, "machine", {
|
|
254
|
+
enumerable: true,
|
|
255
|
+
configurable: true,
|
|
256
|
+
writable: true,
|
|
257
|
+
value: void 0
|
|
258
|
+
});
|
|
259
|
+
Object.defineProperty(this, "repository", {
|
|
260
|
+
enumerable: true,
|
|
261
|
+
configurable: true,
|
|
262
|
+
writable: true,
|
|
263
|
+
value: void 0
|
|
264
|
+
});
|
|
265
|
+
Object.defineProperty(this, "windows", {
|
|
266
|
+
enumerable: true,
|
|
267
|
+
configurable: true,
|
|
268
|
+
writable: true,
|
|
269
|
+
value: new Map()
|
|
270
|
+
});
|
|
271
|
+
/** Each Files window's folder and view, by window id. */
|
|
272
|
+
Object.defineProperty(this, "files", {
|
|
273
|
+
enumerable: true,
|
|
274
|
+
configurable: true,
|
|
275
|
+
writable: true,
|
|
276
|
+
value: new Map()
|
|
277
|
+
});
|
|
278
|
+
Object.defineProperty(this, "status", {
|
|
279
|
+
enumerable: true,
|
|
280
|
+
configurable: true,
|
|
281
|
+
writable: true,
|
|
282
|
+
value: 'BOOTING'
|
|
283
|
+
});
|
|
284
|
+
/** Why the machine failed, when it did. */
|
|
285
|
+
Object.defineProperty(this, "error", {
|
|
286
|
+
enumerable: true,
|
|
287
|
+
configurable: true,
|
|
288
|
+
writable: true,
|
|
289
|
+
value: ''
|
|
290
|
+
});
|
|
291
|
+
/** The repository's name, so a guest who joined by code knows what it is. */
|
|
292
|
+
Object.defineProperty(this, "name", {
|
|
293
|
+
enumerable: true,
|
|
294
|
+
configurable: true,
|
|
295
|
+
writable: true,
|
|
296
|
+
value: ''
|
|
297
|
+
});
|
|
298
|
+
/** Its connections go out from a residential address, billed per GB to whoever pays for the machine. */
|
|
299
|
+
Object.defineProperty(this, "private", {
|
|
300
|
+
enumerable: true,
|
|
301
|
+
configurable: true,
|
|
302
|
+
writable: true,
|
|
303
|
+
value: false
|
|
304
|
+
});
|
|
305
|
+
/** Why private is off when it couldn't turn on or stay on, like a payer out of balance. */
|
|
306
|
+
Object.defineProperty(this, "privateReason", {
|
|
307
|
+
enumerable: true,
|
|
308
|
+
configurable: true,
|
|
309
|
+
writable: true,
|
|
310
|
+
value: ''
|
|
311
|
+
});
|
|
312
|
+
Object.defineProperty(this, "machineHooks", {
|
|
313
|
+
enumerable: true,
|
|
314
|
+
configurable: true,
|
|
315
|
+
writable: true,
|
|
316
|
+
value: void 0
|
|
317
|
+
});
|
|
318
|
+
Object.defineProperty(this, "calls", {
|
|
319
|
+
enumerable: true,
|
|
320
|
+
configurable: true,
|
|
321
|
+
writable: true,
|
|
322
|
+
value: new Map()
|
|
323
|
+
});
|
|
324
|
+
this.machineHooks = hooks;
|
|
325
|
+
this.machine = Number(state?.machine?.id ?? 0);
|
|
326
|
+
this.repository = Number(state?.machine?.repository ?? 0);
|
|
327
|
+
this.hydrate(state);
|
|
328
|
+
}
|
|
329
|
+
/** Apply a connection snapshot `{ machine, bootstrap, cursor, windows }`. */
|
|
330
|
+
hydrate(state) {
|
|
331
|
+
if (state?.machine?.status)
|
|
332
|
+
this.status = state.machine.status;
|
|
333
|
+
if (state?.machine) {
|
|
334
|
+
this.error = String(state.machine.error ?? '');
|
|
335
|
+
this.name = String(state.machine.name ?? '');
|
|
336
|
+
}
|
|
337
|
+
this.applyControl(state?.control);
|
|
338
|
+
if (Array.isArray(state?.windows)) {
|
|
339
|
+
this.windows.clear();
|
|
340
|
+
for (const row of state.windows) {
|
|
341
|
+
const window = Window.fromJSON(row);
|
|
342
|
+
this.windows.set(window.id, window);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (Array.isArray(state?.files)) {
|
|
346
|
+
this.files.clear();
|
|
347
|
+
for (const row of state.files)
|
|
348
|
+
this.files.set(Number(row.window), FilesState.fromJSON(row));
|
|
349
|
+
}
|
|
350
|
+
if (state?.private) {
|
|
351
|
+
this.private = Boolean(state.private.on);
|
|
352
|
+
this.privateReason = String(state.private.reason ?? '');
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
watch(hooks) {
|
|
356
|
+
this.machineHooks = { ...this.machineHooks, ...hooks };
|
|
357
|
+
super.watch(hooks);
|
|
358
|
+
}
|
|
359
|
+
invokeTool(tool) {
|
|
360
|
+
if (DESKTOP_KINDS.has(tool.kind))
|
|
361
|
+
return super.invokeTool(tool);
|
|
362
|
+
return this.call({ kind: 'tool', tool }, CALL_MS).then((result) => String(result ?? ""));
|
|
363
|
+
}
|
|
364
|
+
/** Run a shell command as `agent` in /repository with DISPLAY=:0, so GUI apps open as desktop windows. */
|
|
365
|
+
exec(command, opts) {
|
|
366
|
+
const timeoutMs = opts?.timeoutMs ?? EXEC_MS;
|
|
367
|
+
return this.call({ kind: 'exec', command, timeoutMs }, timeoutMs + 15000).then((result) => ({
|
|
368
|
+
code: Number(result?.code ?? -1),
|
|
369
|
+
stdout: String(result?.stdout ?? ""),
|
|
370
|
+
stderr: String(result?.stderr ?? ""),
|
|
371
|
+
}));
|
|
372
|
+
}
|
|
373
|
+
/** Size the machine's desktop to this seat's screen, clamped to `VIEWPORT`. Only the controller can. */
|
|
374
|
+
async setViewport(width, height) {
|
|
375
|
+
await this.call({ kind: 'viewport', ...clampViewport({ width, height }) }, CALL_MS);
|
|
376
|
+
}
|
|
377
|
+
/** Open a file in an app on the machine, for everyone to see: text in nano inside an
|
|
378
|
+
* xterm, anything else with an installed opener. Only the controller can. */
|
|
379
|
+
async open(path) {
|
|
380
|
+
await this.call({ kind: 'open', path }, CALL_MS);
|
|
381
|
+
}
|
|
382
|
+
/** Put a Files window on a folder and view for every seat. Only the controller can. */
|
|
383
|
+
async setFiles(window, files) {
|
|
384
|
+
await this.call({ kind: 'files', window, path: files.path, view: files.view }, CALL_MS);
|
|
385
|
+
}
|
|
386
|
+
/** Save a new revision and shut down. The room closes afterwards. */
|
|
387
|
+
async stop() {
|
|
388
|
+
try {
|
|
389
|
+
await this.call({ kind: 'stop' }, STOP_MS);
|
|
390
|
+
}
|
|
391
|
+
catch (err) {
|
|
392
|
+
// The machine announces it closed before it answers the stop itself.
|
|
393
|
+
if (this.status !== 'STOPPED')
|
|
394
|
+
throw err;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
call(call, timeoutMs) {
|
|
398
|
+
const id = crypto.randomUUID();
|
|
399
|
+
return new Promise((resolve, reject) => {
|
|
400
|
+
const timer = setTimeout(() => {
|
|
401
|
+
this.calls.delete(id);
|
|
402
|
+
reject(new Error("Machine call timed out."));
|
|
403
|
+
}, timeoutMs);
|
|
404
|
+
this.calls.set(id, {
|
|
405
|
+
resolve: (v) => { clearTimeout(timer); resolve(v); },
|
|
406
|
+
reject: (e) => { clearTimeout(timer); reject(e); },
|
|
407
|
+
});
|
|
408
|
+
this.post({ event: 'machine-call', data: { id, call } });
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
parrotTools() {
|
|
412
|
+
return parrotTools((tool) => this.invokeTool(tool), async (command, timeoutMs) => JSON.stringify(await this.exec(command, { timeoutMs })));
|
|
413
|
+
}
|
|
414
|
+
handleEvent(packet) {
|
|
415
|
+
switch (packet?.event) {
|
|
416
|
+
case 'machine-status': {
|
|
417
|
+
if (packet.data?.status)
|
|
418
|
+
this.status = packet.data.status;
|
|
419
|
+
this.machineHooks.onStatus?.(this);
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
case 'machine-window': {
|
|
423
|
+
if (!packet.data?.window)
|
|
424
|
+
return true;
|
|
425
|
+
const window = Window.fromJSON(packet.data.window);
|
|
426
|
+
this.windows.set(window.id, window);
|
|
427
|
+
this.machineHooks.onWindow?.(this, window);
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
case 'machine-window-closed': {
|
|
431
|
+
const id = Number(packet.data?.id);
|
|
432
|
+
this.windows.delete(id);
|
|
433
|
+
this.files.delete(id);
|
|
434
|
+
this.machineHooks.onWindowClosed?.(this, id);
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
case 'machine-files': {
|
|
438
|
+
const window = Number(packet.data?.window);
|
|
439
|
+
const files = FilesState.fromJSON(packet.data);
|
|
440
|
+
this.files.set(window, files);
|
|
441
|
+
this.machineHooks.onFiles?.(this, window, files);
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
case 'machine-private': {
|
|
445
|
+
this.private = Boolean(packet.data?.on);
|
|
446
|
+
this.privateReason = String(packet.data?.reason ?? '');
|
|
447
|
+
this.machineHooks.onPrivate?.(this);
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
case 'machine-agent-step': {
|
|
451
|
+
if (packet.data)
|
|
452
|
+
this.machineHooks.onAgentStep?.(this, packet.data);
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
case 'machine-result': {
|
|
456
|
+
const pending = this.calls.get(packet.data?.id);
|
|
457
|
+
if (!pending)
|
|
458
|
+
return true;
|
|
459
|
+
this.calls.delete(packet.data.id);
|
|
460
|
+
if (packet.data?.error)
|
|
461
|
+
pending.reject(new Error(String(packet.data.error)));
|
|
462
|
+
else
|
|
463
|
+
pending.resolve(packet.data?.result);
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
case 'machine-closed': {
|
|
467
|
+
if (packet.data?.status)
|
|
468
|
+
this.status = packet.data.status;
|
|
469
|
+
this.error = String(packet.data?.error ?? '');
|
|
470
|
+
this.machineHooks.onStatus?.(this);
|
|
471
|
+
for (const pending of this.calls.values())
|
|
472
|
+
pending.reject(new Error("Machine closed."));
|
|
473
|
+
this.calls.clear();
|
|
474
|
+
return super.handleEvent({ event: 'browser-closed', data: {} });
|
|
475
|
+
}
|
|
476
|
+
default:
|
|
477
|
+
return super.handleEvent(packet);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Boot the repository's machine, or return the one already live, and connect to it.
|
|
482
|
+
* `private` sends its connections out from a residential address, billed per GB to
|
|
483
|
+
* whoever pays for the machine; on a live machine it switches it.
|
|
484
|
+
*/
|
|
485
|
+
static async start(authorization, target, opts) {
|
|
486
|
+
const response = await fetch(`${Termite.getURL()}/termite/repositories/${target.repository}/machine`, {
|
|
487
|
+
method: "POST",
|
|
488
|
+
headers: {
|
|
489
|
+
"Content-Type": "application/json",
|
|
490
|
+
Authorization: "email" in authorization ? `Token ${authorization.token}` : `ApiKey ${authorization.id}`,
|
|
491
|
+
...(opts?.group ? { Group: opts.group.id } : {}),
|
|
492
|
+
},
|
|
493
|
+
body: JSON.stringify(opts?.private === undefined ? {} : { private: opts.private }),
|
|
494
|
+
});
|
|
495
|
+
if (!response.ok)
|
|
496
|
+
throw new Error(await response.text());
|
|
497
|
+
const json = await response.json();
|
|
498
|
+
return MachineRoom.connect(authorization, { id: Number(json.id) }, opts);
|
|
499
|
+
}
|
|
500
|
+
/** Save and shut down a repository's machine without joining its room. For its owner or group members. */
|
|
501
|
+
static async shutdown(authorization, target, opts) {
|
|
502
|
+
const response = await fetch(`${Termite.getURL()}/termite/repositories/${target.repository}/machine/stop`, {
|
|
503
|
+
method: "POST",
|
|
504
|
+
headers: {
|
|
505
|
+
"Content-Type": "application/json",
|
|
506
|
+
Authorization: "email" in authorization ? `Token ${authorization.token}` : `ApiKey ${authorization.id}`,
|
|
507
|
+
...(opts?.group ? { Group: opts.group.id } : {}),
|
|
508
|
+
},
|
|
509
|
+
body: JSON.stringify({}),
|
|
510
|
+
});
|
|
511
|
+
if (!response.ok)
|
|
512
|
+
throw new Error(await response.text());
|
|
513
|
+
}
|
|
514
|
+
/** Turn private on or off for a repository's live machine, for its owner or group members.
|
|
515
|
+
* Resolves to the machine's private state and, when it couldn't turn on, why. */
|
|
516
|
+
static async setPrivate(authorization, target, on, opts) {
|
|
517
|
+
const response = await fetch(`${Termite.getURL()}/termite/repositories/${target.repository}/machine/private`, {
|
|
518
|
+
method: "POST",
|
|
519
|
+
headers: {
|
|
520
|
+
"Content-Type": "application/json",
|
|
521
|
+
Authorization: "email" in authorization ? `Token ${authorization.token}` : `ApiKey ${authorization.id}`,
|
|
522
|
+
...(opts?.group ? { Group: opts.group.id } : {}),
|
|
523
|
+
},
|
|
524
|
+
body: JSON.stringify({ on }),
|
|
525
|
+
});
|
|
526
|
+
if (!response.ok)
|
|
527
|
+
throw new Error(await response.text());
|
|
528
|
+
const json = await response.json();
|
|
529
|
+
return { on: Boolean(json.on), reason: String(json.reason ?? '') };
|
|
530
|
+
}
|
|
531
|
+
/** Connect to a live machine by id, or by its `termite:machine:{id}` reference. */
|
|
532
|
+
static async connect(authorization, target, opts) {
|
|
533
|
+
return MachineRoom.open({
|
|
534
|
+
authorization,
|
|
535
|
+
server: 'id' in target ? `termite:machine:${target.id}` : target.reference,
|
|
536
|
+
...(opts?.group ? { group: opts.group.id } : {}),
|
|
537
|
+
}, opts?.hooks ?? {});
|
|
538
|
+
}
|
|
539
|
+
/** Join a machine someone shared with an access code. */
|
|
540
|
+
static async join(authorization, target, opts) {
|
|
541
|
+
return MachineRoom.open({ authorization, accessCode: target.accessCode }, opts?.hooks ?? {});
|
|
542
|
+
}
|
|
543
|
+
static async open(auth, hooks) {
|
|
544
|
+
let room;
|
|
545
|
+
const self = await connectServer(auth, `${Termite.getURL()}/termite/machine/live`, {
|
|
546
|
+
createRoom: (_endpoint, ws, a, id, state) => {
|
|
547
|
+
room = new MachineRoom(ws, a, id, hooks, state);
|
|
548
|
+
return room;
|
|
549
|
+
},
|
|
550
|
+
createPresence: (r, data) => {
|
|
551
|
+
const presence = new Presence(r, data.id, data.profile);
|
|
552
|
+
presence.update(data);
|
|
553
|
+
return presence;
|
|
554
|
+
},
|
|
555
|
+
onPresence: (r) => r.emitPresence(),
|
|
556
|
+
onConnection: (r, state) => {
|
|
557
|
+
const next = r;
|
|
558
|
+
if (state?.bootstrap)
|
|
559
|
+
next.web.ingestBootstrap(state.bootstrap);
|
|
560
|
+
if (state?.cursor) {
|
|
561
|
+
next.cursor = state.cursor;
|
|
562
|
+
hooks.onCursor?.(next, next.cursor);
|
|
563
|
+
}
|
|
564
|
+
next.hydrate(state);
|
|
565
|
+
},
|
|
566
|
+
onEvent: (r, packet) => r.handleEvent(packet),
|
|
567
|
+
onClosed: (r) => r.emitClosed(),
|
|
568
|
+
});
|
|
569
|
+
room.emitPresence();
|
|
570
|
+
return { room, self };
|
|
571
|
+
}
|
|
572
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { ApiKey, Credentials } from "../authentification.js";
|
|
2
|
+
import { File } from "../drive.js";
|
|
3
|
+
import { Group } from "../groups.js";
|
|
4
|
+
import type { Schema } from "../shared/schema.js";
|
|
5
|
+
import { Termite } from "./index.js";
|
|
6
|
+
import type { MachineStatus } from "./os.js";
|
|
7
|
+
export type Return = {
|
|
8
|
+
code: number;
|
|
9
|
+
description: string;
|
|
10
|
+
body?: Schema;
|
|
11
|
+
};
|
|
12
|
+
export type Route = {
|
|
13
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
14
|
+
path: string;
|
|
15
|
+
description: string;
|
|
16
|
+
deprecated: boolean;
|
|
17
|
+
body?: Schema;
|
|
18
|
+
return: Return[];
|
|
19
|
+
};
|
|
20
|
+
export type Line = {
|
|
21
|
+
path: string;
|
|
22
|
+
index: number;
|
|
23
|
+
kind: 'ADDITION' | 'DELETION';
|
|
24
|
+
};
|
|
25
|
+
export type Change = {
|
|
26
|
+
/** 1, 2, 3 ... within the repository. This is what `rollback` takes. */
|
|
27
|
+
revision: number;
|
|
28
|
+
/** Set when this revision rolled the repository back to an earlier one. */
|
|
29
|
+
rollback?: number;
|
|
30
|
+
/** Git commit sha of /repository, '' when the machine never answered. */
|
|
31
|
+
head: string;
|
|
32
|
+
lines: Line[];
|
|
33
|
+
date: Date;
|
|
34
|
+
};
|
|
35
|
+
export declare const Change: {
|
|
36
|
+
fromJSON(json: any): Change;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* A saved machine: a numbered, principal owned chain of disk revisions on the
|
|
40
|
+
* one Termite base image. `/repository` inside it is a git folder whose history
|
|
41
|
+
* is `changes()`. Start it with `OS.MachineRoom.start`.
|
|
42
|
+
*/
|
|
43
|
+
export declare class Repository {
|
|
44
|
+
readonly id: number;
|
|
45
|
+
readonly name: string;
|
|
46
|
+
readonly sizeBytes: number;
|
|
47
|
+
readonly createdAt: Date;
|
|
48
|
+
readonly updatedAt: Date;
|
|
49
|
+
/** Its machine while one is booting, running, or saving. */
|
|
50
|
+
readonly machine: {
|
|
51
|
+
id: number;
|
|
52
|
+
status: MachineStatus;
|
|
53
|
+
} | null;
|
|
54
|
+
/** The group that owns it, when it isn't personal. */
|
|
55
|
+
readonly group?: Group;
|
|
56
|
+
private readonly authorization;
|
|
57
|
+
constructor(authorization: Credentials | ApiKey, row: {
|
|
58
|
+
id: number;
|
|
59
|
+
name: string;
|
|
60
|
+
sizeBytes: number;
|
|
61
|
+
createdAt: Date;
|
|
62
|
+
updatedAt: Date;
|
|
63
|
+
machine: {
|
|
64
|
+
id: number;
|
|
65
|
+
status: MachineStatus;
|
|
66
|
+
} | null;
|
|
67
|
+
}, group?: Group);
|
|
68
|
+
static fromJSON(json: any, authorization: Credentials | ApiKey, group?: Group): Repository;
|
|
69
|
+
routes(): Route[];
|
|
70
|
+
/** Revisions newest first. `cursor: 0` is the latest revision. */
|
|
71
|
+
changes(query: {
|
|
72
|
+
cursor: number;
|
|
73
|
+
max: number;
|
|
74
|
+
}): Promise<Change[]>;
|
|
75
|
+
/** A folder gives one `File` per entry, a file gives its own `File`. Nothing downloads until asked. */
|
|
76
|
+
open(path?: string): Promise<File | File[]>;
|
|
77
|
+
/** Create or overwrite a file under /repository, running or not. */
|
|
78
|
+
write(path: string, data: Uint8Array | string, onProgress?: (percent: number) => void): Promise<File>;
|
|
79
|
+
/** Record a new revision whose disk is exactly `revision`'s. Refused while a machine is live. */
|
|
80
|
+
rollback(revision: number): Promise<void>;
|
|
81
|
+
/** Rename it. Returns the repository with its new name. */
|
|
82
|
+
rename(name: string): Promise<Repository>;
|
|
83
|
+
delete(): Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
export declare function create(authorization: Credentials | ApiKey, opts?: {
|
|
86
|
+
name?: string;
|
|
87
|
+
group?: Group;
|
|
88
|
+
}): Promise<Repository>;
|
|
89
|
+
export declare function get(authorization: Credentials | ApiKey, id: number, opts?: {
|
|
90
|
+
group?: Group;
|
|
91
|
+
}): Promise<Repository>;
|
|
92
|
+
export declare function list(authorization: Credentials | ApiKey, opts?: {
|
|
93
|
+
group?: Group;
|
|
94
|
+
}): Promise<Repository[]>;
|