@ravefox/space 0.0.0-stage → 0.2.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/LICENSE +203 -0
- package/NOTICE +44 -0
- package/README.md +49 -2
- package/assets/webhook-sdk/python/bot.py +63 -0
- package/assets/webhook-sdk/python/commonly.py +232 -0
- package/node_modules/@commonlyai/seats/README.md +25 -0
- package/node_modules/@commonlyai/seats/package.json +22 -0
- package/node_modules/@commonlyai/seats/src/adapters/hermes.js +264 -0
- package/node_modules/@commonlyai/seats/src/adapters/stub.js +24 -0
- package/node_modules/@commonlyai/seats/src/contract.js +48 -0
- package/node_modules/@commonlyai/seats/src/failures.js +44 -0
- package/node_modules/@commonlyai/seats/src/harness-plan.js +329 -0
- package/node_modules/@commonlyai/seats/src/index.js +22 -0
- package/node_modules/@commonlyai/seats/src/process-group.js +269 -0
- package/node_modules/@commonlyai/seats/src/record/format.js +737 -0
- package/node_modules/@commonlyai/seats/src/record/otel.js +238 -0
- package/node_modules/@commonlyai/seats/src/runtime-observation.js +29 -0
- package/node_modules/@commonlyai/seats/src/seat-model.js +17 -0
- package/node_modules/@commonlyai/seats/src/supervisor.js +308 -0
- package/node_modules/@commonlyai/seats/src/trace/events.js +46 -0
- package/node_modules/@commonlyai/seats/src/trace/store.js +276 -0
- package/node_modules/@commonlyai/seats/src/turn-processes.js +226 -0
- package/node_modules/@commonlyai/seats/src/upstream-refusal.js +418 -0
- package/node_modules/@commonlyai/seats/src/valet/env.js +201 -0
- package/node_modules/@commonlyai/seats/src/valet/grant.js +123 -0
- package/node_modules/@commonlyai/seats/src/valet/permissions.js +213 -0
- package/node_modules/@commonlyai/seats/src/wire/provider-proxy.js +529 -0
- package/package.json +56 -4
- package/skills/commonly/SKILL.md +233 -0
- package/src/commands/agent.js +6334 -0
- package/src/commands/daemon.js +933 -0
- package/src/commands/dev.js +153 -0
- package/src/commands/doctor.js +141 -0
- package/src/commands/inbox.js +330 -0
- package/src/commands/login.js +194 -0
- package/src/commands/mcp.js +56 -0
- package/src/commands/pod.js +120 -0
- package/src/commands/task.js +279 -0
- package/src/commands/verify.js +48 -0
- package/src/index.js +99 -0
- package/src/lib/adapter-login.js +76 -0
- package/src/lib/adapters/broker-bridge-entry.js +76 -0
- package/src/lib/adapters/claude.js +2207 -0
- package/src/lib/adapters/codex-broker-bridge.mjs +298 -0
- package/src/lib/adapters/codex.js +1898 -0
- package/src/lib/adapters/dsh.js +513 -0
- package/src/lib/adapters/goose.js +510 -0
- package/src/lib/adapters/hermes.js +18 -0
- package/src/lib/adapters/index.js +68 -0
- package/src/lib/adapters/opencode.js +1681 -0
- package/src/lib/adapters/pi-commonly-mcp.mjs +71 -0
- package/src/lib/adapters/pi-mcp-client.mjs +617 -0
- package/src/lib/adapters/pi-shell-env.mjs +41 -0
- package/src/lib/adapters/pi.js +1025 -0
- package/src/lib/adapters/qwen.js +523 -0
- package/src/lib/adapters/seat-model.js +8 -0
- package/src/lib/adapters/stub.js +1 -0
- package/src/lib/adapters/tested-version.js +106 -0
- package/src/lib/api.js +205 -0
- package/src/lib/claim-outcome.js +128 -0
- package/src/lib/claimed-work-gate.js +73 -0
- package/src/lib/cli-hint.js +33 -0
- package/src/lib/commit-attribution.js +391 -0
- package/src/lib/config.js +155 -0
- package/src/lib/content-refusal.js +94 -0
- package/src/lib/credential-file.js +208 -0
- package/src/lib/daemon-agent-states.js +95 -0
- package/src/lib/daemon-hint.js +18 -0
- package/src/lib/daemon-logs.js +43 -0
- package/src/lib/daemon-service-windows.js +263 -0
- package/src/lib/daemon-service.js +486 -0
- package/src/lib/daemon-state.js +84 -0
- package/src/lib/daemon-store.js +119 -0
- package/src/lib/daemon-supervisor.js +629 -0
- package/src/lib/daemon-watch.js +115 -0
- package/src/lib/declared-mcp-guard.js +284 -0
- package/src/lib/default-environment.js +207 -0
- package/src/lib/device-login.js +126 -0
- package/src/lib/digest-frame.js +151 -0
- package/src/lib/doctor.js +291 -0
- package/src/lib/egress-proxy.js +160 -0
- package/src/lib/enforcement.js +946 -0
- package/src/lib/environment.js +681 -0
- package/src/lib/first-output-watchdog.js +191 -0
- package/src/lib/git-readonly-server.js +138 -0
- package/src/lib/git-readonly.js +463 -0
- package/src/lib/git-remote-commonly.mjs +100 -0
- package/src/lib/grant-broker-guard.js +345 -0
- package/src/lib/hooks-config.js +238 -0
- package/src/lib/host-gh-broker.js +152 -0
- package/src/lib/host-gh-shim.mjs +98 -0
- package/src/lib/inbox.js +483 -0
- package/src/lib/instance-proxy.js +76 -0
- package/src/lib/machine-host.js +29 -0
- package/src/lib/mcp-credential-delivery.js +121 -0
- package/src/lib/mcp-first-run.js +50 -0
- package/src/lib/mcp-home.js +644 -0
- package/src/lib/mcp-ready.js +68 -0
- package/src/lib/mcp-server-version.js +123 -0
- package/src/lib/mcp-token.js +36 -0
- package/src/lib/mcp-warm-child.mjs +412 -0
- package/src/lib/memory-bridge.js +191 -0
- package/src/lib/memory-import.js +156 -0
- package/src/lib/missing-harness.js +31 -0
- package/src/lib/model-unavailable.js +154 -0
- package/src/lib/native-runtime-inventory.js +40 -0
- package/src/lib/outsider-grant-mcp.js +82 -0
- package/src/lib/own-key-launch.js +42 -0
- package/src/lib/pod-catch-up.js +55 -0
- package/src/lib/pod-focus.js +224 -0
- package/src/lib/pod-tail.js +68 -0
- package/src/lib/poll-liveness.js +103 -0
- package/src/lib/poll-retry.js +81 -0
- package/src/lib/poller.js +200 -0
- package/src/lib/provider-proxy.js +42 -0
- package/src/lib/public-seat-mcp-flag.js +36 -0
- package/src/lib/public-tool-error.js +60 -0
- package/src/lib/public-withheld-tools.js +48 -0
- package/src/lib/reply-receipt.js +55 -0
- package/src/lib/resolve-on-path.js +22 -0
- package/src/lib/runtime-inventory.js +56 -0
- package/src/lib/runtime-observation.js +37 -0
- package/src/lib/sandbox/bwrap.js +275 -0
- package/src/lib/sandbox/codex-perimeter.js +16 -0
- package/src/lib/sandbox/host-check.js +268 -0
- package/src/lib/sandbox/mode.js +49 -0
- package/src/lib/sandbox/opencode-loopback.mjs +68 -0
- package/src/lib/sandbox/seatbelt.js +684 -0
- package/src/lib/sandbox/turn-dir.js +56 -0
- package/src/lib/sandbox/windows-unsandboxed.js +183 -0
- package/src/lib/sandbox/workspace-git-config.js +243 -0
- package/src/lib/sandbox/workspace-pin.js +158 -0
- package/src/lib/seat-runtime-controls.js +61 -0
- package/src/lib/seat-stop.js +97 -0
- package/src/lib/server-skills.js +32 -0
- package/src/lib/session-recycle.js +219 -0
- package/src/lib/session-store.js +215 -0
- package/src/lib/skill-prompt.js +74 -0
- package/src/lib/skills-import.js +113 -0
- package/src/lib/spawn-credential.js +402 -0
- package/src/lib/spawn-retry.js +311 -0
- package/src/lib/stable-install.js +176 -0
- package/src/lib/supervised-turn.js +352 -0
- package/src/lib/task.js +235 -0
- package/src/lib/turn-failure-notice.js +177 -0
- package/src/lib/unconfined-seat-guard.js +94 -0
- package/src/lib/update-check.js +192 -0
- package/src/lib/upstream-refusal.js +1 -0
- package/src/lib/webhook-server.js +109 -0
- package/src/lib/windows-command.js +393 -0
- package/src/lib/workspace-git.js +87 -0
- package/src/lib/workspace-trust.js +162 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A per-spawn loopback CONNECT proxy that reaches only a fixed list of hosts.
|
|
3
|
+
*
|
|
4
|
+
* A confined run gets no outbound network of its own (Seatbelt `network:
|
|
5
|
+
* 'loopback'`); HTTPS_PROXY points it here. Only CONNECT to an allowlisted
|
|
6
|
+
* name on an allowlisted port is opened, and the name is resolved here and
|
|
7
|
+
* the connection made to the address resolved, so a name that resolves to
|
|
8
|
+
* loopback or a private network is refused rather than followed. Plain HTTP
|
|
9
|
+
* requests are refused: the proxy never sees the content of what it carries.
|
|
10
|
+
*
|
|
11
|
+
* There is no proxy credential. Everything the proxy reaches, any process on
|
|
12
|
+
* the host already reaches; it gives the confined run a route, not authority.
|
|
13
|
+
*/
|
|
14
|
+
import { createServer } from 'http';
|
|
15
|
+
import { BlockList, connect, isIP } from 'net';
|
|
16
|
+
import { lookup } from 'dns/promises';
|
|
17
|
+
|
|
18
|
+
// GitHub's download hosts only. github.com and api.github.com accept writes
|
|
19
|
+
// (git push, gists, issues) made with any token a run is handed, and a tunnel
|
|
20
|
+
// cannot tell a read from a write, so they would be an outbound route for
|
|
21
|
+
// anything the run can read. These hosts only serve content.
|
|
22
|
+
export const GITHUB_CONTENT_HOSTS = Object.freeze([
|
|
23
|
+
'codeload.github.com',
|
|
24
|
+
'raw.githubusercontent.com',
|
|
25
|
+
'objects.githubusercontent.com',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const CONNECT_TIMEOUT_MS = 10_000;
|
|
29
|
+
const MAX_REFUSALS_KEPT = 20;
|
|
30
|
+
|
|
31
|
+
const nonPublic = new BlockList();
|
|
32
|
+
for (const [address, prefix] of [
|
|
33
|
+
['0.0.0.0', 8], ['10.0.0.0', 8], ['100.64.0.0', 10], ['127.0.0.0', 8],
|
|
34
|
+
['169.254.0.0', 16], ['172.16.0.0', 12], ['192.0.0.0', 24], ['192.168.0.0', 16],
|
|
35
|
+
['198.18.0.0', 15], ['224.0.0.0', 4], ['240.0.0.0', 4],
|
|
36
|
+
]) nonPublic.addSubnet(address, prefix, 'ipv4');
|
|
37
|
+
for (const [address, prefix] of [
|
|
38
|
+
['::', 128], ['::1', 128], ['fc00::', 7], ['fe80::', 10], ['ff00::', 8], ['64:ff9b::', 96],
|
|
39
|
+
]) nonPublic.addSubnet(address, prefix, 'ipv6');
|
|
40
|
+
|
|
41
|
+
/** True for an address no public host name should resolve to. */
|
|
42
|
+
export const isNonPublicAddress = (address) => {
|
|
43
|
+
const mapped = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i.exec(address);
|
|
44
|
+
if (mapped) return nonPublic.check(mapped[1], 'ipv4');
|
|
45
|
+
const family = isIP(address);
|
|
46
|
+
if (!family) return true;
|
|
47
|
+
return nonPublic.check(address, family === 6 ? 'ipv6' : 'ipv4');
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** `host:port` from a CONNECT target, or null when it is not one. */
|
|
51
|
+
const parseTarget = (target) => {
|
|
52
|
+
const match = /^([A-Za-z0-9.-]+):(\d{1,5})$/.exec(target || '');
|
|
53
|
+
if (!match) return null;
|
|
54
|
+
const port = Number(match[2]);
|
|
55
|
+
if (port < 1 || port > 65535) return null;
|
|
56
|
+
return { host: match[1].toLowerCase().replace(/\.$/, ''), port };
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* startEgressProxy({ allowHosts?, allowPorts? }) → { port, url, refusals, close }
|
|
61
|
+
*
|
|
62
|
+
* Listens on 127.0.0.1 at an ephemeral port. `_resolve` and `_allowPrivate`
|
|
63
|
+
* exist for tests only, so a test can reach a loopback upstream.
|
|
64
|
+
*/
|
|
65
|
+
export const startEgressProxy = async ({
|
|
66
|
+
allowHosts = GITHUB_CONTENT_HOSTS,
|
|
67
|
+
allowPorts = [443],
|
|
68
|
+
_resolve = (host) => lookup(host, { all: true, verbatim: true }),
|
|
69
|
+
_allowPrivate = false,
|
|
70
|
+
} = {}) => {
|
|
71
|
+
const hosts = new Set(allowHosts.map((host) => String(host).toLowerCase()));
|
|
72
|
+
const ports = new Set(allowPorts);
|
|
73
|
+
if (!hosts.size || [...ports].some((port) => !Number.isInteger(port) || port < 1 || port > 65535)) {
|
|
74
|
+
throw new Error('Egress proxy needs at least one host and valid TCP ports');
|
|
75
|
+
}
|
|
76
|
+
const refusals = [];
|
|
77
|
+
let opened = 0;
|
|
78
|
+
const refuse = (socket, status, target, reason) => {
|
|
79
|
+
refusals.push({ target, reason });
|
|
80
|
+
if (refusals.length > MAX_REFUSALS_KEPT) refusals.shift();
|
|
81
|
+
if (!socket.destroyed) socket.end(`HTTP/1.1 ${status}\r\nConnection: close\r\nContent-Length: 0\r\n\r\n`);
|
|
82
|
+
};
|
|
83
|
+
const sockets = new Set();
|
|
84
|
+
const track = (socket) => {
|
|
85
|
+
sockets.add(socket);
|
|
86
|
+
socket.on('close', () => sockets.delete(socket));
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const server = createServer((incoming, outgoing) => {
|
|
90
|
+
incoming.resume();
|
|
91
|
+
refusals.push({ target: String(incoming.url || '').slice(0, 200), reason: 'not CONNECT' });
|
|
92
|
+
if (refusals.length > MAX_REFUSALS_KEPT) refusals.shift();
|
|
93
|
+
outgoing.writeHead(405, { Connection: 'close' }).end();
|
|
94
|
+
});
|
|
95
|
+
server.on('connection', track);
|
|
96
|
+
server.on('connect', async (request, client, head) => {
|
|
97
|
+
client.on('error', () => {});
|
|
98
|
+
const target = parseTarget(request.url);
|
|
99
|
+
const label = String(request.url || '').slice(0, 200);
|
|
100
|
+
if (!target) { refuse(client, '400 Bad Request', label, 'malformed target'); return; }
|
|
101
|
+
if (isIP(target.host)) { refuse(client, '403 Forbidden', label, 'address literal'); return; }
|
|
102
|
+
if (!hosts.has(target.host) || !ports.has(target.port)) {
|
|
103
|
+
refuse(client, '403 Forbidden', label, 'not allowlisted');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
let addresses;
|
|
107
|
+
try {
|
|
108
|
+
addresses = (await _resolve(target.host)).map((entry) => entry.address);
|
|
109
|
+
} catch {
|
|
110
|
+
refuse(client, '502 Bad Gateway', label, 'did not resolve');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Every address must be public, not just the one used: a name that
|
|
114
|
+
// resolves to both is not one this proxy was asked to reach.
|
|
115
|
+
if (!addresses.length || (!_allowPrivate && addresses.some(isNonPublicAddress))) {
|
|
116
|
+
refuse(client, '403 Forbidden', label, 'resolves to a non-public address');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (client.destroyed) return;
|
|
120
|
+
const upstream = connect({ host: addresses[0], port: target.port });
|
|
121
|
+
track(upstream);
|
|
122
|
+
let established = false;
|
|
123
|
+
client.on('close', () => upstream.destroy());
|
|
124
|
+
upstream.setTimeout(CONNECT_TIMEOUT_MS, () => upstream.destroy());
|
|
125
|
+
upstream.on('error', () => {});
|
|
126
|
+
upstream.on('close', () => {
|
|
127
|
+
if (established) client.destroy();
|
|
128
|
+
else refuse(client, '502 Bad Gateway', label, 'upstream unreachable');
|
|
129
|
+
});
|
|
130
|
+
upstream.once('connect', () => {
|
|
131
|
+
upstream.setTimeout(0);
|
|
132
|
+
established = true;
|
|
133
|
+
opened += 1;
|
|
134
|
+
client.write('HTTP/1.1 200 Connection Established\r\n\r\n');
|
|
135
|
+
if (head?.length) upstream.write(head);
|
|
136
|
+
upstream.pipe(client);
|
|
137
|
+
client.pipe(upstream);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await new Promise((resolve, reject) => {
|
|
142
|
+
server.once('error', reject);
|
|
143
|
+
server.listen(0, '127.0.0.1', () => {
|
|
144
|
+
server.removeListener('error', reject);
|
|
145
|
+
resolve();
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
const { port } = server.address();
|
|
149
|
+
return {
|
|
150
|
+
port,
|
|
151
|
+
url: `http://127.0.0.1:${port}`,
|
|
152
|
+
/** { opened, refusals: [{ target, reason }] } — the last 20 refusals. */
|
|
153
|
+
stats: () => ({ opened, refusals: refusals.map((entry) => ({ ...entry })) }),
|
|
154
|
+
close: async () => {
|
|
155
|
+
const closed = new Promise((resolve) => server.close(resolve));
|
|
156
|
+
for (const socket of sockets) socket.destroy();
|
|
157
|
+
await closed;
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
};
|